@kehto/shell 0.26.0 → 0.27.0
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 +47 -9
- package/dist/index.d.ts +168 -20
- package/dist/index.js +277 -91
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -28,8 +28,7 @@ function hexToBytes(hex) {
|
|
|
28
28
|
}
|
|
29
29
|
function createSendToNapplet(originRegistry2) {
|
|
30
30
|
return (windowId, msg) => {
|
|
31
|
-
|
|
32
|
-
if (win) win.postMessage(msg, "*");
|
|
31
|
+
originRegistry2.postToNapplet(windowId, msg);
|
|
33
32
|
};
|
|
34
33
|
}
|
|
35
34
|
function createRelayPoolAdapter(shellHooks, originRegistry2) {
|
|
@@ -61,8 +60,8 @@ function createRelayPoolAdapter(shellHooks, originRegistry2) {
|
|
|
61
60
|
shellHooks.relayPool.untrackSubscription(subKey);
|
|
62
61
|
},
|
|
63
62
|
openScopedRelay(windowId, relayUrl, subId, filters, _sendFn) {
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
63
|
+
const target = originRegistry2.getNappletTarget(windowId);
|
|
64
|
+
if (target) shellHooks.relayPool.openScopedRelay(windowId, relayUrl, subId, filters, target);
|
|
66
65
|
},
|
|
67
66
|
closeScopedRelay(windowId) {
|
|
68
67
|
shellHooks.relayPool.closeScopedRelay(windowId);
|
|
@@ -329,7 +328,21 @@ function adaptHooks(shellHooks, deps) {
|
|
|
329
328
|
|
|
330
329
|
// src/origin-registry.ts
|
|
331
330
|
var registry = /* @__PURE__ */ new Map();
|
|
331
|
+
var spentWindows = /* @__PURE__ */ new WeakSet();
|
|
332
332
|
var nextRegistrationId = 0;
|
|
333
|
+
function entryFor(windowId) {
|
|
334
|
+
for (const entry of registry.values()) {
|
|
335
|
+
if (entry.windowId === windowId) return entry;
|
|
336
|
+
}
|
|
337
|
+
return void 0;
|
|
338
|
+
}
|
|
339
|
+
function closePort(entry) {
|
|
340
|
+
const port = entry.port;
|
|
341
|
+
if (!port) return;
|
|
342
|
+
entry.port = void 0;
|
|
343
|
+
port.onmessage = null;
|
|
344
|
+
port.close();
|
|
345
|
+
}
|
|
333
346
|
var originRegistry = {
|
|
334
347
|
/**
|
|
335
348
|
* Register a window reference with a windowId and optional identity metadata.
|
|
@@ -337,10 +350,17 @@ var originRegistry = {
|
|
|
337
350
|
* @param win - The iframe's contentWindow reference
|
|
338
351
|
* @param windowId - The unique identifier for this napplet window
|
|
339
352
|
* @param identity - Optional NIP-5D identity metadata (dTag and aggregateHash)
|
|
353
|
+
* @throws {Error} When `win` already bound a NIP-5D port. NIP-5D identity is
|
|
354
|
+
* bound to one document; hosts must create a fresh iframe instead.
|
|
340
355
|
*/
|
|
341
356
|
register(win, windowId, identity) {
|
|
357
|
+
if (spentWindows.has(win)) {
|
|
358
|
+
throw new Error("NIP-5D: cannot re-bind a napplet identity to an existing iframe; create a fresh iframe");
|
|
359
|
+
}
|
|
342
360
|
for (const [registeredWin, entry] of registry.entries()) {
|
|
343
361
|
if (registeredWin === win || entry.windowId === windowId) {
|
|
362
|
+
closePort(entry);
|
|
363
|
+
if (registeredWin !== win) spentWindows.add(registeredWin);
|
|
344
364
|
registry.delete(registeredWin);
|
|
345
365
|
}
|
|
346
366
|
}
|
|
@@ -359,10 +379,73 @@ var originRegistry = {
|
|
|
359
379
|
unregister(windowId) {
|
|
360
380
|
for (const [win, entry] of registry.entries()) {
|
|
361
381
|
if (entry.windowId === windowId) {
|
|
382
|
+
closePort(entry);
|
|
383
|
+
spentWindows.add(win);
|
|
362
384
|
registry.delete(win);
|
|
363
385
|
}
|
|
364
386
|
}
|
|
365
387
|
},
|
|
388
|
+
/**
|
|
389
|
+
* Bind the NIP-5D port from a frame's single `napplet.hello`.
|
|
390
|
+
*
|
|
391
|
+
* @param windowId - The registered window identifier
|
|
392
|
+
* @param port - The one port transferred with the hello
|
|
393
|
+
* @returns `false` when the window is unknown or already bound
|
|
394
|
+
*/
|
|
395
|
+
bindPort(windowId, port) {
|
|
396
|
+
for (const [win, entry] of registry.entries()) {
|
|
397
|
+
if (entry.windowId !== windowId) continue;
|
|
398
|
+
if (entry.port || spentWindows.has(win)) return false;
|
|
399
|
+
entry.port = port;
|
|
400
|
+
spentWindows.add(win);
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
return false;
|
|
404
|
+
},
|
|
405
|
+
/**
|
|
406
|
+
* Look up the bound NIP-5D port for a window.
|
|
407
|
+
*
|
|
408
|
+
* @param windowId - The window identifier
|
|
409
|
+
* @returns The bound port, or undefined before `napplet.hello`
|
|
410
|
+
*/
|
|
411
|
+
getPort(windowId) {
|
|
412
|
+
return entryFor(windowId)?.port;
|
|
413
|
+
},
|
|
414
|
+
/**
|
|
415
|
+
* Send one NAP envelope to a napplet over its bound port. Never falls back
|
|
416
|
+
* to `iframeWindow.postMessage`.
|
|
417
|
+
*
|
|
418
|
+
* @param windowId - The window identifier
|
|
419
|
+
* @param message - The envelope to send
|
|
420
|
+
* @returns `true` when a bound port carried the message
|
|
421
|
+
*/
|
|
422
|
+
postToNapplet(windowId, message) {
|
|
423
|
+
const port = entryFor(windowId)?.port;
|
|
424
|
+
if (!port) return false;
|
|
425
|
+
port.postMessage(message);
|
|
426
|
+
return true;
|
|
427
|
+
},
|
|
428
|
+
/**
|
|
429
|
+
* Return a port-backed outbound target for one napplet window.
|
|
430
|
+
*
|
|
431
|
+
* @param windowId - The window identifier
|
|
432
|
+
* @returns A target that sends through {@link OriginRegistry.postToNapplet}, or null when unregistered
|
|
433
|
+
*/
|
|
434
|
+
getNappletTarget(windowId) {
|
|
435
|
+
if (!entryFor(windowId)) return null;
|
|
436
|
+
return { postMessage: (message) => {
|
|
437
|
+
originRegistry.postToNapplet(windowId, message);
|
|
438
|
+
} };
|
|
439
|
+
},
|
|
440
|
+
/**
|
|
441
|
+
* Whether a Window already handed over (or lost) its one NIP-5D binding.
|
|
442
|
+
*
|
|
443
|
+
* @param win - The iframe `contentWindow`
|
|
444
|
+
* @returns `true` when the Window can never be registered again
|
|
445
|
+
*/
|
|
446
|
+
isSpent(win) {
|
|
447
|
+
return spentWindows.has(win);
|
|
448
|
+
},
|
|
366
449
|
/**
|
|
367
450
|
* Look up the windowId for a given Window reference.
|
|
368
451
|
*
|
|
@@ -425,9 +508,11 @@ var originRegistry = {
|
|
|
425
508
|
getRegistrationId(win) {
|
|
426
509
|
return registry.get(win)?.registrationId;
|
|
427
510
|
},
|
|
428
|
-
/** Clear all registrations. */
|
|
511
|
+
/** Clear all registrations and close every bound port. */
|
|
429
512
|
clear() {
|
|
513
|
+
for (const entry of registry.values()) closePort(entry);
|
|
430
514
|
registry.clear();
|
|
515
|
+
spentWindows = /* @__PURE__ */ new WeakSet();
|
|
431
516
|
}
|
|
432
517
|
};
|
|
433
518
|
|
|
@@ -913,7 +998,7 @@ var audioManager = {
|
|
|
913
998
|
bump();
|
|
914
999
|
},
|
|
915
1000
|
/**
|
|
916
|
-
* Mute or unmute an audio source and notify the napplet
|
|
1001
|
+
* Mute or unmute an audio source and notify the napplet over its bound NIP-5D port.
|
|
917
1002
|
*
|
|
918
1003
|
* @param windowId - The window identifier
|
|
919
1004
|
* @param muted - True to mute, false to unmute
|
|
@@ -929,8 +1014,7 @@ var audioManager = {
|
|
|
929
1014
|
src.muted = muted;
|
|
930
1015
|
bump();
|
|
931
1016
|
}
|
|
932
|
-
|
|
933
|
-
if (iframeWindow) {
|
|
1017
|
+
if (originRegistry.getPort(windowId)) {
|
|
934
1018
|
const muteEvent = {
|
|
935
1019
|
kind: 29e3,
|
|
936
1020
|
// INC_PEER — inlined numeric after Phase 24 DRIFT-01 shim removal
|
|
@@ -941,7 +1025,7 @@ var audioManager = {
|
|
|
941
1025
|
id: `audio-mute-${windowId}-${Date.now()}`,
|
|
942
1026
|
sig: ""
|
|
943
1027
|
};
|
|
944
|
-
|
|
1028
|
+
originRegistry.postToNapplet(windowId, ["EVENT", "__shell__", muteEvent]);
|
|
945
1029
|
}
|
|
946
1030
|
},
|
|
947
1031
|
/**
|
|
@@ -1120,7 +1204,7 @@ function handleShellReady({
|
|
|
1120
1204
|
}
|
|
1121
1205
|
const environment = registerSourceSession({ hooks, origin, runtime, state, sourceRegistrationId, sourceWindow, windowId });
|
|
1122
1206
|
if (!environment?.capabilities.domains.includes("shell")) return;
|
|
1123
|
-
postShellInit(
|
|
1207
|
+
postShellInit(windowId, environment);
|
|
1124
1208
|
state.initSent.set(sourceWindow, sourceRegistrationId);
|
|
1125
1209
|
}
|
|
1126
1210
|
function registerSourceSession(options) {
|
|
@@ -1187,16 +1271,17 @@ function resolveNip5dIdentity(hooks, windowId, sourceWindow, existingSession) {
|
|
|
1187
1271
|
aggregateHash: originIdentity.aggregateHash
|
|
1188
1272
|
});
|
|
1189
1273
|
}
|
|
1190
|
-
function postShellInit(
|
|
1274
|
+
function postShellInit(windowId, environment) {
|
|
1191
1275
|
const initMsg = {
|
|
1192
1276
|
type: "shell.init",
|
|
1193
1277
|
capabilities: environment.capabilities,
|
|
1194
1278
|
services: environment.services
|
|
1195
1279
|
};
|
|
1196
|
-
|
|
1280
|
+
originRegistry.postToNapplet(windowId, initMsg);
|
|
1197
1281
|
}
|
|
1198
1282
|
|
|
1199
|
-
// src/
|
|
1283
|
+
// src/nip5d-transport.ts
|
|
1284
|
+
var NAPPLET_HELLO = "napplet.hello";
|
|
1200
1285
|
function reportUnrouted(hooks, event, reason) {
|
|
1201
1286
|
if (!hooks.onUnroutedMessage) return;
|
|
1202
1287
|
const data = event.data;
|
|
@@ -1206,6 +1291,138 @@ function reportUnrouted(hooks, event, reason) {
|
|
|
1206
1291
|
} catch {
|
|
1207
1292
|
}
|
|
1208
1293
|
}
|
|
1294
|
+
var REVOCATION_DETAIL = {
|
|
1295
|
+
"window-message": "A window-level message other than the single napplet.hello arrived from the frame",
|
|
1296
|
+
"second-hello": "A second napplet.hello arrived from the frame",
|
|
1297
|
+
"invalid-hello": "napplet.hello did not carry exactly one MessagePort",
|
|
1298
|
+
"second-load": "The frame loaded a second document (navigation or reload)",
|
|
1299
|
+
"port-closed": "The bound NIP-5D port closed or failed",
|
|
1300
|
+
host: "The host revoked the napplet binding"
|
|
1301
|
+
};
|
|
1302
|
+
function isHello(data) {
|
|
1303
|
+
return typeof data === "object" && data !== null && data.type === NAPPLET_HELLO;
|
|
1304
|
+
}
|
|
1305
|
+
function createNip5dTransport({ hooks, runtime, state: shellReadyState }) {
|
|
1306
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
1307
|
+
function currentBinding(windowId) {
|
|
1308
|
+
const binding = bindings.get(windowId);
|
|
1309
|
+
if (binding && originRegistry.getPort(windowId) !== binding.port) {
|
|
1310
|
+
binding.port.onmessage = null;
|
|
1311
|
+
bindings.delete(windowId);
|
|
1312
|
+
return void 0;
|
|
1313
|
+
}
|
|
1314
|
+
return binding;
|
|
1315
|
+
}
|
|
1316
|
+
function revokeNapplet(windowId, reason = "host") {
|
|
1317
|
+
const binding = currentBinding(windowId);
|
|
1318
|
+
const registered = originRegistry.getIframeWindow(windowId) !== null;
|
|
1319
|
+
if (!binding && !registered) return;
|
|
1320
|
+
bindings.delete(windowId);
|
|
1321
|
+
if (binding) binding.port.onmessage = null;
|
|
1322
|
+
originRegistry.unregister(windowId);
|
|
1323
|
+
runtime.destroyWindow(windowId);
|
|
1324
|
+
runtime.sessionRegistry.unregister(windowId);
|
|
1325
|
+
shellReadyState.environments.delete(windowId);
|
|
1326
|
+
shellReadyState.sessionRegistration.delete(windowId);
|
|
1327
|
+
try {
|
|
1328
|
+
hooks.onNappletRevoked?.({ windowId, reason, detail: REVOCATION_DETAIL[reason] });
|
|
1329
|
+
} catch {
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
function dispatchEnvelope(windowId, binding, msg) {
|
|
1333
|
+
if (typeof msg !== "object" || msg === null || typeof msg.type !== "string") return;
|
|
1334
|
+
const envelope = msg;
|
|
1335
|
+
try {
|
|
1336
|
+
hooks.onNappletMessage?.(windowId, envelope);
|
|
1337
|
+
} catch {
|
|
1338
|
+
}
|
|
1339
|
+
const context = {
|
|
1340
|
+
hooks,
|
|
1341
|
+
origin: binding.origin,
|
|
1342
|
+
runtime,
|
|
1343
|
+
state: shellReadyState,
|
|
1344
|
+
sourceRegistrationId: binding.registrationId,
|
|
1345
|
+
sourceWindow: binding.sourceWindow,
|
|
1346
|
+
windowId
|
|
1347
|
+
};
|
|
1348
|
+
if (!registerSourceSession(context)) return;
|
|
1349
|
+
if (envelope.type === "shell.ready") {
|
|
1350
|
+
handleShellReady(context);
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
runtime.handleMessage(windowId, envelope);
|
|
1354
|
+
}
|
|
1355
|
+
function bindPort(windowId, sourceWindow, origin, port) {
|
|
1356
|
+
if (!originRegistry.bindPort(windowId, port)) {
|
|
1357
|
+
port.close();
|
|
1358
|
+
revokeNapplet(windowId, "second-hello");
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
const binding = {
|
|
1362
|
+
port,
|
|
1363
|
+
sourceWindow,
|
|
1364
|
+
origin,
|
|
1365
|
+
registrationId: originRegistry.getRegistrationId(sourceWindow) ?? 0
|
|
1366
|
+
};
|
|
1367
|
+
bindings.set(windowId, binding);
|
|
1368
|
+
const isCurrent = () => currentBinding(windowId) === binding;
|
|
1369
|
+
port.onmessage = (portEvent) => {
|
|
1370
|
+
if (isCurrent()) dispatchEnvelope(windowId, binding, portEvent.data);
|
|
1371
|
+
};
|
|
1372
|
+
const onPortFailure = () => {
|
|
1373
|
+
if (isCurrent()) revokeNapplet(windowId, "port-closed");
|
|
1374
|
+
};
|
|
1375
|
+
port.addEventListener("close", onPortFailure);
|
|
1376
|
+
port.addEventListener("messageerror", onPortFailure);
|
|
1377
|
+
port.start();
|
|
1378
|
+
}
|
|
1379
|
+
function handleMessage(event) {
|
|
1380
|
+
const sourceWindow = event.source;
|
|
1381
|
+
if (!sourceWindow) {
|
|
1382
|
+
reportUnrouted(hooks, event, "no-source-window");
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
const windowId = originRegistry.getWindowId(sourceWindow);
|
|
1386
|
+
if (!windowId) {
|
|
1387
|
+
reportUnrouted(hooks, event, "unregistered-window");
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
if (currentBinding(windowId) || originRegistry.getPort(windowId)) {
|
|
1391
|
+
revokeNapplet(windowId, isHello(event.data) ? "second-hello" : "window-message");
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
if (!isHello(event.data)) {
|
|
1395
|
+
revokeNapplet(windowId, "window-message");
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
const ports = event.ports ?? [];
|
|
1399
|
+
if (ports.length !== 1) {
|
|
1400
|
+
for (const port of ports) port.close();
|
|
1401
|
+
revokeNapplet(windowId, "invalid-hello");
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
bindPort(windowId, sourceWindow, event.origin, ports[0]);
|
|
1405
|
+
}
|
|
1406
|
+
function observeFrame(frame, windowId) {
|
|
1407
|
+
let loads = 0;
|
|
1408
|
+
const onLoad = () => {
|
|
1409
|
+
loads += 1;
|
|
1410
|
+
if (loads > 1) revokeNapplet(windowId, "second-load");
|
|
1411
|
+
};
|
|
1412
|
+
frame.addEventListener("load", onLoad);
|
|
1413
|
+
return () => frame.removeEventListener("load", onLoad);
|
|
1414
|
+
}
|
|
1415
|
+
function destroy() {
|
|
1416
|
+
for (const [windowId, binding] of bindings) {
|
|
1417
|
+
binding.port.onmessage = null;
|
|
1418
|
+
if (originRegistry.getPort(windowId) === binding.port) originRegistry.unregister(windowId);
|
|
1419
|
+
}
|
|
1420
|
+
bindings.clear();
|
|
1421
|
+
}
|
|
1422
|
+
return { handleMessage, revokeNapplet, observeFrame, destroy };
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// src/shell-bridge.ts
|
|
1209
1426
|
function createShellBridge(hooks) {
|
|
1210
1427
|
const shellReadyState = createShellReadyState();
|
|
1211
1428
|
const runtimeHooks = adaptHooks(hooks, {
|
|
@@ -1224,52 +1441,17 @@ function createShellBridge(hooks) {
|
|
|
1224
1441
|
const environment = shellReadyState.environments.get(entry.windowId);
|
|
1225
1442
|
if (!environment?.capabilities.domains.includes(domain)) continue;
|
|
1226
1443
|
if (!runtime.aclState.check(entry.pubkey, entry.dTag, entry.aggregateHash, capability)) continue;
|
|
1227
|
-
|
|
1228
|
-
if (!win) continue;
|
|
1229
|
-
win.postMessage(envelope, "*");
|
|
1444
|
+
originRegistry.postToNapplet(entry.windowId, envelope);
|
|
1230
1445
|
}
|
|
1231
1446
|
}
|
|
1447
|
+
const transport = createNip5dTransport({ hooks, runtime, state: shellReadyState });
|
|
1232
1448
|
return {
|
|
1233
|
-
handleMessage
|
|
1234
|
-
const sourceWindow = event.source;
|
|
1235
|
-
if (!sourceWindow) {
|
|
1236
|
-
reportUnrouted(hooks, event, "no-source-window");
|
|
1237
|
-
return;
|
|
1238
|
-
}
|
|
1239
|
-
const windowId = originRegistry.getWindowId(sourceWindow);
|
|
1240
|
-
if (!windowId) {
|
|
1241
|
-
reportUnrouted(hooks, event, "unregistered-window");
|
|
1242
|
-
return;
|
|
1243
|
-
}
|
|
1244
|
-
const msg = event.data;
|
|
1245
|
-
if (typeof msg !== "object" || msg === null || typeof msg.type !== "string") return;
|
|
1246
|
-
if (!registerSourceSession({
|
|
1247
|
-
hooks,
|
|
1248
|
-
origin: event.origin,
|
|
1249
|
-
runtime,
|
|
1250
|
-
state: shellReadyState,
|
|
1251
|
-
sourceRegistrationId: originRegistry.getRegistrationId(sourceWindow) ?? 0,
|
|
1252
|
-
sourceWindow,
|
|
1253
|
-
windowId
|
|
1254
|
-
})) return;
|
|
1255
|
-
if (msg.type === "shell.ready") {
|
|
1256
|
-
handleShellReady({
|
|
1257
|
-
hooks,
|
|
1258
|
-
origin: event.origin,
|
|
1259
|
-
runtime,
|
|
1260
|
-
state: shellReadyState,
|
|
1261
|
-
sourceRegistrationId: originRegistry.getRegistrationId(sourceWindow) ?? 0,
|
|
1262
|
-
sourceWindow,
|
|
1263
|
-
windowId
|
|
1264
|
-
});
|
|
1265
|
-
return;
|
|
1266
|
-
}
|
|
1267
|
-
runtime.handleMessage(windowId, msg);
|
|
1268
|
-
},
|
|
1449
|
+
handleMessage: transport.handleMessage,
|
|
1269
1450
|
injectEvent(topic, payload) {
|
|
1270
1451
|
runtime.injectEvent(topic, payload);
|
|
1271
1452
|
},
|
|
1272
1453
|
destroy() {
|
|
1454
|
+
transport.destroy();
|
|
1273
1455
|
runtime.destroy();
|
|
1274
1456
|
shellReadyState.clear();
|
|
1275
1457
|
},
|
|
@@ -1284,6 +1466,8 @@ function createShellBridge(hooks) {
|
|
|
1284
1466
|
const envelope = { type: "identity.changed", pubkey };
|
|
1285
1467
|
publishToEligibleNapplets(envelope, "identity", "identity:read");
|
|
1286
1468
|
},
|
|
1469
|
+
observeFrame: transport.observeFrame,
|
|
1470
|
+
revokeNapplet: transport.revokeNapplet,
|
|
1287
1471
|
get runtime() {
|
|
1288
1472
|
return runtime;
|
|
1289
1473
|
}
|
|
@@ -1515,15 +1699,19 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
1515
1699
|
if (target.crypto?.randomUUID) return target.crypto.randomUUID();
|
|
1516
1700
|
return `napplet-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
1517
1701
|
}
|
|
1702
|
+
const channel = new target.MessageChannel();
|
|
1703
|
+
const port = channel.port1;
|
|
1704
|
+
port.start();
|
|
1705
|
+
target.parent.postMessage({ type: "napplet.hello" }, "*", [channel.port2]);
|
|
1518
1706
|
function post(message) {
|
|
1519
|
-
|
|
1707
|
+
port.postMessage(message);
|
|
1520
1708
|
}
|
|
1521
1709
|
function listen(listener) {
|
|
1522
|
-
|
|
1523
|
-
return () =>
|
|
1710
|
+
port.addEventListener("message", listener);
|
|
1711
|
+
return () => port.removeEventListener("message", listener);
|
|
1524
1712
|
}
|
|
1525
|
-
function
|
|
1526
|
-
return event.
|
|
1713
|
+
function isShellMessage(event) {
|
|
1714
|
+
return event.currentTarget === port;
|
|
1527
1715
|
}
|
|
1528
1716
|
function request(message, resultType, map, options = {}) {
|
|
1529
1717
|
const requestId = message.id ?? id();
|
|
@@ -1544,7 +1732,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
1544
1732
|
reject(new DOMException("Aborted", "AbortError"));
|
|
1545
1733
|
};
|
|
1546
1734
|
off = listen((event) => {
|
|
1547
|
-
if (!
|
|
1735
|
+
if (!isShellMessage(event)) return;
|
|
1548
1736
|
const incoming = event.data;
|
|
1549
1737
|
if (typeof incoming !== "object" || incoming === null) return;
|
|
1550
1738
|
const msg = incoming;
|
|
@@ -1649,7 +1837,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
1649
1837
|
resolveReady = resolve;
|
|
1650
1838
|
});
|
|
1651
1839
|
const off = listen((event) => {
|
|
1652
|
-
if (!
|
|
1840
|
+
if (!isShellMessage(event) || environment) return;
|
|
1653
1841
|
const incoming = event.data;
|
|
1654
1842
|
if (typeof incoming !== "object" || incoming === null) return;
|
|
1655
1843
|
const message = incoming;
|
|
@@ -1703,7 +1891,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
1703
1891
|
}
|
|
1704
1892
|
function domainEventHandle(eventType, callback, select = (message) => message.event) {
|
|
1705
1893
|
const off = listen((event) => {
|
|
1706
|
-
if (!
|
|
1894
|
+
if (!isShellMessage(event)) return;
|
|
1707
1895
|
const msg = event.data;
|
|
1708
1896
|
if (typeof msg === "object" && msg !== null && msg.type === eventType) callback(select(msg));
|
|
1709
1897
|
});
|
|
@@ -1714,7 +1902,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
1714
1902
|
subscribe(filters, onEvent, onEose, options) {
|
|
1715
1903
|
const subId = id();
|
|
1716
1904
|
const off = listen((event) => {
|
|
1717
|
-
if (!
|
|
1905
|
+
if (!isShellMessage(event)) return;
|
|
1718
1906
|
const msg = event.data;
|
|
1719
1907
|
if (typeof msg !== "object" || msg === null || msg.subId !== subId) return;
|
|
1720
1908
|
if (msg.type === "relay.event") onEvent(msg.result);
|
|
@@ -1861,7 +2049,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
1861
2049
|
state.pendingEvents.push(event);
|
|
1862
2050
|
}
|
|
1863
2051
|
listen((event) => {
|
|
1864
|
-
if (!
|
|
2052
|
+
if (!isShellMessage(event)) return;
|
|
1865
2053
|
const msg = event.data;
|
|
1866
2054
|
if (typeof msg !== "object" || msg === null || typeof msg.type !== "string") return;
|
|
1867
2055
|
if (msg.type === "inc.channel.opened" && typeof msg.channelId === "string" && typeof msg.peer === "string") {
|
|
@@ -1896,14 +2084,14 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
1896
2084
|
const handlers = /* @__PURE__ */ new Set();
|
|
1897
2085
|
const subscriptionId = id();
|
|
1898
2086
|
const offEvent = listen((event) => {
|
|
1899
|
-
if (!
|
|
2087
|
+
if (!isShellMessage(event)) return;
|
|
1900
2088
|
const msg = event.data;
|
|
1901
2089
|
if (typeof msg !== "object" || msg === null || msg.type !== "inc.event" || msg.topic !== stableTopic || typeof msg.sender !== "string") return;
|
|
1902
2090
|
const delivered = Object.prototype.hasOwnProperty.call(msg, "payload") ? { topic: stableTopic, sender: msg.sender, payload: msg.payload } : { topic: stableTopic, sender: msg.sender };
|
|
1903
2091
|
for (const handler of handlers) handler(delivered);
|
|
1904
2092
|
});
|
|
1905
2093
|
const offResult = listen((event) => {
|
|
1906
|
-
if (!
|
|
2094
|
+
if (!isShellMessage(event)) return;
|
|
1907
2095
|
const msg = event.data;
|
|
1908
2096
|
if (typeof msg !== "object" || msg === null || msg.type !== "inc.subscribe.result" || msg.id !== subscriptionId) return;
|
|
1909
2097
|
offResult();
|
|
@@ -1958,9 +2146,9 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
1958
2146
|
return request(
|
|
1959
2147
|
{ type: "inc.channel.list" },
|
|
1960
2148
|
"inc.channel.list.result",
|
|
1961
|
-
(msg) => Object.freeze((Array.isArray(msg.channels) ? msg.channels : []).flatMap((
|
|
1962
|
-
id:
|
|
1963
|
-
peer:
|
|
2149
|
+
(msg) => Object.freeze((Array.isArray(msg.channels) ? msg.channels : []).flatMap((channel2) => channel2 && typeof channel2 === "object" && typeof channel2.id === "string" && typeof channel2.peer === "string" ? [Object.freeze({
|
|
2150
|
+
id: channel2.id,
|
|
2151
|
+
peer: channel2.peer
|
|
1964
2152
|
})] : []))
|
|
1965
2153
|
);
|
|
1966
2154
|
},
|
|
@@ -2022,7 +2210,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2022
2210
|
getBadges: () => read("identity.getBadges", "badges", []),
|
|
2023
2211
|
onChanged(handler) {
|
|
2024
2212
|
const off = listen((event) => {
|
|
2025
|
-
if (!
|
|
2213
|
+
if (!isShellMessage(event)) return;
|
|
2026
2214
|
const msg = event.data;
|
|
2027
2215
|
if (typeof msg !== "object" || msg === null || msg.type !== "identity.changed") return;
|
|
2028
2216
|
if (typeof msg.pubkey === "string") handler(msg.pubkey);
|
|
@@ -2036,7 +2224,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2036
2224
|
get: () => request({ type: "theme.get" }, "theme.get.result", (msg) => msg.theme),
|
|
2037
2225
|
onChanged(handler) {
|
|
2038
2226
|
const off = listen((event) => {
|
|
2039
|
-
if (!
|
|
2227
|
+
if (!isShellMessage(event)) return;
|
|
2040
2228
|
const msg = event.data;
|
|
2041
2229
|
if (typeof msg === "object" && msg !== null && msg.type === "theme.changed") handler(msg.theme);
|
|
2042
2230
|
});
|
|
@@ -2152,7 +2340,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2152
2340
|
if (keysSmartForwardingInstalled) return;
|
|
2153
2341
|
keysSmartForwardingInstalled = true;
|
|
2154
2342
|
listen((event) => {
|
|
2155
|
-
if (!
|
|
2343
|
+
if (!isShellMessage(event)) return;
|
|
2156
2344
|
const msg = event.data;
|
|
2157
2345
|
if (typeof msg === "object" && msg !== null && msg.type === "keys.bindings") {
|
|
2158
2346
|
replaceKeyBindings(msg.bindings);
|
|
@@ -2206,7 +2394,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2206
2394
|
}
|
|
2207
2395
|
callbacks.add(callback);
|
|
2208
2396
|
const off = listen((event) => {
|
|
2209
|
-
if (!
|
|
2397
|
+
if (!isShellMessage(event)) return;
|
|
2210
2398
|
const msg = event.data;
|
|
2211
2399
|
if (typeof msg === "object" && msg !== null && msg.type === "keys.action" && msg.actionId === actionId) callback();
|
|
2212
2400
|
});
|
|
@@ -2230,7 +2418,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2230
2418
|
console.warn(`[KEHTO-MEDIA-${name}] Deprecated media API; use ${replacement}. Warned once per endpoint.`);
|
|
2231
2419
|
};
|
|
2232
2420
|
listen((event) => {
|
|
2233
|
-
if (!
|
|
2421
|
+
if (!isShellMessage(event)) return;
|
|
2234
2422
|
const msg = event.data;
|
|
2235
2423
|
if (!msg || typeof msg !== "object" || typeof msg.sessionId !== "string") return;
|
|
2236
2424
|
const sessionId = msg.sessionId;
|
|
@@ -2307,7 +2495,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2307
2495
|
function makeNotify() {
|
|
2308
2496
|
const on = (type, callback) => {
|
|
2309
2497
|
const off = listen((event) => {
|
|
2310
|
-
if (!
|
|
2498
|
+
if (!isShellMessage(event)) return;
|
|
2311
2499
|
const msg = event.data;
|
|
2312
2500
|
if (typeof msg === "object" && msg !== null && msg.type === type) callback(msg);
|
|
2313
2501
|
});
|
|
@@ -2321,9 +2509,9 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2321
2509
|
),
|
|
2322
2510
|
dismiss: (notificationId) => fire({ type: "notify.dismiss", notificationId }),
|
|
2323
2511
|
badge: (count) => fire({ type: "notify.badge", count }),
|
|
2324
|
-
registerChannel: (
|
|
2325
|
-
requestPermission: (
|
|
2326
|
-
{ type: "notify.permission.request", ...
|
|
2512
|
+
registerChannel: (channel2) => fire({ type: "notify.channel.register", ...channel2 }),
|
|
2513
|
+
requestPermission: (channel2) => request(
|
|
2514
|
+
{ type: "notify.permission.request", ...channel2 ? { channel: channel2 } : {} },
|
|
2327
2515
|
"notify.permission.result",
|
|
2328
2516
|
(msg) => ({ granted: msg.granted })
|
|
2329
2517
|
),
|
|
@@ -2347,7 +2535,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2347
2535
|
get: () => request({ type: "config.get" }, "config.values", (msg) => msg.values ?? {}),
|
|
2348
2536
|
subscribe(callback) {
|
|
2349
2537
|
const off = listen((event) => {
|
|
2350
|
-
if (!
|
|
2538
|
+
if (!isShellMessage(event)) return;
|
|
2351
2539
|
const msg = event.data;
|
|
2352
2540
|
if (typeof msg === "object" && msg !== null && msg.type === "config.values") callback(msg.values ?? {});
|
|
2353
2541
|
});
|
|
@@ -2360,7 +2548,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2360
2548
|
openSettings: (options) => fire({ type: "config.openSettings", ...options && typeof options === "object" ? options : {} }),
|
|
2361
2549
|
onSchemaError(callback) {
|
|
2362
2550
|
const off = listen((event) => {
|
|
2363
|
-
if (!
|
|
2551
|
+
if (!isShellMessage(event)) return;
|
|
2364
2552
|
const msg = event.data;
|
|
2365
2553
|
if (typeof msg === "object" && msg !== null && msg.type === "config.schemaError") callback(msg);
|
|
2366
2554
|
});
|
|
@@ -2473,7 +2661,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2473
2661
|
close: (server) => req("cvm.close", { server }, (msg) => voidResult(msg, "cvm close failed")),
|
|
2474
2662
|
onEvent(callback) {
|
|
2475
2663
|
const off = listen((event) => {
|
|
2476
|
-
if (!
|
|
2664
|
+
if (!isShellMessage(event)) return;
|
|
2477
2665
|
const msg = event.data;
|
|
2478
2666
|
if (typeof msg === "object" && msg !== null && msg.type === "cvm.event" && msg.server !== void 0 && msg.message !== void 0) {
|
|
2479
2667
|
callback(msg.server, msg.message);
|
|
@@ -2497,7 +2685,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2497
2685
|
const subId = id();
|
|
2498
2686
|
const handlers = { event: /* @__PURE__ */ new Set(), closed: /* @__PURE__ */ new Set() };
|
|
2499
2687
|
const off = listen((event) => {
|
|
2500
|
-
if (!
|
|
2688
|
+
if (!isShellMessage(event)) return;
|
|
2501
2689
|
const msg = event.data;
|
|
2502
2690
|
if (typeof msg !== "object" || msg === null || msg.subId !== subId) return;
|
|
2503
2691
|
if (msg.type === "outbox.event") for (const handler of handlers.event) handler(msg.result);
|
|
@@ -2531,7 +2719,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2531
2719
|
status: (uploadId) => request({ type: "upload.status", uploadId }, "upload.status.result", (msg) => fieldOrThrow(msg, "status", "upload status unavailable")),
|
|
2532
2720
|
onStatus(callback) {
|
|
2533
2721
|
const off = listen((event) => {
|
|
2534
|
-
if (!
|
|
2722
|
+
if (!isShellMessage(event)) return;
|
|
2535
2723
|
const msg = event.data;
|
|
2536
2724
|
if (typeof msg === "object" && msg !== null && msg.type === "upload.status.changed" && msg.status !== void 0) callback(msg.status);
|
|
2537
2725
|
});
|
|
@@ -2572,7 +2760,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2572
2760
|
}
|
|
2573
2761
|
}
|
|
2574
2762
|
listen((event) => {
|
|
2575
|
-
if (!
|
|
2763
|
+
if (!isShellMessage(event)) return;
|
|
2576
2764
|
const message = event.data;
|
|
2577
2765
|
if (!message || message.type !== "intent.deliver") return;
|
|
2578
2766
|
const value = message.delivery;
|
|
@@ -2846,7 +3034,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2846
3034
|
});
|
|
2847
3035
|
};
|
|
2848
3036
|
listen((event) => {
|
|
2849
|
-
if (!
|
|
3037
|
+
if (!isShellMessage(event)) return;
|
|
2850
3038
|
const incoming = event.data;
|
|
2851
3039
|
if (!incoming || incoming.type !== "gamepad.state" || "id" in incoming || !valid(incoming)) return;
|
|
2852
3040
|
state = copy(incoming);
|
|
@@ -2882,7 +3070,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2882
3070
|
unsubscribe: (subscriptionId) => req("dm.unsubscribe", { subscriptionId }),
|
|
2883
3071
|
onMessage(callback) {
|
|
2884
3072
|
const off = listen((event) => {
|
|
2885
|
-
if (!
|
|
3073
|
+
if (!isShellMessage(event)) return;
|
|
2886
3074
|
const msg = event.data;
|
|
2887
3075
|
if (typeof msg === "object" && msg !== null && msg.type === "dm.message" && typeof msg.subscriptionId === "string") {
|
|
2888
3076
|
callback(msg.message, msg.subscriptionId);
|
|
@@ -2931,7 +3119,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2931
3119
|
post({ type: "llm.cancel", id: id(), requestId });
|
|
2932
3120
|
};
|
|
2933
3121
|
const off = listen((event) => {
|
|
2934
|
-
if (!
|
|
3122
|
+
if (!isShellMessage(event)) return;
|
|
2935
3123
|
const msg = event.data;
|
|
2936
3124
|
if (typeof msg !== "object" || msg === null) return;
|
|
2937
3125
|
if (type === "llm.generate" && msg.type === "llm.delta" && msg.requestId === requestId) {
|
|
@@ -3265,8 +3453,7 @@ function createKeysProxy(deps) {
|
|
|
3265
3453
|
deps.runtime.handleMessage(windowId, envelope);
|
|
3266
3454
|
},
|
|
3267
3455
|
emit(windowId, envelope) {
|
|
3268
|
-
|
|
3269
|
-
if (win) win.postMessage(envelope, "*");
|
|
3456
|
+
deps.originRegistry.postToNapplet(windowId, envelope);
|
|
3270
3457
|
}
|
|
3271
3458
|
};
|
|
3272
3459
|
}
|
|
@@ -3278,8 +3465,7 @@ function createMediaProxy(deps) {
|
|
|
3278
3465
|
deps.runtime.handleMessage(windowId, envelope);
|
|
3279
3466
|
},
|
|
3280
3467
|
emit(windowId, envelope) {
|
|
3281
|
-
|
|
3282
|
-
if (win) win.postMessage(envelope, "*");
|
|
3468
|
+
deps.originRegistry.postToNapplet(windowId, envelope);
|
|
3283
3469
|
}
|
|
3284
3470
|
};
|
|
3285
3471
|
}
|
|
@@ -3291,13 +3477,13 @@ function createNotifyProxy(deps) {
|
|
|
3291
3477
|
deps.runtime.handleMessage(windowId, envelope);
|
|
3292
3478
|
},
|
|
3293
3479
|
emit(windowId, envelope) {
|
|
3294
|
-
|
|
3295
|
-
if (win) win.postMessage(envelope, "*");
|
|
3480
|
+
deps.originRegistry.postToNapplet(windowId, envelope);
|
|
3296
3481
|
}
|
|
3297
3482
|
};
|
|
3298
3483
|
}
|
|
3299
3484
|
export {
|
|
3300
3485
|
ALL_CAPABILITIES2 as ALL_CAPABILITIES,
|
|
3486
|
+
NAPPLET_HELLO,
|
|
3301
3487
|
TOPICS,
|
|
3302
3488
|
adaptHooks,
|
|
3303
3489
|
audioManager,
|