@modularizer/plat 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -0
- package/dist/client-entry.d.ts +1 -0
- package/dist/client-entry.d.ts.map +1 -1
- package/dist/client-entry.js +1 -0
- package/dist/client-entry.js.map +1 -1
- package/dist/client-server-entry.d.ts +5 -0
- package/dist/client-server-entry.d.ts.map +1 -1
- package/dist/client-server-entry.js +5 -0
- package/dist/client-server-entry.js.map +1 -1
- package/dist/client-side-python/runtime.d.ts +1 -0
- package/dist/client-side-python/runtime.d.ts.map +1 -1
- package/dist/client-side-python/runtime.js +18 -1
- package/dist/client-side-python/runtime.js.map +1 -1
- package/dist/client-side-server/channel.d.ts +1 -1
- package/dist/client-side-server/channel.d.ts.map +1 -1
- package/dist/client-side-server/channel.js +113 -4
- package/dist/client-side-server/channel.js.map +1 -1
- package/dist/client-side-server/identity.d.ts +80 -17
- package/dist/client-side-server/identity.d.ts.map +1 -1
- package/dist/client-side-server/identity.js +212 -31
- package/dist/client-side-server/identity.js.map +1 -1
- package/dist/client-side-server/mqtt-webrtc.d.ts +38 -6
- package/dist/client-side-server/mqtt-webrtc.d.ts.map +1 -1
- package/dist/client-side-server/mqtt-webrtc.js +843 -68
- package/dist/client-side-server/mqtt-webrtc.js.map +1 -1
- package/dist/client-side-server/plat-bridge.d.ts +40 -0
- package/dist/client-side-server/plat-bridge.d.ts.map +1 -0
- package/dist/client-side-server/plat-bridge.js +876 -0
- package/dist/client-side-server/plat-bridge.js.map +1 -0
- package/dist/client-side-server/plat-fetch.d.ts +33 -0
- package/dist/client-side-server/plat-fetch.d.ts.map +1 -0
- package/dist/client-side-server/plat-fetch.js +199 -0
- package/dist/client-side-server/plat-fetch.js.map +1 -0
- package/dist/client-side-server/plat-service-worker-client.d.ts +30 -0
- package/dist/client-side-server/plat-service-worker-client.d.ts.map +1 -0
- package/dist/client-side-server/plat-service-worker-client.js +141 -0
- package/dist/client-side-server/plat-service-worker-client.js.map +1 -0
- package/dist/client-side-server/plat-service-worker.d.ts +35 -0
- package/dist/client-side-server/plat-service-worker.d.ts.map +1 -0
- package/dist/client-side-server/plat-service-worker.js +292 -0
- package/dist/client-side-server/plat-service-worker.js.map +1 -0
- package/dist/client-side-server/protocol.d.ts +90 -3
- package/dist/client-side-server/protocol.d.ts.map +1 -1
- package/dist/client-side-server/protocol.js +110 -0
- package/dist/client-side-server/protocol.js.map +1 -1
- package/dist/client-side-server/runtime.d.ts +9 -0
- package/dist/client-side-server/runtime.d.ts.map +1 -1
- package/dist/client-side-server/runtime.js +44 -13
- package/dist/client-side-server/runtime.js.map +1 -1
- package/dist/client-side-server/secure-crypto.d.ts +35 -0
- package/dist/client-side-server/secure-crypto.d.ts.map +1 -0
- package/dist/client-side-server/secure-crypto.js +171 -0
- package/dist/client-side-server/secure-crypto.js.map +1 -0
- package/dist/client-side-server/server.d.ts +17 -0
- package/dist/client-side-server/server.d.ts.map +1 -1
- package/dist/client-side-server/server.js +111 -7
- package/dist/client-side-server/server.js.map +1 -1
- package/dist/client-side-server/signaling.d.ts +1 -0
- package/dist/client-side-server/signaling.d.ts.map +1 -1
- package/dist/client-side-server/signaling.js.map +1 -1
- package/dist/generated/python-browser-sources.js +4 -4
- package/dist/generated/python-browser-sources.js.map +1 -1
- package/dist/server/authority-server.d.ts +4 -4
- package/dist/server/authority-server.d.ts.map +1 -1
- package/dist/server/authority-server.js +23 -21
- package/dist/server/authority-server.js.map +1 -1
- package/dist/server/core.d.ts +8 -0
- package/dist/server/core.d.ts.map +1 -1
- package/dist/server/core.js +21 -0
- package/dist/server/core.js.map +1 -1
- package/dist/server/server.d.ts +4 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +81 -2
- package/dist/server/server.js.map +1 -1
- package/dist/static/file-response.d.ts +42 -0
- package/dist/static/file-response.d.ts.map +1 -0
- package/dist/static/file-response.js +57 -0
- package/dist/static/file-response.js.map +1 -0
- package/dist/static/glob-match.d.ts +16 -0
- package/dist/static/glob-match.d.ts.map +1 -0
- package/dist/static/glob-match.js +48 -0
- package/dist/static/glob-match.js.map +1 -0
- package/dist/static/index.d.ts +9 -0
- package/dist/static/index.d.ts.map +1 -0
- package/dist/static/index.js +6 -0
- package/dist/static/index.js.map +1 -0
- package/dist/static/mime-types.d.ts +6 -0
- package/dist/static/mime-types.d.ts.map +1 -0
- package/dist/static/mime-types.js +74 -0
- package/dist/static/mime-types.js.map +1 -0
- package/dist/static/static-folder.d.ts +69 -0
- package/dist/static/static-folder.d.ts.map +1 -0
- package/dist/static/static-folder.js +206 -0
- package/dist/static/static-folder.js.map +1 -0
- package/dist/static/virtual-file-system.d.ts +45 -0
- package/dist/static/virtual-file-system.d.ts.map +1 -0
- package/dist/static/virtual-file-system.js +100 -0
- package/dist/static/virtual-file-system.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getMimeType } from './mime-types.js';
|
|
2
|
+
export const FILE_RESPONSE_BRAND = Symbol.for('plat:FileResponse');
|
|
3
|
+
/**
|
|
4
|
+
* A response that serves a file. Can be returned from any controller method.
|
|
5
|
+
* Methods returning FileResponse should use @GET({ hidden: true }) to exclude from OpenAPI.
|
|
6
|
+
*/
|
|
7
|
+
export class FileResponse {
|
|
8
|
+
kind;
|
|
9
|
+
source;
|
|
10
|
+
[FILE_RESPONSE_BRAND] = true;
|
|
11
|
+
filename;
|
|
12
|
+
contentType;
|
|
13
|
+
maxAge;
|
|
14
|
+
headers;
|
|
15
|
+
constructor(kind, source, filename, opts) {
|
|
16
|
+
this.kind = kind;
|
|
17
|
+
this.source = source;
|
|
18
|
+
this.filename = filename;
|
|
19
|
+
this.contentType = opts?.contentType ?? getMimeType(filename);
|
|
20
|
+
this.maxAge = opts?.maxAge;
|
|
21
|
+
this.headers = opts?.headers ?? {};
|
|
22
|
+
}
|
|
23
|
+
static from(pathOrContent, filenameOrOpts, opts) {
|
|
24
|
+
if (typeof pathOrContent === 'string' && (filenameOrOpts === undefined || typeof filenameOrOpts === 'object' && !(filenameOrOpts instanceof Buffer))) {
|
|
25
|
+
// from(path) or from(path, opts) — filesystem path
|
|
26
|
+
const path = pathOrContent;
|
|
27
|
+
const filename = path.split('/').pop() ?? path;
|
|
28
|
+
return new FileResponse('path', path, filename, filenameOrOpts);
|
|
29
|
+
}
|
|
30
|
+
// from(content, filename) or from(content, filename, opts) — in-memory content
|
|
31
|
+
return new FileResponse('content', pathOrContent, filenameOrOpts, opts);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get the file content as a Buffer. For path-based responses, reads the file.
|
|
35
|
+
* For content-based responses, converts string to Buffer if needed.
|
|
36
|
+
*/
|
|
37
|
+
async getContent() {
|
|
38
|
+
if (this.kind === 'path') {
|
|
39
|
+
const { readFile } = await import('node:fs/promises');
|
|
40
|
+
return readFile(this.source);
|
|
41
|
+
}
|
|
42
|
+
if (typeof this.source === 'string') {
|
|
43
|
+
return Buffer.from(this.source, 'utf-8');
|
|
44
|
+
}
|
|
45
|
+
if (this.source instanceof Uint8Array) {
|
|
46
|
+
return Buffer.from(this.source);
|
|
47
|
+
}
|
|
48
|
+
return this.source;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if a value is a FileResponse instance (works across module boundaries).
|
|
53
|
+
*/
|
|
54
|
+
export function isFileResponse(value) {
|
|
55
|
+
return value != null && typeof value === 'object' && FILE_RESPONSE_BRAND in value;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=file-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-response.js","sourceRoot":"","sources":["../../src/static/file-response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAQlE;;;GAGG;AACH,MAAM,OAAO,YAAY;IASZ;IACA;IATF,CAAC,mBAAmB,CAAC,GAAG,IAAI,CAAA;IAE5B,QAAQ,CAAQ;IAChB,WAAW,CAAQ;IACnB,MAAM,CAAS;IACf,OAAO,CAAwB;IAExC,YACW,IAAwB,EACxB,MAAoC,EAC7C,QAAgB,EAChB,IAAuB;QAHd,SAAI,GAAJ,IAAI,CAAoB;QACxB,WAAM,GAAN,MAAM,CAA8B;QAI7C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,WAAW,GAAG,IAAI,EAAE,WAAW,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAA;QAC7D,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,CAAA;QAC1B,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,EAAE,CAAA;IACpC,CAAC;IAcD,MAAM,CAAC,IAAI,CACT,aAA2C,EAC3C,cAA0C,EAC1C,IAAuB;QAEvB,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,CAAC,cAAc,KAAK,SAAS,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,CAAC,CAAC,cAAc,YAAY,MAAM,CAAC,CAAC,EAAE,CAAC;YACrJ,mDAAmD;YACnD,MAAM,IAAI,GAAG,aAAa,CAAA;YAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,IAAI,CAAA;YAC9C,OAAO,IAAI,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,cAA8C,CAAC,CAAA;QACjG,CAAC;QAED,+EAA+E;QAC/E,OAAO,IAAI,YAAY,CAAC,SAAS,EAAE,aAAa,EAAE,cAAwB,EAAE,IAAI,CAAC,CAAA;IACnF,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACzB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;YACrD,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAgB,CAAC,CAAA;QACxC,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACpC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAC1C,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,YAAY,UAAU,EAAE,CAAC;YACtC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACjC,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,mBAAmB,IAAI,KAAK,CAAA;AACnF,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gitignore-style glob matching for exclude patterns.
|
|
3
|
+
*
|
|
4
|
+
* Supported patterns:
|
|
5
|
+
* **\/*.map — match extension in any subdirectory
|
|
6
|
+
* .DS_Store — exact filename anywhere
|
|
7
|
+
* secrets/** — entire directory tree
|
|
8
|
+
* **\/.* — all dotfiles in any subdirectory
|
|
9
|
+
* *.txt — match extension in current directory only
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Check if a file path matches any of the exclude patterns.
|
|
13
|
+
* Paths should be relative (no leading slash), using forward slashes.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isExcluded(filePath: string, patterns: string[]): boolean;
|
|
16
|
+
//# sourceMappingURL=glob-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob-match.d.ts","sourceRoot":"","sources":["../../src/static/glob-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA8BH;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CASxE"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gitignore-style glob matching for exclude patterns.
|
|
3
|
+
*
|
|
4
|
+
* Supported patterns:
|
|
5
|
+
* **\/*.map — match extension in any subdirectory
|
|
6
|
+
* .DS_Store — exact filename anywhere
|
|
7
|
+
* secrets/** — entire directory tree
|
|
8
|
+
* **\/.* — all dotfiles in any subdirectory
|
|
9
|
+
* *.txt — match extension in current directory only
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Convert a gitignore-style glob pattern to a RegExp.
|
|
13
|
+
*/
|
|
14
|
+
function globToRegExp(pattern) {
|
|
15
|
+
// Normalize: strip leading/trailing slashes
|
|
16
|
+
let p = pattern.replace(/^\/+|\/+$/g, '');
|
|
17
|
+
// Escape regex special chars except * and ?
|
|
18
|
+
p = p.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
19
|
+
// Handle ** patterns
|
|
20
|
+
// /**/ → match any number of path segments (including zero)
|
|
21
|
+
p = p.replace(/\\\*\\\*\//g, '(?:.+/)?');
|
|
22
|
+
// ** at end → match everything
|
|
23
|
+
p = p.replace(/\\\*\\\*/g, '.*');
|
|
24
|
+
// Single * → match anything except /
|
|
25
|
+
p = p.replace(/\\\*/g, '[^/]*');
|
|
26
|
+
// ? → match single char except /
|
|
27
|
+
p = p.replace(/\\\?/g, '[^/]');
|
|
28
|
+
// If pattern has no slash, match against basename anywhere in path
|
|
29
|
+
if (!pattern.includes('/')) {
|
|
30
|
+
return new RegExp(`(?:^|/)${p}$`);
|
|
31
|
+
}
|
|
32
|
+
return new RegExp(`^${p}$`);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if a file path matches any of the exclude patterns.
|
|
36
|
+
* Paths should be relative (no leading slash), using forward slashes.
|
|
37
|
+
*/
|
|
38
|
+
export function isExcluded(filePath, patterns) {
|
|
39
|
+
// Normalize the path
|
|
40
|
+
const normalized = filePath.replace(/^\/+/, '');
|
|
41
|
+
for (const pattern of patterns) {
|
|
42
|
+
if (globToRegExp(pattern).test(normalized)) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=glob-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob-match.js","sourceRoot":"","sources":["../../src/static/glob-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;GAEG;AACH,SAAS,YAAY,CAAC,OAAe;IACnC,4CAA4C;IAC5C,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;IAEzC,4CAA4C;IAC5C,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAA;IAE1C,qBAAqB;IACrB,6DAA6D;IAC7D,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,CAAA;IACxC,+BAA+B;IAC/B,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAA;IAChC,qCAAqC;IACrC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAC/B,iCAAiC;IACjC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAE9B,mEAAmE;IACnE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;IACnC,CAAC;IAED,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,QAAkB;IAC7D,qBAAqB;IACrB,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAC/C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { FileResponse, isFileResponse } from './file-response';
|
|
2
|
+
export type { FileResponseOpts } from './file-response';
|
|
3
|
+
export { StaticFolder, isStaticFolder } from './static-folder';
|
|
4
|
+
export type { StaticFolderOpts } from './static-folder';
|
|
5
|
+
export type { VirtualFileSystem, MemoryFileEntry } from './virtual-file-system';
|
|
6
|
+
export { NodeFileSystem, MemoryFileSystem } from './virtual-file-system';
|
|
7
|
+
export { getMimeType } from './mime-types';
|
|
8
|
+
export { isExcluded } from './glob-match';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/static/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAC9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAC9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACvD,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { FileResponse, isFileResponse } from './file-response.js';
|
|
2
|
+
export { StaticFolder, isStaticFolder } from './static-folder.js';
|
|
3
|
+
export { NodeFileSystem, MemoryFileSystem } from './virtual-file-system.js';
|
|
4
|
+
export { getMimeType } from './mime-types.js';
|
|
5
|
+
export { isExcluded } from './glob-match.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/static/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAE9D,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAG9D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mime-types.d.ts","sourceRoot":"","sources":["../../src/static/mime-types.ts"],"names":[],"mappings":"AAsEA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKpD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const MIME_TYPES = {
|
|
2
|
+
// Text
|
|
3
|
+
'.html': 'text/html',
|
|
4
|
+
'.htm': 'text/html',
|
|
5
|
+
'.css': 'text/css',
|
|
6
|
+
'.csv': 'text/csv',
|
|
7
|
+
'.txt': 'text/plain',
|
|
8
|
+
'.xml': 'text/xml',
|
|
9
|
+
'.md': 'text/markdown',
|
|
10
|
+
'.yaml': 'text/yaml',
|
|
11
|
+
'.yml': 'text/yaml',
|
|
12
|
+
// JavaScript / JSON
|
|
13
|
+
'.js': 'application/javascript',
|
|
14
|
+
'.mjs': 'application/javascript',
|
|
15
|
+
'.cjs': 'application/javascript',
|
|
16
|
+
'.json': 'application/json',
|
|
17
|
+
'.jsonld': 'application/ld+json',
|
|
18
|
+
'.map': 'application/json',
|
|
19
|
+
'.ts': 'text/typescript',
|
|
20
|
+
// Images
|
|
21
|
+
'.png': 'image/png',
|
|
22
|
+
'.jpg': 'image/jpeg',
|
|
23
|
+
'.jpeg': 'image/jpeg',
|
|
24
|
+
'.gif': 'image/gif',
|
|
25
|
+
'.svg': 'image/svg+xml',
|
|
26
|
+
'.ico': 'image/x-icon',
|
|
27
|
+
'.webp': 'image/webp',
|
|
28
|
+
'.avif': 'image/avif',
|
|
29
|
+
'.bmp': 'image/bmp',
|
|
30
|
+
'.tiff': 'image/tiff',
|
|
31
|
+
'.tif': 'image/tiff',
|
|
32
|
+
// Fonts
|
|
33
|
+
'.woff': 'font/woff',
|
|
34
|
+
'.woff2': 'font/woff2',
|
|
35
|
+
'.ttf': 'font/ttf',
|
|
36
|
+
'.otf': 'font/otf',
|
|
37
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
38
|
+
// Audio
|
|
39
|
+
'.mp3': 'audio/mpeg',
|
|
40
|
+
'.wav': 'audio/wav',
|
|
41
|
+
'.ogg': 'audio/ogg',
|
|
42
|
+
'.flac': 'audio/flac',
|
|
43
|
+
'.aac': 'audio/aac',
|
|
44
|
+
'.m4a': 'audio/mp4',
|
|
45
|
+
// Video
|
|
46
|
+
'.mp4': 'video/mp4',
|
|
47
|
+
'.webm': 'video/webm',
|
|
48
|
+
'.avi': 'video/x-msvideo',
|
|
49
|
+
'.mov': 'video/quicktime',
|
|
50
|
+
'.mkv': 'video/x-matroska',
|
|
51
|
+
// Documents
|
|
52
|
+
'.pdf': 'application/pdf',
|
|
53
|
+
'.zip': 'application/zip',
|
|
54
|
+
'.gz': 'application/gzip',
|
|
55
|
+
'.tar': 'application/x-tar',
|
|
56
|
+
'.7z': 'application/x-7z-compressed',
|
|
57
|
+
'.rar': 'application/vnd.rar',
|
|
58
|
+
// Web
|
|
59
|
+
'.wasm': 'application/wasm',
|
|
60
|
+
'.manifest': 'text/cache-manifest',
|
|
61
|
+
'.webmanifest': 'application/manifest+json',
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Get MIME content-type from a filename or extension.
|
|
65
|
+
* Returns 'application/octet-stream' for unrecognized extensions.
|
|
66
|
+
*/
|
|
67
|
+
export function getMimeType(filename) {
|
|
68
|
+
const dot = filename.lastIndexOf('.');
|
|
69
|
+
if (dot === -1)
|
|
70
|
+
return 'application/octet-stream';
|
|
71
|
+
const ext = filename.slice(dot).toLowerCase();
|
|
72
|
+
return MIME_TYPES[ext] ?? 'application/octet-stream';
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=mime-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mime-types.js","sourceRoot":"","sources":["../../src/static/mime-types.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAA2B;IACzC,OAAO;IACP,OAAO,EAAE,WAAW;IACpB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE,eAAe;IACtB,OAAO,EAAE,WAAW;IACpB,MAAM,EAAE,WAAW;IAEnB,oBAAoB;IACpB,KAAK,EAAE,wBAAwB;IAC/B,MAAM,EAAE,wBAAwB;IAChC,MAAM,EAAE,wBAAwB;IAChC,OAAO,EAAE,kBAAkB;IAC3B,SAAS,EAAE,qBAAqB;IAChC,MAAM,EAAE,kBAAkB;IAC1B,KAAK,EAAE,iBAAiB;IAExB,SAAS;IACT,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,eAAe;IACvB,MAAM,EAAE,cAAc;IACtB,OAAO,EAAE,YAAY;IACrB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,YAAY;IAEpB,QAAQ;IACR,OAAO,EAAE,WAAW;IACpB,QAAQ,EAAE,YAAY;IACtB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,+BAA+B;IAEvC,QAAQ;IACR,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IAEnB,QAAQ;IACR,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,kBAAkB;IAE1B,YAAY;IACZ,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,iBAAiB;IACzB,KAAK,EAAE,kBAAkB;IACzB,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE,6BAA6B;IACpC,MAAM,EAAE,qBAAqB;IAE7B,MAAM;IACN,OAAO,EAAE,kBAAkB;IAC3B,WAAW,EAAE,qBAAqB;IAClC,cAAc,EAAE,2BAA2B;CAC5C,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IACrC,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,0BAA0B,CAAA;IACjD,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAA;IAC7C,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAA;AACtD,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { FileResponse } from './file-response';
|
|
2
|
+
import { type MemoryFileEntry, type VirtualFileSystem } from './virtual-file-system';
|
|
3
|
+
export declare const STATIC_FOLDER_BRAND: unique symbol;
|
|
4
|
+
export interface StaticFolderOpts {
|
|
5
|
+
/** Gitignore-style exclude patterns */
|
|
6
|
+
exclude?: string[];
|
|
7
|
+
/** Cache-Control max-age in seconds */
|
|
8
|
+
maxAge?: number;
|
|
9
|
+
/** Extra response headers */
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
/** How to handle dotfiles: 'ignore' (404), 'allow', or 'deny' (403). Default: 'ignore' */
|
|
12
|
+
dotfiles?: 'ignore' | 'allow' | 'deny';
|
|
13
|
+
/**
|
|
14
|
+
* What to return when someone requests a directory.
|
|
15
|
+
* - 'none' : 404 (default when index is not set)
|
|
16
|
+
* - 'index' : serve the index file if it exists (default when index is set)
|
|
17
|
+
* - 'list' : return a JSON array of filenames
|
|
18
|
+
* - 'directory' : return an HTML directory listing page
|
|
19
|
+
* - function : custom handler — receives file list, returns a FileResponse
|
|
20
|
+
*/
|
|
21
|
+
onDirectory?: 'none' | 'index' | 'list' | 'directory' | ((files: string[]) => FileResponse | Promise<FileResponse>);
|
|
22
|
+
/** Index file to serve for directory requests when onDirectory is 'index' (e.g. 'index.html') */
|
|
23
|
+
index?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A static folder that serves files from a VirtualFileSystem.
|
|
27
|
+
* Used as a class variable on controllers:
|
|
28
|
+
*
|
|
29
|
+
* @Controller()
|
|
30
|
+
* class MyApp {
|
|
31
|
+
* assets = new StaticFolder('./public', { exclude: ['**\/*.map'] })
|
|
32
|
+
* }
|
|
33
|
+
*/
|
|
34
|
+
export declare class StaticFolder {
|
|
35
|
+
readonly [STATIC_FOLDER_BRAND] = true;
|
|
36
|
+
readonly vfs: VirtualFileSystem;
|
|
37
|
+
readonly opts: StaticFolderOpts;
|
|
38
|
+
constructor(directory: string, opts?: StaticFolderOpts);
|
|
39
|
+
constructor(files: Record<string, MemoryFileEntry>, opts?: StaticFolderOpts);
|
|
40
|
+
constructor(vfs: VirtualFileSystem, opts?: StaticFolderOpts);
|
|
41
|
+
/**
|
|
42
|
+
* Recursively enumerate all file paths available in this folder.
|
|
43
|
+
* Returns paths relative to the folder root (no leading slash).
|
|
44
|
+
* Respects exclude patterns and dotfile policy.
|
|
45
|
+
* Useful for building manifests so clients know what paths are serveable.
|
|
46
|
+
*/
|
|
47
|
+
listAllFiles(maxDepth?: number): Promise<string[]>;
|
|
48
|
+
private walkDir;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve a sub-path to a FileResponse, or null if not found.
|
|
51
|
+
* Handles: exclude globs, dotfile policy, stem matching, onDirectory behavior.
|
|
52
|
+
*/
|
|
53
|
+
resolve(subPath: string): Promise<FileResponse | null>;
|
|
54
|
+
private isDotfile;
|
|
55
|
+
private hasExtension;
|
|
56
|
+
/**
|
|
57
|
+
* Stem matching: if request path has no extension and exactly one file matches the stem, serve it.
|
|
58
|
+
* e.g. /assets/readme → serves readme.md if it's the only readme.* in that directory
|
|
59
|
+
*/
|
|
60
|
+
private stemMatch;
|
|
61
|
+
private handleDirectory;
|
|
62
|
+
private renderDirectoryPage;
|
|
63
|
+
private createFileResponse;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if a value is a StaticFolder instance (works across module boundaries).
|
|
67
|
+
*/
|
|
68
|
+
export declare function isStaticFolder(value: unknown): value is StaticFolder;
|
|
69
|
+
//# sourceMappingURL=static-folder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-folder.d.ts","sourceRoot":"","sources":["../../src/static/static-folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAG9C,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAA;AAE9B,eAAO,MAAM,mBAAmB,eAAkC,CAAA;AAElE,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAA;IACtC;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAA;IACnH,iGAAiG;IACjG,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;GAQG;AACH,qBAAa,YAAY;IACvB,QAAQ,CAAC,CAAC,mBAAmB,CAAC,QAAO;IAErC,QAAQ,CAAC,GAAG,EAAE,iBAAiB,CAAA;IAC/B,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;gBAEnB,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB;gBAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,EAAE,gBAAgB;gBAC/D,GAAG,EAAE,iBAAiB,EAAE,IAAI,CAAC,EAAE,gBAAgB;IAgB3D;;;;;OAKG;IACG,YAAY,CAAC,QAAQ,SAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAMtC,OAAO;IAwBrB;;;OAGG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAwC5D,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,YAAY;IAKpB;;;OAGG;YACW,SAAS;YA+BT,eAAe;IAgC7B,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,kBAAkB;CAW3B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { FileResponse } from './file-response.js';
|
|
2
|
+
import { isExcluded } from './glob-match.js';
|
|
3
|
+
import { getMimeType } from './mime-types.js';
|
|
4
|
+
import { isVirtualFileSystem, MemoryFileSystem, NodeFileSystem, } from './virtual-file-system.js';
|
|
5
|
+
export const STATIC_FOLDER_BRAND = Symbol.for('plat:StaticFolder');
|
|
6
|
+
/**
|
|
7
|
+
* A static folder that serves files from a VirtualFileSystem.
|
|
8
|
+
* Used as a class variable on controllers:
|
|
9
|
+
*
|
|
10
|
+
* @Controller()
|
|
11
|
+
* class MyApp {
|
|
12
|
+
* assets = new StaticFolder('./public', { exclude: ['**\/*.map'] })
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
export class StaticFolder {
|
|
16
|
+
[STATIC_FOLDER_BRAND] = true;
|
|
17
|
+
vfs;
|
|
18
|
+
opts;
|
|
19
|
+
constructor(source, opts) {
|
|
20
|
+
this.opts = opts ?? {};
|
|
21
|
+
if (typeof source === 'string') {
|
|
22
|
+
this.vfs = new NodeFileSystem(source);
|
|
23
|
+
}
|
|
24
|
+
else if (isVirtualFileSystem(source)) {
|
|
25
|
+
this.vfs = source;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.vfs = new MemoryFileSystem(source);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Recursively enumerate all file paths available in this folder.
|
|
33
|
+
* Returns paths relative to the folder root (no leading slash).
|
|
34
|
+
* Respects exclude patterns and dotfile policy.
|
|
35
|
+
* Useful for building manifests so clients know what paths are serveable.
|
|
36
|
+
*/
|
|
37
|
+
async listAllFiles(maxDepth = 20) {
|
|
38
|
+
const results = [];
|
|
39
|
+
await this.walkDir('', results, 0, maxDepth);
|
|
40
|
+
return results;
|
|
41
|
+
}
|
|
42
|
+
async walkDir(dirPath, results, depth, maxDepth) {
|
|
43
|
+
if (depth > maxDepth)
|
|
44
|
+
return;
|
|
45
|
+
const entries = await this.vfs.list(dirPath);
|
|
46
|
+
for (const entry of entries) {
|
|
47
|
+
if (entry.endsWith('/')) {
|
|
48
|
+
const dirName = entry.slice(0, -1);
|
|
49
|
+
const fullDirPath = dirPath ? `${dirPath}/${dirName}` : dirName;
|
|
50
|
+
if (this.isDotfile(fullDirPath)) {
|
|
51
|
+
const policy = this.opts.dotfiles ?? 'ignore';
|
|
52
|
+
if (policy === 'ignore' || policy === 'deny')
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
await this.walkDir(fullDirPath, results, depth + 1, maxDepth);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const filePath = dirPath ? `${dirPath}/${entry}` : entry;
|
|
59
|
+
if (this.isDotfile(filePath)) {
|
|
60
|
+
const policy = this.opts.dotfiles ?? 'ignore';
|
|
61
|
+
if (policy === 'ignore' || policy === 'deny')
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (this.opts.exclude && isExcluded(filePath, this.opts.exclude))
|
|
65
|
+
continue;
|
|
66
|
+
results.push(filePath);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Resolve a sub-path to a FileResponse, or null if not found.
|
|
72
|
+
* Handles: exclude globs, dotfile policy, stem matching, onDirectory behavior.
|
|
73
|
+
*/
|
|
74
|
+
async resolve(subPath) {
|
|
75
|
+
// Normalize: strip leading/trailing slashes
|
|
76
|
+
const normalized = subPath.replace(/^\/+|\/+$/g, '');
|
|
77
|
+
// Check dotfile policy
|
|
78
|
+
if (this.isDotfile(normalized)) {
|
|
79
|
+
const policy = this.opts.dotfiles ?? 'ignore';
|
|
80
|
+
if (policy === 'ignore' || policy === 'deny')
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
// Check exclude patterns
|
|
84
|
+
if (this.opts.exclude && normalized && isExcluded(normalized, this.opts.exclude)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
// Empty path = directory root request
|
|
88
|
+
if (!normalized) {
|
|
89
|
+
return this.handleDirectory('');
|
|
90
|
+
}
|
|
91
|
+
// Try exact file match
|
|
92
|
+
const content = await this.vfs.read(normalized);
|
|
93
|
+
if (content !== null) {
|
|
94
|
+
return this.createFileResponse(content, normalized);
|
|
95
|
+
}
|
|
96
|
+
// Check if it's a directory (by listing it)
|
|
97
|
+
const dirEntries = await this.vfs.list(normalized);
|
|
98
|
+
if (dirEntries.length > 0) {
|
|
99
|
+
return this.handleDirectory(normalized);
|
|
100
|
+
}
|
|
101
|
+
// Stem matching: if no extension, look for unique match
|
|
102
|
+
if (!this.hasExtension(normalized)) {
|
|
103
|
+
return this.stemMatch(normalized);
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
isDotfile(path) {
|
|
108
|
+
const parts = path.split('/');
|
|
109
|
+
return parts.some(p => p.startsWith('.') && p.length > 1);
|
|
110
|
+
}
|
|
111
|
+
hasExtension(path) {
|
|
112
|
+
const basename = path.split('/').pop() ?? '';
|
|
113
|
+
return basename.includes('.') && !basename.startsWith('.');
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Stem matching: if request path has no extension and exactly one file matches the stem, serve it.
|
|
117
|
+
* e.g. /assets/readme → serves readme.md if it's the only readme.* in that directory
|
|
118
|
+
*/
|
|
119
|
+
async stemMatch(normalizedPath) {
|
|
120
|
+
const parts = normalizedPath.split('/');
|
|
121
|
+
const stem = parts.pop();
|
|
122
|
+
const dir = parts.join('/');
|
|
123
|
+
const entries = await this.vfs.list(dir);
|
|
124
|
+
const matches = entries.filter(entry => {
|
|
125
|
+
if (entry.endsWith('/'))
|
|
126
|
+
return false;
|
|
127
|
+
const dot = entry.lastIndexOf('.');
|
|
128
|
+
const entryStem = dot > 0 ? entry.slice(0, dot) : entry;
|
|
129
|
+
return entryStem === stem;
|
|
130
|
+
});
|
|
131
|
+
if (matches.length === 1) {
|
|
132
|
+
const matched = matches[0];
|
|
133
|
+
const fullPath = dir ? `${dir}/${matched}` : matched;
|
|
134
|
+
// Check exclude on the resolved path
|
|
135
|
+
if (this.opts.exclude && isExcluded(fullPath, this.opts.exclude)) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const content = await this.vfs.read(fullPath);
|
|
139
|
+
if (content !== null) {
|
|
140
|
+
return this.createFileResponse(content, fullPath);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
async handleDirectory(dirPath) {
|
|
146
|
+
const onDir = this.opts.onDirectory ?? (this.opts.index ? 'index' : 'none');
|
|
147
|
+
if (onDir === 'none')
|
|
148
|
+
return null;
|
|
149
|
+
if (onDir === 'index') {
|
|
150
|
+
if (!this.opts.index)
|
|
151
|
+
return null;
|
|
152
|
+
const indexPath = dirPath ? `${dirPath}/${this.opts.index}` : this.opts.index;
|
|
153
|
+
const content = await this.vfs.read(indexPath);
|
|
154
|
+
if (content === null)
|
|
155
|
+
return null;
|
|
156
|
+
return this.createFileResponse(content, indexPath);
|
|
157
|
+
}
|
|
158
|
+
const entries = await this.vfs.list(dirPath);
|
|
159
|
+
if (onDir === 'list') {
|
|
160
|
+
const json = JSON.stringify(entries);
|
|
161
|
+
return FileResponse.from(json, 'listing.json');
|
|
162
|
+
}
|
|
163
|
+
if (onDir === 'directory') {
|
|
164
|
+
const html = this.renderDirectoryPage(dirPath, entries);
|
|
165
|
+
return FileResponse.from(html, 'index.html');
|
|
166
|
+
}
|
|
167
|
+
if (typeof onDir === 'function') {
|
|
168
|
+
return onDir(entries);
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
renderDirectoryPage(dirPath, entries) {
|
|
173
|
+
const title = dirPath || '/';
|
|
174
|
+
const links = entries
|
|
175
|
+
.map(entry => {
|
|
176
|
+
const href = entry;
|
|
177
|
+
return ` <li><a href="${href}">${entry}</a></li>`;
|
|
178
|
+
})
|
|
179
|
+
.join('\n');
|
|
180
|
+
return `<!DOCTYPE html>
|
|
181
|
+
<html>
|
|
182
|
+
<head><meta charset="utf-8"><title>Index of ${title}</title></head>
|
|
183
|
+
<body>
|
|
184
|
+
<h1>Index of /${title}</h1>
|
|
185
|
+
<ul>
|
|
186
|
+
${links}
|
|
187
|
+
</ul>
|
|
188
|
+
</body>
|
|
189
|
+
</html>`;
|
|
190
|
+
}
|
|
191
|
+
createFileResponse(content, filePath) {
|
|
192
|
+
const filename = filePath.split('/').pop() ?? filePath;
|
|
193
|
+
return FileResponse.from(content, filename, {
|
|
194
|
+
contentType: getMimeType(filename),
|
|
195
|
+
maxAge: this.opts.maxAge,
|
|
196
|
+
headers: this.opts.headers,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Check if a value is a StaticFolder instance (works across module boundaries).
|
|
202
|
+
*/
|
|
203
|
+
export function isStaticFolder(value) {
|
|
204
|
+
return value != null && typeof value === 'object' && STATIC_FOLDER_BRAND in value;
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=static-folder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-folder.js","sourceRoot":"","sources":["../../src/static/static-folder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstraction over file storage. StaticFolder resolves files through this interface.
|
|
3
|
+
* Implementations: NodeFileSystem (real fs), MemoryFileSystem (in-memory map), or any custom backend.
|
|
4
|
+
*/
|
|
5
|
+
export interface VirtualFileSystem {
|
|
6
|
+
/**
|
|
7
|
+
* List entries at a path. Return filenames only (not full paths).
|
|
8
|
+
* Directories should be suffixed with '/'.
|
|
9
|
+
* path is '' for root, 'subdir' for a subdirectory.
|
|
10
|
+
*/
|
|
11
|
+
list(path: string): string[] | Promise<string[]>;
|
|
12
|
+
/**
|
|
13
|
+
* Read a file's content. Return null if not found.
|
|
14
|
+
*/
|
|
15
|
+
read(path: string): string | Uint8Array | null | Promise<string | Uint8Array | null>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Check if a value implements the VirtualFileSystem interface.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isVirtualFileSystem(value: unknown): value is VirtualFileSystem;
|
|
21
|
+
/**
|
|
22
|
+
* VFS backed by Node.js filesystem. Used when StaticFolder is given a directory path string.
|
|
23
|
+
*/
|
|
24
|
+
export declare class NodeFileSystem implements VirtualFileSystem {
|
|
25
|
+
private root;
|
|
26
|
+
constructor(directory: string);
|
|
27
|
+
private resolveSafe;
|
|
28
|
+
list(subPath: string): Promise<string[]>;
|
|
29
|
+
read(subPath: string): Promise<Uint8Array | null>;
|
|
30
|
+
}
|
|
31
|
+
export type MemoryFileEntry = string | Uint8Array | {
|
|
32
|
+
read(): string | Uint8Array | Promise<string | Uint8Array>;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* VFS backed by an in-memory file map.
|
|
36
|
+
* Keys are paths like 'index.html' or 'css/style.css'.
|
|
37
|
+
* Values can be raw content or objects with a read() method for lazy evaluation.
|
|
38
|
+
*/
|
|
39
|
+
export declare class MemoryFileSystem implements VirtualFileSystem {
|
|
40
|
+
private files;
|
|
41
|
+
constructor(files: Record<string, MemoryFileEntry>);
|
|
42
|
+
list(subPath: string): string[];
|
|
43
|
+
read(subPath: string): Promise<string | Uint8Array | null>;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=virtual-file-system.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"virtual-file-system.d.ts","sourceRoot":"","sources":["../../src/static/virtual-file-system.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAEhD;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC,CAAA;CACrF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAO9E;AAED;;GAEG;AACH,qBAAa,cAAe,YAAW,iBAAiB;IACtD,OAAO,CAAC,IAAI,CAAQ;gBAER,SAAS,EAAE,MAAM;IAM7B,OAAO,CAAC,WAAW;IAeb,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAexC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;CAUxD;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,GAAG;IAAE,IAAI,IAAI,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC,CAAA;CAAE,CAAA;AAElH;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,iBAAiB;IAC5C,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC;IAE1D,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAkBzB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;CAOjE"}
|