@swissjs/swite 0.3.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/.changeset/config.json +11 -0
- package/.github/workflows/ci.yml +59 -0
- package/.github/workflows/publish.yml +50 -0
- package/.github/workflows/release.yml +53 -0
- package/BUILD_ANALYSIS.md +89 -0
- package/BUILD_STRATEGY.md +75 -0
- package/CHANGELOG.md +53 -0
- package/DIRECTIVE.md +488 -0
- package/__tests__/css-extraction.test.ts +261 -0
- package/__tests__/css-injection-integration.test.ts +247 -0
- package/__tests__/css-middleware.test.ts +191 -0
- package/__tests__/import-rewriter-bug.test.ts +135 -0
- package/dist/builder.d.ts +36 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +772 -0
- package/dist/cache/compilation-cache.d.ts +33 -0
- package/dist/cache/compilation-cache.d.ts.map +1 -0
- package/dist/cache/compilation-cache.js +130 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +85 -0
- package/dist/config-loader.d.ts +8 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-loader.js +40 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +7 -0
- package/dist/dev/pythonDevManager.d.ts +12 -0
- package/dist/dev/pythonDevManager.d.ts.map +1 -0
- package/dist/dev/pythonDevManager.js +85 -0
- package/dist/env.d.ts +19 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +112 -0
- package/dist/handlers/base-handler.d.ts +21 -0
- package/dist/handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/base-handler.js +38 -0
- package/dist/handlers/js-handler.d.ts +10 -0
- package/dist/handlers/js-handler.d.ts.map +1 -0
- package/dist/handlers/js-handler.js +87 -0
- package/dist/handlers/mjs-handler.d.ts +8 -0
- package/dist/handlers/mjs-handler.d.ts.map +1 -0
- package/dist/handlers/mjs-handler.js +44 -0
- package/dist/handlers/node-module-handler.d.ts +16 -0
- package/dist/handlers/node-module-handler.d.ts.map +1 -0
- package/dist/handlers/node-module-handler.js +267 -0
- package/dist/handlers/ts-handler.d.ts +11 -0
- package/dist/handlers/ts-handler.d.ts.map +1 -0
- package/dist/handlers/ts-handler.js +120 -0
- package/dist/handlers/ui-handler.d.ts +12 -0
- package/dist/handlers/ui-handler.d.ts.map +1 -0
- package/dist/handlers/ui-handler.js +182 -0
- package/dist/handlers/uix-handler.d.ts +12 -0
- package/dist/handlers/uix-handler.d.ts.map +1 -0
- package/dist/handlers/uix-handler.js +135 -0
- package/dist/hmr.d.ts +20 -0
- package/dist/hmr.d.ts.map +1 -0
- package/dist/hmr.js +265 -0
- package/dist/import-rewriter.d.ts +3 -0
- package/dist/import-rewriter.d.ts.map +1 -0
- package/dist/import-rewriter.js +351 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/middleware/hmr-routes.d.ts +12 -0
- package/dist/middleware/hmr-routes.d.ts.map +1 -0
- package/dist/middleware/hmr-routes.js +97 -0
- package/dist/middleware/middleware-setup.d.ts +23 -0
- package/dist/middleware/middleware-setup.d.ts.map +1 -0
- package/dist/middleware/middleware-setup.js +596 -0
- package/dist/middleware/static-files.d.ts +15 -0
- package/dist/middleware/static-files.d.ts.map +1 -0
- package/dist/middleware/static-files.js +585 -0
- package/dist/proxy/SwiteProxyError.d.ts +6 -0
- package/dist/proxy/SwiteProxyError.d.ts.map +1 -0
- package/dist/proxy/SwiteProxyError.js +9 -0
- package/dist/proxy/proxyToPython.d.ts +28 -0
- package/dist/proxy/proxyToPython.d.ts.map +1 -0
- package/dist/proxy/proxyToPython.js +66 -0
- package/dist/resolver/bare-import-resolver.d.ts +9 -0
- package/dist/resolver/bare-import-resolver.d.ts.map +1 -0
- package/dist/resolver/bare-import-resolver.js +363 -0
- package/dist/resolver/symlink-registry.d.ts +13 -0
- package/dist/resolver/symlink-registry.d.ts.map +1 -0
- package/dist/resolver/symlink-registry.js +98 -0
- package/dist/resolver/url-resolver.d.ts +11 -0
- package/dist/resolver/url-resolver.d.ts.map +1 -0
- package/dist/resolver/url-resolver.js +268 -0
- package/dist/resolver/workspace-package-resolver.d.ts +10 -0
- package/dist/resolver/workspace-package-resolver.d.ts.map +1 -0
- package/dist/resolver/workspace-package-resolver.js +185 -0
- package/dist/resolver.d.ts +17 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +191 -0
- package/dist/router/file-router.d.ts +19 -0
- package/dist/router/file-router.d.ts.map +1 -0
- package/dist/router/file-router.js +114 -0
- package/dist/server.d.ts +22 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +122 -0
- package/dist/utils/cdn-fallback.d.ts +14 -0
- package/dist/utils/cdn-fallback.d.ts.map +1 -0
- package/dist/utils/cdn-fallback.js +36 -0
- package/dist/utils/file-path-resolver.d.ts +9 -0
- package/dist/utils/file-path-resolver.d.ts.map +1 -0
- package/dist/utils/file-path-resolver.js +187 -0
- package/dist/utils/generate-import-map-cli.d.ts +3 -0
- package/dist/utils/generate-import-map-cli.d.ts.map +1 -0
- package/dist/utils/generate-import-map-cli.js +32 -0
- package/dist/utils/generate-import-map.d.ts +21 -0
- package/dist/utils/generate-import-map.d.ts.map +1 -0
- package/dist/utils/generate-import-map.js +119 -0
- package/dist/utils/package-finder.d.ts +24 -0
- package/dist/utils/package-finder.d.ts.map +1 -0
- package/dist/utils/package-finder.js +161 -0
- package/dist/utils/package-registry.d.ts +36 -0
- package/dist/utils/package-registry.d.ts.map +1 -0
- package/dist/utils/package-registry.js +159 -0
- package/dist/utils/workspace.d.ts +6 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +65 -0
- package/docs/IMPORT_REWRITING.md +164 -0
- package/docs/IMPORT_REWRITING_TROUBLESHOOTING.md +139 -0
- package/docs/PATH_RESOLUTION_GUIDE.md +221 -0
- package/package.json +49 -0
- package/src/adapters/proxy/SwiteProxyError.ts +12 -0
- package/src/adapters/proxy/proxyToPython.ts +88 -0
- package/src/build-engine/builder.ts +960 -0
- package/src/cli.ts +109 -0
- package/src/config/config-loader.ts +46 -0
- package/src/config/config.ts +34 -0
- package/src/config/env.ts +98 -0
- package/src/dev-engine/handlers/base-handler.ts +68 -0
- package/src/dev-engine/handlers/js-handler.ts +134 -0
- package/src/dev-engine/handlers/mjs-handler.ts +65 -0
- package/src/dev-engine/handlers/node-module-handler.ts +339 -0
- package/src/dev-engine/handlers/ts-handler.ts +143 -0
- package/src/dev-engine/handlers/ui-handler.ts +105 -0
- package/src/dev-engine/handlers/uix-handler.ts +90 -0
- package/src/dev-engine/hmr/hmr-client-template.ts +122 -0
- package/src/dev-engine/hmr/hmr.ts +173 -0
- package/src/dev-engine/middleware/hmr-routes.ts +120 -0
- package/src/dev-engine/middleware/middleware-setup.ts +351 -0
- package/src/dev-engine/middleware/static-files.ts +728 -0
- package/src/dev-engine/pythonDevManager.ts +116 -0
- package/src/dev-engine/router/file-router.ts +164 -0
- package/src/dev-engine/server.ts +152 -0
- package/src/index.ts +26 -0
- package/src/internal/cache/compilation-cache.ts +182 -0
- package/src/internal/generate-import-map-cli.ts +40 -0
- package/src/internal/generate-import-map.ts +154 -0
- package/src/kernel/package-finder.ts +164 -0
- package/src/kernel/package-registry.ts +198 -0
- package/src/kernel/workspace.ts +62 -0
- package/src/resolution/bare-import-resolver.ts +400 -0
- package/src/resolution/cdn/cdn-fallback.ts +37 -0
- package/src/resolution/path/file-path-resolver.ts +190 -0
- package/src/resolution/path/path-fixup.ts +19 -0
- package/src/resolution/resolver.ts +198 -0
- package/src/resolution/rewriting/import-rewriter.ts +237 -0
- package/src/resolution/symlink-registry.ts +114 -0
- package/src/resolution/url-resolver.ts +231 -0
- package/src/resolution/workspace-package-resolver.ts +94 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compilation cache with dependency tracking
|
|
3
|
+
* Invalidates when source file or dependencies change
|
|
4
|
+
*/
|
|
5
|
+
export declare class CompilationCache {
|
|
6
|
+
private cache;
|
|
7
|
+
private readonly maxSize;
|
|
8
|
+
/**
|
|
9
|
+
* Get cached compilation result if valid
|
|
10
|
+
*/
|
|
11
|
+
get(filePath: string, getDependencies: (compiled: string) => Promise<string[]>): Promise<string | null>;
|
|
12
|
+
/**
|
|
13
|
+
* Store compilation result in cache
|
|
14
|
+
*/
|
|
15
|
+
set(filePath: string, compiled: string, rewritten: string, getDependencies: (compiled: string) => Promise<string[]>): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Clear cache for a specific file
|
|
18
|
+
*/
|
|
19
|
+
clear(filePath: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Clear entire cache
|
|
22
|
+
*/
|
|
23
|
+
clearAll(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Get cache statistics
|
|
26
|
+
*/
|
|
27
|
+
getStats(): {
|
|
28
|
+
size: number;
|
|
29
|
+
maxSize: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export declare const compilationCache: CompilationCache;
|
|
33
|
+
//# sourceMappingURL=compilation-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compilation-cache.d.ts","sourceRoot":"","sources":["../../src/cache/compilation-cache.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAEhC;;OAEG;IACG,GAAG,CACP,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,GACvD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA+EzB;;OAEG;IACG,GAAG,CACP,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,GACvD,OAAO,CAAC,IAAI,CAAC;IAiChB;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI7B;;OAEG;IACH,QAAQ,IAAI,IAAI;IAKhB;;OAEG;IACH,QAAQ,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAM9C;AAGD,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
* SWITE - SWISS Development Server
|
|
4
|
+
* Compilation Cache for .ui, .uix, .ts files
|
|
5
|
+
* Licensed under the MIT License.
|
|
6
|
+
*/
|
|
7
|
+
import { promises as fs } from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
/**
|
|
11
|
+
* Compilation cache with dependency tracking
|
|
12
|
+
* Invalidates when source file or dependencies change
|
|
13
|
+
*/
|
|
14
|
+
export class CompilationCache {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.cache = new Map();
|
|
17
|
+
this.maxSize = 1000; // Prevent memory leaks
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get cached compilation result if valid
|
|
21
|
+
*/
|
|
22
|
+
async get(filePath, getDependencies) {
|
|
23
|
+
const entry = this.cache.get(filePath);
|
|
24
|
+
if (!entry) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
// Check if source file changed
|
|
28
|
+
try {
|
|
29
|
+
const stats = await fs.stat(filePath);
|
|
30
|
+
if (stats.mtimeMs !== entry.mtime) {
|
|
31
|
+
console.log(chalk.yellow(`[Cache] Invalidating ${filePath}: file modified`));
|
|
32
|
+
this.cache.delete(filePath);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
// File deleted or inaccessible
|
|
38
|
+
this.cache.delete(filePath);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
// Check if dependencies changed
|
|
42
|
+
const currentDeps = await getDependencies(entry.compiled);
|
|
43
|
+
const depsChanged = currentDeps.length !== entry.dependencies.length ||
|
|
44
|
+
currentDeps.some((dep, i) => dep !== entry.dependencies[i]);
|
|
45
|
+
if (depsChanged) {
|
|
46
|
+
console.log(chalk.yellow(`[Cache] Invalidating ${filePath}: dependencies changed`));
|
|
47
|
+
this.cache.delete(filePath);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
// Check if dependencies still exist and haven't changed
|
|
51
|
+
for (const dep of entry.dependencies) {
|
|
52
|
+
try {
|
|
53
|
+
const depStats = await fs.stat(dep);
|
|
54
|
+
// If dependency was modified after cache entry, invalidate
|
|
55
|
+
if (depStats.mtimeMs > entry.timestamp) {
|
|
56
|
+
console.log(chalk.yellow(`[Cache] Invalidating ${filePath}: dependency ${dep} modified`));
|
|
57
|
+
this.cache.delete(filePath);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Dependency deleted or inaccessible
|
|
63
|
+
console.log(chalk.yellow(`[Cache] Invalidating ${filePath}: dependency ${dep} not found`));
|
|
64
|
+
this.cache.delete(filePath);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Check if cached content has stale CDN URLs (from before import rewriter fix)
|
|
69
|
+
if (entry.rewritten.includes("cdn.jsdelivr.net") || entry.rewritten.includes("esm.sh")) {
|
|
70
|
+
console.log(chalk.yellow(`[Cache] Invalidating ${filePath}: contains stale CDN URLs`));
|
|
71
|
+
this.cache.delete(filePath);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
console.log(chalk.green(`[Cache] ✅ Cache hit for ${filePath}`));
|
|
75
|
+
return entry.rewritten;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Store compilation result in cache
|
|
79
|
+
*/
|
|
80
|
+
async set(filePath, compiled, rewritten, getDependencies) {
|
|
81
|
+
// Enforce max size (LRU eviction)
|
|
82
|
+
if (this.cache.size >= this.maxSize) {
|
|
83
|
+
// Remove oldest entry (simple FIFO)
|
|
84
|
+
const firstKey = this.cache.keys().next().value;
|
|
85
|
+
if (firstKey) {
|
|
86
|
+
this.cache.delete(firstKey);
|
|
87
|
+
console.log(chalk.gray(`[Cache] Evicted ${firstKey} (cache full)`));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const stats = await fs.stat(filePath);
|
|
92
|
+
const dependencies = await getDependencies(compiled);
|
|
93
|
+
this.cache.set(filePath, {
|
|
94
|
+
compiled,
|
|
95
|
+
rewritten,
|
|
96
|
+
mtime: stats.mtimeMs,
|
|
97
|
+
dependencies,
|
|
98
|
+
timestamp: Date.now(),
|
|
99
|
+
});
|
|
100
|
+
console.log(chalk.green(`[Cache] ✅ Cached ${filePath} (${dependencies.length} deps)`));
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
console.warn(chalk.yellow(`[Cache] Failed to cache ${filePath}:`, error));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Clear cache for a specific file
|
|
108
|
+
*/
|
|
109
|
+
clear(filePath) {
|
|
110
|
+
this.cache.delete(filePath);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Clear entire cache
|
|
114
|
+
*/
|
|
115
|
+
clearAll() {
|
|
116
|
+
this.cache.clear();
|
|
117
|
+
console.log(chalk.gray("[Cache] Cleared all entries"));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get cache statistics
|
|
121
|
+
*/
|
|
122
|
+
getStats() {
|
|
123
|
+
return {
|
|
124
|
+
size: this.cache.size,
|
|
125
|
+
maxSize: this.maxSize,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Singleton instance
|
|
130
|
+
export const compilationCache = new CompilationCache();
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { SwiteServer } from "./server.js";
|
|
5
|
+
import { loadUserConfig } from "./config-loader.js";
|
|
6
|
+
import { startPythonDevService, stopPythonDevService, } from "./dev/pythonDevManager.js";
|
|
7
|
+
import { setProductionMode } from "./proxy/proxyToPython.js";
|
|
8
|
+
const [, , command, ...args] = process.argv;
|
|
9
|
+
const root = resolve(process.cwd());
|
|
10
|
+
async function dev() {
|
|
11
|
+
const config = await loadUserConfig(root);
|
|
12
|
+
const python = config.services?.python;
|
|
13
|
+
if (python?.autoStart) {
|
|
14
|
+
await startPythonDevService(python, root);
|
|
15
|
+
}
|
|
16
|
+
// Relay SIGINT: kill Python, then exit cleanly
|
|
17
|
+
process.on("SIGINT", () => {
|
|
18
|
+
stopPythonDevService();
|
|
19
|
+
process.exit(0);
|
|
20
|
+
});
|
|
21
|
+
// Ensure Python is killed if Node crashes
|
|
22
|
+
process.on("exit", () => {
|
|
23
|
+
stopPythonDevService();
|
|
24
|
+
});
|
|
25
|
+
const server = new SwiteServer({
|
|
26
|
+
root,
|
|
27
|
+
port: config.server?.port ?? 3000,
|
|
28
|
+
host: config.server?.host ?? "localhost",
|
|
29
|
+
publicDir: "public",
|
|
30
|
+
open: false,
|
|
31
|
+
});
|
|
32
|
+
await server.start();
|
|
33
|
+
}
|
|
34
|
+
async function start() {
|
|
35
|
+
const config = await loadUserConfig(root);
|
|
36
|
+
const python = config.services?.python;
|
|
37
|
+
setProductionMode();
|
|
38
|
+
if (python && !process.env["PYTHON_SERVICE_URL"]) {
|
|
39
|
+
console.warn(chalk.yellow("[swite] WARNING: services.python is configured but PYTHON_SERVICE_URL is not set.\n" +
|
|
40
|
+
" Proxy calls to Python will fail. Set PYTHON_SERVICE_URL to the running service URL."));
|
|
41
|
+
}
|
|
42
|
+
const server = new SwiteServer({
|
|
43
|
+
root,
|
|
44
|
+
port: config.server?.port ?? 3000,
|
|
45
|
+
host: config.server?.host ?? "localhost",
|
|
46
|
+
publicDir: "public",
|
|
47
|
+
open: false,
|
|
48
|
+
});
|
|
49
|
+
await server.start();
|
|
50
|
+
}
|
|
51
|
+
async function build() {
|
|
52
|
+
const { SwiteBuilder } = await import("./builder.js");
|
|
53
|
+
const config = await loadUserConfig(root);
|
|
54
|
+
const builder = new SwiteBuilder({
|
|
55
|
+
root,
|
|
56
|
+
entry: resolve(root, "src/index.ui"),
|
|
57
|
+
outDir: resolve(root, "dist"),
|
|
58
|
+
});
|
|
59
|
+
await builder.build();
|
|
60
|
+
}
|
|
61
|
+
switch (command) {
|
|
62
|
+
case "dev":
|
|
63
|
+
dev().catch((err) => {
|
|
64
|
+
console.error(chalk.red("[swite] fatal:"), err);
|
|
65
|
+
stopPythonDevService();
|
|
66
|
+
process.exit(1);
|
|
67
|
+
});
|
|
68
|
+
break;
|
|
69
|
+
case "start":
|
|
70
|
+
start().catch((err) => {
|
|
71
|
+
console.error(chalk.red("[swite] fatal:"), err);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
});
|
|
74
|
+
break;
|
|
75
|
+
case "build":
|
|
76
|
+
build().catch((err) => {
|
|
77
|
+
console.error(chalk.red("[swite] build failed:"), err);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
});
|
|
80
|
+
break;
|
|
81
|
+
default:
|
|
82
|
+
console.error(chalk.red(`[swite] unknown command: ${command ?? "(none)"}`));
|
|
83
|
+
console.error("Usage: swite <dev|build|start>");
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SwiteUserConfig } from "./config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Load swiss.config.ts from the project root.
|
|
4
|
+
* Transpiles to a temp ESM file via esbuild, imports it, then cleans up.
|
|
5
|
+
* Returns empty config if no config file found.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadUserConfig(root: string): Promise<SwiteUserConfig>;
|
|
8
|
+
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../src/config-loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAgC3E"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { build } from "esbuild";
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
/**
|
|
8
|
+
* Load swiss.config.ts from the project root.
|
|
9
|
+
* Transpiles to a temp ESM file via esbuild, imports it, then cleans up.
|
|
10
|
+
* Returns empty config if no config file found.
|
|
11
|
+
*/
|
|
12
|
+
export async function loadUserConfig(root) {
|
|
13
|
+
const tsConfig = join(root, "swiss.config.ts");
|
|
14
|
+
const jsConfig = join(root, "swiss.config.js");
|
|
15
|
+
const configPath = existsSync(tsConfig)
|
|
16
|
+
? tsConfig
|
|
17
|
+
: existsSync(jsConfig)
|
|
18
|
+
? jsConfig
|
|
19
|
+
: null;
|
|
20
|
+
if (!configPath) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
const tempDir = await mkdtemp(join(tmpdir(), "swite-config-"));
|
|
24
|
+
const outFile = join(tempDir, "swiss.config.mjs");
|
|
25
|
+
try {
|
|
26
|
+
await build({
|
|
27
|
+
entryPoints: [configPath],
|
|
28
|
+
bundle: false,
|
|
29
|
+
format: "esm",
|
|
30
|
+
outfile: outFile,
|
|
31
|
+
platform: "node",
|
|
32
|
+
logLevel: "silent",
|
|
33
|
+
});
|
|
34
|
+
const mod = await import(pathToFileURL(outFile).href);
|
|
35
|
+
return (mod.default ?? {});
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface PythonServiceConfig {
|
|
2
|
+
/** Path to the Python entry file, relative to project root */
|
|
3
|
+
entry: string;
|
|
4
|
+
/** Port the Python service listens on */
|
|
5
|
+
port: number;
|
|
6
|
+
/** Whether swite dev should spawn the Python process automatically */
|
|
7
|
+
autoStart: boolean;
|
|
8
|
+
/** Health check endpoint polled before Node server starts */
|
|
9
|
+
healthCheck: string;
|
|
10
|
+
/** Additional environment variables passed to the Python process */
|
|
11
|
+
env?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
export interface ServicesConfig {
|
|
14
|
+
python?: PythonServiceConfig;
|
|
15
|
+
}
|
|
16
|
+
export interface ServerConfig {
|
|
17
|
+
port?: number;
|
|
18
|
+
host?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface SwiteUserConfig {
|
|
21
|
+
server?: ServerConfig;
|
|
22
|
+
services?: ServicesConfig;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Define swite configuration with full TypeScript validation.
|
|
26
|
+
* Unknown fields are rejected at compile time.
|
|
27
|
+
*/
|
|
28
|
+
export declare function defineConfig(config: SwiteUserConfig): SwiteUserConfig;
|
|
29
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,SAAS,EAAE,OAAO,CAAC;IACnB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAErE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PythonServiceConfig } from "../config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Spawn the Python service and wait until its health endpoint responds 200.
|
|
4
|
+
* Streams stdout/stderr line-buffered, prefixed with [python].
|
|
5
|
+
* Also calls initPythonProxy so proxyToPython works without PYTHON_SERVICE_URL.
|
|
6
|
+
*/
|
|
7
|
+
export declare function startPythonDevService(config: PythonServiceConfig, projectRoot: string): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Send SIGTERM to the Python child process if running.
|
|
10
|
+
*/
|
|
11
|
+
export declare function stopPythonDevService(): void;
|
|
12
|
+
//# sourceMappingURL=pythonDevManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pythonDevManager.d.ts","sourceRoot":"","sources":["../../src/dev/pythonDevManager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAQxD;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,mBAAmB,EAC3B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAuCf;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAM3C"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { initPythonProxy } from "../proxy/proxyToPython.js";
|
|
5
|
+
const POLL_INTERVAL_MS = 500;
|
|
6
|
+
const HEALTH_TIMEOUT_MS = 15000;
|
|
7
|
+
const BACKOFF_THRESHOLD = 5;
|
|
8
|
+
let _child = null;
|
|
9
|
+
/**
|
|
10
|
+
* Spawn the Python service and wait until its health endpoint responds 200.
|
|
11
|
+
* Streams stdout/stderr line-buffered, prefixed with [python].
|
|
12
|
+
* Also calls initPythonProxy so proxyToPython works without PYTHON_SERVICE_URL.
|
|
13
|
+
*/
|
|
14
|
+
export async function startPythonDevService(config, projectRoot) {
|
|
15
|
+
const entryPath = resolve(projectRoot, config.entry);
|
|
16
|
+
const healthUrl = `http://localhost:${config.port}${config.healthCheck}`;
|
|
17
|
+
const pythonCmd = process.platform === "win32" ? "python" : "python3";
|
|
18
|
+
console.log(chalk.blue(`[python] spawning: ${pythonCmd} ${config.entry} (port ${config.port})`));
|
|
19
|
+
const env = {
|
|
20
|
+
...process.env,
|
|
21
|
+
...config.env,
|
|
22
|
+
PORT: String(config.port),
|
|
23
|
+
};
|
|
24
|
+
_child = spawn(pythonCmd, [entryPath], {
|
|
25
|
+
env,
|
|
26
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
27
|
+
});
|
|
28
|
+
pipeLines(_child.stdout, chalk.cyan("[python] "));
|
|
29
|
+
pipeLines(_child.stderr, chalk.yellow("[python] "));
|
|
30
|
+
_child.on("exit", (code) => {
|
|
31
|
+
if (code !== null && code !== 0) {
|
|
32
|
+
console.error(chalk.red(`\n[python] process exited with code ${code} — Node server continuing in degraded mode\n`));
|
|
33
|
+
}
|
|
34
|
+
_child = null;
|
|
35
|
+
});
|
|
36
|
+
initPythonProxy(config);
|
|
37
|
+
await pollHealth(healthUrl);
|
|
38
|
+
console.log(chalk.green(`[python] healthy — ${healthUrl}`));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Send SIGTERM to the Python child process if running.
|
|
42
|
+
*/
|
|
43
|
+
export function stopPythonDevService() {
|
|
44
|
+
if (_child) {
|
|
45
|
+
console.log(chalk.gray("[python] shutting down..."));
|
|
46
|
+
_child.kill("SIGTERM");
|
|
47
|
+
_child = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// ── internals ────────────────────────────────────────────────────────────────
|
|
51
|
+
function pipeLines(stream, prefix) {
|
|
52
|
+
if (!stream)
|
|
53
|
+
return;
|
|
54
|
+
let buffer = "";
|
|
55
|
+
stream.on("data", (chunk) => {
|
|
56
|
+
buffer += chunk.toString();
|
|
57
|
+
const lines = buffer.split("\n");
|
|
58
|
+
buffer = lines.pop() ?? "";
|
|
59
|
+
for (const line of lines) {
|
|
60
|
+
if (line.trim())
|
|
61
|
+
process.stdout.write(prefix + line + "\n");
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async function pollHealth(url) {
|
|
66
|
+
const deadline = Date.now() + HEALTH_TIMEOUT_MS;
|
|
67
|
+
let attempt = 0;
|
|
68
|
+
while (Date.now() < deadline) {
|
|
69
|
+
try {
|
|
70
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(1000) });
|
|
71
|
+
if (res.ok)
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// not ready yet
|
|
76
|
+
}
|
|
77
|
+
attempt++;
|
|
78
|
+
const delay = attempt <= BACKOFF_THRESHOLD
|
|
79
|
+
? POLL_INTERVAL_MS
|
|
80
|
+
: Math.min(POLL_INTERVAL_MS * Math.pow(2, attempt - BACKOFF_THRESHOLD), 3000);
|
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
82
|
+
}
|
|
83
|
+
stopPythonDevService();
|
|
84
|
+
throw new Error(`[python] health check timed out after ${HEALTH_TIMEOUT_MS}ms — is ${url} reachable?`);
|
|
85
|
+
}
|
package/dist/env.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface EnvConfig {
|
|
2
|
+
mode?: "development" | "production";
|
|
3
|
+
prefix?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Load environment variables from .env files
|
|
7
|
+
* Supports .env, .env.local, .env.[mode], .env.[mode].local
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadEnv(root: string, mode?: string): Record<string, string>;
|
|
10
|
+
/**
|
|
11
|
+
* Generate import.meta.env replacement code
|
|
12
|
+
* Injects environment variables as a module that can be imported
|
|
13
|
+
*/
|
|
14
|
+
export declare function generateEnvModule(env: Record<string, string>, prefix?: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Inject import.meta.env polyfill into code
|
|
17
|
+
*/
|
|
18
|
+
export declare function injectEnvPolyfill(code: string): string;
|
|
19
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,MAAsB,GAC3B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAwBxB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,MAAM,GAAE,MAAiB,GACxB,MAAM,CAwCR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqCtD"}
|
package/dist/env.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Environment Variable Support for SWITE
|
|
3
|
+
* Provides import.meta.env replacement for SWITE
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
/**
|
|
8
|
+
* Load environment variables from .env files
|
|
9
|
+
* Supports .env, .env.local, .env.[mode], .env.[mode].local
|
|
10
|
+
*/
|
|
11
|
+
export function loadEnv(root, mode = "development") {
|
|
12
|
+
const env = {};
|
|
13
|
+
const envFiles = [`.env.${mode}.local`, `.env.${mode}`, `.env.local`, `.env`];
|
|
14
|
+
for (const file of envFiles) {
|
|
15
|
+
const envPath = join(root, file);
|
|
16
|
+
if (existsSync(envPath)) {
|
|
17
|
+
const content = readFileSync(envPath, "utf-8");
|
|
18
|
+
for (const line of content.split("\n")) {
|
|
19
|
+
const trimmed = line.trim();
|
|
20
|
+
if (trimmed && !trimmed.startsWith("#")) {
|
|
21
|
+
const match = trimmed.match(/^([^=]+)=(.*)$/);
|
|
22
|
+
if (match) {
|
|
23
|
+
const [, key, value] = match;
|
|
24
|
+
// Remove quotes if present
|
|
25
|
+
const cleanValue = value.replace(/^["']|["']$/g, "");
|
|
26
|
+
env[key.trim()] = cleanValue;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return env;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Generate import.meta.env replacement code
|
|
36
|
+
* Injects environment variables as a module that can be imported
|
|
37
|
+
*/
|
|
38
|
+
export function generateEnvModule(env, prefix = "SWITE_") {
|
|
39
|
+
// Filter env vars by prefix and expose them
|
|
40
|
+
const switeEnv = {};
|
|
41
|
+
const publicEnv = {};
|
|
42
|
+
for (const [key, value] of Object.entries(env)) {
|
|
43
|
+
if (key.startsWith(prefix)) {
|
|
44
|
+
// Remove prefix for SWITE_ prefixed vars
|
|
45
|
+
const cleanKey = key.slice(prefix.length);
|
|
46
|
+
switeEnv[cleanKey] = value;
|
|
47
|
+
publicEnv[key] = value;
|
|
48
|
+
}
|
|
49
|
+
else if (key.startsWith("PUBLIC_")) {
|
|
50
|
+
// PUBLIC_ vars are always exposed
|
|
51
|
+
publicEnv[key] = value;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Build env object with all variables
|
|
55
|
+
const allEnvEntries = [
|
|
56
|
+
...Object.entries(switeEnv).map(([key, value]) => [key, value]),
|
|
57
|
+
...Object.entries(publicEnv).map(([key, value]) => [key, value]),
|
|
58
|
+
["MODE", process.env.NODE_ENV || "development"],
|
|
59
|
+
["DEV", process.env.NODE_ENV !== "production"],
|
|
60
|
+
["PROD", process.env.NODE_ENV === "production"],
|
|
61
|
+
];
|
|
62
|
+
return `
|
|
63
|
+
// SWITE Environment Variables
|
|
64
|
+
// Generated at runtime - do not edit manually
|
|
65
|
+
export const env = {
|
|
66
|
+
${allEnvEntries
|
|
67
|
+
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
|
68
|
+
.join(",\n ")},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// For import.meta.env compatibility
|
|
72
|
+
if (typeof globalThis !== 'undefined') {
|
|
73
|
+
globalThis.__swite_env__ = env;
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Inject import.meta.env polyfill into code
|
|
79
|
+
*/
|
|
80
|
+
export function injectEnvPolyfill(code) {
|
|
81
|
+
// Check if import.meta.env is used
|
|
82
|
+
if (!code.includes("import.meta.env") && !code.includes("__swite_env__")) {
|
|
83
|
+
return code;
|
|
84
|
+
}
|
|
85
|
+
// Inject polyfill at the top - load env module first
|
|
86
|
+
const polyfill = `
|
|
87
|
+
// SWITE import.meta.env polyfill
|
|
88
|
+
import { env as switeEnv } from '/__swite_env';
|
|
89
|
+
if (typeof globalThis !== 'undefined') {
|
|
90
|
+
globalThis.__swite_env__ = switeEnv;
|
|
91
|
+
}
|
|
92
|
+
if (typeof import !== 'undefined' && import.meta) {
|
|
93
|
+
import.meta.env = switeEnv;
|
|
94
|
+
} else if (typeof globalThis !== 'undefined') {
|
|
95
|
+
// Fallback for environments without import.meta
|
|
96
|
+
if (!globalThis.import) {
|
|
97
|
+
globalThis.import = { meta: {} };
|
|
98
|
+
}
|
|
99
|
+
if (!globalThis.import.meta) {
|
|
100
|
+
globalThis.import.meta = {};
|
|
101
|
+
}
|
|
102
|
+
globalThis.import.meta.env = switeEnv;
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
// Find the first import statement or start of file
|
|
106
|
+
const firstImport = code.match(/^import\s+/m);
|
|
107
|
+
if (firstImport) {
|
|
108
|
+
const insertIndex = firstImport.index;
|
|
109
|
+
return (code.slice(0, insertIndex) + polyfill + "\n" + code.slice(insertIndex));
|
|
110
|
+
}
|
|
111
|
+
return polyfill + "\n" + code;
|
|
112
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Response } from "express";
|
|
2
|
+
import { ModuleResolver } from "../resolver.js";
|
|
3
|
+
export interface HandlerContext {
|
|
4
|
+
resolver: ModuleResolver;
|
|
5
|
+
root: string;
|
|
6
|
+
workspaceRoot: string | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Set cache-busting headers for development
|
|
10
|
+
*/
|
|
11
|
+
export declare function setDevHeaders(res: Response): void;
|
|
12
|
+
/**
|
|
13
|
+
* Base handler utilities
|
|
14
|
+
*/
|
|
15
|
+
export declare class BaseHandler {
|
|
16
|
+
protected context: HandlerContext;
|
|
17
|
+
constructor(context: HandlerContext);
|
|
18
|
+
protected resolveFilePath(url: string): Promise<string>;
|
|
19
|
+
protected fileExists(filePath: string): Promise<boolean>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=base-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/base-handler.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAMjD;AAED;;GAEG;AACH,qBAAa,WAAW;IACV,SAAS,CAAC,OAAO,EAAE,cAAc;gBAAvB,OAAO,EAAE,cAAc;cAE7B,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;cAI7C,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAQ/D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
* SWITE - SWISS Development Server
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from "node:fs";
|
|
7
|
+
import { ModuleResolver } from "../resolver.js";
|
|
8
|
+
import { resolveFilePath } from "../utils/file-path-resolver.js";
|
|
9
|
+
/**
|
|
10
|
+
* Set cache-busting headers for development
|
|
11
|
+
*/
|
|
12
|
+
export function setDevHeaders(res) {
|
|
13
|
+
// Prevent all caching during development
|
|
14
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
|
|
15
|
+
res.setHeader("Pragma", "no-cache");
|
|
16
|
+
res.setHeader("Expires", "0");
|
|
17
|
+
res.setHeader("Surrogate-Control", "no-store");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Base handler utilities
|
|
21
|
+
*/
|
|
22
|
+
export class BaseHandler {
|
|
23
|
+
constructor(context) {
|
|
24
|
+
this.context = context;
|
|
25
|
+
}
|
|
26
|
+
async resolveFilePath(url) {
|
|
27
|
+
return resolveFilePath(url, this.context.root, this.context.workspaceRoot);
|
|
28
|
+
}
|
|
29
|
+
async fileExists(filePath) {
|
|
30
|
+
try {
|
|
31
|
+
await fs.access(filePath);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Response } from "express";
|
|
2
|
+
import { BaseHandler, type HandlerContext } from "./base-handler.js";
|
|
3
|
+
export declare class JSHandler extends BaseHandler {
|
|
4
|
+
private uiHandler;
|
|
5
|
+
private uixHandler;
|
|
6
|
+
private tsHandler;
|
|
7
|
+
constructor(context: HandlerContext);
|
|
8
|
+
handle(url: string, res: Response): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=js-handler.d.ts.map
|