@gjsify/resolve-npm 0.22.0 → 0.24.1
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/lib/globals-map.mjs +30 -0
- package/lib/index.mjs +202 -46
- package/lib/register-globals-closure.mjs +83 -3
- package/lib/runtime-aliases.mjs +74 -4
- package/package.json +1 -1
package/lib/globals-map.mjs
CHANGED
|
@@ -214,6 +214,23 @@ export const GJS_GLOBALS_MAP = {
|
|
|
214
214
|
matchMedia: '@gjsify/dom-elements/register/match-media',
|
|
215
215
|
location: '@gjsify/dom-elements/register/location',
|
|
216
216
|
navigator: '@gjsify/dom-elements/register/navigator',
|
|
217
|
+
|
|
218
|
+
// --- Canvas 2D + IFrame + WebGL (GTK/WebKit-backed DOM classes) --------
|
|
219
|
+
//
|
|
220
|
+
// These live in `packages/framework/*` rather than a Web/DOM pillar because
|
|
221
|
+
// their implementations are GTK/Cairo-, WebKit- and Gwebgl/GLArea-bound
|
|
222
|
+
// (ADR 0012). They are DELIBERATELY NOT members of `GJS_GLOBALS_GROUPS.dom`:
|
|
223
|
+
// that group is the coarse `--globals auto,dom` safety net, and pulling
|
|
224
|
+
// these into it would make every `auto,dom` build hard-require
|
|
225
|
+
// `@gjsify/canvas2d` / `@gjsify/iframe` / `@gjsify/webgl` (and, for the
|
|
226
|
+
// second, WebKitGTK) as an installed dependency. Auto-detection injects them
|
|
227
|
+
// only when the identifier is actually referenced in the bundled,
|
|
228
|
+
// tree-shaken output.
|
|
229
|
+
ImageData: '@gjsify/canvas2d/register',
|
|
230
|
+
Path2D: '@gjsify/canvas2d/register',
|
|
231
|
+
HTMLIFrameElement: '@gjsify/iframe/register',
|
|
232
|
+
WebGLRenderingContext: '@gjsify/webgl/register',
|
|
233
|
+
WebGL2RenderingContext: '@gjsify/webgl/register',
|
|
217
234
|
};
|
|
218
235
|
|
|
219
236
|
/**
|
|
@@ -268,6 +285,19 @@ export const GJS_GI_BACKED_REGISTERS = {
|
|
|
268
285
|
'@gjsify/webaudio/register': ['Gst', 'GstApp'],
|
|
269
286
|
'@gjsify/webrtc/register': ['Gst', 'GstWebRTC', 'GstSdp'],
|
|
270
287
|
'@gjsify/gamepad/register': ['Manette'],
|
|
288
|
+
// Imports `@gjsify/dom-elements/register/canvas` (Gdk/GdkPixbuf/Pango/
|
|
289
|
+
// PangoCairo) plus `@gjsify/canvas2d-core`, which uses the `cairo` foreign
|
|
290
|
+
// struct module.
|
|
291
|
+
'@gjsify/canvas2d/register': ['Gdk', 'GdkPixbuf', 'Pango', 'PangoCairo'],
|
|
292
|
+
'@gjsify/iframe/register': ['WebKit'],
|
|
293
|
+
// The WebGL context classes reach the GL driver through the `gwebgl` Vala
|
|
294
|
+
// bridge shipped as this package's own prebuild, and decode `texImage2D`
|
|
295
|
+
// sources via GdkPixbuf. Gtk/Gdk are NOT listed: they are pulled by
|
|
296
|
+
// `webgl-bridge.ts` (the `Gtk.GLArea` widget), which the register does not
|
|
297
|
+
// import — its chain is the two context classes only. As with the entries
|
|
298
|
+
// above, the always-present base namespaces (GLib) are omitted; only the
|
|
299
|
+
// ones whose absence breaks a GTK-less host are worth naming.
|
|
300
|
+
'@gjsify/webgl/register': ['GdkPixbuf', 'Gwebgl'],
|
|
271
301
|
};
|
|
272
302
|
|
|
273
303
|
// ─── Browser target ────────────────────────────────────────────────────────
|
package/lib/index.mjs
CHANGED
|
@@ -11,6 +11,87 @@ export {
|
|
|
11
11
|
resetRuntimeAliasesCache,
|
|
12
12
|
} from './runtime-aliases.mjs';
|
|
13
13
|
|
|
14
|
+
import { getDerivedAliasesSync as _getDerivedAliasesSync } from './runtime-aliases.mjs';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Wrap a bare-specifier alias table so its `@gjsify/<X>` VALUES are passed
|
|
18
|
+
* through the derived per-target slot routing before the caller sees them.
|
|
19
|
+
*
|
|
20
|
+
* ## Why this exists (ADR 0014)
|
|
21
|
+
*
|
|
22
|
+
* A curated table maps a bare specifier (`os`, `node:os`) to `@gjsify/<X>`,
|
|
23
|
+
* while the derived per-runtimes-triplet map (`getDerivedAliasesSync`) maps
|
|
24
|
+
* `@gjsify/os` → `@gjsify/os/browser`. Applying the second to the values of
|
|
25
|
+
* the first is what makes ADR 0014's platform-entry routing reach a browser
|
|
26
|
+
* bundle at all: those bundles hit the packages through the bare/`node:` form,
|
|
27
|
+
* not by package name. (A direct `import … from '@gjsify/os'` routes on its own
|
|
28
|
+
* — it is a plain key hit in the merged map.)
|
|
29
|
+
*
|
|
30
|
+
* ## Why HERE and not as a second pass in `aliasPlugin`
|
|
31
|
+
*
|
|
32
|
+
* This looks like it wants to be a resolver chain — `os` → `@gjsify/os` →
|
|
33
|
+
* `@gjsify/os/browser` — but it must not be one. Slot routing may only be
|
|
34
|
+
* applied to values that CAME FROM a curated/derived table, and by the time
|
|
35
|
+
* `aliasPlugin` sees the map it has been merged flat across four tiers
|
|
36
|
+
* (derived → curated `ALIASES_*` → per-target overrides → user `--alias`,
|
|
37
|
+
* later wins — see `app/*.ts`). Tier information is gone, so a chain there
|
|
38
|
+
* would re-route USER aliases too:
|
|
39
|
+
*
|
|
40
|
+
* - `scripts/node-gi-consumer-harness.mjs` builds `--alias node:<x>=@gjsify/<x>`
|
|
41
|
+
* to put the POLYFILL under test behind a Node builtin. 34 of those
|
|
42
|
+
* polyfills declare `runtimes.node: "native"`, so a second hop hands back
|
|
43
|
+
* `@gjsify/<x>/globals` — Node's own builtin — and the suite measures
|
|
44
|
+
* nothing while reporting green.
|
|
45
|
+
* - Value-level chaining also has no notion of the documented KEY-level
|
|
46
|
+
* "curated wins over derived" precedence. Measured on the composed maps:
|
|
47
|
+
* `gjs` 0 of 114 entries would change, `node` 0 of 148 — and on
|
|
48
|
+
* `nativescript` the only 4 changes are regressions (`fs` →
|
|
49
|
+
* `@gjsify/native-fs-bridge` → `@gjsify/empty`, because a bridge package
|
|
50
|
+
* that IS the native implementation declares `nativescript: "native"`,
|
|
51
|
+
* which routes to a `globals.mjs` it does not ship).
|
|
52
|
+
*
|
|
53
|
+
* So composition happens at materialisation, where the tiers are still
|
|
54
|
+
* distinguishable, the curated table stays a pure bare-name → package
|
|
55
|
+
* statement (no slot policy duplicated into it), and `aliasPlugin` keeps its
|
|
56
|
+
* one-hop `skipSelf: true` contract. Composing a table is therefore an
|
|
57
|
+
* explicit, per-table decision; `ALIASES_NODE_FOR_NATIVESCRIPT` is
|
|
58
|
+
* deliberately NOT composed (see the STATUS.md TODO on the `native` slot's
|
|
59
|
+
* double meaning for the bridge packages).
|
|
60
|
+
*
|
|
61
|
+
* Evaluated LAZILY (on property read) and cached: `getDerivedAliasesSync` walks
|
|
62
|
+
* the workspace, and importing `@gjsify/resolve-npm` must not pay for a
|
|
63
|
+
* filesystem scan that a `--app gjs` build never needs. The trap set covers
|
|
64
|
+
* everything a consumer does with these tables — `Object.entries`, spread and
|
|
65
|
+
* direct indexing.
|
|
66
|
+
*
|
|
67
|
+
* @param {Record<string,string>} table
|
|
68
|
+
* @param {'gjs'|'node'|'browser'|'nativescript'} target
|
|
69
|
+
* @returns {Record<string,string>}
|
|
70
|
+
*/
|
|
71
|
+
function withDerivedSlotRouting(table, target) {
|
|
72
|
+
/** @type {Record<string,string>|null} */
|
|
73
|
+
let routed = null;
|
|
74
|
+
const materialise = () => {
|
|
75
|
+
if (routed) return routed;
|
|
76
|
+
const derived = _getDerivedAliasesSync(target);
|
|
77
|
+
/** @type {Record<string,string>} */
|
|
78
|
+
const out = {};
|
|
79
|
+
for (const [bare, pkg] of Object.entries(table)) {
|
|
80
|
+
out[bare] = Object.prototype.hasOwnProperty.call(derived, pkg) ? derived[pkg] : pkg;
|
|
81
|
+
}
|
|
82
|
+
routed = out;
|
|
83
|
+
return routed;
|
|
84
|
+
};
|
|
85
|
+
return new Proxy(table, {
|
|
86
|
+
get: (_t, prop) =>
|
|
87
|
+
typeof prop === 'string' ? materialise()[prop] : Reflect.get(table, prop),
|
|
88
|
+
has: (_t, prop) => Reflect.has(materialise(), prop),
|
|
89
|
+
ownKeys: () => Reflect.ownKeys(materialise()),
|
|
90
|
+
getOwnPropertyDescriptor: (_t, prop) =>
|
|
91
|
+
Reflect.getOwnPropertyDescriptor(materialise(), prop),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
14
95
|
/** Array of Node.js build in module names */
|
|
15
96
|
export const EXTERNALS_NODE = [
|
|
16
97
|
'assert',
|
|
@@ -187,45 +268,93 @@ export const ALIASES_NODE_FOR_GJS = {
|
|
|
187
268
|
* the routing in a second pass (`@gjsify/<X>` → `@gjsify/<X>/globals` or
|
|
188
269
|
* `@gjsify/empty` depending on the slot declaration).
|
|
189
270
|
*
|
|
190
|
-
* `none`-slot entries are deliberately hardcoded
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
271
|
+
* `none`-slot entries are deliberately hardcoded here rather than relying on the
|
|
272
|
+
* dynamic runtime layer — bare `fs` / `net` / ... do NOT start with `@gjsify/`
|
|
273
|
+
* and therefore never enter `getDerivedAliasesSync`. This table is the bridge.
|
|
274
|
+
*
|
|
275
|
+
* ## `@gjsify/empty` is a LAST resort, and every remaining one is classified
|
|
276
|
+
*
|
|
277
|
+
* A value of `@gjsify/empty` is an ANONYMOUS redirect: a shared
|
|
278
|
+
* `export default {}` that a dozen unrelated specifiers also resolve to. That
|
|
279
|
+
* makes it indistinguishable, in an emitted bundle, from an ACCIDENTAL alias —
|
|
280
|
+
* which is how `@gjsify/canvas2d`'s build could start emitting a stray
|
|
281
|
+
* `@gjsify/empty` import without anything noticing. It is also export-less, so
|
|
282
|
+
* `import { spawn } from 'child_process'` binds `undefined` and dies later at
|
|
283
|
+
* `spawn is not a function`, naming neither the module nor the platform.
|
|
194
284
|
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
285
|
+
* So a redirect is NAMED wherever a name exists, and each entry still pointing
|
|
286
|
+
* at `@gjsify/empty` carries an inline classification so the remaining work is
|
|
287
|
+
* visible rather than implied:
|
|
288
|
+
*
|
|
289
|
+
* (A) wireable now — the package declares a browser slot and ships the entry;
|
|
290
|
+
* the value names that entry. No `(A)` entries remain except
|
|
291
|
+
* `dns/promises`, which needs a `./browser/promises` subpath first.
|
|
292
|
+
* (A') native-available — the browser HAS the API (`fetch`, `WebSocket`); the
|
|
293
|
+
* honest target is a `/globals` re-export, not an empty module.
|
|
294
|
+
* (B) simulatable but unbuilt — a real Web analogue exists (`cluster` over
|
|
295
|
+
* Web Workers, `dgram` over WebRTC data channels, `v8.serialize` over
|
|
296
|
+
* `structuredClone`, `readline` over any stream). `@gjsify/empty` until
|
|
297
|
+
* someone builds it.
|
|
298
|
+
* (C) impossible — no Web API can ever back it. These get a NAMED throwing
|
|
299
|
+
* stub at `@gjsify/<X>/browser` (today: `child_process`, `net`, `tls`).
|
|
300
|
+
* `http2` / `inspector` are also `(C)` but not yet written; `repl` /
|
|
301
|
+
* `wasi` are `(C)` with no owning `@gjsify/*` package to host a stub.
|
|
302
|
+
*
|
|
303
|
+
* A `(C)` stub keeps its package's `runtimes.browser: "none"` declaration: the
|
|
304
|
+
* MODULE genuinely is not usable on the runtime. The stub does not make it
|
|
305
|
+
* usable, it makes the unusability legible — it exports the module's real named
|
|
306
|
+
* shape (so linking succeeds) and throws a structured `ENOTSUP` naming the
|
|
307
|
+
* module, the runtime and the calling site. Exports whose answer IS
|
|
308
|
+
* platform-independent (`net.isIP`, `tls.checkServerIdentity`) stay real; a
|
|
309
|
+
* blanket thrower would replace one lie with another.
|
|
310
|
+
*
|
|
311
|
+
* A `partial`-slot package names its `./browser` PLATFORM ENTRY here, not its
|
|
312
|
+
* package root. `withDerivedSlotRouting` only rewrites a value when the slot is
|
|
313
|
+
* `polyfill` (ADR 0014), so a curated value of `@gjsify/<X>` for a `partial`
|
|
314
|
+
* package would hand the bundler the GJS root body — whose `@girs/*` imports
|
|
315
|
+
* `gjsImportsEmptyPlugin` replaces with `{}`, producing the silent
|
|
316
|
+
* `GLib.Checksum is not a constructor` failure ADR 0014 exists to eliminate.
|
|
317
|
+
* `partial` must mean "degrades at call time", never "crashes at first use".
|
|
318
|
+
* Naming the subpath is safe precisely where the platform entry is already at
|
|
319
|
+
* VALUE-export parity with the root (else routing would trade a call-time
|
|
320
|
+
* degradation for a build-time MISSING_EXPORT) — which is why this is a
|
|
321
|
+
* per-package curated decision and NOT a blanket change to `resolveSlot`'s
|
|
322
|
+
* `partial` case. Machine-checked by `scripts/audit-runtimes.mjs`'
|
|
323
|
+
* `curated-alias-routing` invariant.
|
|
324
|
+
*
|
|
325
|
+
* WIRED. The browser-app orchestrator
|
|
326
|
+
* (`packages/infra/rolldown-plugin-gjsify/src/app/browser.ts`) spreads this map
|
|
327
|
+
* — plus a generated `node:*` prefix variant of every key — into its `aliasMap`
|
|
328
|
+
* between the derived slot routing and the per-target / user aliases. So every
|
|
329
|
+
* value here is a resolve target a `--app browser` build actually takes; a
|
|
330
|
+
* mistake in this table is a shipping bug, not a staged proposal. (The former
|
|
331
|
+
* "UNWIRED IN THIS PR" note dated from before that wiring landed and is what
|
|
332
|
+
* made the `crypto` / `zlib` root-body routing read as latent.)
|
|
204
333
|
*/
|
|
205
|
-
|
|
334
|
+
const ALIASES_NODE_FOR_BROWSER_TABLE = {
|
|
206
335
|
'assert': '@gjsify/assert',
|
|
207
336
|
'assert/strict': '@gjsify/assert/strict',
|
|
208
337
|
'async_hooks': '@gjsify/async_hooks',
|
|
209
338
|
'buffer': '@gjsify/buffer',
|
|
210
|
-
'child_process': '@gjsify/
|
|
211
|
-
'cluster': '@gjsify/empty',
|
|
339
|
+
'child_process': '@gjsify/child_process/browser', // none slot — NAMED throwing stub
|
|
340
|
+
'cluster': '@gjsify/empty', // (B) simulatable over Web Workers — unbuilt
|
|
212
341
|
'console': '@gjsify/console',
|
|
213
342
|
'constants': '@gjsify/constants',
|
|
214
|
-
'crypto': '@gjsify/crypto',
|
|
215
|
-
'dgram': '@gjsify/empty',
|
|
343
|
+
'crypto': '@gjsify/crypto/browser', // partial slot — name the platform entry
|
|
344
|
+
'dgram': '@gjsify/empty', // (B) simulatable over WebRTC data channels — unbuilt
|
|
216
345
|
'diagnostics_channel': '@gjsify/diagnostics_channel',
|
|
217
|
-
'dns': '@gjsify/
|
|
218
|
-
'dns/promises': '@gjsify/empty',
|
|
346
|
+
'dns': '@gjsify/dns/browser', // partial slot — name the platform entry
|
|
347
|
+
'dns/promises': '@gjsify/empty', // (A) blocked: no ./browser/promises subpath yet
|
|
219
348
|
'domain': '@gjsify/domain',
|
|
220
349
|
'events': '@gjsify/events',
|
|
221
|
-
'fs': '@gjsify/
|
|
222
|
-
'fs/promises': '@gjsify/
|
|
223
|
-
'http': '@gjsify/
|
|
224
|
-
'http2': '@gjsify/empty',
|
|
225
|
-
'https': '@gjsify/
|
|
226
|
-
'inspector': '@gjsify/empty',
|
|
227
|
-
'module': '@gjsify/
|
|
228
|
-
'net': '@gjsify/
|
|
350
|
+
'fs': '@gjsify/fs/browser', // partial slot — name the platform entry
|
|
351
|
+
'fs/promises': '@gjsify/fs/browser/promises',
|
|
352
|
+
'http': '@gjsify/http/browser', // partial slot — name the platform entry
|
|
353
|
+
'http2': '@gjsify/empty', // (C) impossible — needs a named throwing stub
|
|
354
|
+
'https': '@gjsify/https/browser', // partial slot — name the platform entry
|
|
355
|
+
'inspector': '@gjsify/empty', // (C) impossible — needs a named throwing stub
|
|
356
|
+
'module': '@gjsify/module/browser', // partial slot — name the platform entry
|
|
357
|
+
'net': '@gjsify/net/browser', // none slot — NAMED throwing stub
|
|
229
358
|
'os': '@gjsify/os',
|
|
230
359
|
'path': '@gjsify/path',
|
|
231
360
|
'path/posix': '@gjsify/path/posix',
|
|
@@ -234,9 +363,9 @@ export const ALIASES_NODE_FOR_BROWSER = {
|
|
|
234
363
|
'process': '@gjsify/process', // PR-D: flips today's `@gjsify/empty`
|
|
235
364
|
'punycode': '@gjsify/punycode',
|
|
236
365
|
'querystring': '@gjsify/querystring',
|
|
237
|
-
'readline': '@gjsify/empty',
|
|
238
|
-
'readline/promises': '@gjsify/empty',
|
|
239
|
-
'repl': '@gjsify/empty',
|
|
366
|
+
'readline': '@gjsify/empty', // (B) stream-generic; simulatable — unbuilt
|
|
367
|
+
'readline/promises': '@gjsify/empty', // (B) stream-generic; simulatable — unbuilt
|
|
368
|
+
'repl': '@gjsify/empty', // (C) impossible, and no @gjsify/repl package exists
|
|
240
369
|
'stream': '@gjsify/stream',
|
|
241
370
|
'stream/web': '@gjsify/stream/web',
|
|
242
371
|
'stream/consumers': '@gjsify/stream/consumers',
|
|
@@ -245,24 +374,36 @@ export const ALIASES_NODE_FOR_BROWSER = {
|
|
|
245
374
|
'sys': '@gjsify/sys',
|
|
246
375
|
'timers': '@gjsify/timers',
|
|
247
376
|
'timers/promises': '@gjsify/timers/promises',
|
|
248
|
-
'tls': '@gjsify/
|
|
249
|
-
'tty': '@gjsify/empty',
|
|
377
|
+
'tls': '@gjsify/tls/browser', // none slot — NAMED throwing stub
|
|
378
|
+
'tty': '@gjsify/empty', // (B) isatty()===false is honest — unbuilt
|
|
250
379
|
'url': '@gjsify/url',
|
|
251
380
|
'util': '@gjsify/util',
|
|
252
381
|
'util/types': '@gjsify/util/types',
|
|
253
|
-
'v8': '@gjsify/empty',
|
|
382
|
+
'v8': '@gjsify/empty', // (B) serialize/deserialize over structuredClone — unbuilt
|
|
254
383
|
'vm': '@gjsify/vm',
|
|
255
|
-
'wasi': '@gjsify/empty',
|
|
256
|
-
'sqlite': '@gjsify/
|
|
257
|
-
'worker_threads': '@gjsify/
|
|
258
|
-
'zlib': '@gjsify/zlib',
|
|
384
|
+
'wasi': '@gjsify/empty', // (C) impossible, and no @gjsify/wasi package exists
|
|
385
|
+
'sqlite': '@gjsify/sqlite/browser', // partial slot — name the platform entry
|
|
386
|
+
'worker_threads': '@gjsify/worker_threads/browser', // partial slot — name the platform entry
|
|
387
|
+
'zlib': '@gjsify/zlib/browser', // partial slot — name the platform entry
|
|
259
388
|
|
|
260
389
|
// Third-party
|
|
261
|
-
'node-fetch': '@gjsify/empty', //
|
|
262
|
-
'ws': '@gjsify/
|
|
263
|
-
'isomorphic-ws': '@gjsify/empty',
|
|
390
|
+
'node-fetch': '@gjsify/empty', // (A') native-available: wants @gjsify/fetch/globals
|
|
391
|
+
'ws': '@gjsify/ws/browser', // partial slot — name the platform entry
|
|
392
|
+
'isomorphic-ws': '@gjsify/empty', // (A') native-available: wants a WebSocket re-export
|
|
264
393
|
}
|
|
265
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Bare Node-builtin specifier → polyfill / empty-stub mapping for `--app
|
|
397
|
+
* browser`, with ADR-0014 platform-entry routing already applied to the values
|
|
398
|
+
* (`os` → `@gjsify/os/browser` when that package's browser slot is `polyfill`
|
|
399
|
+
* and it exports a `./browser` subpath). See `withDerivedSlotRouting` above for
|
|
400
|
+
* why the two-pass chain has to be collapsed here.
|
|
401
|
+
*/
|
|
402
|
+
export const ALIASES_NODE_FOR_BROWSER = withDerivedSlotRouting(
|
|
403
|
+
ALIASES_NODE_FOR_BROWSER_TABLE,
|
|
404
|
+
'browser',
|
|
405
|
+
);
|
|
406
|
+
|
|
266
407
|
/**
|
|
267
408
|
* Bare Node-builtin specifier → polyfill / empty-stub mapping for `--app
|
|
268
409
|
* nativescript` builds. Mirrors `ALIASES_NODE_FOR_BROWSER` in shape, with
|
|
@@ -277,11 +418,18 @@ export const ALIASES_NODE_FOR_BROWSER = {
|
|
|
277
418
|
* - **Mobile-tractable Node built-ins → `@gjsify/<X>`.** `assert`,
|
|
278
419
|
* `async_hooks`, `buffer`, `crypto`, `events`, `fs`, `os`, `path`,
|
|
279
420
|
* `process`, `stream`, `string_decoder`, `url`, `util`, `querystring`
|
|
280
|
-
* route to their gjsify polyfills.
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
421
|
+
* route to their gjsify polyfills. NOTE: unlike `ALIASES_NODE_FOR_BROWSER`,
|
|
422
|
+
* this table is deliberately NOT wrapped in `withDerivedSlotRouting` — the
|
|
423
|
+
* VALUES here are taken as final. Composing it today would only change 4 of
|
|
424
|
+
* 122 entries and all four are regressions: `fs`/`fs/promises` point at
|
|
425
|
+
* `@gjsify/native-fs-bridge`, which declares `nativescript: "native"` meaning
|
|
426
|
+
* "this package IS the native implementation", while the slot vocabulary's
|
|
427
|
+
* `native` means "the RUNTIME provides it, route to `<pkg>/globals`" — a file
|
|
428
|
+
* the bridge does not ship, so the composed value degrades to
|
|
429
|
+
* `@gjsify/empty`. Settle the bridge packages' slot declarations first (see
|
|
430
|
+
* STATUS.md `Open TODOs`), then compose. An import by PACKAGE NAME does route
|
|
431
|
+
* per the declared slot (plain key hit in the merged map) — which is why the
|
|
432
|
+
* bridges are ALREADY emptied on that path today, same TODO.
|
|
285
433
|
* - **`ws` / `isomorphic-ws` → `@gjsify/empty`.** NS apps use `WebSocket`
|
|
286
434
|
* from the global runtime, not the `ws` package.
|
|
287
435
|
*
|
|
@@ -559,6 +707,14 @@ export const ALIASES_WEB_FOR_NODE = {
|
|
|
559
707
|
'@gjsify/dom-elements/register/match-media': '@gjsify/empty',
|
|
560
708
|
'@gjsify/dom-elements/register/location': '@gjsify/empty',
|
|
561
709
|
'@gjsify/dom-elements/register/navigator': '@gjsify/empty',
|
|
710
|
+
// Canvas 2D + IFrame + WebGL registers (GTK/Cairo-, WebKit- and
|
|
711
|
+
// Gwebgl/GLArea-backed, ADR 0012) — no-op on Node, exactly like the
|
|
712
|
+
// dom-elements registers above. Only the `@gjsify/*`-qualified form is
|
|
713
|
+
// mirrored: per ADR 0012 no bare-specifier `/register` alias is invented for
|
|
714
|
+
// a framework package.
|
|
715
|
+
'@gjsify/canvas2d/register': '@gjsify/empty',
|
|
716
|
+
'@gjsify/iframe/register': '@gjsify/empty',
|
|
717
|
+
'@gjsify/webgl/register': '@gjsify/empty',
|
|
562
718
|
'@gjsify/buffer/register': '@gjsify/empty',
|
|
563
719
|
|
|
564
720
|
// xmlhttprequest + DOMParser — no-op on Node
|
|
@@ -33,6 +33,32 @@ export const REGISTER_GLOBALS_CLOSURE = {
|
|
|
33
33
|
"setTimeout",
|
|
34
34
|
"structuredClone"
|
|
35
35
|
],
|
|
36
|
+
"@gjsify/canvas2d/register": [
|
|
37
|
+
"AbortController",
|
|
38
|
+
"AbortSignal",
|
|
39
|
+
"Blob",
|
|
40
|
+
"Buffer",
|
|
41
|
+
"ByteLengthQueuingStrategy",
|
|
42
|
+
"CountQueuingStrategy",
|
|
43
|
+
"DOMException",
|
|
44
|
+
"File",
|
|
45
|
+
"HTMLCanvasElement",
|
|
46
|
+
"Path2D",
|
|
47
|
+
"ReadableStream",
|
|
48
|
+
"TextDecoderStream",
|
|
49
|
+
"TextEncoderStream",
|
|
50
|
+
"TransformStream",
|
|
51
|
+
"WritableStream",
|
|
52
|
+
"atob",
|
|
53
|
+
"btoa",
|
|
54
|
+
"clearTimeout",
|
|
55
|
+
"document",
|
|
56
|
+
"performance",
|
|
57
|
+
"process",
|
|
58
|
+
"queueMicrotask",
|
|
59
|
+
"setTimeout",
|
|
60
|
+
"structuredClone"
|
|
61
|
+
],
|
|
36
62
|
"@gjsify/dom-elements/register/canvas": [
|
|
37
63
|
"AbortController",
|
|
38
64
|
"AbortSignal",
|
|
@@ -43,6 +69,7 @@ export const REGISTER_GLOBALS_CLOSURE = {
|
|
|
43
69
|
"DOMException",
|
|
44
70
|
"File",
|
|
45
71
|
"HTMLCanvasElement",
|
|
72
|
+
"Path2D",
|
|
46
73
|
"ReadableStream",
|
|
47
74
|
"TextDecoderStream",
|
|
48
75
|
"TextEncoderStream",
|
|
@@ -68,6 +95,7 @@ export const REGISTER_GLOBALS_CLOSURE = {
|
|
|
68
95
|
"DOMException",
|
|
69
96
|
"File",
|
|
70
97
|
"HTMLCanvasElement",
|
|
98
|
+
"Path2D",
|
|
71
99
|
"ReadableStream",
|
|
72
100
|
"TextDecoderStream",
|
|
73
101
|
"TextEncoderStream",
|
|
@@ -115,6 +143,7 @@ export const REGISTER_GLOBALS_CLOSURE = {
|
|
|
115
143
|
"DOMException",
|
|
116
144
|
"File",
|
|
117
145
|
"HTMLCanvasElement",
|
|
146
|
+
"Path2D",
|
|
118
147
|
"ReadableStream",
|
|
119
148
|
"TextDecoderStream",
|
|
120
149
|
"TextEncoderStream",
|
|
@@ -262,6 +291,33 @@ export const REGISTER_GLOBALS_CLOSURE = {
|
|
|
262
291
|
"setTimeout",
|
|
263
292
|
"structuredClone"
|
|
264
293
|
],
|
|
294
|
+
"@gjsify/iframe/register": [
|
|
295
|
+
"AbortController",
|
|
296
|
+
"AbortSignal",
|
|
297
|
+
"Blob",
|
|
298
|
+
"Buffer",
|
|
299
|
+
"ByteLengthQueuingStrategy",
|
|
300
|
+
"CountQueuingStrategy",
|
|
301
|
+
"DOMException",
|
|
302
|
+
"File",
|
|
303
|
+
"HTMLCanvasElement",
|
|
304
|
+
"HTMLIFrameElement",
|
|
305
|
+
"Path2D",
|
|
306
|
+
"ReadableStream",
|
|
307
|
+
"TextDecoderStream",
|
|
308
|
+
"TextEncoderStream",
|
|
309
|
+
"TransformStream",
|
|
310
|
+
"WritableStream",
|
|
311
|
+
"atob",
|
|
312
|
+
"btoa",
|
|
313
|
+
"clearTimeout",
|
|
314
|
+
"document",
|
|
315
|
+
"performance",
|
|
316
|
+
"process",
|
|
317
|
+
"queueMicrotask",
|
|
318
|
+
"setTimeout",
|
|
319
|
+
"structuredClone"
|
|
320
|
+
],
|
|
265
321
|
"@gjsify/node-globals/register/buffer": [
|
|
266
322
|
"AbortController",
|
|
267
323
|
"AbortSignal",
|
|
@@ -489,6 +545,33 @@ export const REGISTER_GLOBALS_CLOSURE = {
|
|
|
489
545
|
"setTimeout",
|
|
490
546
|
"structuredClone"
|
|
491
547
|
],
|
|
548
|
+
"@gjsify/webgl/register": [
|
|
549
|
+
"AbortController",
|
|
550
|
+
"AbortSignal",
|
|
551
|
+
"Blob",
|
|
552
|
+
"Buffer",
|
|
553
|
+
"ByteLengthQueuingStrategy",
|
|
554
|
+
"CountQueuingStrategy",
|
|
555
|
+
"DOMException",
|
|
556
|
+
"File",
|
|
557
|
+
"HTMLCanvasElement",
|
|
558
|
+
"Path2D",
|
|
559
|
+
"ReadableStream",
|
|
560
|
+
"TextDecoderStream",
|
|
561
|
+
"TextEncoderStream",
|
|
562
|
+
"TransformStream",
|
|
563
|
+
"WebGL2RenderingContext",
|
|
564
|
+
"WritableStream",
|
|
565
|
+
"atob",
|
|
566
|
+
"btoa",
|
|
567
|
+
"clearTimeout",
|
|
568
|
+
"document",
|
|
569
|
+
"performance",
|
|
570
|
+
"process",
|
|
571
|
+
"queueMicrotask",
|
|
572
|
+
"setTimeout",
|
|
573
|
+
"structuredClone"
|
|
574
|
+
],
|
|
492
575
|
"@gjsify/webrtc/register/data-channel": [
|
|
493
576
|
"AbortController",
|
|
494
577
|
"AbortSignal",
|
|
@@ -831,11 +914,8 @@ export const REGISTER_GLOBALS_CLOSURE = {
|
|
|
831
914
|
"CountQueuingStrategy",
|
|
832
915
|
"DOMException",
|
|
833
916
|
"DecompressionStream",
|
|
834
|
-
"Event",
|
|
835
|
-
"EventTarget",
|
|
836
917
|
"File",
|
|
837
918
|
"Headers",
|
|
838
|
-
"ProgressEvent",
|
|
839
919
|
"ReadableStream",
|
|
840
920
|
"Request",
|
|
841
921
|
"Response",
|
package/lib/runtime-aliases.mjs
CHANGED
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
// The bundler's alias resolver consults this triplet when routing bare
|
|
17
17
|
// `@gjsify/<X>` specifiers per `--app <target>`:
|
|
18
18
|
//
|
|
19
|
-
// slot=polyfill →
|
|
19
|
+
// slot=polyfill → `@gjsify/<X>/<target>` when the package's `exports` map
|
|
20
|
+
// declares that platform-entry subpath (ADR 0014), else keep
|
|
21
|
+
// as-is (`@gjsify/<X>` — the shared impl ships)
|
|
20
22
|
// slot=native → redirect to `@gjsify/<X>/globals` (re-exports native value)
|
|
21
23
|
// slot=partial → keep as-is (our impl, gracefully degrades at runtime)
|
|
22
24
|
// slot=none → no rewrite (the package's polyfill resolves normally;
|
|
@@ -32,6 +34,30 @@
|
|
|
32
34
|
// When a slot=native package is missing the corresponding `globals.mjs`
|
|
33
35
|
// re-export file, the resolver emits a warn-once log and falls back to
|
|
34
36
|
// `@gjsify/empty` (the current behavior) — surfacing the gap is desirable.
|
|
37
|
+
//
|
|
38
|
+
// ── Platform-entry routing (ADR 0014) ──────────────────────────────────────
|
|
39
|
+
//
|
|
40
|
+
// `polyfill` is the STRONG promise: "our implementation fully covers this API
|
|
41
|
+
// on this runtime". A package can only keep that promise off GJS if the code
|
|
42
|
+
// the target actually resolves is free of GLib/Gio. Several packages already
|
|
43
|
+
// ship a real per-target implementation (`src/browser.ts` → the `./browser`
|
|
44
|
+
// export subpath) but nothing ever routed to it: the root `.` export has no
|
|
45
|
+
// `"browser"` condition and the alias layer kept `polyfill` as a no-op, so the
|
|
46
|
+
// GJS body was bundled for the browser and every GLib call TypeError'd against
|
|
47
|
+
// the `{}` that `gjsImportsEmptyPlugin` substitutes for `@girs/*`.
|
|
48
|
+
//
|
|
49
|
+
// So: on a NON-gjs target, `slot=polyfill` routes to `@gjsify/<X>/<target>`
|
|
50
|
+
// whenever the package's `exports` map declares that subpath. The `gjs` target
|
|
51
|
+
// is never rerouted (`src/index.ts` IS the GJS implementation).
|
|
52
|
+
//
|
|
53
|
+
// `partial` deliberately does NOT route. `partial` is the weak promise ("works
|
|
54
|
+
// here, degrades") and the shared body's degradation IS its contract; the
|
|
55
|
+
// `partial` packages' platform entries are also not yet at export parity with
|
|
56
|
+
// their root entry (`@gjsify/fs`'s `src/browser.ts` is missing 34 value
|
|
57
|
+
// exports), so routing them would turn a call-time degradation into a
|
|
58
|
+
// build-time MISSING_EXPORT. Reaching parity is exactly the work that promotes
|
|
59
|
+
// a package from `partial` to `polyfill`; `scripts/audit-runtimes.mjs`
|
|
60
|
+
// (`platform-entry-parity` probe) gates that promotion.
|
|
35
61
|
|
|
36
62
|
import { readdir, readFile } from 'node:fs/promises';
|
|
37
63
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
@@ -41,6 +67,28 @@ import { fileURLToPath } from 'node:url';
|
|
|
41
67
|
const VALID_SLOTS = new Set(['polyfill', 'native', 'partial', 'none']);
|
|
42
68
|
const VALID_TARGETS = new Set(['gjs', 'node', 'browser', 'nativescript']);
|
|
43
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Collect the target names for which `exports` declares a `./<target>`
|
|
72
|
+
* platform-entry subpath. Shared by the sync + async package ingesters so the
|
|
73
|
+
* two paths cannot drift.
|
|
74
|
+
*
|
|
75
|
+
* @param {unknown} exportsField
|
|
76
|
+
* @returns {Set<string>}
|
|
77
|
+
*/
|
|
78
|
+
function collectPlatformEntries(exportsField) {
|
|
79
|
+
/** @type {Set<string>} */
|
|
80
|
+
const found = new Set();
|
|
81
|
+
if (!exportsField || typeof exportsField !== 'object') return found;
|
|
82
|
+
for (const target of VALID_TARGETS) {
|
|
83
|
+
// The `gjs` target is never a platform entry — `.` IS the GJS impl.
|
|
84
|
+
if (target === 'gjs') continue;
|
|
85
|
+
if (Object.prototype.hasOwnProperty.call(exportsField, `./${target}`)) {
|
|
86
|
+
found.add(target);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return found;
|
|
90
|
+
}
|
|
91
|
+
|
|
44
92
|
/** @typedef {'polyfill'|'native'|'partial'|'none'} Slot */
|
|
45
93
|
/**
|
|
46
94
|
* Per-package runtime slot declaration. Quadruplet (gjs / node / browser /
|
|
@@ -50,7 +98,14 @@ const VALID_TARGETS = new Set(['gjs', 'node', 'browser', 'nativescript']);
|
|
|
50
98
|
*
|
|
51
99
|
* @typedef {{gjs?:Slot, node?:Slot, browser?:Slot, nativescript?:Slot}} RuntimeTriplet
|
|
52
100
|
*/
|
|
53
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* @typedef {{name:string, dir:string, runtimes:RuntimeTriplet, hasGlobals:boolean,
|
|
103
|
+
* platformEntries:Set<string>}} PackageRecord
|
|
104
|
+
*
|
|
105
|
+
* `platformEntries` holds the target names (`browser` / `nativescript` / …)
|
|
106
|
+
* for which the package's `exports` map declares a `./<target>` subpath — the
|
|
107
|
+
* per-runtime implementation entry that `slot=polyfill` routes to (ADR 0014).
|
|
108
|
+
*/
|
|
54
109
|
|
|
55
110
|
let _cache = null;
|
|
56
111
|
const _warned = new Set();
|
|
@@ -145,7 +200,8 @@ async function ingestPackageDir(dir, out) {
|
|
|
145
200
|
}
|
|
146
201
|
}
|
|
147
202
|
const hasGlobals = existsSync(join(dir, 'globals.mjs'));
|
|
148
|
-
|
|
203
|
+
const platformEntries = collectPlatformEntries(json?.exports);
|
|
204
|
+
out.set(name, { name, dir, runtimes: triplet, hasGlobals, platformEntries });
|
|
149
205
|
}
|
|
150
206
|
} catch {
|
|
151
207
|
// Ignore unreadable / invalid package.json
|
|
@@ -180,7 +236,8 @@ function ingestPackageDirSync(dir, out) {
|
|
|
180
236
|
}
|
|
181
237
|
}
|
|
182
238
|
const hasGlobals = existsSync(join(dir, 'globals.mjs'));
|
|
183
|
-
|
|
239
|
+
const platformEntries = collectPlatformEntries(json?.exports);
|
|
240
|
+
out.set(name, { name, dir, runtimes: triplet, hasGlobals, platformEntries });
|
|
184
241
|
}
|
|
185
242
|
} catch {
|
|
186
243
|
// Ignore unreadable / invalid package.json
|
|
@@ -328,8 +385,21 @@ function resolveSlot(rec, target) {
|
|
|
328
385
|
if (!slot) return null; // Slot undeclared → no opinion, leave to hardcoded maps.
|
|
329
386
|
switch (slot) {
|
|
330
387
|
case 'polyfill':
|
|
388
|
+
// ADR 0014 — platform-entry routing. `polyfill` promises a FULL
|
|
389
|
+
// implementation on this runtime; when the package ships a
|
|
390
|
+
// per-target entry (`exports["./<target>"]`), that entry IS the
|
|
391
|
+
// implementation for the target and the shared `.` body (which on
|
|
392
|
+
// most packages is the GLib/Gio-backed GJS impl) must not be
|
|
393
|
+
// bundled. Never applies to `gjs` — `.` is the GJS impl.
|
|
394
|
+
if (target !== 'gjs' && rec.platformEntries?.has(target)) {
|
|
395
|
+
return `${rec.name}/${target}`;
|
|
396
|
+
}
|
|
397
|
+
// No platform entry → the shared body is the implementation.
|
|
398
|
+
return null;
|
|
331
399
|
case 'partial':
|
|
332
400
|
// Keep as-is — bundler resolves `@gjsify/<X>` to its `lib/esm/index.js`.
|
|
401
|
+
// `partial` does NOT route to a platform entry even when one exists;
|
|
402
|
+
// see the header note (parity is the promotion gate to `polyfill`).
|
|
333
403
|
return null;
|
|
334
404
|
case 'native':
|
|
335
405
|
if (rec.hasGlobals) {
|