or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdfile-system.mdindex.mdrequest-handling.md

file-system.mddocs/

0

# File System Integration

1

2

Pluggable file system abstraction that allows custom implementations for different storage backends, cloud storage, or specialized file handling requirements.

3

4

## Capabilities

5

6

### Methods Override Object

7

8

Custom file system method implementations that override the default Node.js fs operations.

9

10

```javascript { .api }

11

interface Methods {

12

/** Custom lstat implementation for getting file/directory stats */

13

lstat?: (path: string, fromDir?: boolean) => Promise<Stats>;

14

/** Custom realpath implementation for resolving symbolic links */

15

realpath?: (path: string) => Promise<string>;

16

/** Custom read stream creator for file content streaming */

17

createReadStream?: (path: string, options?: object) => ReadableStream;

18

/** Custom directory reader for listing directory contents */

19

readdir?: (path: string) => Promise<string[]>;

20

/** Custom error handler for generating error responses */

21

sendError?: (absolutePath: string, response: ServerResponse, acceptsJSON: boolean, current: string, handlers: object, config: Config, spec: ErrorSpec) => Promise<void>;

22

}

23

```

24

25

### File Statistics

26

27

Custom file stat implementation for getting file and directory information.

28

29

```javascript { .api }

30

/**

31

* Custom lstat implementation for getting file/directory stats

32

* @param path - Absolute path to the file or directory

33

* @param fromDir - Whether the call originated from directory listing (optional)

34

* @returns Promise resolving to file stats object

35

*/

36

lstat?: (path: string, fromDir?: boolean) => Promise<Stats>;

37

38

interface Stats {

39

/** File size in bytes */

40

size: number;

41

/** Last modified time */

42

mtime: Date;

43

/** Whether this is a directory */

44

isDirectory(): boolean;

45

/** Whether this is a symbolic link */

46

isSymbolicLink(): boolean;

47

}

48

```

49

50

**Usage Examples:**

51

52

```javascript

53

const handler = require('serve-handler');

54

55

// Custom lstat for cloud storage

56

const methods = {

57

lstat: async (path, fromDir) => {

58

const cloudFile = await cloudStorage.stat(path);

59

return {

60

size: cloudFile.size,

61

mtime: new Date(cloudFile.lastModified),

62

isDirectory: () => cloudFile.type === 'directory',

63

isSymbolicLink: () => false

64

};

65

}

66

};

67

68

await handler(req, res, {}, methods);

69

```

70

71

### Symbolic Link Resolution

72

73

Custom implementation for resolving symbolic links to their target paths.

74

75

```javascript { .api }

76

/**

77

* Custom realpath implementation for resolving symbolic links

78

* @param path - Path to resolve

79

* @returns Promise resolving to the real path

80

*/

81

realpath?: (path: string) => Promise<string>;

82

```

83

84

**Usage Examples:**

85

86

```javascript

87

const methods = {

88

realpath: async (path) => {

89

// Custom symlink resolution logic

90

return await customResolveSymlink(path);

91

}

92

};

93

```

94

95

### File Content Streaming

96

97

Custom read stream implementation for streaming file content to clients.

98

99

```javascript { .api }

100

/**

101

* Custom read stream creator for file content streaming

102

* @param path - Absolute path to the file

103

* @param options - Stream options including start/end for range requests

104

* @returns Readable stream of file content

105

*/

106

createReadStream?: (path: string, options?: StreamOptions) => ReadableStream;

107

108

interface StreamOptions {

109

/** Start byte position for range requests */

110

start?: number;

111

/** End byte position for range requests */

112

end?: number;

113

}

114

```

115

116

**Usage Examples:**

117

118

```javascript

119

const methods = {

120

createReadStream: (path, options = {}) => {

121

// Custom stream implementation (e.g., from database or cloud storage)

122

return cloudStorage.createReadStream(path, {

123

start: options.start,

124

end: options.end

125

});

126

}

127

};

128

129

// Example with S3

130

const AWS = require('aws-sdk');

131

const s3 = new AWS.S3();

132

133

const methods = {

134

createReadStream: (path, options = {}) => {

135

const params = {

136

Bucket: 'my-bucket',

137

Key: path.replace('/public/', '')

138

};

139

140

if (options.start !== undefined || options.end !== undefined) {

141

params.Range = `bytes=${options.start || 0}-${options.end || ''}`;

142

}

143

144

return s3.getObject(params).createReadStream();

145

}

146

};

147

```

148

149

### Directory Listing

150

151

Custom directory reading implementation for listing directory contents.

152

153

```javascript { .api }

154

/**

155

* Custom directory reader for listing directory contents

156

* @param path - Absolute path to the directory

157

* @returns Promise resolving to array of file/directory names

158

*/

159

readdir?: (path: string) => Promise<string[]>;

160

```

161

162

**Usage Examples:**

163

164

```javascript

165

const methods = {

166

readdir: async (path) => {

167

// Custom directory listing (e.g., from database)

168

const files = await database.listFiles(path);

169

return files.map(f => f.name);

170

}

171

};

172

173

// Example with cloud storage

174

const methods = {

175

readdir: async (path) => {

176

const objects = await cloudStorage.listObjects({

177

prefix: path.replace('/public/', ''),

178

delimiter: '/'

179

});

180

181

return objects.map(obj => obj.name);

182

}

183

};

184

```

185

186

### Error Handling

187

188

Custom error response handler for generating custom error pages and responses.

189

190

```javascript { .api }

191

/**

192

* Custom error handler for generating error responses

193

* @param absolutePath - Absolute path that caused the error

194

* @param response - HTTP response object

195

* @param acceptsJSON - Whether client accepts JSON responses

196

* @param current - Current working directory

197

* @param handlers - Handler utilities object

198

* @param config - Configuration object

199

* @param spec - Error specification object

200

* @returns Promise that resolves when error response is sent

201

*/

202

sendError?: (

203

absolutePath: string,

204

response: ServerResponse,

205

acceptsJSON: boolean,

206

current: string,

207

handlers: object,

208

config: Config,

209

spec: ErrorSpec

210

) => Promise<void>;

211

212

interface ErrorSpec {

213

/** HTTP status code */

214

statusCode: number;

215

/** Error code identifier */

216

code: string;

217

/** Error message */

218

message: string;

219

/** Original error object (optional) */

220

err?: Error;

221

}

222

```

223

224

**Usage Examples:**

225

226

```javascript

227

const methods = {

228

sendError: async (absolutePath, response, acceptsJSON, current, handlers, config, spec) => {

229

// Custom error handling

230

if (spec.statusCode === 404) {

231

response.statusCode = 404;

232

response.setHeader('Content-Type', 'text/html');

233

response.end(`

234

<html>

235

<body>

236

<h1>Custom 404 Page</h1>

237

<p>The file ${absolutePath} was not found.</p>

238

</body>

239

</html>

240

`);

241

} else {

242

// Log error to external service

243

await errorLogger.log({

244

path: absolutePath,

245

error: spec.err,

246

statusCode: spec.statusCode

247

});

248

249

// Use default error handling

250

await handlers.sendError(absolutePath, response, acceptsJSON, current, handlers, config, spec);

251

}

252

}

253

};

254

```

255

256

### Complete Custom Implementation Example

257

258

```javascript

259

const handler = require('serve-handler');

260

const AWS = require('aws-sdk');

261

const redis = require('redis');

262

263

const s3 = new AWS.S3();

264

const redisClient = redis.createClient();

265

266

// Complete custom file system implementation for S3 + Redis caching

267

const methods = {

268

lstat: async (path, fromDir) => {

269

// Check cache first

270

const cached = await redisClient.get(`stat:${path}`);

271

if (cached) {

272

return JSON.parse(cached);

273

}

274

275

// Get from S3

276

try {

277

const object = await s3.headObject({

278

Bucket: 'my-static-site',

279

Key: path.replace('/public/', '')

280

}).promise();

281

282

const stats = {

283

size: object.ContentLength,

284

mtime: object.LastModified,

285

isDirectory: () => false,

286

isSymbolicLink: () => false

287

};

288

289

// Cache for 5 minutes

290

await redisClient.setex(`stat:${path}`, 300, JSON.stringify(stats));

291

return stats;

292

} catch (err) {

293

if (err.code === 'NotFound') {

294

// Check if it's a "directory" (prefix)

295

const objects = await s3.listObjectsV2({

296

Bucket: 'my-static-site',

297

Prefix: path.replace('/public/', '') + '/',

298

MaxKeys: 1

299

}).promise();

300

301

if (objects.Contents.length > 0) {

302

return {

303

size: 0,

304

mtime: new Date(),

305

isDirectory: () => true,

306

isSymbolicLink: () => false

307

};

308

}

309

}

310

throw err;

311

}

312

},

313

314

createReadStream: (path, options = {}) => {

315

const params = {

316

Bucket: 'my-static-site',

317

Key: path.replace('/public/', '')

318

};

319

320

if (options.start !== undefined || options.end !== undefined) {

321

params.Range = `bytes=${options.start || 0}-${options.end || ''}`;

322

}

323

324

return s3.getObject(params).createReadStream();

325

},

326

327

readdir: async (path) => {

328

const objects = await s3.listObjectsV2({

329

Bucket: 'my-static-site',

330

Prefix: path.replace('/public/', '') + '/',

331

Delimiter: '/'

332

}).promise();

333

334

const files = [];

335

336

// Add files in this directory

337

objects.Contents.forEach(obj => {

338

const name = obj.Key.split('/').pop();

339

if (name) files.push(name);

340

});

341

342

// Add subdirectories

343

objects.CommonPrefixes.forEach(prefix => {

344

const name = prefix.Prefix.split('/').slice(-2)[0];

345

if (name) files.push(name);

346

});

347

348

return files;

349

},

350

351

sendError: async (absolutePath, response, acceptsJSON, current, handlers, config, spec) => {

352

// Log to CloudWatch

353

const cloudwatch = new AWS.CloudWatchLogs();

354

await cloudwatch.putLogEvents({

355

logGroupName: '/aws/lambda/static-site',

356

logStreamName: 'errors',

357

logEvents: [{

358

timestamp: Date.now(),

359

message: JSON.stringify({

360

path: absolutePath,

361

statusCode: spec.statusCode,

362

message: spec.message

363

})

364

}]

365

}).promise();

366

367

// Use default error handling

368

await handlers.sendError(absolutePath, response, acceptsJSON, current, handlers, config, spec);

369

}

370

};

371

372

// Use with custom methods

373

await handler(request, response, { public: '/public' }, methods);

374

```