@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.
- package/dist/cli.js.map +1 -1
- package/dist/host-bundle.global.js +200 -199
- package/dist/ui/assets/{index-CXfC5ZeJ.js → index-O6oNQSqa.js} +210 -202
- package/dist/ui/index.html +1 -1
- package/package.json +5 -5
|
@@ -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
|
|
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, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
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
|
-
|
|
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
|
|
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);
|