@modularizer/plat 0.7.0 → 0.9.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-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 +10 -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 +4 -2
- package/dist/client-side-server/identity.d.ts.map +1 -1
- package/dist/client-side-server/identity.js +30 -2
- package/dist/client-side-server/identity.js.map +1 -1
- package/dist/client-side-server/mqtt-webrtc.d.ts +84 -3
- package/dist/client-side-server/mqtt-webrtc.d.ts.map +1 -1
- package/dist/client-side-server/mqtt-webrtc.js +267 -10
- 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 +68 -1
- package/dist/client-side-server/protocol.d.ts.map +1 -1
- package/dist/client-side-server/protocol.js +7 -0
- package/dist/client-side-server/protocol.js.map +1 -1
- package/dist/client-side-server/runtime.d.ts +16 -1
- package/dist/client-side-server/runtime.d.ts.map +1 -1
- package/dist/client-side-server/runtime.js +41 -13
- package/dist/client-side-server/runtime.js.map +1 -1
- package/dist/client-side-server/server.d.ts +34 -1
- package/dist/client-side-server/server.d.ts.map +1 -1
- package/dist/client-side-server/server.js +171 -7
- package/dist/client-side-server/server.js.map +1 -1
- package/dist/client-side-server/worker-pool.d.ts +20 -0
- package/dist/client-side-server/worker-pool.d.ts.map +1 -0
- package/dist/client-side-server/worker-pool.js +336 -0
- package/dist/client-side-server/worker-pool.js.map +1 -0
- 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 +1 -1
- package/dist/server/authority-server.d.ts.map +1 -1
- package/dist/server/authority-server.js +27 -14
- 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,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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a value implements the VirtualFileSystem interface.
|
|
3
|
+
*/
|
|
4
|
+
export function isVirtualFileSystem(value) {
|
|
5
|
+
return (value != null &&
|
|
6
|
+
typeof value === 'object' &&
|
|
7
|
+
'list' in value && typeof value.list === 'function' &&
|
|
8
|
+
'read' in value && typeof value.read === 'function');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* VFS backed by Node.js filesystem. Used when StaticFolder is given a directory path string.
|
|
12
|
+
*/
|
|
13
|
+
export class NodeFileSystem {
|
|
14
|
+
root;
|
|
15
|
+
constructor(directory) {
|
|
16
|
+
// Resolve to absolute path at construction time
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
this.root = path.resolve(directory);
|
|
19
|
+
}
|
|
20
|
+
resolveSafe(subPath) {
|
|
21
|
+
const path = require('node:path');
|
|
22
|
+
// Reject paths with .. segments to prevent directory traversal
|
|
23
|
+
const normalized = path.normalize(subPath);
|
|
24
|
+
if (normalized.startsWith('..') || path.isAbsolute(normalized)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const resolved = path.resolve(this.root, normalized);
|
|
28
|
+
// Verify the resolved path is still within root
|
|
29
|
+
if (!resolved.startsWith(this.root + path.sep) && resolved !== this.root) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return resolved;
|
|
33
|
+
}
|
|
34
|
+
async list(subPath) {
|
|
35
|
+
const fs = require('node:fs/promises');
|
|
36
|
+
const path = require('node:path');
|
|
37
|
+
const dir = subPath ? this.resolveSafe(subPath) : this.root;
|
|
38
|
+
if (!dir)
|
|
39
|
+
return [];
|
|
40
|
+
try {
|
|
41
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
42
|
+
return entries.map((e) => e.isDirectory() ? e.name + '/' : e.name);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async read(subPath) {
|
|
49
|
+
const fs = require('node:fs/promises');
|
|
50
|
+
const resolved = this.resolveSafe(subPath);
|
|
51
|
+
if (!resolved)
|
|
52
|
+
return null;
|
|
53
|
+
try {
|
|
54
|
+
return await fs.readFile(resolved);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* VFS backed by an in-memory file map.
|
|
63
|
+
* Keys are paths like 'index.html' or 'css/style.css'.
|
|
64
|
+
* Values can be raw content or objects with a read() method for lazy evaluation.
|
|
65
|
+
*/
|
|
66
|
+
export class MemoryFileSystem {
|
|
67
|
+
files;
|
|
68
|
+
constructor(files) {
|
|
69
|
+
this.files = files;
|
|
70
|
+
}
|
|
71
|
+
list(subPath) {
|
|
72
|
+
const prefix = subPath ? (subPath.endsWith('/') ? subPath : subPath + '/') : '';
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
for (const key of Object.keys(this.files)) {
|
|
75
|
+
if (!key.startsWith(prefix))
|
|
76
|
+
continue;
|
|
77
|
+
const rest = key.slice(prefix.length);
|
|
78
|
+
const slashIndex = rest.indexOf('/');
|
|
79
|
+
if (slashIndex === -1) {
|
|
80
|
+
// Direct file in this directory
|
|
81
|
+
seen.add(rest);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Subdirectory
|
|
85
|
+
seen.add(rest.slice(0, slashIndex) + '/');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return Array.from(seen);
|
|
89
|
+
}
|
|
90
|
+
async read(subPath) {
|
|
91
|
+
const entry = this.files[subPath];
|
|
92
|
+
if (entry === undefined)
|
|
93
|
+
return null;
|
|
94
|
+
if (typeof entry === 'string' || entry instanceof Uint8Array)
|
|
95
|
+
return entry;
|
|
96
|
+
// Lazy read() object
|
|
97
|
+
return entry.read();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=virtual-file-system.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"virtual-file-system.js","sourceRoot":"","sources":["../../src/static/virtual-file-system.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modularizer/plat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Protocol and Language Agnostic Tooling Yielding Universal Semantics",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
@@ -38,6 +38,10 @@
|
|
|
38
38
|
"./python-browser": {
|
|
39
39
|
"import": "./dist/python-browser-entry.js",
|
|
40
40
|
"types": "./dist/python-browser-entry.d.ts"
|
|
41
|
+
},
|
|
42
|
+
"./static": {
|
|
43
|
+
"import": "./dist/static/index.js",
|
|
44
|
+
"types": "./dist/static/index.d.ts"
|
|
41
45
|
}
|
|
42
46
|
},
|
|
43
47
|
"files": [
|