veryfront 0.1.73 → 0.1.75
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/esm/cli/commands/knowledge/command-help.d.ts.map +1 -1
- package/esm/cli/commands/knowledge/command-help.js +3 -1
- package/esm/cli/commands/knowledge/command.d.ts +34 -5
- package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
- package/esm/cli/commands/knowledge/command.js +151 -22
- package/esm/cli/commands/knowledge/parser-source.d.ts.map +1 -1
- package/esm/cli/commands/knowledge/parser-source.js +110 -5
- package/esm/deno.d.ts +2 -0
- package/esm/deno.js +3 -1
- package/esm/src/data/data-fetcher.d.ts +11 -1
- package/esm/src/data/data-fetcher.d.ts.map +1 -1
- package/esm/src/data/data-fetcher.js +5 -2
- package/esm/src/data/index.d.ts +1 -1
- package/esm/src/data/index.d.ts.map +1 -1
- package/esm/src/data/server-data-fetcher.d.ts +14 -1
- package/esm/src/data/server-data-fetcher.d.ts.map +1 -1
- package/esm/src/data/server-data-fetcher.js +49 -3
- package/esm/src/rendering/orchestrator/lifecycle.d.ts +4 -0
- package/esm/src/rendering/orchestrator/lifecycle.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/lifecycle.js +8 -0
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/pipeline.js +6 -1
- package/esm/src/rendering/orchestrator/ssr-orchestrator.d.ts +26 -1
- package/esm/src/rendering/orchestrator/ssr-orchestrator.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/ssr-orchestrator.js +77 -1
- package/esm/src/routing/api/handler.d.ts.map +1 -1
- package/esm/src/routing/api/handler.js +6 -2
- package/esm/src/routing/api/route-executor.d.ts +8 -2
- package/esm/src/routing/api/route-executor.d.ts.map +1 -1
- package/esm/src/routing/api/route-executor.js +131 -3
- package/esm/src/security/deno-permissions.d.ts +6 -0
- package/esm/src/security/deno-permissions.d.ts.map +1 -1
- package/esm/src/security/deno-permissions.js +10 -0
- package/esm/src/security/sandbox/project-worker.d.ts +61 -0
- package/esm/src/security/sandbox/project-worker.d.ts.map +1 -0
- package/esm/src/security/sandbox/project-worker.js +318 -0
- package/esm/src/security/sandbox/worker-permissions.d.ts +30 -0
- package/esm/src/security/sandbox/worker-permissions.d.ts.map +1 -0
- package/esm/src/security/sandbox/worker-permissions.js +60 -0
- package/esm/src/security/sandbox/worker-pool.d.ts +87 -0
- package/esm/src/security/sandbox/worker-pool.d.ts.map +1 -0
- package/esm/src/security/sandbox/worker-pool.js +356 -0
- package/esm/src/security/sandbox/worker-types.d.ts +165 -0
- package/esm/src/security/sandbox/worker-types.d.ts.map +1 -0
- package/esm/src/security/sandbox/worker-types.js +17 -0
- package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts +2 -0
- package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/ssr/ssr.handler.js +6 -2
- package/esm/src/server/project-env/storage.d.ts +6 -0
- package/esm/src/server/project-env/storage.d.ts.map +1 -1
- package/esm/src/server/project-env/storage.js +8 -0
- package/esm/src/server/runtime-handler/adapter-factory.d.ts +3 -0
- package/esm/src/server/runtime-handler/adapter-factory.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/adapter-factory.js +6 -5
- package/esm/src/server/runtime-handler/index.d.ts +33 -0
- package/esm/src/server/runtime-handler/index.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/index.js +103 -37
- package/esm/src/server/runtime-handler/local-project-discovery.d.ts +32 -4
- package/esm/src/server/runtime-handler/local-project-discovery.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/local-project-discovery.js +46 -16
- package/esm/src/server/runtime-handler/project-isolation.d.ts +5 -0
- package/esm/src/server/runtime-handler/project-isolation.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/project-isolation.js +44 -0
- package/esm/src/server/services/rendering/ssr.service.d.ts +19 -1
- package/esm/src/server/services/rendering/ssr.service.d.ts.map +1 -1
- package/esm/src/server/services/rendering/ssr.service.js +9 -1
- package/esm/src/server/shared/renderer/adapter.d.ts +25 -0
- package/esm/src/server/shared/renderer/adapter.d.ts.map +1 -1
- package/esm/src/server/shared/renderer/adapter.js +83 -10
- package/esm/src/server/shared/renderer/index.d.ts +1 -1
- package/esm/src/server/shared/renderer/index.d.ts.map +1 -1
- package/esm/src/server/shared/renderer/index.js +1 -1
- package/esm/src/server/shared/renderer/memory/pressure.d.ts +7 -0
- package/esm/src/server/shared/renderer/memory/pressure.d.ts.map +1 -1
- package/esm/src/server/shared/renderer/memory/pressure.js +7 -0
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/path-resolver.d.ts +4 -4
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/path-resolver.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/path-resolver.js +15 -15
- package/esm/src/utils/index.d.ts +10 -1
- package/esm/src/utils/index.d.ts.map +1 -1
- package/esm/src/utils/index.js +9 -1
- package/esm/src/utils/logger/index.d.ts +1 -1
- package/esm/src/utils/logger/index.d.ts.map +1 -1
- package/esm/src/utils/logger/index.js +1 -1
- package/esm/src/utils/logger/logger.d.ts +14 -0
- package/esm/src/utils/logger/logger.d.ts.map +1 -1
- package/esm/src/utils/logger/logger.js +17 -0
- package/esm/src/workflow/claude-code/tool.d.ts +5 -5
- package/package.json +4 -1
- package/src/cli/commands/knowledge/command-help.ts +3 -1
- package/src/cli/commands/knowledge/command.ts +180 -22
- package/src/cli/commands/knowledge/parser-source.ts +110 -5
- package/src/deno.js +3 -1
- package/src/src/data/data-fetcher.ts +18 -2
- package/src/src/data/index.ts +1 -1
- package/src/src/data/server-data-fetcher.ts +78 -3
- package/src/src/rendering/orchestrator/lifecycle.ts +11 -0
- package/src/src/rendering/orchestrator/pipeline.ts +7 -2
- package/src/src/rendering/orchestrator/ssr-orchestrator.ts +119 -0
- package/src/src/routing/api/handler.ts +16 -3
- package/src/src/routing/api/route-executor.ts +222 -1
- package/src/src/security/deno-permissions.ts +11 -0
- package/src/src/security/sandbox/project-worker.ts +416 -0
- package/src/src/security/sandbox/worker-permissions.ts +74 -0
- package/src/src/security/sandbox/worker-pool.ts +451 -0
- package/src/src/security/sandbox/worker-types.ts +209 -0
- package/src/src/server/handlers/request/ssr/ssr.handler.ts +11 -2
- package/src/src/server/project-env/storage.ts +9 -0
- package/src/src/server/runtime-handler/adapter-factory.ts +13 -5
- package/src/src/server/runtime-handler/index.ts +132 -39
- package/src/src/server/runtime-handler/local-project-discovery.ts +51 -17
- package/src/src/server/runtime-handler/project-isolation.ts +53 -0
- package/src/src/server/services/rendering/ssr.service.ts +34 -3
- package/src/src/server/shared/renderer/adapter.ts +107 -8
- package/src/src/server/shared/renderer/index.ts +7 -1
- package/src/src/server/shared/renderer/memory/pressure.ts +8 -0
- package/src/src/transforms/pipeline/stages/ssr-vf-modules/path-resolver.ts +18 -12
- package/src/src/utils/index.ts +11 -0
- package/src/src/utils/logger/index.ts +1 -0
- package/src/src/utils/logger/logger.ts +34 -0
|
@@ -15,7 +15,7 @@ export class DataFetcher {
|
|
|
15
15
|
this.staticFetcher = new StaticDataFetcher(this.cacheManager);
|
|
16
16
|
this.pathsFetcher = new StaticPathsFetcher();
|
|
17
17
|
}
|
|
18
|
-
fetchData(pageModule, context, mode = "development") {
|
|
18
|
+
fetchData(pageModule, context, mode = "development", options) {
|
|
19
19
|
const preferServerData = mode === "development" || !pageModule.getStaticData;
|
|
20
20
|
const useServer = preferServerData && !!pageModule.getServerData;
|
|
21
21
|
const useStatic = !useServer && !!pageModule.getStaticData;
|
|
@@ -24,9 +24,12 @@ export class DataFetcher {
|
|
|
24
24
|
: useStatic
|
|
25
25
|
? "static"
|
|
26
26
|
: "none";
|
|
27
|
+
const isolationOptions = options
|
|
28
|
+
? { modulePath: options.modulePath, projectDir: options.projectDir }
|
|
29
|
+
: undefined;
|
|
27
30
|
return withSpan(SpanNames.DATA_FETCH, () => {
|
|
28
31
|
if (useServer)
|
|
29
|
-
return this.serverFetcher.fetch(pageModule, context);
|
|
32
|
+
return this.serverFetcher.fetch(pageModule, context, isolationOptions);
|
|
30
33
|
if (useStatic)
|
|
31
34
|
return this.staticFetcher.fetch(pageModule, context);
|
|
32
35
|
return Promise.resolve({ props: {} });
|
package/esm/src/data/index.d.ts
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
* @module data
|
|
6
6
|
*/
|
|
7
7
|
export type { CacheEntry, DataContext, DataResult, InferGetServerDataProps, PageWithData, StaticPathsResult, } from "./types.js";
|
|
8
|
-
export { DataFetcher } from "./data-fetcher.js";
|
|
8
|
+
export { DataFetcher, type FetchDataOptions } from "./data-fetcher.js";
|
|
9
9
|
export { notFound, redirect } from "./helpers.js";
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/data/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,uBAAuB,EACvB,YAAY,EACZ,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/data/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,uBAAuB,EACvB,YAAY,EACZ,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import type { DataContext, DataResult, PageWithData } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options for isolated data fetching through Worker pool.
|
|
4
|
+
*/
|
|
5
|
+
export interface ServerDataFetchOptions {
|
|
6
|
+
/** Absolute path to the module containing getServerData */
|
|
7
|
+
modulePath?: string;
|
|
8
|
+
/** Project directory for worker scoping */
|
|
9
|
+
projectDir?: string;
|
|
10
|
+
}
|
|
2
11
|
export declare class ServerDataFetcher {
|
|
3
|
-
fetch(pageModule: PageWithData, context: DataContext): Promise<DataResult>;
|
|
12
|
+
fetch(pageModule: PageWithData, context: DataContext, options?: ServerDataFetchOptions): Promise<DataResult>;
|
|
13
|
+
/**
|
|
14
|
+
* Execute getServerData in a per-project Worker.
|
|
15
|
+
*/
|
|
16
|
+
private fetchIsolated;
|
|
4
17
|
/**
|
|
5
18
|
* Log errors unconditionally. Production errors should always be logged.
|
|
6
19
|
* @see plans/architecture-audit/010-error-handling.md
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-data-fetcher.d.ts","sourceRoot":"","sources":["../../../src/src/data/server-data-fetcher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server-data-fetcher.d.ts","sourceRoot":"","sources":["../../../src/src/data/server-data-fetcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AASxE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,iBAAiB;IAC5B,KAAK,CACH,UAAU,EAAE,YAAY,EACxB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,UAAU,CAAC;IA8EtB;;OAEG;YACW,aAAa;IA2C3B;;;OAGG;IACH,OAAO,CAAC,QAAQ;CAGjB"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
1
2
|
import { serverLogger } from "../utils/index.js";
|
|
2
3
|
import { DATA_FETCH_TIMEOUT_MS } from "../config/defaults.js";
|
|
3
4
|
import { TimeoutError, withTimeoutThrow } from "../rendering/utils/stream-utils.js";
|
|
4
5
|
import { withSpan } from "../observability/tracing/otlp-setup.js";
|
|
5
6
|
import { CircuitBreakerOpen, getCircuitBreaker } from "../utils/circuit-breaker.js";
|
|
7
|
+
import { getWorkerPool, isDataIsolationEnabled } from "../security/sandbox/worker-pool.js";
|
|
6
8
|
export class ServerDataFetcher {
|
|
7
|
-
fetch(pageModule, context) {
|
|
9
|
+
fetch(pageModule, context, options) {
|
|
8
10
|
if (typeof pageModule.getServerData !== "function") {
|
|
9
11
|
return Promise.resolve({ props: {} });
|
|
10
12
|
}
|
|
@@ -15,10 +17,16 @@ export class ServerDataFetcher {
|
|
|
15
17
|
resetTimeoutMs: 30_000,
|
|
16
18
|
successThreshold: 2,
|
|
17
19
|
});
|
|
20
|
+
// Choose isolated or direct execution
|
|
21
|
+
const useIsolation = isDataIsolationEnabled() &&
|
|
22
|
+
!!options?.modulePath &&
|
|
23
|
+
!!options?.projectDir;
|
|
18
24
|
return withSpan("data.fetch_server", async () => {
|
|
19
25
|
const start = performance.now();
|
|
20
26
|
try {
|
|
21
|
-
const result = await circuitBreaker.execute(() => withTimeoutThrow(
|
|
27
|
+
const result = await circuitBreaker.execute(() => withTimeoutThrow(useIsolation
|
|
28
|
+
? this.fetchIsolated(options.modulePath, options.projectDir, context)
|
|
29
|
+
: Promise.resolve(pageModule.getServerData(context)), DATA_FETCH_TIMEOUT_MS, `getServerData for ${pathname}`));
|
|
22
30
|
if (result.redirect)
|
|
23
31
|
return { redirect: result.redirect };
|
|
24
32
|
if (result.notFound)
|
|
@@ -43,7 +51,11 @@ export class ServerDataFetcher {
|
|
|
43
51
|
});
|
|
44
52
|
throw error;
|
|
45
53
|
}
|
|
46
|
-
this.logError("DATA_FETCH_ERROR getServerData failed", error, {
|
|
54
|
+
this.logError("DATA_FETCH_ERROR getServerData failed", error, {
|
|
55
|
+
pathname,
|
|
56
|
+
durationMs,
|
|
57
|
+
isolated: useIsolation,
|
|
58
|
+
});
|
|
47
59
|
throw error;
|
|
48
60
|
}
|
|
49
61
|
}, {
|
|
@@ -51,8 +63,42 @@ export class ServerDataFetcher {
|
|
|
51
63
|
"data.pathname": pathname,
|
|
52
64
|
"data.timeout_ms": DATA_FETCH_TIMEOUT_MS,
|
|
53
65
|
"data.project_id": projectId,
|
|
66
|
+
"data.isolated": useIsolation,
|
|
54
67
|
});
|
|
55
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Execute getServerData in a per-project Worker.
|
|
71
|
+
*/
|
|
72
|
+
async fetchIsolated(modulePath, projectDir, context) {
|
|
73
|
+
const pool = getWorkerPool();
|
|
74
|
+
const body = context.request?.body ? new Uint8Array(await context.request.arrayBuffer()) : null;
|
|
75
|
+
const workerResponse = await pool.execute(projectDir, [projectDir], {
|
|
76
|
+
type: "fetch-data",
|
|
77
|
+
id: dntShim.crypto.randomUUID(),
|
|
78
|
+
modulePath,
|
|
79
|
+
context: {
|
|
80
|
+
params: context.params,
|
|
81
|
+
query: context.query?.toString() ?? "",
|
|
82
|
+
request: {
|
|
83
|
+
url: context.request?.url ?? context.url?.toString() ?? "http://localhost",
|
|
84
|
+
method: context.request?.method ?? "GET",
|
|
85
|
+
headers: context.request ? [...context.request.headers.entries()] : [],
|
|
86
|
+
body,
|
|
87
|
+
},
|
|
88
|
+
url: context.url?.toString() ?? "http://localhost",
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
if (workerResponse.type === "error") {
|
|
92
|
+
const err = new Error(workerResponse.error.message);
|
|
93
|
+
err.name = workerResponse.error.name;
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
if (workerResponse.type === "data-result") {
|
|
97
|
+
return workerResponse.result;
|
|
98
|
+
}
|
|
99
|
+
// Unexpected response type — shouldn't happen but be defensive
|
|
100
|
+
throw new Error(`Unexpected worker response type: ${workerResponse.type}`);
|
|
101
|
+
}
|
|
56
102
|
/**
|
|
57
103
|
* Log errors unconditionally. Production errors should always be logged.
|
|
58
104
|
* @see plans/architecture-audit/010-error-handling.md
|
|
@@ -8,6 +8,7 @@ import { PageResolver } from "../page-resolution/index.js";
|
|
|
8
8
|
import { ElementValidator } from "../element-validator/index.js";
|
|
9
9
|
import { SSRRenderer } from "../ssr-renderer.js";
|
|
10
10
|
import type { ConfigurationManager } from "./config.js";
|
|
11
|
+
import type { RuntimeAdapter } from "../../platform/adapters/base.js";
|
|
11
12
|
import type { MdxBundle } from "../../types/index.js";
|
|
12
13
|
import { CompilerService } from "./compiler-service.js";
|
|
13
14
|
export interface LifecycleOptions {
|
|
@@ -18,6 +19,8 @@ export interface LifecycleOptions {
|
|
|
18
19
|
projectId?: string;
|
|
19
20
|
/** Content source identifier for cache isolation (branch or release) */
|
|
20
21
|
contentSourceId?: string;
|
|
22
|
+
/** Injectable factory for testing — bypasses real service construction */
|
|
23
|
+
servicesFactory?: (adapter: RuntimeAdapter) => RendererServices;
|
|
21
24
|
}
|
|
22
25
|
export interface RendererServices {
|
|
23
26
|
componentRegistry: ComponentRegistry;
|
|
@@ -40,6 +43,7 @@ export declare class RendererLifecycle {
|
|
|
40
43
|
private contentSourceId?;
|
|
41
44
|
private services?;
|
|
42
45
|
private adapter;
|
|
46
|
+
private servicesFactory?;
|
|
43
47
|
constructor(options: LifecycleOptions);
|
|
44
48
|
initialize(): Promise<RendererServices>;
|
|
45
49
|
updateCompileMDX(compileMDX: (content: string, frontmatter?: Record<string, unknown>, filePath?: string) => Promise<MdxBundle>): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/lifecycle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AASrD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/lifecycle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AASrD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AASxD,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,oBAAoB,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,gBAAgB,CAAC;CACjE;AAED,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,cAAc,EAAE,mBAAmB,CAAC;IACpC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,eAAe,EAAE,eAAe,CAAC;IACjC,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,eAAe,CAAC;CAClC;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,CAAmB;IACpC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,eAAe,CAAC,CAAgD;gBAE5D,OAAO,EAAE,gBAAgB;IAS/B,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAqJ7C,gBAAgB,CACd,UAAU,EAAE,CACV,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,QAAQ,CAAC,EAAE,MAAM,KACd,OAAO,CAAC,SAAS,CAAC,GACtB,IAAI;IAcP,WAAW,IAAI,gBAAgB;IAazB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3C,cAAc,IAAI,IAAI;IAWtB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAS5B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|
|
@@ -26,12 +26,14 @@ export class RendererLifecycle {
|
|
|
26
26
|
contentSourceId;
|
|
27
27
|
services;
|
|
28
28
|
adapter;
|
|
29
|
+
servicesFactory;
|
|
29
30
|
constructor(options) {
|
|
30
31
|
this.configManager = options.configManager;
|
|
31
32
|
this.port = options.port;
|
|
32
33
|
this.moduleServerUrl = options.moduleServerUrl;
|
|
33
34
|
this.projectId = options.projectId;
|
|
34
35
|
this.contentSourceId = options.contentSourceId;
|
|
36
|
+
this.servicesFactory = options.servicesFactory;
|
|
35
37
|
}
|
|
36
38
|
async initialize() {
|
|
37
39
|
logger.debug("Initializing renderer services", {
|
|
@@ -43,6 +45,12 @@ export class RendererLifecycle {
|
|
|
43
45
|
const { runtime } = await import("../../platform/adapters/detect.js");
|
|
44
46
|
this.adapter = await runtime.get();
|
|
45
47
|
}
|
|
48
|
+
// Allow tests to bypass the full service graph construction
|
|
49
|
+
if (this.servicesFactory) {
|
|
50
|
+
this.services = this.servicesFactory(this.adapter);
|
|
51
|
+
logger.debug("Renderer services initialized via injected factory");
|
|
52
|
+
return this.services;
|
|
53
|
+
}
|
|
46
54
|
const projectDir = this.configManager.getProjectDir();
|
|
47
55
|
const mode = this.configManager.getMode();
|
|
48
56
|
const debugMode = this.configManager.isDebugMode();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA6ChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACxE,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,YAAY,EAAE,YAAY,CAAC;IAC3B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,qBAAqB,EAAE,sBAAsB,CAAC;CAC1E;AAQD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,kBAAkB,CAAqB;gBAEnC,MAAM,EAAE,oBAAoB;IAaxC;;;OAGG;IACH,gBAAgB,IAAI,IAAI;IAKxB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,sBAAsB;YAIhB,0BAA0B;IAaxC;;;;;;;;;OASG;YACW,qBAAqB;IAyDnC;;;OAGG;YACW,mBAAmB;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA6ChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACxE,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,YAAY,EAAE,YAAY,CAAC;IAC3B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,qBAAqB,EAAE,sBAAsB,CAAC;CAC1E;AAQD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,kBAAkB,CAAqB;gBAEnC,MAAM,EAAE,oBAAoB;IAaxC;;;OAGG;IACH,gBAAgB,IAAI,IAAI;IAKxB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,sBAAsB;YAIhB,0BAA0B;IAaxC;;;;;;;;;OASG;YACW,qBAAqB;IAyDnC;;;OAGG;YACW,mBAAmB;IA0H3B,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IA8P9E,+EAA+E;IACzE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA6LvF;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CAetB"}
|
|
@@ -183,8 +183,13 @@ export class RenderPipeline {
|
|
|
183
183
|
}
|
|
184
184
|
const dataResults = await withSpan(SpanNames.RENDER_FETCH_DATA, () => withTimeoutThrow(Promise.all(dataJobs.map(async (job) => {
|
|
185
185
|
try {
|
|
186
|
+
const jobPath = job.path;
|
|
187
|
+
const fetchOptions = {
|
|
188
|
+
modulePath: jobPath,
|
|
189
|
+
projectDir: this.config.projectDir,
|
|
190
|
+
};
|
|
186
191
|
const result = await this.dataFetcher
|
|
187
|
-
.fetchData(job.mod, dataContext, this.config.mode);
|
|
192
|
+
.fetchData(job.mod, dataContext, this.config.mode, fetchOptions);
|
|
188
193
|
return { ...job, result, error: null };
|
|
189
194
|
}
|
|
190
195
|
catch (error) {
|
|
@@ -15,10 +15,35 @@ export interface SSRRenderingResult {
|
|
|
15
15
|
finalStream: ReadableStream | null;
|
|
16
16
|
ssrHash: string;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Options for isolated SSR rendering through the Worker pool.
|
|
20
|
+
* When provided and SSR isolation is enabled, the rendering happens
|
|
21
|
+
* in a per-project Worker instead of the main process.
|
|
22
|
+
*/
|
|
23
|
+
export interface SSRIsolationOptions {
|
|
24
|
+
/** Temp file path for the page component module */
|
|
25
|
+
pageModulePath: string;
|
|
26
|
+
/** Ordered layout module temp paths (innermost → outermost) */
|
|
27
|
+
layoutModulePaths: string[];
|
|
28
|
+
/** Page component props */
|
|
29
|
+
pageProps: Record<string, unknown>;
|
|
30
|
+
/** Layout props (one entry per layout, matching layoutModulePaths order) */
|
|
31
|
+
layoutProps: Record<string, unknown>[];
|
|
32
|
+
/** Project directory for worker scoping */
|
|
33
|
+
projectDir: string;
|
|
34
|
+
}
|
|
18
35
|
export declare class SSROrchestrator {
|
|
19
36
|
private config;
|
|
20
37
|
constructor(config: SSROrchestratorConfig);
|
|
21
|
-
performSSRRendering(pageElement: React.ReactElement, generationContext: Omit<HTMLGenerationContext, "html" | "ssrHash">, options?: RenderOptions): Promise<SSRRenderingResult>;
|
|
38
|
+
performSSRRendering(pageElement: React.ReactElement, generationContext: Omit<HTMLGenerationContext, "html" | "ssrHash">, options?: RenderOptions, isolationOptions?: SSRIsolationOptions): Promise<SSRRenderingResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Perform SSR rendering in an isolated per-project Worker.
|
|
41
|
+
*
|
|
42
|
+
* The Worker imports user modules from their temp file paths,
|
|
43
|
+
* constructs the React element tree, and renders to HTML.
|
|
44
|
+
* For streaming, the Worker sends chunks via postMessage.
|
|
45
|
+
*/
|
|
46
|
+
private performIsolatedSSR;
|
|
22
47
|
private createStream;
|
|
23
48
|
}
|
|
24
49
|
//# sourceMappingURL=ssr-orchestrator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-orchestrator.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/ssr-orchestrator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAIpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"ssr-orchestrator.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/ssr-orchestrator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAIpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOhD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,cAAc,GAAG,IAAI,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACvC,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;CACpB;AAUD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAwB;gBAE1B,MAAM,EAAE,qBAAqB;IAInC,mBAAmB,CACvB,WAAW,EAAE,KAAK,CAAC,YAAY,EAC/B,iBAAiB,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC,EAClE,OAAO,CAAC,EAAE,aAAa,EACvB,gBAAgB,CAAC,EAAE,mBAAmB,GACrC,OAAO,CAAC,kBAAkB,CAAC;IAiG9B;;;;;;OAMG;YACW,kBAAkB;IAiFhC,OAAO,CAAC,YAAY;CAerB"}
|
|
@@ -5,6 +5,7 @@ import { withSpan } from "../../observability/tracing/otlp-setup.js";
|
|
|
5
5
|
import { SpanNames } from "../../observability/tracing/span-names.js";
|
|
6
6
|
import { computeHash } from "../utils/index.js";
|
|
7
7
|
import { runWithHeadCollector } from "../../react/head-collector.js";
|
|
8
|
+
import { getWorkerPool, isSSRIsolationEnabled } from "../../security/sandbox/worker-pool.js";
|
|
8
9
|
const logger = rendererLogger.component("ssr-orchestrator");
|
|
9
10
|
function getElementTypeName(el) {
|
|
10
11
|
if (!el?.type)
|
|
@@ -19,7 +20,14 @@ export class SSROrchestrator {
|
|
|
19
20
|
constructor(config) {
|
|
20
21
|
this.config = config;
|
|
21
22
|
}
|
|
22
|
-
async performSSRRendering(pageElement, generationContext, options) {
|
|
23
|
+
async performSSRRendering(pageElement, generationContext, options, isolationOptions) {
|
|
24
|
+
// Isolated SSR path: render in per-project Worker
|
|
25
|
+
if (isSSRIsolationEnabled() &&
|
|
26
|
+
isolationOptions?.pageModulePath &&
|
|
27
|
+
isolationOptions?.projectDir) {
|
|
28
|
+
return this.performIsolatedSSR(generationContext, options, isolationOptions);
|
|
29
|
+
}
|
|
30
|
+
// Default path: render in main process
|
|
23
31
|
logger.debug("performSSRRendering called", {
|
|
24
32
|
elementType: getElementTypeName(pageElement),
|
|
25
33
|
hasChildren: !!pageElement.props?.children,
|
|
@@ -77,6 +85,74 @@ export class SSROrchestrator {
|
|
|
77
85
|
ssrHash,
|
|
78
86
|
};
|
|
79
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Perform SSR rendering in an isolated per-project Worker.
|
|
90
|
+
*
|
|
91
|
+
* The Worker imports user modules from their temp file paths,
|
|
92
|
+
* constructs the React element tree, and renders to HTML.
|
|
93
|
+
* For streaming, the Worker sends chunks via postMessage.
|
|
94
|
+
*/
|
|
95
|
+
async performIsolatedSSR(generationContext, options, isolation) {
|
|
96
|
+
const wantsStream = options?.delivery === "stream";
|
|
97
|
+
const pool = getWorkerPool();
|
|
98
|
+
const requestId = dntShim.crypto.randomUUID();
|
|
99
|
+
return withSpan("ssr.isolated_render", async () => {
|
|
100
|
+
const worker = pool.getOrCreateWorker(isolation.projectDir, [isolation.projectDir]);
|
|
101
|
+
if (wantsStream) {
|
|
102
|
+
// Streaming mode: get a ReadableStream of chunks from the Worker
|
|
103
|
+
const stream = worker.executeStream({
|
|
104
|
+
type: "render-ssr",
|
|
105
|
+
id: requestId,
|
|
106
|
+
pageModulePath: isolation.pageModulePath,
|
|
107
|
+
layoutModulePaths: isolation.layoutModulePaths,
|
|
108
|
+
pageProps: isolation.pageProps,
|
|
109
|
+
layoutProps: isolation.layoutProps,
|
|
110
|
+
delivery: "stream",
|
|
111
|
+
});
|
|
112
|
+
const ssrHash = `stream-isolated-${Date.now()}`;
|
|
113
|
+
// Generate HTML stream using the framework's HTML generator
|
|
114
|
+
const finalStream = await this.config.htmlGenerator.generateHTMLStream(stream, {
|
|
115
|
+
...generationContext,
|
|
116
|
+
ssrHash,
|
|
117
|
+
options: { ...generationContext.options, ...options },
|
|
118
|
+
collectedHead: undefined,
|
|
119
|
+
});
|
|
120
|
+
return { fullHtml: "", finalStream, ssrHash };
|
|
121
|
+
}
|
|
122
|
+
// String mode: render to HTML in Worker, get result back
|
|
123
|
+
const workerResponse = await worker.execute({
|
|
124
|
+
type: "render-ssr",
|
|
125
|
+
id: requestId,
|
|
126
|
+
pageModulePath: isolation.pageModulePath,
|
|
127
|
+
layoutModulePaths: isolation.layoutModulePaths,
|
|
128
|
+
pageProps: isolation.pageProps,
|
|
129
|
+
layoutProps: isolation.layoutProps,
|
|
130
|
+
delivery: "string",
|
|
131
|
+
});
|
|
132
|
+
if (workerResponse.type === "error") {
|
|
133
|
+
const err = new Error(workerResponse.error.message);
|
|
134
|
+
err.name = workerResponse.error.name;
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
if (workerResponse.type !== "ssr-result") {
|
|
138
|
+
throw new Error(`Unexpected worker response type: ${workerResponse.type}`);
|
|
139
|
+
}
|
|
140
|
+
const html = workerResponse.html;
|
|
141
|
+
const ssrHash = await computeHash(html);
|
|
142
|
+
const fullHtml = await this.config.htmlGenerator.generateFullHTML({
|
|
143
|
+
...generationContext,
|
|
144
|
+
html,
|
|
145
|
+
ssrHash,
|
|
146
|
+
options: { ...generationContext.options, ...options },
|
|
147
|
+
collectedHead: undefined,
|
|
148
|
+
});
|
|
149
|
+
return { fullHtml, finalStream: null, ssrHash };
|
|
150
|
+
}, {
|
|
151
|
+
"ssr.isolated": true,
|
|
152
|
+
"ssr.wants_stream": wantsStream,
|
|
153
|
+
"ssr.project_dir": isolation.projectDir,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
80
156
|
createStream(html) {
|
|
81
157
|
try {
|
|
82
158
|
return new dntShim.Response(html).body ?? null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/src/routing/api/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAGlD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAMlD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAO9E,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAErC;;GAEG;AACH,UAAU,mBAAmB;IAC3B,iBAAiB,CAAC,EAAE,OAAO,iBAAiB,CAAC;IAC7C,mBAAmB,CAAC,EAAE,OAAO,mBAAmB,CAAC;IACjD,iBAAiB,CAAC,EAAE,OAAO,iBAAiB,CAAC;IAC7C,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;CAC9B;AAID;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAE3E;AAWD,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;CAC/B;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;AAE3F,qBAAa,eAAe;IAgBxB,OAAO,CAAC,UAAU;IAfpB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAA6E;IAC/F,OAAO,CAAC,gBAAgB,CAAuB;IAE/C,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,UAAU,CAAqC;IACvD,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,iBAAiB,CAA8B;IAEvD,OAAO,CAAC,MAAM,CAAsD;IACpE,OAAO,CAAC,aAAa,CAA8B;gBAGzC,UAAU,EAAE,MAAM,EAC1B,OAAO,CAAC,EAAE,cAAc;IAM1B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgD3B,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/src/routing/api/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAGlD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAMlD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAO9E,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAErC;;GAEG;AACH,UAAU,mBAAmB;IAC3B,iBAAiB,CAAC,EAAE,OAAO,iBAAiB,CAAC;IAC7C,mBAAmB,CAAC,EAAE,OAAO,mBAAmB,CAAC;IACjD,iBAAiB,CAAC,EAAE,OAAO,iBAAiB,CAAC;IAC7C,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;CAC9B;AAID;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAE3E;AAWD,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;CAC/B;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;AAE3F,qBAAa,eAAe;IAgBxB,OAAO,CAAC,UAAU;IAfpB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAA6E;IAC/F,OAAO,CAAC,gBAAgB,CAAuB;IAE/C,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,UAAU,CAAqC;IACvD,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,iBAAiB,CAA8B;IAEvD,OAAO,CAAC,MAAM,CAAsD;IACpE,OAAO,CAAC,aAAa,CAA8B;gBAGzC,UAAU,EAAE,MAAM,EAC1B,OAAO,CAAC,EAAE,cAAc;IAM1B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgD3B,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAyFlE,OAAO,CAAC,WAAW;IAuCnB,UAAU,IAAI,IAAI;IAKlB,OAAO,IAAI,IAAI;YAKD,aAAa;YAsBb,gBAAgB;YAOhB,cAAc;YAcd,YAAY;YAOZ,cAAc;CAW7B;AAED,OAAO,EACL,UAAU,EACV,SAAS,EACT,mBAAmB,IAAI,WAAW,EAClC,YAAY,IAAI,IAAI,EACpB,QAAQ,EACR,gBAAgB,IAAI,QAAQ,EAC5B,YAAY,GACb,MAAM,yCAAyC,CAAC"}
|
|
@@ -129,9 +129,13 @@ export class APIRouteHandler {
|
|
|
129
129
|
// Pages Router routes have descriptive names like articles.ts
|
|
130
130
|
// Note: Cannot use path-based detection (/app/) as projectDir may be '/app' in production
|
|
131
131
|
const isAppRoute = /\/route\.(ts|js|tsx|jsx)$/.test(match.route.page);
|
|
132
|
+
const isolationOptions = {
|
|
133
|
+
modulePath: match.route.page,
|
|
134
|
+
projectDir: this.projectDir,
|
|
135
|
+
};
|
|
132
136
|
const response = isAppRoute
|
|
133
|
-
? await executeAppRoute(handler, request, match, pathname, adapter)
|
|
134
|
-
: await executePagesRoute(handler, request, match, pathname, adapter, this.projectDir);
|
|
137
|
+
? await executeAppRoute(handler, request, match, pathname, adapter, isolationOptions)
|
|
138
|
+
: await executePagesRoute(handler, request, match, pathname, adapter, this.projectDir, isolationOptions);
|
|
135
139
|
const corsResponse = await applyCORSHeaders({
|
|
136
140
|
request,
|
|
137
141
|
response,
|
|
@@ -2,6 +2,12 @@ import * as dntShim from "../../../_dnt.shims.js";
|
|
|
2
2
|
import type { RuntimeAdapter } from "../../platform/adapters/base.js";
|
|
3
3
|
import type { RouteMatch } from "./api-route-matcher.js";
|
|
4
4
|
import type { APIRoute } from "./module-loader/types.js";
|
|
5
|
-
export
|
|
6
|
-
|
|
5
|
+
export interface ExecuteRouteOptions {
|
|
6
|
+
/** Absolute path to the handler module on disk (for isolated execution) */
|
|
7
|
+
modulePath?: string;
|
|
8
|
+
/** Project directory (for isolated execution scope) */
|
|
9
|
+
projectDir?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function executeAppRoute(handler: APIRoute, request: dntShim.Request, match: RouteMatch, pathname: string, adapter: RuntimeAdapter, options?: ExecuteRouteOptions): Promise<dntShim.Response>;
|
|
12
|
+
export declare function executePagesRoute(handler: APIRoute, request: dntShim.Request, match: RouteMatch, pathname: string, adapter: RuntimeAdapter, projectDir?: string, options?: ExecuteRouteOptions): Promise<dntShim.Response>;
|
|
7
13
|
//# sourceMappingURL=route-executor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-executor.d.ts","sourceRoot":"","sources":["../../../../src/src/routing/api/route-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAClD,OAAO,KAAK,EAAqB,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEzF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,KAAK,EACV,QAAQ,EAKT,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"route-executor.d.ts","sourceRoot":"","sources":["../../../../src/src/routing/api/route-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAClD,OAAO,KAAK,EAAqB,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEzF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,KAAK,EACV,QAAQ,EAKT,MAAM,0BAA0B,CAAC;AA4RlC,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,OAAO,CAAC,OAAO,EACxB,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CA6C3B;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,OAAO,CAAC,OAAO,EACxB,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,EACvB,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAuC3B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as dntShim from "../../../_dnt.shims.js";
|
|
2
|
-
import { createContext, normalizeParams } from "./context-builder.js";
|
|
2
|
+
import { createContext, normalizeParams, parseCookies } from "./context-builder.js";
|
|
3
3
|
import { createError, toError } from "../../errors/veryfront-error.js";
|
|
4
4
|
import { createAppRouteMethodNotAllowed, createPagesRouteMethodNotAllowed, } from "./method-validator.js";
|
|
5
5
|
import { isAbsolute, join } from "../../platform/compat/path/index.js";
|
|
@@ -7,6 +7,8 @@ import { withSpan } from "../../observability/tracing/otlp-setup.js";
|
|
|
7
7
|
import { errorToRFC9457Response } from "../../errors/middleware/http-error-boundary.js";
|
|
8
8
|
import { serverLogger as logger } from "../../utils/index.js";
|
|
9
9
|
import { isDevelopment as isDevelopmentEnv } from "../../build/config/environment.js";
|
|
10
|
+
import { getWorkerPool, isWorkerIsolationEnabled, } from "../../security/sandbox/worker-pool.js";
|
|
11
|
+
import { getProjectEnvSnapshot } from "../../server/project-env/storage.js";
|
|
10
12
|
function isDevelopment(adapter) {
|
|
11
13
|
const env = adapter.env.get("MODE") ??
|
|
12
14
|
adapter.env.get("NODE_ENV") ??
|
|
@@ -80,7 +82,126 @@ function validateResponse(response) {
|
|
|
80
82
|
function toHeadResponse(response) {
|
|
81
83
|
return new dntShim.Response(null, { status: response.status, headers: response.headers });
|
|
82
84
|
}
|
|
83
|
-
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Worker Isolation Helpers
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
async function serializeRequest(request) {
|
|
89
|
+
const body = request.body ? new Uint8Array(await request.arrayBuffer()) : null;
|
|
90
|
+
return {
|
|
91
|
+
url: request.url,
|
|
92
|
+
method: request.method,
|
|
93
|
+
headers: [...request.headers.entries()],
|
|
94
|
+
body,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function deserializeResponse(s) {
|
|
98
|
+
return new dntShim.Response(s.body, {
|
|
99
|
+
status: s.status,
|
|
100
|
+
statusText: s.statusText,
|
|
101
|
+
headers: s.headers,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function workerResponseToResponse(workerResponse, pathname, adapter) {
|
|
105
|
+
if (workerResponse.type === "error") {
|
|
106
|
+
const { error } = workerResponse;
|
|
107
|
+
logger.error(`API route error in ${pathname} (worker):`, error.message);
|
|
108
|
+
// If the worker serialized RFC 9457 fields, return them directly
|
|
109
|
+
// to preserve the original status code, type, and detail.
|
|
110
|
+
if (error.status && error.type) {
|
|
111
|
+
return dntShim.Response.json({
|
|
112
|
+
type: error.type,
|
|
113
|
+
title: error.name,
|
|
114
|
+
status: error.status,
|
|
115
|
+
detail: error.detail ?? error.message,
|
|
116
|
+
instance: pathname,
|
|
117
|
+
}, { status: error.status });
|
|
118
|
+
}
|
|
119
|
+
const ctx = { isLocalProject: isDevelopment(adapter) };
|
|
120
|
+
const req = new dntShim.Request(`http://localhost${pathname}`);
|
|
121
|
+
const err = new Error(error.message);
|
|
122
|
+
err.name = error.name;
|
|
123
|
+
return errorToRFC9457Response(err, ctx, req);
|
|
124
|
+
}
|
|
125
|
+
if (workerResponse.type === "result") {
|
|
126
|
+
return deserializeResponse(workerResponse.response);
|
|
127
|
+
}
|
|
128
|
+
// data-result type is not expected in API route execution
|
|
129
|
+
throw new Error(`Unexpected worker response type: ${workerResponse.type}`);
|
|
130
|
+
}
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// Isolated Execution (Worker Path)
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
function executeAppRouteIsolated(modulePath, request, match, pathname, adapter, projectDir) {
|
|
135
|
+
const method = request.method.toUpperCase();
|
|
136
|
+
return withSpan("api.executeAppRoute.isolated", async () => {
|
|
137
|
+
try {
|
|
138
|
+
const pool = getWorkerPool();
|
|
139
|
+
const serialized = await serializeRequest(request);
|
|
140
|
+
const workerResponse = await pool.execute(projectDir, [projectDir], {
|
|
141
|
+
type: "execute-app-route",
|
|
142
|
+
id: dntShim.crypto.randomUUID(),
|
|
143
|
+
modulePath,
|
|
144
|
+
method,
|
|
145
|
+
request: serialized,
|
|
146
|
+
params: match.params,
|
|
147
|
+
projectDir,
|
|
148
|
+
projectEnv: getProjectEnvSnapshot(),
|
|
149
|
+
});
|
|
150
|
+
const response = workerResponseToResponse(workerResponse, pathname, adapter);
|
|
151
|
+
return method === "HEAD" ? toHeadResponse(response) : response;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
return handleAPIError(error, pathname, adapter);
|
|
155
|
+
}
|
|
156
|
+
}, {
|
|
157
|
+
"http.method": method,
|
|
158
|
+
"http.path": pathname,
|
|
159
|
+
"api.route.pattern": match.route.pattern,
|
|
160
|
+
"api.isolated": true,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function executePagesRouteIsolated(modulePath, request, match, pathname, adapter, projectDir) {
|
|
164
|
+
const method = request.method;
|
|
165
|
+
return withSpan("api.executePagesRoute.isolated", async () => {
|
|
166
|
+
try {
|
|
167
|
+
const pool = getWorkerPool();
|
|
168
|
+
const body = request.body ? new Uint8Array(await request.arrayBuffer()) : null;
|
|
169
|
+
const workerResponse = await pool.execute(projectDir, [projectDir], {
|
|
170
|
+
type: "execute-pages-route",
|
|
171
|
+
id: dntShim.crypto.randomUUID(),
|
|
172
|
+
modulePath,
|
|
173
|
+
method,
|
|
174
|
+
context: {
|
|
175
|
+
url: request.url,
|
|
176
|
+
method: request.method,
|
|
177
|
+
headers: [...request.headers.entries()],
|
|
178
|
+
body,
|
|
179
|
+
params: match.params,
|
|
180
|
+
cookies: parseCookies(request.headers.get("cookie") ?? ""),
|
|
181
|
+
},
|
|
182
|
+
projectDir,
|
|
183
|
+
projectEnv: getProjectEnvSnapshot(),
|
|
184
|
+
});
|
|
185
|
+
return workerResponseToResponse(workerResponse, pathname, adapter);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return handleAPIError(error, pathname, adapter);
|
|
189
|
+
}
|
|
190
|
+
}, {
|
|
191
|
+
"http.method": method,
|
|
192
|
+
"http.path": pathname,
|
|
193
|
+
"api.route.pattern": match.route.pattern,
|
|
194
|
+
"api.isolated": true,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
export function executeAppRoute(handler, request, match, pathname, adapter, options) {
|
|
198
|
+
// Isolated path: execute in per-project Worker, fall back to main process on error
|
|
199
|
+
if (isWorkerIsolationEnabled() &&
|
|
200
|
+
options?.modulePath &&
|
|
201
|
+
options?.projectDir) {
|
|
202
|
+
return executeAppRouteIsolated(options.modulePath, request, match, pathname, adapter, options.projectDir);
|
|
203
|
+
}
|
|
204
|
+
// Default path: execute in main process (existing behavior)
|
|
84
205
|
const method = request.method.toUpperCase();
|
|
85
206
|
return withSpan("api.executeAppRoute", async () => {
|
|
86
207
|
const handlerModule = handler;
|
|
@@ -102,7 +223,14 @@ export function executeAppRoute(handler, request, match, pathname, adapter) {
|
|
|
102
223
|
}
|
|
103
224
|
}, { "http.method": method, "http.path": pathname, "api.route.pattern": match.route.pattern });
|
|
104
225
|
}
|
|
105
|
-
export function executePagesRoute(handler, request, match, pathname, adapter, projectDir) {
|
|
226
|
+
export function executePagesRoute(handler, request, match, pathname, adapter, projectDir, options) {
|
|
227
|
+
// Isolated path: execute in per-project Worker, fall back to main process on error
|
|
228
|
+
if (isWorkerIsolationEnabled() &&
|
|
229
|
+
options?.modulePath &&
|
|
230
|
+
(options?.projectDir ?? projectDir)) {
|
|
231
|
+
return executePagesRouteIsolated(options.modulePath, request, match, pathname, adapter, options.projectDir ?? projectDir);
|
|
232
|
+
}
|
|
233
|
+
// Default path: execute in main process (existing behavior)
|
|
106
234
|
const method = request.method;
|
|
107
235
|
return withSpan("api.executePagesRoute", async () => {
|
|
108
236
|
const methodHandler = handler[method] ?? handler.default;
|
|
@@ -21,4 +21,10 @@ export declare const WORKFLOW_JOB_PERMISSIONS: readonly ["--allow-read", "--allo
|
|
|
21
21
|
* Only needs filesystem + env access.
|
|
22
22
|
*/
|
|
23
23
|
export declare const BUILD_HELPER_PERMISSIONS: readonly ["--allow-read", "--allow-write", "--allow-env"];
|
|
24
|
+
/**
|
|
25
|
+
* RENDER_WORKER — Per-project Worker for isolated code execution.
|
|
26
|
+
* Read-only filesystem (transformed modules), network (data fetchers),
|
|
27
|
+
* env (API keys and config). No subprocess/ffi/sys.
|
|
28
|
+
*/
|
|
29
|
+
export declare const RENDER_WORKER_PERMISSIONS: readonly ["--allow-read", "--allow-net", "--allow-env"];
|
|
24
30
|
//# sourceMappingURL=deno-permissions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deno-permissions.d.ts","sourceRoot":"","sources":["../../../src/src/security/deno-permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,uHAQrB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,wBAAwB,0EAK3B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,wBAAwB,2DAI3B,CAAC"}
|
|
1
|
+
{"version":3,"file":"deno-permissions.d.ts","sourceRoot":"","sources":["../../../src/src/security/deno-permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,uHAQrB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,wBAAwB,0EAK3B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,wBAAwB,2DAI3B,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,yDAI5B,CAAC"}
|
|
@@ -38,3 +38,13 @@ export const BUILD_HELPER_PERMISSIONS = [
|
|
|
38
38
|
"--allow-write",
|
|
39
39
|
"--allow-env",
|
|
40
40
|
];
|
|
41
|
+
/**
|
|
42
|
+
* RENDER_WORKER — Per-project Worker for isolated code execution.
|
|
43
|
+
* Read-only filesystem (transformed modules), network (data fetchers),
|
|
44
|
+
* env (API keys and config). No subprocess/ffi/sys.
|
|
45
|
+
*/
|
|
46
|
+
export const RENDER_WORKER_PERMISSIONS = [
|
|
47
|
+
"--allow-read",
|
|
48
|
+
"--allow-net",
|
|
49
|
+
"--allow-env",
|
|
50
|
+
];
|