veryfront 0.1.755 → 0.1.756

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.
Files changed (100) hide show
  1. package/esm/cli/auth/callback-server.d.ts +4 -1
  2. package/esm/cli/auth/callback-server.d.ts.map +1 -1
  3. package/esm/cli/auth/callback-server.js +52 -13
  4. package/esm/cli/auth/index.d.ts +1 -1
  5. package/esm/cli/auth/index.d.ts.map +1 -1
  6. package/esm/cli/auth/index.js +1 -1
  7. package/esm/cli/auth/login.d.ts +2 -0
  8. package/esm/cli/auth/login.d.ts.map +1 -1
  9. package/esm/cli/auth/login.js +18 -6
  10. package/esm/cli/commands/demo/demo.d.ts.map +1 -1
  11. package/esm/cli/commands/demo/demo.js +6 -7
  12. package/esm/deno.js +1 -1
  13. package/esm/src/agent/runtime/index.d.ts +3 -96
  14. package/esm/src/agent/runtime/index.d.ts.map +1 -1
  15. package/esm/src/agent/runtime/index.js +21 -341
  16. package/esm/src/agent/runtime/model-capabilities.d.ts +3 -0
  17. package/esm/src/agent/runtime/model-capabilities.d.ts.map +1 -0
  18. package/esm/src/agent/runtime/model-capabilities.js +18 -0
  19. package/esm/src/agent/runtime/runtime-tool-config.d.ts +14 -0
  20. package/esm/src/agent/runtime/runtime-tool-config.d.ts.map +1 -0
  21. package/esm/src/agent/runtime/runtime-tool-config.js +37 -0
  22. package/esm/src/agent/runtime/skill-policy-enforcement.d.ts +16 -0
  23. package/esm/src/agent/runtime/skill-policy-enforcement.d.ts.map +1 -0
  24. package/esm/src/agent/runtime/skill-policy-enforcement.js +55 -0
  25. package/esm/src/agent/runtime/tool-result-continuation.d.ts +34 -0
  26. package/esm/src/agent/runtime/tool-result-continuation.d.ts.map +1 -0
  27. package/esm/src/agent/runtime/tool-result-continuation.js +194 -0
  28. package/esm/src/agent/schemas/agent.schema.d.ts +7 -0
  29. package/esm/src/agent/schemas/agent.schema.d.ts.map +1 -1
  30. package/esm/src/agent/schemas/agent.schema.js +9 -3
  31. package/esm/src/agent/schemas/index.d.ts +1 -1
  32. package/esm/src/agent/schemas/index.d.ts.map +1 -1
  33. package/esm/src/agent/schemas/index.js +1 -1
  34. package/esm/src/modules/server/module-server.d.ts.map +1 -1
  35. package/esm/src/modules/server/module-server.js +31 -9
  36. package/esm/src/modules/server/rate-limiter.d.ts +3 -2
  37. package/esm/src/modules/server/rate-limiter.d.ts.map +1 -1
  38. package/esm/src/platform/adapters/base.d.ts +24 -3
  39. package/esm/src/platform/adapters/base.d.ts.map +1 -1
  40. package/esm/src/platform/adapters/base.js +15 -1
  41. package/esm/src/platform/adapters/runtime/bun/websocket-adapter.d.ts +2 -0
  42. package/esm/src/platform/adapters/runtime/bun/websocket-adapter.d.ts.map +1 -1
  43. package/esm/src/platform/adapters/runtime/bun/websocket-adapter.js +33 -1
  44. package/esm/src/platform/adapters/runtime/node/websocket-adapter.d.ts +1 -1
  45. package/esm/src/platform/adapters/runtime/node/websocket-adapter.d.ts.map +1 -1
  46. package/esm/src/platform/adapters/runtime/node/websocket-adapter.js +3 -10
  47. package/esm/src/proxy/handler.d.ts.map +1 -1
  48. package/esm/src/proxy/handler.js +25 -116
  49. package/esm/src/proxy/local-project-resolver.d.ts +18 -0
  50. package/esm/src/proxy/local-project-resolver.d.ts.map +1 -0
  51. package/esm/src/proxy/local-project-resolver.js +63 -0
  52. package/esm/src/proxy/proxy-token-resolution.d.ts +37 -0
  53. package/esm/src/proxy/proxy-token-resolution.d.ts.map +1 -0
  54. package/esm/src/proxy/proxy-token-resolution.js +51 -0
  55. package/esm/src/rendering/orchestrator/module-loader/index.d.ts +10 -0
  56. package/esm/src/rendering/orchestrator/module-loader/index.d.ts.map +1 -1
  57. package/esm/src/rendering/orchestrator/module-loader/index.js +23 -158
  58. package/esm/src/rendering/orchestrator/module-loader/module-persistence.d.ts +20 -0
  59. package/esm/src/rendering/orchestrator/module-loader/module-persistence.d.ts.map +1 -0
  60. package/esm/src/rendering/orchestrator/module-loader/module-persistence.js +80 -0
  61. package/esm/src/rendering/orchestrator/module-loader/module-transform-cache.d.ts +59 -0
  62. package/esm/src/rendering/orchestrator/module-loader/module-transform-cache.d.ts.map +1 -0
  63. package/esm/src/rendering/orchestrator/module-loader/module-transform-cache.js +97 -0
  64. package/esm/src/routing/api/handler.d.ts +2 -1
  65. package/esm/src/routing/api/handler.d.ts.map +1 -1
  66. package/esm/src/routing/api/handler.js +2 -1
  67. package/esm/src/routing/api/route-executor.d.ts +3 -0
  68. package/esm/src/routing/api/route-executor.d.ts.map +1 -1
  69. package/esm/src/routing/api/route-executor.js +28 -0
  70. package/esm/src/runtime/runtime-bridge.js +2 -2
  71. package/esm/src/security/http/base-handler.d.ts +3 -2
  72. package/esm/src/security/http/base-handler.d.ts.map +1 -1
  73. package/esm/src/security/http/base-handler.js +1 -1
  74. package/esm/src/security/sandbox/project-worker.d.ts +1 -0
  75. package/esm/src/security/sandbox/project-worker.d.ts.map +1 -1
  76. package/esm/src/security/sandbox/project-worker.js +17 -1
  77. package/esm/src/security/sandbox/worker-egress-guard.d.ts +15 -0
  78. package/esm/src/security/sandbox/worker-egress-guard.d.ts.map +1 -0
  79. package/esm/src/security/sandbox/worker-egress-guard.js +201 -0
  80. package/esm/src/security/sandbox/worker-permissions.d.ts +6 -2
  81. package/esm/src/security/sandbox/worker-permissions.d.ts.map +1 -1
  82. package/esm/src/security/sandbox/worker-permissions.js +34 -3
  83. package/esm/src/security/sandbox/worker-pool.d.ts +1 -1
  84. package/esm/src/security/sandbox/worker-pool.d.ts.map +1 -1
  85. package/esm/src/security/sandbox/worker-pool.js +35 -9
  86. package/esm/src/server/handlers/preview/hmr-client-manager.d.ts +3 -2
  87. package/esm/src/server/handlers/preview/hmr-client-manager.d.ts.map +1 -1
  88. package/esm/src/server/handlers/preview/hmr.handler.d.ts.map +1 -1
  89. package/esm/src/server/handlers/preview/hmr.handler.js +4 -1
  90. package/esm/src/server/handlers/request/api/api-handler-wrapper.js +1 -1
  91. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/http-fallback.d.ts +32 -0
  92. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/http-fallback.d.ts.map +1 -0
  93. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/http-fallback.js +27 -0
  94. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/import-rewriter.d.ts.map +1 -1
  95. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/import-rewriter.js +23 -3
  96. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.d.ts.map +1 -1
  97. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.js +15 -15
  98. package/esm/src/utils/version-constant.d.ts +1 -1
  99. package/esm/src/utils/version-constant.js +1 -1
  100. package/package.json +1 -1
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Worker network egress guard.
3
+ *
4
+ * Project workers need outbound network access for public API calls, but user
5
+ * code must not reach host-internal networks or cloud metadata endpoints.
6
+ *
7
+ * @module security/sandbox/worker-egress-guard
8
+ */
9
+ import * as dntShim from "../../../_dnt.shims.js";
10
+ export const WORKER_INTERNAL_EGRESS_OVERRIDE_ENV = "VERYFRONT_WORKER_ALLOW_INTERNAL_EGRESS";
11
+ export class WorkerEgressBlockedError extends Error {
12
+ name = "WorkerEgressBlockedError";
13
+ }
14
+ const guardInstalled = Symbol.for("veryfront.workerEgressGuardInstalled");
15
+ const guardedFetch = Symbol.for("veryfront.workerEgressGuard.fetch");
16
+ const guardedConnect = Symbol.for("veryfront.workerEgressGuard.connect");
17
+ const guardedConnectTls = Symbol.for("veryfront.workerEgressGuard.connectTls");
18
+ function isObject(value) {
19
+ return typeof value === "object" && value !== null;
20
+ }
21
+ export function isInternalEgressOverrideEnabled(value) {
22
+ if (value === undefined)
23
+ return false;
24
+ switch (value.trim().toLowerCase()) {
25
+ case "1":
26
+ case "true":
27
+ case "yes":
28
+ case "on":
29
+ return true;
30
+ default:
31
+ return false;
32
+ }
33
+ }
34
+ function stripIpv6Brackets(value) {
35
+ return value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
36
+ }
37
+ function stripIpv6Zone(value) {
38
+ const zoneIndex = value.indexOf("%");
39
+ return zoneIndex === -1 ? value : value.slice(0, zoneIndex);
40
+ }
41
+ function parseIpv4(value) {
42
+ const parts = value.split(".");
43
+ if (parts.length !== 4)
44
+ return null;
45
+ const octets = [];
46
+ for (const part of parts) {
47
+ if (!/^\d{1,3}$/.test(part))
48
+ return null;
49
+ const octet = Number(part);
50
+ if (!Number.isInteger(octet) || octet < 0 || octet > 255)
51
+ return null;
52
+ octets.push(octet);
53
+ }
54
+ return octets;
55
+ }
56
+ function isPrivateIpv4(octets) {
57
+ const [a = -1, b = -1] = octets;
58
+ return (a === 10 ||
59
+ a === 127 ||
60
+ (a === 169 && b === 254) ||
61
+ (a === 172 && b >= 16 && b <= 31) ||
62
+ (a === 192 && b === 168) ||
63
+ a === 0);
64
+ }
65
+ function isInternalIpv6(value) {
66
+ const normalized = stripIpv6Zone(stripIpv6Brackets(value)).toLowerCase();
67
+ if (normalized === "::" || normalized === "::1")
68
+ return true;
69
+ const mappedIpv4 = normalized.match(/(?:::ffff:)(\d{1,3}(?:\.\d{1,3}){3})$/);
70
+ if (mappedIpv4?.[1]) {
71
+ const octets = parseIpv4(mappedIpv4[1]);
72
+ return octets !== null && isPrivateIpv4(octets);
73
+ }
74
+ const firstHextetText = normalized.split(":", 1)[0] ?? "";
75
+ if (!/^[0-9a-f]{1,4}$/.test(firstHextetText))
76
+ return false;
77
+ const firstHextet = Number.parseInt(firstHextetText, 16);
78
+ // fe80::/10 link-local, fc00::/7 unique local.
79
+ return (firstHextet & 0xffc0) === 0xfe80 || (firstHextet & 0xfe00) === 0xfc00;
80
+ }
81
+ export function isInternalEgressIp(address) {
82
+ const host = stripIpv6Zone(stripIpv6Brackets(address.trim().toLowerCase()));
83
+ const ipv4 = parseIpv4(host);
84
+ if (ipv4)
85
+ return isPrivateIpv4(ipv4);
86
+ return host.includes(":") && isInternalIpv6(host);
87
+ }
88
+ function isIpLiteral(address) {
89
+ const host = stripIpv6Zone(stripIpv6Brackets(address.trim().toLowerCase()));
90
+ return parseIpv4(host) !== null || host.includes(":");
91
+ }
92
+ function isLocalhostName(hostname) {
93
+ const normalized = hostname.trim().toLowerCase();
94
+ return normalized === "localhost" || normalized.endsWith(".localhost");
95
+ }
96
+ async function defaultResolveHost(hostname) {
97
+ const results = [];
98
+ const resolver = dntShim.Deno.resolveDns;
99
+ for (const recordType of ["A", "AAAA"]) {
100
+ try {
101
+ results.push(...await resolver(hostname, recordType));
102
+ }
103
+ catch {
104
+ // A host may legitimately have only one address family.
105
+ }
106
+ }
107
+ return results;
108
+ }
109
+ function getUrlHostname(input) {
110
+ const url = input instanceof URL
111
+ ? input
112
+ : input instanceof Request
113
+ ? new URL(input.url)
114
+ : new URL(String(input));
115
+ if (url.protocol !== "http:" && url.protocol !== "https:" && url.protocol !== "ws:" &&
116
+ url.protocol !== "wss:") {
117
+ return null;
118
+ }
119
+ return stripIpv6Brackets(url.hostname);
120
+ }
121
+ export async function assertWorkerEgressAllowed(target, options = {}) {
122
+ if (options.allowInternalEgress)
123
+ return;
124
+ const hostname = getUrlHostname(target);
125
+ if (!hostname)
126
+ return;
127
+ await assertWorkerHostEgressAllowed(hostname, options);
128
+ }
129
+ export async function assertWorkerHostEgressAllowed(hostname, options = {}) {
130
+ if (options.allowInternalEgress)
131
+ return;
132
+ const host = stripIpv6Brackets(hostname.trim().toLowerCase());
133
+ if (isLocalhostName(host) || isInternalEgressIp(host)) {
134
+ throw new WorkerEgressBlockedError(`Worker network egress blocked for internal host: ${hostname}`);
135
+ }
136
+ if (isIpLiteral(host))
137
+ return;
138
+ const resolveHost = options.resolveHost ?? defaultResolveHost;
139
+ const addresses = await resolveHost(host);
140
+ if (addresses.length === 0) {
141
+ throw new WorkerEgressBlockedError(`Worker network egress blocked: unable to resolve host ${hostname}`);
142
+ }
143
+ for (const address of addresses) {
144
+ if (isInternalEgressIp(address)) {
145
+ throw new WorkerEgressBlockedError(`Worker network egress blocked for host ${hostname} resolved to internal address ${address}`);
146
+ }
147
+ }
148
+ }
149
+ function getConnectHostname(options) {
150
+ if (!isObject(options))
151
+ return null;
152
+ const hostname = options.hostname;
153
+ return typeof hostname === "string" ? hostname : null;
154
+ }
155
+ function getAllowInternalEgress() {
156
+ try {
157
+ return isInternalEgressOverrideEnabled(dntShim.Deno.env.get(WORKER_INTERNAL_EGRESS_OVERRIDE_ENV));
158
+ }
159
+ catch {
160
+ return false;
161
+ }
162
+ }
163
+ export function installWorkerEgressGuard(options = {}) {
164
+ const globalRecord = dntShim.dntGlobalThis;
165
+ if (globalRecord[guardInstalled])
166
+ return;
167
+ const baseOptions = {
168
+ ...options,
169
+ allowInternalEgress: options.allowInternalEgress ?? getAllowInternalEgress(),
170
+ };
171
+ const originalFetch = globalThis.fetch.bind(dntShim.dntGlobalThis);
172
+ const fetchWrapper = async (input, init) => {
173
+ await assertWorkerEgressAllowed(input, baseOptions);
174
+ return await originalFetch(input, init);
175
+ };
176
+ Object.defineProperty(fetchWrapper, guardedFetch, { value: true });
177
+ globalThis.fetch = fetchWrapper;
178
+ if (typeof dntShim.Deno.connect === "function") {
179
+ const originalConnect = dntShim.Deno.connect.bind(dntShim.Deno);
180
+ const connectWrapper = async (options) => {
181
+ const hostname = getConnectHostname(options);
182
+ if (hostname)
183
+ await assertWorkerHostEgressAllowed(hostname, baseOptions);
184
+ return await originalConnect(options);
185
+ };
186
+ Object.defineProperty(connectWrapper, guardedConnect, { value: true });
187
+ dntShim.Deno.connect = connectWrapper;
188
+ }
189
+ if (typeof dntShim.Deno.connectTls === "function") {
190
+ const originalConnectTls = dntShim.Deno.connectTls.bind(dntShim.Deno);
191
+ const connectTlsWrapper = async (options) => {
192
+ const hostname = getConnectHostname(options);
193
+ if (hostname)
194
+ await assertWorkerHostEgressAllowed(hostname, baseOptions);
195
+ return await originalConnectTls(options);
196
+ };
197
+ Object.defineProperty(connectTlsWrapper, guardedConnectTls, { value: true });
198
+ dntShim.Deno.connectTls = connectTlsWrapper;
199
+ }
200
+ globalRecord[guardInstalled] = true;
201
+ }
@@ -6,7 +6,7 @@ export interface WorkerPermissions {
6
6
  read: string[] | boolean;
7
7
  write: boolean;
8
8
  net: boolean;
9
- env: boolean;
9
+ env: string[] | boolean;
10
10
  run: boolean;
11
11
  ffi: boolean;
12
12
  sys: boolean;
@@ -16,14 +16,18 @@ interface WorkerPermissionOptions {
16
16
  isCompiledBinary?: boolean;
17
17
  /** Override for tests that need deterministic compiled-binary support paths. */
18
18
  compiledReadPaths?: string[];
19
+ /** Project-configured env keys that route code may read inside the worker. */
20
+ projectEnvKeys?: Iterable<string | undefined>;
19
21
  }
22
+ export declare const FRAMEWORK_WORKER_ENV_ALLOWLIST: readonly ["NODE_ENV", "DENO_ENV", "VERYFRONT_ENV", "LOG_LEVEL", "LOG_FORMAT", "NO_COLOR", "FORCE_COLOR", "CI", "VERYFRONT_WORKER_ALLOW_INTERNAL_EGRESS"];
23
+ export declare function buildWorkerEnvAllowlist(projectEnvKeys?: Iterable<string | undefined>): string[];
20
24
  /**
21
25
  * Build scoped permissions for a project worker.
22
26
  *
23
27
  * - read: restricted to the project temp dir (transformed modules) and cache dirs
24
28
  * - write: denied (workers produce output via postMessage, not filesystem)
25
29
  * - net: allowed (data fetchers and API routes may call external APIs)
26
- * - env: allowed (user code reads API keys and config from environment)
30
+ * - env: restricted to framework keys and the project's configured env keys
27
31
  * - run: denied (no subprocess spawning from user code)
28
32
  * - ffi: denied (no native code from user code)
29
33
  * - sys: denied (no system info access from user code)
@@ -1 +1 @@
1
- {"version":3,"file":"worker-permissions.d.ts","sourceRoot":"","sources":["../../../../src/src/security/sandbox/worker-permissions.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;CACd;AAED,UAAU,uBAAuB;IAC/B,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAmCD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,GAAE,uBAA4B,GACpC,iBAAiB,CA2BnB"}
1
+ {"version":3,"file":"worker-permissions.d.ts","sourceRoot":"","sources":["../../../../src/src/security/sandbox/worker-permissions.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACxB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;CACd;AAED,UAAU,uBAAuB;IAC/B,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC/C;AAED,eAAO,MAAM,8BAA8B,0JAUjC,CAAC;AAoCX,wBAAgB,uBAAuB,CACrC,cAAc,GAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAM,GAChD,MAAM,EAAE,CAKV;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,GAAE,uBAA4B,GACpC,iBAAiB,CA4BnB"}
@@ -10,6 +10,18 @@ import * as dntShim from "../../../_dnt.shims.js";
10
10
  import { getFrameworkRootFromMeta } from "../../platform/compat/vfs-paths.js";
11
11
  import { getHostEnv } from "../../platform/compat/process.js";
12
12
  import { getCacheBaseDir, getHttpBundleCacheDir, getMdxEsmCacheDir, } from "../../utils/cache-dir.js";
13
+ import { WORKER_INTERNAL_EGRESS_OVERRIDE_ENV } from "./worker-egress-guard.js";
14
+ export const FRAMEWORK_WORKER_ENV_ALLOWLIST = [
15
+ "NODE_ENV",
16
+ "DENO_ENV",
17
+ "VERYFRONT_ENV",
18
+ "LOG_LEVEL",
19
+ "LOG_FORMAT",
20
+ "NO_COLOR",
21
+ "FORCE_COLOR",
22
+ "CI",
23
+ WORKER_INTERNAL_EGRESS_OVERRIDE_ENV,
24
+ ];
13
25
  // Cache compiled binary check — Deno.execPath() is a syscall that never changes at runtime
14
26
  const _isCompiledBinary = (() => {
15
27
  try {
@@ -35,6 +47,24 @@ function normalizeReadPaths(paths) {
35
47
  }
36
48
  return [...unique];
37
49
  }
50
+ function normalizeEnvKeys(keys) {
51
+ const unique = new Set();
52
+ for (const key of keys) {
53
+ if (!key)
54
+ continue;
55
+ const trimmed = key.trim();
56
+ if (!trimmed)
57
+ continue;
58
+ unique.add(trimmed);
59
+ }
60
+ return [...unique];
61
+ }
62
+ export function buildWorkerEnvAllowlist(projectEnvKeys = []) {
63
+ return normalizeEnvKeys([
64
+ ...FRAMEWORK_WORKER_ENV_ALLOWLIST,
65
+ ...projectEnvKeys,
66
+ ]);
67
+ }
38
68
  function getDefaultCompiledReadPaths() {
39
69
  return normalizeReadPaths([
40
70
  getFrameworkRootFromMeta(globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).url),
@@ -50,7 +80,7 @@ function getDefaultCompiledReadPaths() {
50
80
  * - read: restricted to the project temp dir (transformed modules) and cache dirs
51
81
  * - write: denied (workers produce output via postMessage, not filesystem)
52
82
  * - net: allowed (data fetchers and API routes may call external APIs)
53
- * - env: allowed (user code reads API keys and config from environment)
83
+ * - env: restricted to framework keys and the project's configured env keys
54
84
  * - run: denied (no subprocess spawning from user code)
55
85
  * - ffi: denied (no native code from user code)
56
86
  * - sys: denied (no system info access from user code)
@@ -58,6 +88,7 @@ function getDefaultCompiledReadPaths() {
58
88
  export function buildWorkerPermissions(readPaths, options = {}) {
59
89
  const isCompiledBinary = options.isCompiledBinary ?? _isCompiledBinary;
60
90
  const normalizedReadPaths = normalizeReadPaths(readPaths);
91
+ const env = buildWorkerEnvAllowlist(options.projectEnvKeys);
61
92
  if (isCompiledBinary) {
62
93
  const compiledReadPaths = options.compiledReadPaths ?? getDefaultCompiledReadPaths();
63
94
  const scopedReadPaths = normalizeReadPaths([...normalizedReadPaths, ...compiledReadPaths]);
@@ -65,7 +96,7 @@ export function buildWorkerPermissions(readPaths, options = {}) {
65
96
  read: scopedReadPaths.length > 0 ? scopedReadPaths : false,
66
97
  write: false,
67
98
  net: true,
68
- env: true,
99
+ env,
69
100
  run: false,
70
101
  ffi: false,
71
102
  sys: false,
@@ -75,7 +106,7 @@ export function buildWorkerPermissions(readPaths, options = {}) {
75
106
  read: normalizedReadPaths.length > 0 ? normalizedReadPaths : false,
76
107
  write: false,
77
108
  net: true,
78
- env: true,
109
+ env,
79
110
  run: false,
80
111
  ffi: false,
81
112
  sys: false,
@@ -10,7 +10,7 @@ export declare class WorkerPool {
10
10
  /**
11
11
  * Get or create a worker for the given project.
12
12
  */
13
- getOrCreateWorker(projectId: string, readPaths: string[]): ProjectWorker;
13
+ getOrCreateWorker(projectId: string, readPaths: string[], projectEnvKeys?: Iterable<string | undefined>): ProjectWorker;
14
14
  /**
15
15
  * Execute a request in a project worker. Convenience method that
16
16
  * combines getOrCreateWorker + execute.
@@ -1 +1 @@
1
- {"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../../../src/src/security/sandbox/worker-pool.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAWzF,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,MAAM,CAAmB;IAEjC,OAAO,CAAC,eAAe,CAAqD;IAC5E,OAAO,CAAC,mBAAmB,CAAqD;gBAEpE,MAAM,GAAE,OAAO,CAAC,gBAAgB,CAAM;IAMlD;;OAEG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,aAAa;IA0CxE;;;OAGG;IACH,OAAO,CACL,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,cAAc,CAAC;IAiE1B;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAUpC;;OAEG;IACH,QAAQ,IAAI;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;YACtB,MAAM,EAAE,MAAM,CAAC;YACf,YAAY,EAAE,MAAM,CAAC;YACrB,UAAU,EAAE,OAAO,CAAC;YACpB,MAAM,EAAE,MAAM,CAAC;YACf,KAAK,EAAE,MAAM,CAAC;SACf,CAAC,CAAC;KACJ;IA4BD;;OAEG;IACH,UAAU,IAAI;QACZ,uCAAuC;QACvC,cAAc,EAAE,MAAM,CAAC;QACvB,oDAAoD;QACpD,kBAAkB,EAAE,MAAM,CAAC;QAC3B,kDAAkD;QAClD,sBAAsB,EAAE,MAAM,CAAC;QAC/B,qDAAqD;QACrD,WAAW,EAAE,MAAM,CAAC;QACpB,kEAAkE;QAClE,cAAc,EAAE,MAAM,CAAC;KACxB;IAoBD;;OAEG;IACH,QAAQ,IAAI,IAAI;IAgBhB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,aAAa;YA4BP,WAAW;IAmBzB;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;CA8BjC;AAqBD;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAGlD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAG/C;AAKD,wBAAgB,aAAa,IAAI,UAAU,CAiB1C;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C"}
1
+ {"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../../../src/src/security/sandbox/worker-pool.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AA4BzF,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,MAAM,CAAmB;IAEjC,OAAO,CAAC,eAAe,CAAqD;IAC5E,OAAO,CAAC,mBAAmB,CAAqD;gBAEpE,MAAM,GAAE,OAAO,CAAC,gBAAgB,CAAM;IAMlD;;OAEG;IACH,iBAAiB,CACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EAAE,EACnB,cAAc,GAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAM,GAChD,aAAa;IAmDhB;;;OAGG;IACH,OAAO,CACL,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,cAAc,CAAC;IAkE1B;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAUpC;;OAEG;IACH,QAAQ,IAAI;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;YACtB,MAAM,EAAE,MAAM,CAAC;YACf,YAAY,EAAE,MAAM,CAAC;YACrB,UAAU,EAAE,OAAO,CAAC;YACpB,MAAM,EAAE,MAAM,CAAC;YACf,KAAK,EAAE,MAAM,CAAC;SACf,CAAC,CAAC;KACJ;IA4BD;;OAEG;IACH,UAAU,IAAI;QACZ,uCAAuC;QACvC,cAAc,EAAE,MAAM,CAAC;QACvB,oDAAoD;QACpD,kBAAkB,EAAE,MAAM,CAAC;QAC3B,kDAAkD;QAClD,sBAAsB,EAAE,MAAM,CAAC;QAC/B,qDAAqD;QACrD,WAAW,EAAE,MAAM,CAAC;QACpB,kEAAkE;QAClE,cAAc,EAAE,MAAM,CAAC;KACxB;IAoBD;;OAEG;IACH,QAAQ,IAAI,IAAI;IAgBhB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,aAAa;YA4BP,WAAW;IAmBzB;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;CA8BjC;AAqBD;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAGlD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAG/C;AAKD,wBAAgB,aAAa,IAAI,UAAU,CAiB1C;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C"}
@@ -13,9 +13,24 @@ import { getEnvBoolean, getEnvNumber, unrefTimer } from "../../platform/compat/p
13
13
  import { withSpan } from "../../observability/tracing/otlp-setup.js";
14
14
  import { SECURITY_VIOLATION } from "../../errors/index.js";
15
15
  import { ProjectWorker } from "./project-worker.js";
16
- import { buildWorkerPermissions } from "./worker-permissions.js";
16
+ import { buildWorkerEnvAllowlist, buildWorkerPermissions } from "./worker-permissions.js";
17
17
  import { DEFAULT_WORKER_POOL_CONFIG } from "./worker-types.js";
18
18
  const logger = serverLogger.component("worker-pool");
19
+ function extractProjectEnvKeys(request) {
20
+ if (!("projectEnv" in request) || !request.projectEnv)
21
+ return [];
22
+ return Object.keys(request.projectEnv);
23
+ }
24
+ function normalizeProjectEnvKeys(keys) {
25
+ const frameworkEnvKeyCount = buildWorkerEnvAllowlist([]).length;
26
+ return buildWorkerEnvAllowlist(keys).slice(frameworkEnvKeyCount);
27
+ }
28
+ function sameEnvKeySet(left, right) {
29
+ if (left.length !== right.length)
30
+ return false;
31
+ const rightSet = new Set(right);
32
+ return left.every((key) => rightSet.has(key));
33
+ }
19
34
  export class WorkerPool {
20
35
  pool = new Map();
21
36
  recycling = new Set();
@@ -30,20 +45,29 @@ export class WorkerPool {
30
45
  /**
31
46
  * Get or create a worker for the given project.
32
47
  */
33
- getOrCreateWorker(projectId, readPaths) {
48
+ getOrCreateWorker(projectId, readPaths, projectEnvKeys = []) {
49
+ const normalizedProjectEnvKeys = normalizeProjectEnvKeys(projectEnvKeys);
34
50
  const existing = this.pool.get(projectId);
35
51
  if (existing && existing.worker.status !== "crashed" && existing.worker.status !== "terminated") {
36
- existing.lastAccessedAt = Date.now();
37
- return existing.worker;
52
+ if (!sameEnvKeySet(existing.projectEnvKeys, normalizedProjectEnvKeys)) {
53
+ existing.worker.terminate();
54
+ this.pool.delete(projectId);
55
+ }
56
+ else {
57
+ existing.lastAccessedAt = Date.now();
58
+ return existing.worker;
59
+ }
38
60
  }
39
61
  // If an existing entry is crashed/terminated, clean it up
40
- if (existing) {
62
+ if (existing && this.pool.has(projectId)) {
41
63
  existing.worker.terminate();
42
64
  this.pool.delete(projectId);
43
65
  }
44
66
  // Evict LRU if at capacity
45
67
  this.evictIfNeeded();
46
- const permissions = buildWorkerPermissions(readPaths);
68
+ const permissions = buildWorkerPermissions(readPaths, {
69
+ projectEnvKeys: normalizedProjectEnvKeys,
70
+ });
47
71
  const worker = new ProjectWorker({
48
72
  projectId,
49
73
  permissions,
@@ -55,6 +79,7 @@ export class WorkerPool {
55
79
  worker,
56
80
  lastAccessedAt: now,
57
81
  createdAt: now,
82
+ projectEnvKeys: normalizedProjectEnvKeys,
58
83
  });
59
84
  logger.debug("Worker created", {
60
85
  projectId,
@@ -78,7 +103,8 @@ export class WorkerPool {
78
103
  }
79
104
  }
80
105
  return withSpan("workerPool.execute", async () => {
81
- const worker = this.getOrCreateWorker(projectId, readPaths);
106
+ const projectEnvKeys = extractProjectEnvKeys(request);
107
+ const worker = this.getOrCreateWorker(projectId, readPaths, projectEnvKeys);
82
108
  // Check if worker should be recycled (request count or age)
83
109
  const entry = this.pool.get(projectId);
84
110
  const shouldRecycle = worker.requestCount >= this.config.maxRequestsPerWorker ||
@@ -100,11 +126,11 @@ export class WorkerPool {
100
126
  const result = worker.execute(request);
101
127
  void result.then(() => {
102
128
  this.evictWorker(projectId);
103
- this.getOrCreateWorker(projectId, readPaths);
129
+ this.getOrCreateWorker(projectId, readPaths, projectEnvKeys);
104
130
  this.recycling.delete(projectId);
105
131
  }, () => {
106
132
  this.evictWorker(projectId);
107
- this.getOrCreateWorker(projectId, readPaths);
133
+ this.getOrCreateWorker(projectId, readPaths, projectEnvKeys);
108
134
  this.recycling.delete(projectId);
109
135
  });
110
136
  return result;
@@ -1,7 +1,8 @@
1
+ import type { WebSocketConnection } from "../../../platform/adapters/base.js";
1
2
  /** Client metadata for observability */
2
3
  export interface HMRClientInfo {
3
4
  id: string;
4
- socket: WebSocket;
5
+ socket: WebSocketConnection;
5
6
  connectedAt: number;
6
7
  projectSlug?: string;
7
8
  userAgent?: string;
@@ -24,7 +25,7 @@ export declare function getClientDetails(): HMRClientDetail[];
24
25
  * Get all open WebSocket connections, optionally filtered by projectSlug.
25
26
  * This replaces the old exported `clientSockets` Set that drifted out of sync.
26
27
  */
27
- export declare function getOpenSockets(projectSlug?: string): WebSocket[];
28
+ export declare function getOpenSockets(projectSlug?: string): WebSocketConnection[];
28
29
  export declare function clearAll(): void;
29
30
  export {};
30
31
  //# sourceMappingURL=hmr-client-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hmr-client-manager.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/preview/hmr-client-manager.ts"],"names":[],"mappings":"AAIA,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,yEAAyE;AACzE,UAAU,eAAe;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AASD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAEnD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAYnD;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAErE;AAED,wBAAgB,gBAAgB,IAAI,eAAe,EAAE,CASpD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,CAQhE;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAS/B"}
1
+ {"version":3,"file":"hmr-client-manager.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/preview/hmr-client-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAI9E,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,mBAAmB,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,yEAAyE;AACzE,UAAU,eAAe;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AASD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAEnD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAYnD;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAErE;AAED,wBAAgB,gBAAgB,IAAI,eAAe,EAAE,CASpD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAQ1E;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAS/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"hmr.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/preview/hmr.handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,EAEpB,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAuBrB,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAK7D,qBAAa,UAAW,SAAQ,WAAW;IACzC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAgD;IAC1E,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAA6B;IAC7D,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAK;IAChD,OAAO,CAAC,MAAM,CAAC,WAAW,CAAS;IAEnC,QAAQ,EAAE,eAAe,CAKvB;IAEF,OAAO,CAAC,MAAM,CAAC,UAAU;IAsCnB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA4IvE;;;;;OAKG;YACW,wBAAwB;IAuCtC,MAAM,CAAC,cAAc,IAAI,MAAM;IAI/B,MAAM,CAAC,UAAU,IAAI;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;KAC3B;IAID,MAAM,CAAC,+BAA+B,IAAI,MAAM,IAAI;IAWpD,MAAM,CAAC,QAAQ,IAAI,IAAI;CAaxB"}
1
+ {"version":3,"file":"hmr.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/preview/hmr.handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,EAEpB,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAuBrB,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAS7D,qBAAa,UAAW,SAAQ,WAAW;IACzC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAgD;IAC1E,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAA6B;IAC7D,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAK;IAChD,OAAO,CAAC,MAAM,CAAC,WAAW,CAAS;IAEnC,QAAQ,EAAE,eAAe,CAKvB;IAEF,OAAO,CAAC,MAAM,CAAC,UAAU;IAsCnB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA4IvE;;;;;OAKG;YACW,wBAAwB;IAuCtC,MAAM,CAAC,cAAc,IAAI,MAAM;IAI/B,MAAM,CAAC,UAAU,IAAI;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;KAC3B;IAID,MAAM,CAAC,+BAA+B,IAAI,MAAM,IAAI;IAWpD,MAAM,CAAC,QAAQ,IAAI,IAAI;CAaxB"}
@@ -18,6 +18,9 @@ import { getHostEnv } from "../../../platform/compat/process.js";
18
18
  const logger = serverLogger.component("hmr-handler");
19
19
  // Priority between auth (0) and high (100)
20
20
  const PRIORITY_HMR = HandlerPriority.EARLY;
21
+ function getMessageEventData(event) {
22
+ return "data" in event ? event.data : undefined;
23
+ }
21
24
  export class HMRHandler extends BaseHandler {
22
25
  static rateLimiter = new RateLimiter(HMR_MAX_MESSAGES_PER_MINUTE);
23
26
  static reloadUnsubscribe = null;
@@ -149,7 +152,7 @@ export class HMRHandler extends BaseHandler {
149
152
  socket.addEventListener("message", (event) => {
150
153
  handleHmrClientMessage({
151
154
  socket,
152
- data: event.data,
155
+ data: getMessageEventData(event),
153
156
  rateLimiter: HMRHandler.rateLimiter,
154
157
  onActivity: () => {
155
158
  const client = getClient(clientId);
@@ -55,7 +55,7 @@ export class ApiHandlerWrapper extends BaseHandler {
55
55
  // Must run within runWithContext so VFS and registry scope are correct.
56
56
  await ensureProjectDiscovery(ctx);
57
57
  const api = await getApiHandler(ctx);
58
- const apiRes = await api.handle(req);
58
+ const apiRes = await api.handle(req, ctx);
59
59
  if (!apiRes) {
60
60
  this.logDebug("[API-Wrapper] API handler returned null, continuing to next handler", { pathname }, ctx);
61
61
  return this.continue();
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Unresolved module HTTP fallback phase for the MDX ESM module fetcher.
3
+ *
4
+ * @module transforms/mdx/esm-module-loader/module-fetcher/http-fallback
5
+ */
6
+ import type { RuntimeAdapter } from "../../../../platform/adapters/base.js";
7
+ import type { Logger } from "../../../../utils/logger/logger.js";
8
+ import { fetchModuleViaHTTP } from "./http-fetcher.js";
9
+ import { cacheModule } from "./module-cache.js";
10
+ type FetchModuleViaHttpFn = typeof fetchModuleViaHTTP;
11
+ type CacheLocalModuleFn = typeof cacheModule;
12
+ export interface ResolveUnresolvedModuleViaHttpFallbackInput {
13
+ normalizedPath: string;
14
+ parentModulePath?: string;
15
+ adapter: RuntimeAdapter;
16
+ fetchAndCacheModule: (path: string, parent?: string) => Promise<string | null>;
17
+ log: Logger;
18
+ projectSlug: string;
19
+ isLocalProject?: boolean;
20
+ strictMissingModules?: boolean;
21
+ esmCacheDir: string;
22
+ pathCache: Map<string, string>;
23
+ reactVersion?: string;
24
+ fetchViaHttp?: FetchModuleViaHttpFn;
25
+ cacheLocalModule?: CacheLocalModuleFn;
26
+ }
27
+ /**
28
+ * Resolve an unresolved filesystem module through the local HTTP fallback.
29
+ */
30
+ export declare function resolveUnresolvedModuleViaHttpFallback(input: ResolveUnresolvedModuleViaHttpFallbackInput): Promise<string | null>;
31
+ export {};
32
+ //# sourceMappingURL=http-fallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-fallback.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/mdx/esm-module-loader/module-fetcher/http-fallback.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,KAAK,oBAAoB,GAAG,OAAO,kBAAkB,CAAC;AACtD,KAAK,kBAAkB,GAAG,OAAO,WAAW,CAAC;AAE7C,MAAM,WAAW,2CAA2C;IAC1D,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC/E,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;CACvC;AAED;;GAEG;AACH,wBAAsB,sCAAsC,CAC1D,KAAK,EAAE,2CAA2C,GACjD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Unresolved module HTTP fallback phase for the MDX ESM module fetcher.
3
+ *
4
+ * @module transforms/mdx/esm-module-loader/module-fetcher/http-fallback
5
+ */
6
+ import { buildMissingModuleError } from "../missing-module.js";
7
+ import { fetchModuleViaHTTP } from "./http-fetcher.js";
8
+ import { cacheModule } from "./module-cache.js";
9
+ /**
10
+ * Resolve an unresolved filesystem module through the local HTTP fallback.
11
+ */
12
+ export async function resolveUnresolvedModuleViaHttpFallback(input) {
13
+ const fetchViaHttp = input.fetchViaHttp ?? fetchModuleViaHTTP;
14
+ const cacheLocalModule = input.cacheLocalModule ?? cacheModule;
15
+ const moduleCode = await fetchViaHttp(input.normalizedPath, input.adapter, input.fetchAndCacheModule, input.log, input.projectSlug, input.isLocalProject);
16
+ if (moduleCode) {
17
+ return await cacheLocalModule(input.normalizedPath, moduleCode, input.esmCacheDir, input.pathCache, input.log, input.reactVersion);
18
+ }
19
+ if (input.strictMissingModules ?? true) {
20
+ throw buildMissingModuleError({
21
+ modulePath: input.normalizedPath,
22
+ importer: input.parentModulePath,
23
+ projectSlug: input.projectSlug,
24
+ });
25
+ }
26
+ return null;
27
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"import-rewriter.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/mdx/esm-module-loader/module-fetcher/import-rewriter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgB5D;AAwCD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0D7F"}
1
+ {"version":3,"file":"import-rewriter.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/mdx/esm-module-loader/module-fetcher/import-rewriter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA2CH;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgB5D;AAwCD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0D7F"}
@@ -5,23 +5,43 @@
5
5
  */
6
6
  import { dirname, join, resolve } from "../../../../platform/compat/path/index.js";
7
7
  import { FRAMEWORK_ROOT } from "../constants.js";
8
- import { resolveVeryfrontModuleUrl } from "../../../veryfront-module-urls.js";
8
+ import { DEFAULT_REACT_VERSION, veryfrontStrategy, } from "../../../import-rewriter/index.js";
9
9
  import { getLocalFs } from "../cache/index.js";
10
10
  import { findStaticImportFromSpans, findStaticSideEffectImportSpans, replaceSourceSpans, } from "../utils/source-spans.js";
11
+ const MODULE_FETCHER_VERYFRONT_CONTEXT = {
12
+ filePath: "",
13
+ projectDir: "",
14
+ projectId: "",
15
+ target: "ssr",
16
+ dev: false,
17
+ reactVersion: DEFAULT_REACT_VERSION,
18
+ };
19
+ function rewriteVeryfrontModuleSpecifier(specifier) {
20
+ const result = veryfrontStrategy.rewrite({
21
+ specifier,
22
+ isDynamic: false,
23
+ start: 0,
24
+ end: specifier.length,
25
+ statementStart: 0,
26
+ statementEnd: 0,
27
+ raw: {},
28
+ }, MODULE_FETCHER_VERYFRONT_CONTEXT);
29
+ return result.specifier;
30
+ }
11
31
  /**
12
32
  * Rewrite veryfront/* imports to /_vf_modules/_veryfront/ paths for MDX module loading.
13
33
  * Uses deno.json exports/imports as the source of truth and appends ?ssr=true.
14
34
  */
15
35
  export function rewriteVeryfrontImports(code) {
16
36
  const replacements = findStaticImportFromSpans(code, (specifier) => specifier.startsWith("veryfront/") ? specifier : null).flatMap(({ original, path, start, end }) => {
17
- const mapped = resolveVeryfrontModuleUrl(path);
37
+ const mapped = rewriteVeryfrontModuleSpecifier(path);
18
38
  if (!mapped)
19
39
  return [];
20
40
  return [{
21
41
  start,
22
42
  end,
23
43
  expected: original,
24
- replacement: `from "${mapped}?ssr=true"`,
44
+ replacement: `from "${mapped}"`,
25
45
  }];
26
46
  });
27
47
  return replaceSourceSpans(code, replacements);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/mdx/esm-module-loader/module-fetcher/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAG5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAexD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAS9B;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAOtD;AAED;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,SAAS,EAAE,MAAM;CAI9B;AAcD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,oBAAoB,EAC7B,gBAAgB,CAAC,EAAE,MAAM,EACzB,OAAO,GAAE,GAAG,CAAC,MAAM,CAAa,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwFxB;AA8JD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IACR,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,GACA,oBAAoB,CAUtB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/mdx/esm-module-loader/module-fetcher/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAG5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAcxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAS9B;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAOtD;AAED;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,SAAS,EAAE,MAAM;CAI9B;AAcD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,oBAAoB,EAC7B,gBAAgB,CAAC,EAAE,MAAM,EACzB,OAAO,GAAE,GAAG,CAAC,MAAM,CAAa,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwFxB;AA8ID;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IACR,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,GACA,oBAAoB,CAUtB"}