@napplet/conformance-cli 0.4.1 → 0.4.3

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.
@@ -2,6 +2,197 @@
2
2
  var NappletConformanceHostBundle = (() => {
3
3
  // ../core/dist/index.js
4
4
  var NAP_DOMAINS = ["shell", "relay", "identity", "storage", "inc", "theme", "keys", "media", "notify", "config", "resource", "cvm", "outbox", "upload", "intent", "ble", "webrtc", "link", "count", "lists", "serial", "fs", "common", "dm", "gamepad", "llm"];
5
+ var NAP_IMPLEMENTATIONS = {
6
+ shell: {
7
+ proposal: { url: "https://github.com/napplet/naps/pull/101", status: "merged", checkedAt: "2026-10-31" },
8
+ maturity: "supported",
9
+ rationale: "Optional readiness and environment binding has focused shim and reference-runtime coverage.",
10
+ evidence: ["packages/nap/src/shell/shim.test.ts"]
11
+ },
12
+ relay: {
13
+ proposal: { url: "https://github.com/napplet/naps/pull/2", status: "in-review", checkedAt: "2026-10-31" },
14
+ maturity: "supported",
15
+ rationale: "Relay reads, publishing, and subscription lifecycle have maintained shim and integration tests; upstream naming remains under review.",
16
+ evidence: ["packages/nap/src/relay/shim.test.ts"],
17
+ implementationReference: { revision: "cb8f8eabb2d09439e9d174cb9eb6ef2caad0eca1", evidence: ".planning/quick/260703-ghg-align-napplet-packages-with-nap-relayeve/260703-ghg-SUMMARY.md", scope: "RelayEventResult sidecar migration; subsequent changes are not covered." }
18
+ },
19
+ identity: {
20
+ proposal: { url: "https://github.com/napplet/naps/pull/102", status: "merged", checkedAt: "2026-10-31" },
21
+ maturity: "supported",
22
+ rationale: "Read-only identity queries and updates have focused binding and reference-runtime coverage.",
23
+ evidence: ["packages/nap/src/identity/shim.test.ts"]
24
+ },
25
+ storage: {
26
+ proposal: { url: "https://github.com/napplet/naps/pull/3", status: "in-review", checkedAt: "2026-10-31" },
27
+ maturity: "supported",
28
+ rationale: "Key-value binding has focused request/result tests and tutorial usage; host storage isolation remains the host responsibility.",
29
+ evidence: ["packages/nap/src/storage/shim.test.ts"]
30
+ },
31
+ inc: {
32
+ proposal: { url: "https://github.com/napplet/naps/pull/104", status: "merged", checkedAt: "2026-10-31" },
33
+ maturity: "supported",
34
+ rationale: "Topic and channel bindings have compatibility tests and authenticated-endpoint reference-runtime coverage.",
35
+ evidence: ["packages/nap/src/inc/shim.ts", "packages/conformance-cli/src/browser.test.ts"]
36
+ },
37
+ theme: {
38
+ proposal: { url: "https://github.com/napplet/naps/pull/103", status: "merged", checkedAt: "2026-10-31" },
39
+ maturity: "supported",
40
+ rationale: "Theme queries and pushed updates have focused binding and opaque-iframe integration coverage.",
41
+ evidence: ["packages/nap/src/theme/shim.test.ts", "packages/conformance-cli/src/browser.test.ts"]
42
+ },
43
+ keys: {
44
+ proposal: { url: "https://github.com/napplet/naps/pull/9", status: "in-review", checkedAt: "2026-10-31" },
45
+ maturity: "experimental",
46
+ rationale: "Keyboard binding ships, but dedicated domain regression coverage is limited; retain explicit evaluation opt-in.",
47
+ evidence: ["packages/nap/src/keys/shim.ts"]
48
+ },
49
+ media: {
50
+ proposal: { url: "https://github.com/napplet/naps/pull/10", status: "in-review", checkedAt: "2026-10-31" },
51
+ maturity: "supported",
52
+ rationale: "Session creation and playback binding have focused regression coverage; proposed torrent playback amendments are not implied.",
53
+ evidence: ["packages/nap/src/media/shim.test.ts"]
54
+ },
55
+ notify: {
56
+ proposal: { url: "https://github.com/napplet/naps/pull/11", status: "in-review", checkedAt: "2026-10-31" },
57
+ maturity: "experimental",
58
+ rationale: "Notification binding ships with limited dedicated domain regression evidence; broader host integration confidence is unverified.",
59
+ evidence: ["packages/nap/src/notify/shim.ts"]
60
+ },
61
+ config: {
62
+ proposal: { url: "https://github.com/napplet/naps/pull/14", status: "draft", checkedAt: "2026-10-31" },
63
+ maturity: "exploratory",
64
+ rationale: "Configuration binding is tested, but the upstream draft is marked MAY DEFER; use for design exploration.",
65
+ evidence: ["packages/nap/src/config/shim.test.ts"]
66
+ },
67
+ resource: {
68
+ proposal: { url: "https://github.com/napplet/naps/pull/80", status: "in-review", checkedAt: "2026-10-31" },
69
+ maturity: "supported",
70
+ rationale: "Resource fetching has focused shim and type tests; the reverted merge and reopened proposal do not change that implementation assessment.",
71
+ evidence: ["packages/nap/src/resource/shim.test.ts"],
72
+ implementationReference: { revision: "8e75eadf317d7fe809415e4755cd48f63cfb63f3", evidence: ".planning/quick/260703-ghg-align-napplet-packages-with-nap-relayeve/260703-ghg-SUMMARY.md", scope: "Shared ResourceSidecarEntry types during the sidecar migration; subsequent resource changes are not covered." }
73
+ },
74
+ cvm: {
75
+ proposal: { url: "https://github.com/napplet/naps/pull/31", status: "in-review", checkedAt: "2026-10-31" },
76
+ maturity: "experimental",
77
+ rationale: "Session and registry bindings are tested; upstream explicitly marks the contract NEEDS WORK.",
78
+ evidence: ["packages/nap/src/cvm/shim.test.ts"]
79
+ },
80
+ outbox: {
81
+ proposal: { url: "https://github.com/napplet/naps/pull/32", status: "in-review", checkedAt: "2026-10-31" },
82
+ maturity: "supported",
83
+ rationale: "Routing and subscription bindings have focused tests and an integration regression for structured-clone boundaries.",
84
+ evidence: ["packages/nap/src/outbox/shim.test.ts", "packages/nap/src/boundary-integration.test.ts"],
85
+ implementationReference: { revision: "57db924bac022cc14c67d9fca1a4d92cf1bd4725", evidence: ".planning/quick/260703-ghg-align-napplet-packages-with-nap-relayeve/260703-ghg-SUMMARY.md", scope: "RelayEventResult and stream lifecycle migration; subsequent changes are not covered." }
86
+ },
87
+ upload: {
88
+ proposal: { url: "https://github.com/napplet/naps/pull/33", status: "in-review", checkedAt: "2026-10-31" },
89
+ maturity: "supported",
90
+ rationale: "Upload binding has focused tests for request/result behavior; host rail support remains runtime-specific.",
91
+ evidence: ["packages/nap/src/upload/shim.test.ts"]
92
+ },
93
+ intent: {
94
+ proposal: { url: "https://github.com/napplet/naps/pull/91", status: "merged", checkedAt: "2026-10-31" },
95
+ maturity: "supported",
96
+ rationale: "Convention normalization and dispatch have focused binding and reference-runtime integration tests.",
97
+ evidence: ["packages/nap/src/intent/shim.test.ts"]
98
+ },
99
+ ble: {
100
+ proposal: { url: "https://github.com/napplet/naps/pull/62", status: "in-review", checkedAt: "2026-10-31" },
101
+ maturity: "experimental",
102
+ rationale: "GATT binding has unit coverage; real-device behavior across host backends is not established by these tests.",
103
+ evidence: ["packages/nap/src/ble/shim.test.ts"]
104
+ },
105
+ webrtc: {
106
+ proposal: { url: "https://github.com/napplet/naps/pull/59", status: "in-review", checkedAt: "2026-10-31" },
107
+ maturity: "experimental",
108
+ rationale: "Signaling and session binding have unit coverage; interoperable live peer sessions remain unverified here.",
109
+ evidence: ["packages/nap/src/webrtc/shim.test.ts"]
110
+ },
111
+ link: {
112
+ proposal: { url: "https://github.com/napplet/naps/pull/53", status: "in-review", checkedAt: "2026-10-31" },
113
+ maturity: "supported",
114
+ rationale: "The small link-opening binding has focused regression coverage; navigation authorization stays with the host.",
115
+ evidence: ["packages/nap/src/link/shim.test.ts"]
116
+ },
117
+ count: {
118
+ proposal: { url: "https://github.com/napplet/naps/pull/69", status: "in-review", checkedAt: "2026-10-31" },
119
+ maturity: "experimental",
120
+ rationale: "Count binding is tested, but accuracy and aggregation across real relay backends remain outside that evidence.",
121
+ evidence: ["packages/nap/src/count/shim.test.ts"]
122
+ },
123
+ lists: {
124
+ proposal: { url: "https://github.com/napplet/naps/pull/68", status: "in-review", checkedAt: "2026-10-31" },
125
+ maturity: "experimental",
126
+ rationale: "List mutation binding is tested; concurrent real-host mutation behavior is not established by binding tests.",
127
+ evidence: ["packages/nap/src/lists/shim.test.ts"]
128
+ },
129
+ serial: {
130
+ proposal: { url: "https://github.com/napplet/naps/pull/61", status: "in-review", checkedAt: "2026-10-31" },
131
+ maturity: "experimental",
132
+ rationale: "Serial binding has unit coverage; device and native-host integration evidence is limited.",
133
+ evidence: ["packages/nap/src/serial/shim.test.ts"]
134
+ },
135
+ fs: {
136
+ proposal: { url: "https://github.com/napplet/naps/pull/88", status: "in-review", checkedAt: "2026-10-31" },
137
+ maturity: "experimental",
138
+ rationale: "Filesystem binding has MVP coverage; portability and authorization across storage backends remain under evaluation.",
139
+ evidence: ["packages/nap/src/fs/shim.test.ts"]
140
+ },
141
+ common: {
142
+ proposal: { url: "https://github.com/napplet/naps/pull/67", status: "in-review", checkedAt: "2026-10-31" },
143
+ maturity: "experimental",
144
+ rationale: "Social action binding is tested; broader host integration and evolving action semantics warrant explicit opt-in.",
145
+ evidence: ["packages/nap/src/common/shim.test.ts"]
146
+ },
147
+ dm: {
148
+ proposal: { url: "https://github.com/napplet/naps/pull/74", status: "draft", checkedAt: "2026-10-31" },
149
+ maturity: "experimental",
150
+ rationale: "Direct-message binding has shim and type coverage; transport interoperability is not established by those tests.",
151
+ evidence: ["packages/nap/src/dm/shim.test.ts"]
152
+ },
153
+ gamepad: {
154
+ proposal: { url: "https://github.com/napplet/naps/pull/130", status: "in-review", checkedAt: "2026-10-31" },
155
+ maturity: "supported",
156
+ rationale: "Cross-repository delivery includes binding, security, runtime, and conformance verification; the reopened proposal is still subject to change.",
157
+ evidence: ["packages/nap/src/gamepad/shim.test.ts", ".planning/phases/163-nap-gamepad-across-repositories/163-VERIFICATION.md"],
158
+ implementationReference: { revision: "0b28f77e3dfa0f07dd7c0f9d61a7090ee99f3a5e", evidence: ".planning/phases/163-nap-gamepad-across-repositories/163-SECURITY.md", scope: "Phase 163 binding, runtime isolation, and conformance verification." }
159
+ },
160
+ llm: {
161
+ proposal: { url: "https://github.com/napplet/naps/pull/134", status: "draft", checkedAt: "2026-10-31" },
162
+ maturity: "exploratory",
163
+ rationale: "Early published draft has binding and mock-runtime tests; provider behavior and API design remain exploratory.",
164
+ evidence: ["packages/nap/src/llm/shim.test.ts", "packages/conformance-cli/src/browser.test.ts"]
165
+ }
166
+ };
167
+ for (const entry of Object.values(NAP_IMPLEMENTATIONS)) {
168
+ Object.freeze(entry.proposal);
169
+ Object.freeze(entry.evidence);
170
+ if (entry.implementationReference) Object.freeze(entry.implementationReference);
171
+ Object.freeze(entry);
172
+ }
173
+ Object.freeze(NAP_IMPLEMENTATIONS);
174
+ function getNapImplementation(domain) {
175
+ return Object.hasOwn(NAP_IMPLEMENTATIONS, domain) ? NAP_IMPLEMENTATIONS[domain] : void 0;
176
+ }
177
+ function selectNapDomains(options) {
178
+ const allowed = new Set(options.allowedMaturities ?? ["supported"]);
179
+ const optIn = new Set(options.optIn ?? []);
180
+ const domains = [];
181
+ const excluded = [];
182
+ for (const domain of new Set(options.candidates)) {
183
+ const metadata = getNapImplementation(domain);
184
+ if (!metadata) {
185
+ excluded.push({ domain, reason: "unknown-domain" });
186
+ } else if (!metadata.maturity || !["supported", "experimental", "exploratory"].includes(metadata.maturity)) {
187
+ excluded.push({ domain, reason: "unclassified" });
188
+ } else if (allowed.has(metadata.maturity) || optIn.has(domain)) {
189
+ domains.push(domain);
190
+ } else {
191
+ excluded.push({ domain, reason: "maturity-not-allowed", maturity: metadata.maturity });
192
+ }
193
+ }
194
+ return { domains, excluded };
195
+ }
5
196
  function createDispatch() {
6
197
  const handlers = /* @__PURE__ */ new Map();
7
198
  function registerNap2(domain, handler) {
@@ -1110,13 +1301,15 @@ var NappletConformanceHostBundle = (() => {
1110
1301
  }
1111
1302
  function createReferenceHandler(options) {
1112
1303
  const initialized = /* @__PURE__ */ new Set();
1304
+ const hostDomains = options.domains ? new Set(options.domains) : null;
1113
1305
  const llm = createLlmHandlers();
1114
1306
  const handle2 = (endpoint, envelope) => {
1115
1307
  const type = getEnvelopeType(envelope);
1116
1308
  const verdict = validateEnvelope(envelope);
1117
1309
  options.records.push({ envelope, verdict, timestamp: options.now() });
1118
1310
  if (!type || !verdict.ok) return [];
1119
- const domains = endpoint.domains ?? NAP_DOMAINS;
1311
+ const candidates = endpoint.domains ?? NAP_DOMAINS;
1312
+ const domains = hostDomains ? candidates.filter((domain) => hostDomains.has(domain)) : candidates;
1120
1313
  if (!domains.includes(type.slice(0, type.indexOf(".")))) return [];
1121
1314
  if (type === "shell.ready") {
1122
1315
  if (initialized.has(endpoint.id)) return [];
@@ -1222,6 +1415,7 @@ var NappletConformanceHostBundle = (() => {
1222
1415
  const handleFrom = createReferenceHandler({
1223
1416
  now: options.now ?? Date.now,
1224
1417
  records,
1418
+ domains: options.domains,
1225
1419
  queueDelivery: deliveries.queue,
1226
1420
  intents: createIntentHandlers(deliveries.queue, options.manifests, options.defaults)
1227
1421
  });
@@ -1257,56 +1451,6 @@ var NappletConformanceHostBundle = (() => {
1257
1451
  }
1258
1452
  };
1259
1453
  }
1260
- var LLM_PRELUDE_SOURCE = `
1261
- if (domains.includes('llm')) {
1262
- const llmFields = { 'llm.select.result': 'model', 'llm.describe.result': 'model', 'llm.generate.result': 'result', 'llm.cancel.result': 'cancelled' };
1263
- const llmDeltas = new Map();
1264
- window.addEventListener('message', (event) => {
1265
- if (event.source !== window.parent) return;
1266
- const msg = event.data;
1267
- if (!msg || typeof msg !== 'object' || msg.type !== 'llm.delta' || 'id' in msg || typeof msg.requestId !== 'string') return;
1268
- const entry = llmDeltas.get(msg.requestId);
1269
- if (!entry || typeof msg.sequence !== 'number' || typeof msg.text !== 'string') return;
1270
- if (msg.sequence !== entry.next) { entry.next = Infinity; return; }
1271
- entry.next += 1;
1272
- if (entry.onDelta) entry.onDelta({ requestId: msg.requestId, sequence: msg.sequence, text: msg.text });
1273
- });
1274
- const llmRequest = (payload, id) => {
1275
- const type = payload.type;
1276
- return request(id === undefined ? payload : { ...payload, id }, type + '.result').then((message) => {
1277
- const field = llmFields[message.type];
1278
- const hasValue = message[field] !== undefined && message[field] !== null;
1279
- const hasError = message.error !== undefined && message.error !== null;
1280
- if (hasValue === hasError) throw new Error(type + '.result must carry exactly one of ' + field + ' or error');
1281
- if (hasError) {
1282
- const failure = new Error(String(message.error.message));
1283
- failure.name = 'LlmError';
1284
- failure.code = message.error.code;
1285
- throw failure;
1286
- }
1287
- return message[field];
1288
- });
1289
- };
1290
- napplet.llm = {
1291
- select: (requirements) => llmRequest(requirements === undefined ? { type: 'llm.select' } : { type: 'llm.select', requirements }),
1292
- describe: (handle) => llmRequest({ type: 'llm.describe', handle }),
1293
- generate: (generation, options) => {
1294
- const id = nextId();
1295
- const entry = { next: 0, onDelta: options && options.onDelta };
1296
- llmDeltas.set(id, entry);
1297
- const signal = options && options.signal;
1298
- const abort = () => { napplet.llm.cancel(id).catch(() => undefined); };
1299
- if (signal) { if (signal.aborted) abort(); else signal.addEventListener('abort', abort, { once: true }); }
1300
- const pendingGeneration = llmRequest({ type: 'llm.generate', request: generation }, id).finally(() => {
1301
- llmDeltas.delete(id);
1302
- if (signal) signal.removeEventListener('abort', abort);
1303
- });
1304
- return Object.assign(pendingGeneration, { requestId: id });
1305
- },
1306
- cancel: (requestId) => llmRequest({ type: 'llm.cancel', requestId }),
1307
- };
1308
- }
1309
- `;
1310
1454
  var INTERNAL_BOOT_ERROR = "__nappletConformance.error";
1311
1455
  var INTERNAL_INSTALLED = "__nappletConformance.installed";
1312
1456
  function sleep(ms) {
@@ -1315,34 +1459,6 @@ var NappletConformanceHostBundle = (() => {
1315
1459
  function escapeHtmlAttr(value) {
1316
1460
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1317
1461
  }
1318
- var GAMEPAD_STATE_TOOLS_SOURCE = `(() => {
1319
- const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
1320
- const numberIn = (value, min, max) => typeof value === 'number' && Number.isFinite(value) && value >= min && value <= max;
1321
- const isGamepadState = (value) => {
1322
- if (!record(value) || typeof value.available !== 'boolean' || typeof value.focused !== 'boolean' || !Array.isArray(value.pads)) return false;
1323
- if (value.available) {
1324
- if (value.reason !== undefined) return false;
1325
- } else if (value.focused || (value.reason !== 'blocked' && value.reason !== 'unavailable')) return false;
1326
- return Array.from(value.pads).every((pad) => {
1327
- if (pad === null) return true;
1328
- if (!record(pad) || !Number.isInteger(pad.index) || pad.index < 0 || typeof pad.id !== 'string' || typeof pad.mapping !== 'string' || pad.connected !== true) return false;
1329
- if (typeof pad.timestamp !== 'number' || !Number.isFinite(pad.timestamp) || !Array.isArray(pad.axes) || !Array.isArray(pad.buttons)) return false;
1330
- if (!value.focused && pad.timestamp !== 0) return false;
1331
- if (!Array.from(pad.axes).every((axis) => numberIn(axis, -1, 1) && (value.focused || axis === 0))) return false;
1332
- return Array.from(pad.buttons).every((button) => record(button) && numberIn(button.value, 0, 1) && typeof button.pressed === 'boolean' && typeof button.touched === 'boolean' && (value.focused || (button.value === 0 && !button.pressed && !button.touched)));
1333
- });
1334
- };
1335
- const copyGamepadState = (state) => {
1336
- const copy = { available: state.available, focused: state.focused };
1337
- if (state.reason !== undefined) copy.reason = state.reason;
1338
- copy.pads = state.pads.map((pad) => pad === null ? null : {
1339
- index: pad.index, id: pad.id, mapping: pad.mapping, connected: pad.connected,
1340
- timestamp: pad.timestamp, axes: pad.axes.slice(), buttons: pad.buttons.map((button) => ({ value: button.value, pressed: button.pressed, touched: button.touched })),
1341
- });
1342
- return copy;
1343
- };
1344
- return { isGamepadState, copyGamepadState };
1345
- })()`;
1346
1462
  function errorObserver() {
1347
1463
  return `(() => {
1348
1464
  const report = (reason) => {
@@ -1353,128 +1469,11 @@ var NappletConformanceHostBundle = (() => {
1353
1469
  window.addEventListener('unhandledrejection', (event) => report(event.reason));
1354
1470
  })();`;
1355
1471
  }
1356
- var FALLBACK_RUNTIME_OPEN = `
1357
- (() => {
1358
- const domains = `;
1359
- var FALLBACK_RUNTIME_BEFORE_GAMEPAD_TOOLS = `;
1360
- const napplet = {};
1361
- for (const domain of domains) {
1362
- if (domain === 'resource' || domain === 'gamepad') napplet[domain] = {};
1363
- }
1364
- const gamepadTools = `;
1365
- var FALLBACK_RUNTIME_AFTER_GAMEPAD_TOOLS = `;
1366
- let gamepadState = { available: true, focused: false, pads: [] };
1367
- const gamepadHandlers = new Set();
1368
- // Subscribe on first use, like the nap binding, so napplets that never touch
1369
- // gamepad emit no gamepad traffic for checks to attribute to them.
1370
- let gamepadSubscribed = false;
1371
- const gamepadSubscribe = () => {
1372
- if (gamepadSubscribed) return;
1373
- gamepadSubscribed = true;
1374
- window.parent.postMessage({ type: 'gamepad.subscribe' }, '*');
1375
- };
1376
- const pending = new Map();
1377
- let seq = 0;
1378
- const nextId = () => {
1379
- try {
1380
- if (window.crypto && typeof window.crypto.randomUUID === 'function') return window.crypto.randomUUID();
1381
- } catch {}
1382
- seq += 1;
1383
- return 'conformance-' + Date.now().toString(36) + '-' + seq.toString(36);
1384
- };
1385
- const request = (message, resultType, errorType) => new Promise((resolve, reject) => {
1386
- const id = message.id || nextId();
1387
- const timeout = window.setTimeout(() => {
1388
- pending.delete(id);
1389
- reject(new Error(message.type + ' timed out'));
1390
- }, 5000);
1391
- pending.set(id, { resolve, reject, resultType, errorType, timeout });
1392
- window.parent.postMessage({ ...message, id }, '*');
1393
- });
1394
- window.addEventListener('message', (event) => {
1395
- if (event.source !== window.parent) return;
1396
- const msg = event.data;
1397
- if (gamepadSubscribed && msg && msg.type === 'gamepad.state' && !('id' in msg) && gamepadTools.isGamepadState(msg)) {
1398
- gamepadState = gamepadTools.copyGamepadState(msg);
1399
- const recipients = Array.from(gamepadHandlers);
1400
- for (const entry of recipients) {
1401
- if (!gamepadHandlers.has(entry)) continue;
1402
- try { entry.handler(gamepadTools.copyGamepadState(gamepadState)); }
1403
- catch (error) { queueMicrotask(() => { throw error; }); }
1404
- }
1405
- return;
1406
- }
1407
- if (!msg || typeof msg !== 'object' || typeof msg.id !== 'string') return;
1408
- const slot = pending.get(msg.id);
1409
- if (!slot) return;
1410
- if (msg.type !== slot.resultType && msg.type !== slot.errorType) return;
1411
- pending.delete(msg.id);
1412
- window.clearTimeout(slot.timeout);
1413
- if (msg.type === slot.errorType) {
1414
- slot.reject(new Error(msg.message ? msg.error + ': ' + msg.message : String(msg.error || 'request failed')));
1415
- return;
1416
- }
1417
- slot.resolve(msg);
1418
- });
1419
- if (napplet.gamepad) {
1420
- napplet.gamepad = {
1421
- get available() { gamepadSubscribe(); return gamepadState.available; },
1422
- get focused() { gamepadSubscribe(); return gamepadState.focused; },
1423
- getGamepads() { gamepadSubscribe(); return gamepadTools.copyGamepadState(gamepadState).pads; },
1424
- onChange(handler) {
1425
- const entry = { handler };
1426
- gamepadHandlers.add(entry);
1427
- gamepadSubscribe();
1428
- return { close() { gamepadHandlers.delete(entry); } };
1429
- },
1430
- };
1431
- }
1432
- if (napplet.resource) {
1433
- napplet.resource.info = () =>
1434
- request({ type: 'resource.info' }, 'resource.info.result', 'resource.info.error')
1435
- .then((msg) => msg.info);
1436
- napplet.resource.bytes = (url, options) => {
1437
- const message = { type: 'resource.bytes', url: String(url) };
1438
- if (options && options.servers !== undefined) message.servers = options.servers;
1439
- return request(message, 'resource.bytes.result', 'resource.bytes.error')
1440
- .then((msg) => msg.blob);
1441
- };
1442
- napplet.resource.bytesMany = (requests) =>
1443
- request({ type: 'resource.bytesMany', requests: Array.from(requests || []) }, 'resource.bytesMany.result', 'resource.bytesMany.error')
1444
- .then((msg) => msg.items);
1445
- napplet.resource.bytesAsObjectURL = (url) => {
1446
- const handle = { url: '', revoke: () => {} };
1447
- let objectUrl = '';
1448
- let revoked = false;
1449
- const ready = napplet.resource.bytes(url).then((blob) => {
1450
- if (revoked) return;
1451
- objectUrl = URL.createObjectURL(blob);
1452
- handle.url = objectUrl;
1453
- return objectUrl;
1454
- });
1455
- handle.revoke = () => {
1456
- if (revoked) return;
1457
- revoked = true;
1458
- if (objectUrl) URL.revokeObjectURL(objectUrl);
1459
- };
1460
- Object.defineProperty(handle, 'ready', { value: ready, enumerable: false });
1461
- return handle;
1462
- };
1463
- }
1464
- ${LLM_PRELUDE_SOURCE}
1465
- window.napplet = napplet;
1466
- `;
1467
- var FALLBACK_RUNTIME_CLOSE = `
1468
- })();
1469
- `;
1470
1472
  function runtimePrelude(domains, runtimePreludeSource) {
1471
1473
  const installed = `window.parent.postMessage({ type: '${INTERNAL_INSTALLED}', installed: !!window.napplet }, '*');`;
1472
- if (runtimePreludeSource !== void 0) {
1473
- return `${runtimePreludeSource}
1474
+ return `${runtimePreludeSource}
1474
1475
  ;globalThis.NappletShimPrelude.install(${JSON.stringify({ domains })});
1475
1476
  ${installed}`;
1476
- }
1477
- return FALLBACK_RUNTIME_OPEN + JSON.stringify(domains) + FALLBACK_RUNTIME_BEFORE_GAMEPAD_TOOLS + GAMEPAD_STATE_TOOLS_SOURCE + FALLBACK_RUNTIME_AFTER_GAMEPAD_TOOLS + installed + FALLBACK_RUNTIME_CLOSE;
1478
1477
  }
1479
1478
  function injectRuntime(html, url, domains, runtimePreludeSource) {
1480
1479
  const base = `<base href="${escapeHtmlAttr(url)}">`;
@@ -1505,7 +1504,7 @@ ${installed}`;
1505
1504
  return response.text();
1506
1505
  }
1507
1506
  async function bootOnce(domains, opts, doc, win) {
1508
- const shell = createReferenceShell();
1507
+ const shell = createReferenceShell({ domains });
1509
1508
  const iframe = doc.createElement("iframe");
1510
1509
  iframe.setAttribute("sandbox", opts.allowSameOrigin ? "allow-scripts allow-same-origin" : "allow-scripts");
1511
1510
  iframe.setAttribute("allow", "gamepad 'none'");
@@ -1573,7 +1572,9 @@ ${installed}`;
1573
1572
  allowSameOrigin: options.allowSameOrigin ?? false,
1574
1573
  runtimePreludeSource: options.runtimePreludeSource
1575
1574
  };
1576
- const primary = await bootOnce(NAP_DOMAINS, resolved, doc, win);
1575
+ const candidates = options.domains ?? NAP_DOMAINS;
1576
+ const domains = options.maturity ? selectNapDomains({ ...options.maturity, candidates }).domains : candidates;
1577
+ const primary = await bootOnce(domains, resolved, doc, win);
1577
1578
  let degraded = null;
1578
1579
  if (options.runDegraded ?? true) {
1579
1580
  const d = await bootOnce([], resolved, doc, win);