@nimbus-sh/worker 0.1.4 → 0.1.6

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 (141) hide show
  1. package/README.md +37 -0
  2. package/dist/_shared/crypto.d.ts +15 -0
  3. package/dist/_shared/crypto.d.ts.map +1 -0
  4. package/dist/_shared/crypto.js +122 -0
  5. package/dist/facets/manager.d.ts +24 -0
  6. package/dist/facets/manager.d.ts.map +1 -1
  7. package/dist/facets/manager.js +279 -70
  8. package/dist/facets/process.d.ts +11 -2
  9. package/dist/facets/process.d.ts.map +1 -1
  10. package/dist/facets/process.js +187 -21
  11. package/dist/facets/vite-dev-server.d.ts +2 -0
  12. package/dist/facets/vite-dev-server.d.ts.map +1 -1
  13. package/dist/facets/vite-dev-server.js +39 -12
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2 -0
  17. package/dist/loaders/child-process/spawn-facet.d.ts +1 -3
  18. package/dist/loaders/child-process/spawn-facet.d.ts.map +1 -1
  19. package/dist/loaders/child-process/spawn-facet.js +1 -12
  20. package/dist/loaders/child-process/spawn-pool.d.ts +1 -1
  21. package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
  22. package/dist/loaders/child-process/spawn-pool.js +1 -6
  23. package/dist/loaders/loader-pool.d.ts +7 -0
  24. package/dist/loaders/loader-pool.d.ts.map +1 -1
  25. package/dist/loaders/loader-pool.js +3 -1
  26. package/dist/npm/installer.d.ts.map +1 -1
  27. package/dist/npm/installer.js +47 -27
  28. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  29. package/dist/npm/pre-bundle-facet.js +2 -1
  30. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  31. package/dist/npm/resolve-one-facet.js +41 -13
  32. package/dist/npm/resolver.d.ts.map +1 -1
  33. package/dist/npm/resolver.js +70 -35
  34. package/dist/router/index.d.ts.map +1 -1
  35. package/dist/router/index.js +45 -21
  36. package/dist/router/remote-api.d.ts.map +1 -1
  37. package/dist/router/remote-api.js +29 -8
  38. package/dist/runtime/barrel-synthesizer.d.ts +7 -0
  39. package/dist/runtime/barrel-synthesizer.d.ts.map +1 -1
  40. package/dist/runtime/barrel-synthesizer.js +11 -0
  41. package/dist/runtime/clang-runner.d.ts +2 -2
  42. package/dist/runtime/clang-runner.d.ts.map +1 -1
  43. package/dist/runtime/clang-runner.js +64 -47
  44. package/dist/runtime/esbuild-service.d.ts +12 -5
  45. package/dist/runtime/esbuild-service.d.ts.map +1 -1
  46. package/dist/runtime/esbuild-service.js +28 -14
  47. package/dist/runtime/node-runner.d.ts +2 -0
  48. package/dist/runtime/node-runner.d.ts.map +1 -1
  49. package/dist/runtime/node-shims.d.ts +4 -2
  50. package/dist/runtime/node-shims.d.ts.map +1 -1
  51. package/dist/runtime/node-shims.js +404 -55
  52. package/dist/runtime/os-contracts.d.ts +128 -0
  53. package/dist/runtime/os-contracts.d.ts.map +1 -0
  54. package/dist/runtime/os-contracts.js +1 -0
  55. package/dist/runtime/package-manager.d.ts +31 -0
  56. package/dist/runtime/package-manager.d.ts.map +1 -1
  57. package/dist/runtime/package-manager.js +162 -15
  58. package/dist/runtime/port-registry.d.ts +5 -0
  59. package/dist/runtime/port-registry.d.ts.map +1 -1
  60. package/dist/runtime/port-registry.js +22 -1
  61. package/dist/runtime/pyodide-runtime-assets.d.ts +19 -0
  62. package/dist/runtime/pyodide-runtime-assets.d.ts.map +1 -0
  63. package/dist/runtime/pyodide-runtime-assets.js +44 -0
  64. package/dist/runtime/python-repl.d.ts +3 -0
  65. package/dist/runtime/python-repl.d.ts.map +1 -1
  66. package/dist/runtime/python-repl.js +18 -32
  67. package/dist/runtime/python-runner.d.ts +8 -9
  68. package/dist/runtime/python-runner.d.ts.map +1 -1
  69. package/dist/runtime/python-runner.js +958 -118
  70. package/dist/runtime/python-socket-shim.d.ts +11 -0
  71. package/dist/runtime/python-socket-shim.d.ts.map +1 -0
  72. package/dist/runtime/python-socket-shim.js +526 -0
  73. package/dist/runtime/require-resolver.d.ts.map +1 -1
  74. package/dist/runtime/require-resolver.js +2 -11
  75. package/dist/runtime/ruby-gems.d.ts +25 -0
  76. package/dist/runtime/ruby-gems.d.ts.map +1 -0
  77. package/dist/runtime/ruby-gems.js +582 -0
  78. package/dist/runtime/ruby-runner.d.ts +2 -4
  79. package/dist/runtime/ruby-runner.d.ts.map +1 -1
  80. package/dist/runtime/ruby-runner.js +335 -34
  81. package/dist/runtime/runtime-catalog.d.ts +16 -4
  82. package/dist/runtime/runtime-catalog.d.ts.map +1 -1
  83. package/dist/runtime/runtime-catalog.js +21 -10
  84. package/dist/runtime/runtime-registry.d.ts +3 -0
  85. package/dist/runtime/runtime-registry.d.ts.map +1 -1
  86. package/dist/runtime/runtime-registry.js +4 -0
  87. package/dist/runtime/sqlite-runtime-fs-bridge.d.ts +43 -0
  88. package/dist/runtime/sqlite-runtime-fs-bridge.d.ts.map +1 -0
  89. package/dist/runtime/sqlite-runtime-fs-bridge.js +224 -0
  90. package/dist/runtime/static-server.d.ts +3 -0
  91. package/dist/runtime/static-server.d.ts.map +1 -0
  92. package/dist/runtime/static-server.js +121 -0
  93. package/dist/runtime/vfs-snapshot.d.ts +57 -0
  94. package/dist/runtime/vfs-snapshot.d.ts.map +1 -0
  95. package/dist/runtime/vfs-snapshot.js +141 -0
  96. package/dist/runtime/virtual-socket-kernel.d.ts +16 -0
  97. package/dist/runtime/virtual-socket-kernel.d.ts.map +1 -0
  98. package/dist/runtime/virtual-socket-kernel.js +360 -0
  99. package/dist/runtime/wasi-instance.d.ts +6 -3
  100. package/dist/runtime/wasi-instance.d.ts.map +1 -1
  101. package/dist/runtime/wasm-runner.d.ts +1 -25
  102. package/dist/runtime/wasm-runner.d.ts.map +1 -1
  103. package/dist/runtime/wasm-runner.js +1 -160
  104. package/dist/session/agent.d.ts +43 -0
  105. package/dist/session/agent.d.ts.map +1 -0
  106. package/dist/session/agent.js +1252 -0
  107. package/dist/session/init.d.ts.map +1 -1
  108. package/dist/session/init.js +64 -19
  109. package/dist/session/nimbus-session.d.ts +14 -2
  110. package/dist/session/nimbus-session.d.ts.map +1 -1
  111. package/dist/session/nimbus-session.js +104 -30
  112. package/dist/session/programmatic.d.ts +10 -0
  113. package/dist/session/programmatic.d.ts.map +1 -1
  114. package/dist/session/programmatic.js +121 -0
  115. package/dist/session/routes.d.ts.map +1 -1
  116. package/dist/session/routes.js +19 -1
  117. package/dist/session/rpc.d.ts +11 -0
  118. package/dist/session/rpc.d.ts.map +1 -1
  119. package/dist/session/rpc.js +56 -43
  120. package/dist/session/supervisor-rpc.d.ts +15 -0
  121. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  122. package/dist/session/supervisor-rpc.js +32 -0
  123. package/dist/session/ws.d.ts +4 -3
  124. package/dist/session/ws.d.ts.map +1 -1
  125. package/dist/session/ws.js +11 -1
  126. package/dist/shell/unix-commands.d.ts.map +1 -1
  127. package/dist/shell/unix-commands.js +52 -51
  128. package/dist/vfs/path.d.ts +8 -4
  129. package/dist/vfs/path.d.ts.map +1 -1
  130. package/dist/vfs/path.js +16 -4
  131. package/dist/vfs/sqlite-vfs.d.ts +2 -0
  132. package/dist/vfs/sqlite-vfs.d.ts.map +1 -1
  133. package/dist/vfs/sqlite-vfs.js +11 -0
  134. package/dist/vfs/symlink-registry.d.ts +7 -1
  135. package/dist/vfs/symlink-registry.d.ts.map +1 -1
  136. package/dist/vfs/symlink-registry.js +16 -11
  137. package/package.json +4 -1
  138. package/public/index.html +240 -0
  139. package/public/s/index.html +1316 -128
  140. package/runtime-contracts/pyodide-workerd-adapter.json +18 -0
  141. package/scripts/bundle-runtime.mjs +99 -24
@@ -33,9 +33,10 @@
33
33
  */
34
34
  import { generateSessionId, isValidSessionId, } from '../_shared/session-id.js';
35
35
  import { parseSessionRoute, forwardToSession, renderInvalidSessionHtml, SESSION_ROUTE_PREFIX, LEGACY_PUBLIC_DO_SEGMENT, } from '../_shared/session-router.js';
36
- import { verifyRequestToken, authErrorResponse, NimbusAuthError, NimbusTokenMalformedError, } from '../auth/index.js';
36
+ import { verifyRequestToken, requireScopes, requireSessionPin, authErrorResponse, NimbusAuthError, NimbusTokenMalformedError, } from '../auth/index.js';
37
37
  import { setCtxExports } from '../session/ctx-exports.js';
38
38
  import { handleNimbusRemoteApi, } from './remote-api.js';
39
+ import { parseAgentOAuthStateParam } from '../session/agent.js';
39
40
  /**
40
41
  * Build a Nimbus default-export handler. The returned object is exactly
41
42
  * what `export default` expects in a Workers entry module.
@@ -98,11 +99,27 @@ export function createNimbusHandler(options = {}) {
98
99
  const sdkResponse = await handleNimbusRemoteApi(request, env, options.sdk);
99
100
  if (sdkResponse)
100
101
  return sdkResponse;
102
+ if (url.pathname === '/api/nimbus/oauth/callback') {
103
+ const payload = parseAgentOAuthStateParam(url.searchParams.get('state'));
104
+ if (!payload || !isValidSessionId(payload.sessionId)) {
105
+ return new Response('Invalid OAuth state', { status: 400 });
106
+ }
107
+ return forwardToSession(request, {
108
+ sessionId: payload.sessionId,
109
+ innerPath: '/api/agent/oauth/callback',
110
+ basePath: `${SESSION_ROUTE_PREFIX}/${payload.sessionId}`,
111
+ }, env, { tenantSegment: payload.tenantSegment });
112
+ }
101
113
  // ── /new — spawn a fresh session and redirect ───────────────────
102
114
  if (url.pathname === '/new') {
103
115
  if (request.method !== 'POST' && request.method !== 'GET') {
104
116
  return new Response('Method not allowed', { status: 405 });
105
117
  }
118
+ const auth = await resolveNimbusRouteAuth(request, env, explicitMode, {
119
+ requiredScopes: ['session:create'],
120
+ });
121
+ if (auth instanceof Response)
122
+ return auth;
106
123
  const sessionId = generateSessionId();
107
124
  return new Response(null, {
108
125
  status: 302,
@@ -124,10 +141,16 @@ export function createNimbusHandler(options = {}) {
124
141
  },
125
142
  });
126
143
  }
127
- // Resolve tenant segment per auth mode.
128
- const tenantSegment = await resolveTenantSegment(request, env, explicitMode);
129
- if (tenantSegment instanceof Response)
130
- return tenantSegment;
144
+ // Resolve tenant segment per auth mode and enforce session attach
145
+ // semantics. In enforced mode a sid-pinned token can only attach
146
+ // to the exact session it was minted for.
147
+ const auth = await resolveNimbusRouteAuth(request, env, explicitMode, {
148
+ requiredScopes: ['session:attach'],
149
+ sessionId: route.sessionId,
150
+ });
151
+ if (auth instanceof Response)
152
+ return auth;
153
+ const tenantSegment = auth.tenantSegment;
131
154
  // `/s/<id>` and `/s/<id>/` (no inner path) → serve the xterm UI shell.
132
155
  if (route.innerPath === '/' || route.innerPath === '') {
133
156
  if (env.ASSETS) {
@@ -183,25 +206,17 @@ function isLegacyRootPath(pathname) {
183
206
  }
184
207
  return false;
185
208
  }
186
- /**
187
- * Decide which tenant segment to use for DO naming. Returns either the
188
- * segment string or a short-circuit Response (auth failure).
189
- *
190
- * Mode `'auto'`:
191
- * - If JWT_SECRET is set AND legacy env var not "1" → enforce verify.
192
- * - Otherwise → legacy public.
193
- *
194
- * Mode `'enforce'`: always verify; 401 on missing/invalid token.
195
- *
196
- * Mode `'legacy'`: always legacy public.
197
- */
198
- async function resolveTenantSegment(request, env, explicitMode) {
209
+ async function resolveNimbusRouteAuth(request, env, explicitMode, options = {}) {
199
210
  const envLegacyFlag = (env?.NIMBUS_LEGACY_PUBLIC === '1' || env?.NIMBUS_LEGACY_PUBLIC === true);
200
211
  const hasSecret = typeof env?.JWT_SECRET === 'string' && env.JWT_SECRET.length > 0;
201
212
  const mode = explicitMode
202
213
  ?? (hasSecret && !envLegacyFlag ? 'enforce' : 'legacy');
203
- if (mode === 'legacy')
204
- return LEGACY_PUBLIC_DO_SEGMENT;
214
+ if (mode === 'legacy') {
215
+ return {
216
+ tenantSegment: LEGACY_PUBLIC_DO_SEGMENT,
217
+ verified: null,
218
+ };
219
+ }
205
220
  if (!hasSecret) {
206
221
  // Enforce mode but no secret — config error. 500, no info leak.
207
222
  console.error('[nimbus] auth.mode="enforce" but JWT_SECRET is missing');
@@ -209,7 +224,16 @@ async function resolveTenantSegment(request, env, explicitMode) {
209
224
  }
210
225
  try {
211
226
  const verified = await verifyRequestToken(request, env);
212
- return verified.doInstanceName;
227
+ if (options.requiredScopes?.length) {
228
+ requireScopes(verified, options.requiredScopes);
229
+ }
230
+ if (options.sessionId) {
231
+ requireSessionPin(verified, options.sessionId);
232
+ }
233
+ return {
234
+ tenantSegment: verified.doInstanceName,
235
+ verified,
236
+ };
213
237
  }
214
238
  catch (e) {
215
239
  if (e instanceof NimbusAuthError) {
@@ -1 +1 @@
1
- {"version":3,"file":"remote-api.d.ts","sourceRoot":"","sources":["../../src/router/remote-api.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,KAAK,GACL,KAAK,GACL,KAAK,GACL,QAAQ,GACR,MAAM,GACN,OAAO,GACP,OAAO,GACP,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,KAAK,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,qBAAqB;IACpC,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;IACzC,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAqBD,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,qBAAqB,GAAG,SAAS,GACrC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA8D1B"}
1
+ {"version":3,"file":"remote-api.d.ts","sourceRoot":"","sources":["../../src/router/remote-api.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,KAAK,GACL,KAAK,GACL,KAAK,GACL,QAAQ,GACR,MAAM,GACN,OAAO,GACP,OAAO,GACP,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,KAAK,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,qBAAqB;IACpC,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;IACzC,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AA2BD,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,qBAAqB,GAAG,SAAS,GACrC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA+D1B"}
@@ -27,15 +27,15 @@ export async function handleNimbusRemoteApi(request, env, sdk) {
27
27
  catch (e) {
28
28
  return remoteJson({ ok: false, error: `Invalid JSON body: ${e?.message || e}`, code: 'E_BAD_JSON' }, 400);
29
29
  }
30
- const tenantSegment = await resolveRemoteTenantSegment(request, env, remote, match.sandboxId);
31
- if (tenantSegment instanceof Response)
32
- return tenantSegment;
30
+ const remoteAuth = await resolveRemoteAuth(request, env, remote, match.sandboxId);
31
+ if (remoteAuth instanceof Response)
32
+ return remoteAuth;
33
33
  const profileName = body.profile ?? 'default';
34
34
  const profile = sdk?.config?.sandboxes?.[profileName]
35
35
  ?? sdk?.config?.sandboxes?.default
36
36
  ?? {};
37
37
  const root = body.root ?? profile.root ?? '/home/user';
38
- const doName = `${tenantSegment}:${match.sandboxId}`;
38
+ const doName = `${remoteAuth.tenantSegment}:${match.sandboxId}`;
39
39
  const id = env.NIMBUS_SESSION.idFromName(doName);
40
40
  const stub = env.NIMBUS_SESSION.get(id);
41
41
  const ctx = {
@@ -45,6 +45,7 @@ export async function handleNimbusRemoteApi(request, env, sdk) {
45
45
  profileName,
46
46
  profile,
47
47
  root,
48
+ verified: remoteAuth.verified,
48
49
  };
49
50
  try {
50
51
  const result = await dispatchRemoteRpc(ctx);
@@ -81,11 +82,15 @@ function matchRemoteRpc(pathname, basePath) {
81
82
  return null;
82
83
  }
83
84
  }
84
- async function resolveRemoteTenantSegment(request, env, remote, sandboxId) {
85
+ async function resolveRemoteAuth(request, env, remote, sandboxId) {
85
86
  const hasSecret = typeof env?.JWT_SECRET === 'string' && env.JWT_SECRET.length > 0;
86
87
  if (!hasSecret) {
87
- if (remote.allowLegacy)
88
- return LEGACY_PUBLIC_DO_SEGMENT;
88
+ if (remote.allowLegacy) {
89
+ return {
90
+ tenantSegment: LEGACY_PUBLIC_DO_SEGMENT,
91
+ verified: null,
92
+ };
93
+ }
89
94
  return remoteJson({
90
95
  ok: false,
91
96
  error: 'Remote Nimbus sandbox API requires JWT_SECRET',
@@ -96,7 +101,10 @@ async function resolveRemoteTenantSegment(request, env, remote, sandboxId) {
96
101
  const verified = await verifyRequestToken(request, env);
97
102
  requireScopes(verified, remote.requiredScopes);
98
103
  requireSessionPin(verified, sandboxId);
99
- return verified.doInstanceName;
104
+ return {
105
+ tenantSegment: verified.doInstanceName,
106
+ verified,
107
+ };
100
108
  }
101
109
  catch (e) {
102
110
  if (e instanceof NimbusAuthError || e instanceof NimbusTokenMalformedError) {
@@ -133,6 +141,8 @@ async function dispatchRemoteRpc(ctx) {
133
141
  return ctx.stub._rpcReadFileBytes(stringArg(args[0], 'path'));
134
142
  case 'writeFile':
135
143
  return ctx.stub._rpcWriteFile(stringArg(args[0], 'path'), args[1]);
144
+ case 'stat':
145
+ return ctx.stub._rpcStat(stringArg(args[0], 'path'));
136
146
  case 'readdir':
137
147
  return ctx.stub._rpcReaddir(stringArg(args[0], 'path'));
138
148
  case 'exists':
@@ -166,10 +176,21 @@ async function dispatchRemoteRpc(ctx) {
166
176
  return ctx.stub._rpcExposePort(numberArg(args[0], 'port'));
167
177
  case 'unexposePort':
168
178
  return ctx.stub._rpcUnexposePort(numberArg(args[0], 'port'));
179
+ case 'destroy':
180
+ requireAnyScope(ctx, ['session:destroy', 'session:admin']);
181
+ return ctx.stub._rpcDestroy(objectArg(args[0]));
169
182
  default:
170
183
  throw apiError(`Unknown Nimbus sandbox operation: ${String(op)}`, 'E_REMOTE_OP', 400);
171
184
  }
172
185
  }
186
+ function requireAnyScope(ctx, scopes) {
187
+ const explicit = ctx.verified?.claims.scopes;
188
+ if (explicit === undefined)
189
+ return;
190
+ if (scopes.some((scope) => explicit.includes(scope)))
191
+ return;
192
+ throw apiError(`Nimbus token missing required scope: ${scopes.join(' or ')}`, 'E_SCOPE_MISSING', 403);
193
+ }
173
194
  function execOptions(ctx, value) {
174
195
  const options = objectArg(value);
175
196
  return {
@@ -62,6 +62,13 @@ import type { SliceEntry } from '../npm/pre-bundle-facet.js';
62
62
  * directly without barrel pressure.
63
63
  */
64
64
  export type NamedImportMap = Map<string, Set<string>>;
65
+ /**
66
+ * Stable signature for a barrel bundle's source import set. Stored in
67
+ * pkg_esm_bundles.input_hash so a synthetic bundle generated for
68
+ * `{ Home }` is never reused after the project starts importing
69
+ * `{ Home, AlertTriangle }`.
70
+ */
71
+ export declare function namedImportSignature(pkgName: string, names: ReadonlySet<string> | null | undefined): string | null;
65
72
  /**
66
73
  * Static-import scanner. Walks the user's source tree under projDir
67
74
  * and extracts named-import sets. Returns a map keyed by package name.
@@ -1 +1 @@
1
- {"version":3,"file":"barrel-synthesizer.d.ts","sourceRoot":"","sources":["../../src/runtime/barrel-synthesizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAG7D;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,MAAM,GACd,cAAc,CAgFhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GACzB,oBAAoB,GAAG,IAAI,CAmL7B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EAAE,EACzB,aAAa,SAAM,GAClB;IAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAiE/C;AA0DD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG3E"}
1
+ {"version":3,"file":"barrel-synthesizer.d.ts","sourceRoot":"","sources":["../../src/runtime/barrel-synthesizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAG7D;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGlH;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,MAAM,GACd,cAAc,CAgFhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GACzB,oBAAoB,GAAG,IAAI,CAmL7B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EAAE,EACzB,aAAa,SAAM,GAClB;IAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAiE/C;AA0DD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG3E"}
@@ -53,6 +53,17 @@
53
53
  * dynamically-referenced icon."
54
54
  */
55
55
  import { packageNameFromSpecifier } from './barrel-detect.js';
56
+ /**
57
+ * Stable signature for a barrel bundle's source import set. Stored in
58
+ * pkg_esm_bundles.input_hash so a synthetic bundle generated for
59
+ * `{ Home }` is never reused after the project starts importing
60
+ * `{ Home, AlertTriangle }`.
61
+ */
62
+ export function namedImportSignature(pkgName, names) {
63
+ if (!names || names.size === 0)
64
+ return null;
65
+ return `barrel:${pkgName}:${Array.from(names).sort().join(',')}`;
66
+ }
56
67
  /**
57
68
  * Static-import scanner. Walks the user's source tree under projDir
58
69
  * and extracts named-import sets. Returns a map keyed by package name.
@@ -19,8 +19,8 @@
19
19
  * tiny .o = ~20 MiB.
20
20
  *
21
21
  * Sysroot subset extraction happens supervisor-side via a small ustar
22
- * parser. The full 9.3 MiB sysroot.tar is parsed once per compile;
23
- * only the files matching the role's prefix-allowlist are forwarded.
22
+ * parser. The full sysroot.tar is parsed once when the clang runtime
23
+ * warms for a session; compile/link calls reuse the filtered subsets.
24
24
  *
25
25
  * Dispatch stays direct: no sleeps, no caller-side retries, and no
26
26
  * catch-and-continue around loader failures.
@@ -1 +1 @@
1
- {"version":3,"file":"clang-runner.d.ts","sourceRoot":"","sources":["../../src/runtime/clang-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,4DAA4D;AAC5D,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,QAAQ,EAAE,YAAY,CAAC;IACvB,GAAG,EAAE,SAAS,CAAC;CAChB,GAAG,CAAC,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,KAC7F,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,MAAM,CAAC,CAqShC;AAqZD,eAAO,MAAM,qBAAqB,imXA4UjC,CAAC"}
1
+ {"version":3,"file":"clang-runner.d.ts","sourceRoot":"","sources":["../../src/runtime/clang-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAIzD,4DAA4D;AAC5D,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,QAAQ,EAAE,YAAY,CAAC;IACvB,GAAG,EAAE,SAAS,CAAC;CAChB,GAAG,CAAC,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,KAC7F,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,MAAM,CAAC,CAyShC;AAybD,eAAO,MAAM,qBAAqB,imXA4UjC,CAAC"}
@@ -19,12 +19,13 @@
19
19
  * tiny .o = ~20 MiB.
20
20
  *
21
21
  * Sysroot subset extraction happens supervisor-side via a small ustar
22
- * parser. The full 9.3 MiB sysroot.tar is parsed once per compile;
23
- * only the files matching the role's prefix-allowlist are forwarded.
22
+ * parser. The full sysroot.tar is parsed once when the clang runtime
23
+ * warms for a session; compile/link calls reuse the filtered subsets.
24
24
  *
25
25
  * Dispatch stays direct: no sleeps, no caller-side retries, and no
26
26
  * catch-and-continue around loader failures.
27
27
  */
28
+ import { resolveVfsPath } from '../vfs/path.js';
28
29
  /** Build the runner factory. Closes over facetMgr + vfs. */
29
30
  export function makeClangRunnerFactory(deps) {
30
31
  const { facetMgr, vfs } = deps;
@@ -37,6 +38,7 @@ export function makeClangRunnerFactory(deps) {
37
38
  const lldVfsPath = findFile('bin/wasm-ld');
38
39
  const memfsVfsPath = findFile('share/clang/memfs.wasm');
39
40
  const sysrootVfsPath = findFile('share/clang/sysroot.tar');
41
+ let runtimePromise = null;
40
42
  return async function clangBinHandler(ctx) {
41
43
  const argv = ctx.args || [];
42
44
  const cwd = ctx.cwd || '/home/user';
@@ -66,8 +68,6 @@ export function makeClangRunnerFactory(deps) {
66
68
  ctx.stderr.write(`${binName}: clang/wasm-ld missing from install\n`);
67
69
  return 127;
68
70
  }
69
- const memfsBytes = vfs.readFile(memfsVfsPath);
70
- const sysrootBytes = vfs.readFile(sysrootVfsPath);
71
71
  // Parse argv: find input .c + output path.
72
72
  const parsed = parseUserArgv(argv);
73
73
  if (parsed.error) {
@@ -80,8 +80,6 @@ export function makeClangRunnerFactory(deps) {
80
80
  ctx.stderr.write(`${binName}: direct wasm-ld invocation not yet wired (v1.2)\n`);
81
81
  return 2;
82
82
  }
83
- // Parse sysroot.tar ONCE on supervisor side.
84
- const sysroot = parseUstar(sysrootBytes);
85
83
  // Validate all user-supplied source inputs exist in the user's
86
84
  // session VFS. Collect their bytes for memfs population.
87
85
  const userSourceFiles = {};
@@ -110,6 +108,24 @@ export function makeClangRunnerFactory(deps) {
110
108
  ctx.stderr.write(`${binName}: no compilable / linkable inputs\n`);
111
109
  return 1;
112
110
  }
111
+ let runtime;
112
+ try {
113
+ if (!runtimePromise) {
114
+ runtimePromise = createClangFacetRuntime(facetMgr, {
115
+ clangVfsPath,
116
+ lldVfsPath,
117
+ memfsVfsPath,
118
+ sysrootVfsPath,
119
+ vfs,
120
+ });
121
+ }
122
+ runtime = await runtimePromise;
123
+ }
124
+ catch (e) {
125
+ runtimePromise = null;
126
+ ctx.stderr.write(`${binName}: clang runtime warm-up failed: ${e?.message || e}\n`);
127
+ return 1;
128
+ }
113
129
  // Walk the user's cwd to gather headers (.h/.hpp/.hxx/.inc/...)
114
130
  // and any sibling headers users typically expect to be visible
115
131
  // to #include "..." resolution. Real clang/gcc auto-search the
@@ -122,10 +138,8 @@ export function makeClangRunnerFactory(deps) {
122
138
  // are vastly under the cap.
123
139
  const userIncludeBundle = collectIncludeBundle(vfs, cwd.replace(/^\/+/, ''));
124
140
  // ── COMPILE PHASE ────────────────────────────────────────────
125
- // Ship: clang.wasm + memfs.wasm + C-include subset of sysroot
126
- // + user's source files + user's header bundle.
127
- const compileSysroot = filterSysrootForCompile(sysroot);
128
- const clangBytes = vfs.readFile(clangVfsPath);
141
+ // Reuse the warm clang/memfs pool and ship only the C-include
142
+ // subset plus user source/header files for this invocation.
129
143
  // Build -I flag list. We pass each user -I path verbatim AND add
130
144
  // an implicit '.' (cwd) for quote-form lookup. wasm-clang's -cc1
131
145
  // mode does NOT add cwd to the quote search list by default
@@ -170,12 +184,9 @@ export function makeClangRunnerFactory(deps) {
170
184
  // so sibling sources stay out of memfs (smaller payload, no
171
185
  // surface for unintended cross-TU textual inclusion via -I.).
172
186
  const oneSourceFile = { [src]: userSourceFiles[src] };
173
- const compileResult = await dispatchClangFacet(facetMgr, {
174
- primaryName: 'clang',
175
- primaryBytes: clangBytes,
176
- memfsBytes,
187
+ const compileResult = await dispatchClangFacet(runtime.compile, {
177
188
  sysrootFiles: {
178
- ...compileSysroot,
189
+ ...runtime.compile.sysrootFiles,
179
190
  ...oneSourceFile,
180
191
  // User's headers from cwd tree (so quote-form #include
181
192
  // resolves; this is the primary clang-include-fix payload).
@@ -235,9 +246,8 @@ export function makeClangRunnerFactory(deps) {
235
246
  preBuiltBytesMap[lp] = userSourceFiles[lp];
236
247
  }
237
248
  // ── LINK PHASE ───────────────────────────────────────────────
238
- // Ship: lld.wasm + memfs.wasm + lib subset of sysroot + .o's.
239
- const linkSysroot = filterSysrootForLink(sysroot);
240
- const lldBytes = vfs.readFile(lldVfsPath);
249
+ // Reuse the warm wasm-ld/memfs pool and ship only the link
250
+ // sysroot subset plus object/archive inputs for this invocation.
241
251
  const stackSize = 1024 * 1024;
242
252
  // User-supplied -L paths and -l libraries flow through. The user
243
253
  // -L paths point at user-VFS dirs; we currently don't ship user
@@ -270,11 +280,8 @@ export function makeClangRunnerFactory(deps) {
270
280
  '-lclang_rt.builtins-wasm32',
271
281
  '-o', parsed.outputPath,
272
282
  ];
273
- const linkResult = await dispatchClangFacet(facetMgr, {
274
- primaryName: 'wasm-ld',
275
- primaryBytes: lldBytes,
276
- memfsBytes,
277
- sysrootFiles: { ...linkSysroot, ...objBytesMap, ...preBuiltBytesMap },
283
+ const linkResult = await dispatchClangFacet(runtime.link, {
284
+ sysrootFiles: { ...runtime.link.sysrootFiles, ...objBytesMap, ...preBuiltBytesMap },
278
285
  argv: linkArgv,
279
286
  outputPaths: [parsed.outputPath],
280
287
  });
@@ -393,14 +400,6 @@ function parseUserArgv(argv) {
393
400
  outputPath, compileOnly, exitCode: 0,
394
401
  };
395
402
  }
396
- function resolveVfsPath(rel, cwd) {
397
- const cwdN = cwd.replace(/^\/+/, '').replace(/\/+$/, '');
398
- if (rel.startsWith('/'))
399
- return rel.replace(/^\/+/, '');
400
- if (rel === '.')
401
- return cwdN;
402
- return `${cwdN}/${rel}`;
403
- }
404
403
  /**
405
404
  * Recognise headers / inline-include files. The compile facet ships
406
405
  * these alongside the .c sources so `#include "foo.h"` (quote-form)
@@ -600,23 +599,45 @@ function filterSysrootForLink(all) {
600
599
  }
601
600
  return out;
602
601
  }
603
- async function dispatchClangFacet(facetMgr, args) {
602
+ async function createClangFacetRuntime(facetMgr, args) {
603
+ if (!args.clangVfsPath || !args.lldVfsPath || !args.memfsVfsPath || !args.sysrootVfsPath) {
604
+ throw new Error('installed clang manifest is missing required files');
605
+ }
604
606
  const toAB = (u8) => u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength);
607
+ const memfsBytes = args.vfs.readFile(args.memfsVfsPath);
608
+ const clangBytes = args.vfs.readFile(args.clangVfsPath);
609
+ const lldBytes = args.vfs.readFile(args.lldVfsPath);
610
+ const sysroot = parseUstar(args.vfs.readFile(args.sysrootVfsPath));
611
+ const { NimbusLoaderPool } = await import('../loaders/loader-pool.js');
612
+ const env = facetMgr.env;
613
+ const ctx = facetMgr.ctx;
614
+ const makeTarget = (primaryName, primaryBytes, sysrootFiles) => ({
615
+ primaryName,
616
+ sysrootFiles,
617
+ pool: new NimbusLoaderPool(env, ctx, {
618
+ tag: `clang-runner-${primaryName}`,
619
+ concurrency: 1,
620
+ omitSupervisor: true,
621
+ cacheScope: 'global',
622
+ preamble: CLANG_RUNNER_PREAMBLE,
623
+ wasmModules: {
624
+ 'memfs.wasm': toAB(memfsBytes),
625
+ 'primary.wasm': toAB(primaryBytes),
626
+ },
627
+ }),
628
+ });
629
+ return {
630
+ compile: makeTarget('clang', clangBytes, filterSysrootForCompile(sysroot)),
631
+ link: makeTarget('wasm-ld', lldBytes, filterSysrootForLink(sysroot)),
632
+ };
633
+ }
634
+ async function dispatchClangFacet(target, args) {
605
635
  // Encode sysroot files as base64 for context ferrying. We do this on
606
636
  // the supervisor to keep the facet preamble small and CPU-light.
607
637
  const filesB64 = {};
608
638
  for (const [path, bytes] of Object.entries(args.sysrootFiles)) {
609
639
  filesB64[path] = uint8ToBase64(bytes);
610
640
  }
611
- const { NimbusLoaderPool } = await import('../loaders/loader-pool.js');
612
- const env = facetMgr.env;
613
- const ctx = facetMgr.ctx;
614
- const pool = new NimbusLoaderPool(env, ctx, {
615
- tag: `clang-runner-${args.primaryName}`,
616
- concurrency: 1,
617
- omitSupervisor: true,
618
- preamble: CLANG_RUNNER_PREAMBLE,
619
- });
620
641
  const facetFn = async function clangFacetCall(inArgs) {
621
642
  const wasmTable = globalThis.__NIMBUS_WASM || {};
622
643
  const memfsMod = wasmTable['memfs.wasm'];
@@ -646,16 +667,12 @@ async function dispatchClangFacet(facetMgr, args) {
646
667
  });
647
668
  };
648
669
  try {
649
- const result = await pool.submit(facetFn, {
650
- primaryName: args.primaryName,
670
+ const result = await target.pool.submit(facetFn, {
671
+ primaryName: target.primaryName,
651
672
  argv: args.argv,
652
673
  filesB64,
653
674
  outputPaths: args.outputPaths,
654
675
  }, {
655
- wasmModules: {
656
- 'memfs.wasm': toAB(args.memfsBytes),
657
- 'primary.wasm': toAB(args.primaryBytes),
658
- },
659
676
  timeoutMs: 300_000,
660
677
  });
661
678
  // Decode outputFiles from base64 → Uint8Array.
@@ -39,8 +39,12 @@ import type { SqliteVFS } from '../vfs/sqlite-vfs.js';
39
39
  * esbuild's entry-point external check rejected the externals when
40
40
  * passed at the top level. v5 cache entries are wrong (contain
41
41
  * embedded react copies) and must be invalidated.
42
+ * v7 — barrel-package bundles include a named-import signature in
43
+ * pkg_esm_bundles.input_hash. Prevents reusing a lucide-react
44
+ * bundle synthesized for one icon set after user source imports
45
+ * additional icons.
42
46
  */
43
- export declare const BUNDLER_VERSION = "v6";
47
+ export declare const BUNDLER_VERSION = "v7";
44
48
  /**
45
49
  * Returns the list of specifiers that must be marked `external` when bundling
46
50
  * `specifier` so that React / React-DOM / Scheduler share a single instance
@@ -118,13 +122,16 @@ export declare class EsbuildService {
118
122
  * fix this without rewriting upstream.
119
123
  *
120
124
  * Fix: when caller asks for format 'cjs' AND the source has a
121
- * top-level await, wrap the source in an async IIFE:
125
+ * top-level await, wrap the source in an async IIFE and return its
126
+ * Promise to the facet runner:
122
127
  *
123
- * ;(async () => {
128
+ * return (async () => {
124
129
  * <original-source>
125
130
  * })();
126
131
  *
127
- * Inside the IIFE, await is legal.
132
+ * Inside the IIFE, await is legal. Returning the Promise is required:
133
+ * the facet runner awaits promise-returning entry functions so
134
+ * sequential TLA execution cannot race process teardown or VFS flushes.
128
135
  *
129
136
  * ESM-imports-in-CJS note (nuxt-esm-in-cjs wave):
130
137
  * ─────────────────────────────────────────────────
@@ -145,7 +152,7 @@ export declare class EsbuildService {
145
152
  * 2. Extract top-level imports from the pass-1 output and rewrite
146
153
  * them as `const X = require(...)` shims (see
147
154
  * `convertEsmImportsToRequire` for the contract / shape).
148
- * 3. Wrap the remaining body in the async IIFE.
155
+ * 3. Wrap the remaining body in a returned async IIFE.
149
156
  * 4. Return the assembled string as the transform result.
150
157
  *
151
158
  * The require-shim emits the standard `__esModule` interop check
@@ -1 +1 @@
1
- {"version":3,"file":"esbuild-service.d.ts","sourceRoot":"","sources":["../../src/runtime/esbuild-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"esbuild-service.d.ts","sourceRoot":"","sources":["../../src/runtime/esbuild-service.ts"],"names":[],"mappings":"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"}
@@ -40,8 +40,12 @@ import { normalizeVfsPath, stripLeadingSlashes } from '../vfs/path.js';
40
40
  * esbuild's entry-point external check rejected the externals when
41
41
  * passed at the top level. v5 cache entries are wrong (contain
42
42
  * embedded react copies) and must be invalidated.
43
+ * v7 — barrel-package bundles include a named-import signature in
44
+ * pkg_esm_bundles.input_hash. Prevents reusing a lucide-react
45
+ * bundle synthesized for one icon set after user source imports
46
+ * additional icons.
43
47
  */
44
- export const BUNDLER_VERSION = 'v6';
48
+ export const BUNDLER_VERSION = 'v7';
45
49
  // ── Shared-runtime externals ────────────────────────────────────────────
46
50
  /**
47
51
  * Returns the list of specifiers that must be marked `external` when bundling
@@ -277,6 +281,12 @@ function hasEsmImports(src) {
277
281
  const re = /^[ \t]*import\b(?!\s*\()/m;
278
282
  return re.test(stripped);
279
283
  }
284
+ function hasEsmExports(src) {
285
+ if (!src || src.indexOf('export') === -1)
286
+ return false;
287
+ const stripped = stripCommentsAndStrings(src);
288
+ return /^[ \t]*export\b/m.test(stripped);
289
+ }
280
290
  /**
281
291
  * Strip `//` and `/* * /` comments and string / template literals from
282
292
  * source, replacing each with a single space. The result is byte-aligned
@@ -929,13 +939,16 @@ export class EsbuildService {
929
939
  * fix this without rewriting upstream.
930
940
  *
931
941
  * Fix: when caller asks for format 'cjs' AND the source has a
932
- * top-level await, wrap the source in an async IIFE:
942
+ * top-level await, wrap the source in an async IIFE and return its
943
+ * Promise to the facet runner:
933
944
  *
934
- * ;(async () => {
945
+ * return (async () => {
935
946
  * <original-source>
936
947
  * })();
937
948
  *
938
- * Inside the IIFE, await is legal.
949
+ * Inside the IIFE, await is legal. Returning the Promise is required:
950
+ * the facet runner awaits promise-returning entry functions so
951
+ * sequential TLA execution cannot race process teardown or VFS flushes.
939
952
  *
940
953
  * ESM-imports-in-CJS note (nuxt-esm-in-cjs wave):
941
954
  * ─────────────────────────────────────────────────
@@ -956,7 +969,7 @@ export class EsbuildService {
956
969
  * 2. Extract top-level imports from the pass-1 output and rewrite
957
970
  * them as `const X = require(...)` shims (see
958
971
  * `convertEsmImportsToRequire` for the contract / shape).
959
- * 3. Wrap the remaining body in the async IIFE.
972
+ * 3. Wrap the remaining body in a returned async IIFE.
960
973
  * 4. Return the assembled string as the transform result.
961
974
  *
962
975
  * The require-shim emits the standard `__esModule` interop check
@@ -974,11 +987,12 @@ export class EsbuildService {
974
987
  await this.ensureInit();
975
988
  const format = options?.format || 'esm';
976
989
  const loader = options?.loader || 'ts';
977
- // ── CJS + TLA + ESM imports: two-pass with import rewrite ──
978
- // This precedes the simple TLA-only IIFE wrap. We test ESM imports
979
- // first because the IIFE wrap is incompatible with them; if both
980
- // conditions hold we MUST go through the rewrite path.
981
- if (format === 'cjs' && hasTopLevelAwait(code) && hasEsmImports(code)) {
990
+ // ── CJS + TLA + ESM module syntax: two-pass with module rewrite ──
991
+ // This precedes the simple TLA-only IIFE wrap. We test ESM module
992
+ // syntax first because the IIFE wrap is incompatible with import
993
+ // and export statements; if both conditions hold we MUST go through
994
+ // the rewrite path.
995
+ if (format === 'cjs' && hasTopLevelAwait(code) && (hasEsmImports(code) || hasEsmExports(code))) {
982
996
  // Pass 1: emit ESM so esbuild accepts TLA + imports without complaint.
983
997
  // We use the same loader/target so TS/JSX is handled here too.
984
998
  const pass1 = await this._esbuild.transform(code, {
@@ -1005,9 +1019,9 @@ export class EsbuildService {
1005
1019
  });
1006
1020
  const { requires, body } = convertEsmImportsToRequire(pass1.code);
1007
1021
  const assembled = requires + '\n' +
1008
- ';(async () => {\n' +
1022
+ 'return (async () => {\n' +
1009
1023
  body +
1010
- '\n})().catch((e) => { console.error(e && e.stack || e); });\n';
1024
+ '\n})();\n';
1011
1025
  return {
1012
1026
  code: assembled,
1013
1027
  map: '',
@@ -1021,9 +1035,9 @@ export class EsbuildService {
1021
1035
  let sourceToTransform = code;
1022
1036
  if (format === 'cjs' && hasTopLevelAwait(code)) {
1023
1037
  sourceToTransform =
1024
- ';(async () => {\n' +
1038
+ 'return (async () => {\n' +
1025
1039
  code +
1026
- '\n})().catch((e) => { console.error(e && e.stack || e); });\n';
1040
+ '\n})();\n';
1027
1041
  }
1028
1042
  const result = await this._esbuild.transform(sourceToTransform, {
1029
1043
  loader,
@@ -81,6 +81,8 @@ export interface RunFreshOpts {
81
81
  cwd?: string;
82
82
  filename?: string;
83
83
  dirname?: string;
84
+ stdin?: string;
85
+ captureOutput?: boolean;
84
86
  /** Display label for the long-running spawn. Defaults to the
85
87
  * command + filename. Surfaced in the [started (long-running)]
86
88
  * notice + /api/processes listing. */