@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.
- package/README.md +37 -0
- package/dist/_shared/crypto.d.ts +15 -0
- package/dist/_shared/crypto.d.ts.map +1 -0
- package/dist/_shared/crypto.js +122 -0
- package/dist/facets/manager.d.ts +24 -0
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +279 -70
- package/dist/facets/process.d.ts +11 -2
- package/dist/facets/process.d.ts.map +1 -1
- package/dist/facets/process.js +187 -21
- package/dist/facets/vite-dev-server.d.ts +2 -0
- package/dist/facets/vite-dev-server.d.ts.map +1 -1
- package/dist/facets/vite-dev-server.js +39 -12
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/loaders/child-process/spawn-facet.d.ts +1 -3
- package/dist/loaders/child-process/spawn-facet.d.ts.map +1 -1
- package/dist/loaders/child-process/spawn-facet.js +1 -12
- package/dist/loaders/child-process/spawn-pool.d.ts +1 -1
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
- package/dist/loaders/child-process/spawn-pool.js +1 -6
- package/dist/loaders/loader-pool.d.ts +7 -0
- package/dist/loaders/loader-pool.d.ts.map +1 -1
- package/dist/loaders/loader-pool.js +3 -1
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +47 -27
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
- package/dist/npm/pre-bundle-facet.js +2 -1
- package/dist/npm/resolve-one-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.js +41 -13
- package/dist/npm/resolver.d.ts.map +1 -1
- package/dist/npm/resolver.js +70 -35
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +45 -21
- package/dist/router/remote-api.d.ts.map +1 -1
- package/dist/router/remote-api.js +29 -8
- package/dist/runtime/barrel-synthesizer.d.ts +7 -0
- package/dist/runtime/barrel-synthesizer.d.ts.map +1 -1
- package/dist/runtime/barrel-synthesizer.js +11 -0
- package/dist/runtime/clang-runner.d.ts +2 -2
- package/dist/runtime/clang-runner.d.ts.map +1 -1
- package/dist/runtime/clang-runner.js +64 -47
- package/dist/runtime/esbuild-service.d.ts +12 -5
- package/dist/runtime/esbuild-service.d.ts.map +1 -1
- package/dist/runtime/esbuild-service.js +28 -14
- package/dist/runtime/node-runner.d.ts +2 -0
- package/dist/runtime/node-runner.d.ts.map +1 -1
- package/dist/runtime/node-shims.d.ts +4 -2
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +404 -55
- package/dist/runtime/os-contracts.d.ts +128 -0
- package/dist/runtime/os-contracts.d.ts.map +1 -0
- package/dist/runtime/os-contracts.js +1 -0
- package/dist/runtime/package-manager.d.ts +31 -0
- package/dist/runtime/package-manager.d.ts.map +1 -1
- package/dist/runtime/package-manager.js +162 -15
- package/dist/runtime/port-registry.d.ts +5 -0
- package/dist/runtime/port-registry.d.ts.map +1 -1
- package/dist/runtime/port-registry.js +22 -1
- package/dist/runtime/pyodide-runtime-assets.d.ts +19 -0
- package/dist/runtime/pyodide-runtime-assets.d.ts.map +1 -0
- package/dist/runtime/pyodide-runtime-assets.js +44 -0
- package/dist/runtime/python-repl.d.ts +3 -0
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +18 -32
- package/dist/runtime/python-runner.d.ts +8 -9
- package/dist/runtime/python-runner.d.ts.map +1 -1
- package/dist/runtime/python-runner.js +958 -118
- package/dist/runtime/python-socket-shim.d.ts +11 -0
- package/dist/runtime/python-socket-shim.d.ts.map +1 -0
- package/dist/runtime/python-socket-shim.js +526 -0
- package/dist/runtime/require-resolver.d.ts.map +1 -1
- package/dist/runtime/require-resolver.js +2 -11
- package/dist/runtime/ruby-gems.d.ts +25 -0
- package/dist/runtime/ruby-gems.d.ts.map +1 -0
- package/dist/runtime/ruby-gems.js +582 -0
- package/dist/runtime/ruby-runner.d.ts +2 -4
- package/dist/runtime/ruby-runner.d.ts.map +1 -1
- package/dist/runtime/ruby-runner.js +335 -34
- package/dist/runtime/runtime-catalog.d.ts +16 -4
- package/dist/runtime/runtime-catalog.d.ts.map +1 -1
- package/dist/runtime/runtime-catalog.js +21 -10
- package/dist/runtime/runtime-registry.d.ts +3 -0
- package/dist/runtime/runtime-registry.d.ts.map +1 -1
- package/dist/runtime/runtime-registry.js +4 -0
- package/dist/runtime/sqlite-runtime-fs-bridge.d.ts +43 -0
- package/dist/runtime/sqlite-runtime-fs-bridge.d.ts.map +1 -0
- package/dist/runtime/sqlite-runtime-fs-bridge.js +224 -0
- package/dist/runtime/static-server.d.ts +3 -0
- package/dist/runtime/static-server.d.ts.map +1 -0
- package/dist/runtime/static-server.js +121 -0
- package/dist/runtime/vfs-snapshot.d.ts +57 -0
- package/dist/runtime/vfs-snapshot.d.ts.map +1 -0
- package/dist/runtime/vfs-snapshot.js +141 -0
- package/dist/runtime/virtual-socket-kernel.d.ts +16 -0
- package/dist/runtime/virtual-socket-kernel.d.ts.map +1 -0
- package/dist/runtime/virtual-socket-kernel.js +360 -0
- package/dist/runtime/wasi-instance.d.ts +6 -3
- package/dist/runtime/wasi-instance.d.ts.map +1 -1
- package/dist/runtime/wasm-runner.d.ts +1 -25
- package/dist/runtime/wasm-runner.d.ts.map +1 -1
- package/dist/runtime/wasm-runner.js +1 -160
- package/dist/session/agent.d.ts +43 -0
- package/dist/session/agent.d.ts.map +1 -0
- package/dist/session/agent.js +1252 -0
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +64 -19
- package/dist/session/nimbus-session.d.ts +14 -2
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +104 -30
- package/dist/session/programmatic.d.ts +10 -0
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +121 -0
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +19 -1
- package/dist/session/rpc.d.ts +11 -0
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +56 -43
- package/dist/session/supervisor-rpc.d.ts +15 -0
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +32 -0
- package/dist/session/ws.d.ts +4 -3
- package/dist/session/ws.d.ts.map +1 -1
- package/dist/session/ws.js +11 -1
- package/dist/shell/unix-commands.d.ts.map +1 -1
- package/dist/shell/unix-commands.js +52 -51
- package/dist/vfs/path.d.ts +8 -4
- package/dist/vfs/path.d.ts.map +1 -1
- package/dist/vfs/path.js +16 -4
- package/dist/vfs/sqlite-vfs.d.ts +2 -0
- package/dist/vfs/sqlite-vfs.d.ts.map +1 -1
- package/dist/vfs/sqlite-vfs.js +11 -0
- package/dist/vfs/symlink-registry.d.ts +7 -1
- package/dist/vfs/symlink-registry.d.ts.map +1 -1
- package/dist/vfs/symlink-registry.js +16 -11
- package/package.json +4 -1
- package/public/index.html +240 -0
- package/public/s/index.html +1316 -128
- package/runtime-contracts/pyodide-workerd-adapter.json +18 -0
- package/scripts/bundle-runtime.mjs +99 -24
package/dist/router/index.js
CHANGED
|
@@ -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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
|
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
|
-
|
|
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":"
|
|
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
|
|
31
|
-
if (
|
|
32
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
23
|
-
*
|
|
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;
|
|
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
|
|
23
|
-
*
|
|
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
|
-
//
|
|
126
|
-
//
|
|
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(
|
|
174
|
-
primaryName: 'clang',
|
|
175
|
-
primaryBytes: clangBytes,
|
|
176
|
-
memfsBytes,
|
|
187
|
+
const compileResult = await dispatchClangFacet(runtime.compile, {
|
|
177
188
|
sysrootFiles: {
|
|
178
|
-
...
|
|
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
|
-
//
|
|
239
|
-
|
|
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(
|
|
274
|
-
|
|
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
|
|
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:
|
|
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 = "
|
|
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
|
-
*
|
|
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
|
|
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":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAItD
|
|
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 = '
|
|
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
|
-
*
|
|
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
|
|
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
|
|
978
|
-
// This precedes the simple TLA-only IIFE wrap. We test ESM
|
|
979
|
-
// first because the IIFE wrap is incompatible with
|
|
980
|
-
// conditions hold we MUST go through
|
|
981
|
-
|
|
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
|
-
'
|
|
1022
|
+
'return (async () => {\n' +
|
|
1009
1023
|
body +
|
|
1010
|
-
'\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
|
-
'
|
|
1038
|
+
'return (async () => {\n' +
|
|
1025
1039
|
code +
|
|
1026
|
-
'\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. */
|