@kehto/shell 0.17.2 → 0.19.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 +94 -9
- package/dist/index.d.ts +110 -182
- package/dist/index.js +519 -177
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ function createRelayPoolAdapter(shellHooks, originRegistry2) {
|
|
|
35
35
|
const pool = shellHooks.relayPool.getRelayPool();
|
|
36
36
|
if (!pool) return;
|
|
37
37
|
const relayUrls = shellHooks.relayPool.selectRelayTier([]);
|
|
38
|
-
pool.publish(relayUrls, event);
|
|
38
|
+
return pool.publish(relayUrls, event);
|
|
39
39
|
},
|
|
40
40
|
selectRelayTier(filters) {
|
|
41
41
|
return shellHooks.relayPool.selectRelayTier(filters);
|
|
@@ -392,6 +392,19 @@ var originRegistry = {
|
|
|
392
392
|
if (!entry?.dTag || !entry?.aggregateHash) return void 0;
|
|
393
393
|
return { dTag: entry.dTag, aggregateHash: entry.aggregateHash };
|
|
394
394
|
},
|
|
395
|
+
/** Store the immutable environment captured for this exact frame registration. */
|
|
396
|
+
setEnvironment(win, environment) {
|
|
397
|
+
const entry = registry.get(win);
|
|
398
|
+
if (!entry) return;
|
|
399
|
+
entry.environment = Object.freeze({
|
|
400
|
+
capabilities: Object.freeze({ domains: Object.freeze([...environment.capabilities.domains]) }),
|
|
401
|
+
services: Object.freeze([...environment.services])
|
|
402
|
+
});
|
|
403
|
+
},
|
|
404
|
+
/** Return the immutable environment captured before this frame executes. */
|
|
405
|
+
getEnvironment(win) {
|
|
406
|
+
return registry.get(win)?.environment;
|
|
407
|
+
},
|
|
395
408
|
/**
|
|
396
409
|
* Look up the monotonically increasing registration id for a Window.
|
|
397
410
|
*
|
|
@@ -962,113 +975,153 @@ var audioManager = {
|
|
|
962
975
|
};
|
|
963
976
|
|
|
964
977
|
// src/shell-init.ts
|
|
965
|
-
var
|
|
978
|
+
var RUNTIME_NATIVE_DOMAINS = [
|
|
979
|
+
"relay",
|
|
966
980
|
"identity",
|
|
967
981
|
"storage",
|
|
968
982
|
"inc",
|
|
969
983
|
"theme",
|
|
970
984
|
"keys",
|
|
971
985
|
"media",
|
|
972
|
-
"notify"
|
|
986
|
+
"notify"
|
|
987
|
+
];
|
|
988
|
+
var SERVICE_ONLY_DOMAINS = [
|
|
989
|
+
"outbox",
|
|
973
990
|
"config",
|
|
974
991
|
"resource",
|
|
975
|
-
"cvm"
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
"
|
|
979
|
-
"
|
|
980
|
-
"
|
|
981
|
-
"
|
|
982
|
-
"
|
|
983
|
-
"
|
|
992
|
+
"cvm",
|
|
993
|
+
"upload",
|
|
994
|
+
"intent",
|
|
995
|
+
"link",
|
|
996
|
+
"common",
|
|
997
|
+
"lists",
|
|
998
|
+
"serial",
|
|
999
|
+
"ble",
|
|
1000
|
+
"webrtc",
|
|
1001
|
+
"dm",
|
|
1002
|
+
"count"
|
|
984
1003
|
];
|
|
985
1004
|
function buildShellCapabilities(hooks) {
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
const
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
if (hooks.common?.isAvailable()) naps.push("common");
|
|
1009
|
-
if (hooks.lists?.isAvailable()) naps.push("lists");
|
|
1010
|
-
if (hooks.serial?.isAvailable()) naps.push("serial");
|
|
1011
|
-
if (hooks.ble?.isAvailable()) naps.push("ble");
|
|
1012
|
-
if (hooks.webrtc?.isAvailable()) naps.push("webrtc");
|
|
1013
|
-
if (hooks.dm) naps.push("dm");
|
|
1014
|
-
return applyCapabilityOverrides(
|
|
1015
|
-
{ domains, protocols, naps, sandbox },
|
|
1016
|
-
hooks.capabilities?.disabledDomains ?? []
|
|
1017
|
-
);
|
|
1005
|
+
const disabled = new Set(hooks.capabilities?.disabledDomains ?? []);
|
|
1006
|
+
const domains = RUNTIME_NATIVE_DOMAINS.filter((domain) => !disabled.has(domain));
|
|
1007
|
+
for (const domain of SERVICE_ONLY_DOMAINS) {
|
|
1008
|
+
if (!disabled.has(domain) && isServiceDomainLive(hooks, domain)) domains.push(domain);
|
|
1009
|
+
}
|
|
1010
|
+
return Object.freeze({ domains: freezeList(domains) });
|
|
1011
|
+
}
|
|
1012
|
+
function resolveShellEnvironment(hooks, identity) {
|
|
1013
|
+
const capabilities = buildShellCapabilities(hooks);
|
|
1014
|
+
const disabled = new Set(hooks.capabilities?.disabledDomains ?? []);
|
|
1015
|
+
const services = getLiveServices(hooks, capabilities.domains, disabled);
|
|
1016
|
+
const available = Object.freeze({
|
|
1017
|
+
domains: freezeList(capabilities.domains),
|
|
1018
|
+
services: freezeList(services)
|
|
1019
|
+
});
|
|
1020
|
+
const granted = hooks.capabilities?.resolveEnvironment?.(identity, available) ?? available;
|
|
1021
|
+
return Object.freeze({
|
|
1022
|
+
capabilities: Object.freeze({
|
|
1023
|
+
domains: freezeList(intersectInOrder(available.domains, granted.domains))
|
|
1024
|
+
}),
|
|
1025
|
+
services: freezeList(intersectInOrder(available.services, granted.services))
|
|
1026
|
+
});
|
|
1018
1027
|
}
|
|
1019
|
-
function
|
|
1020
|
-
if (
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1028
|
+
function isServiceDomainLive(hooks, domain) {
|
|
1029
|
+
if (!hooks.services?.[domain]) return false;
|
|
1030
|
+
switch (domain) {
|
|
1031
|
+
case "outbox":
|
|
1032
|
+
return Boolean(hooks.relayPool);
|
|
1033
|
+
case "upload":
|
|
1034
|
+
return hooks.upload?.getUploader() !== null && hooks.upload?.getUploader() !== void 0;
|
|
1035
|
+
case "intent":
|
|
1036
|
+
return hooks.intent?.isAvailable() === true;
|
|
1037
|
+
case "link":
|
|
1038
|
+
return hooks.link?.isAvailable() === true;
|
|
1039
|
+
case "common":
|
|
1040
|
+
return hooks.common?.isAvailable() === true;
|
|
1041
|
+
case "lists":
|
|
1042
|
+
return hooks.lists?.isAvailable() === true;
|
|
1043
|
+
case "serial":
|
|
1044
|
+
return hooks.serial?.isAvailable() === true;
|
|
1045
|
+
case "ble":
|
|
1046
|
+
return hooks.ble?.isAvailable() === true;
|
|
1047
|
+
case "webrtc":
|
|
1048
|
+
return hooks.webrtc?.isAvailable() === true;
|
|
1049
|
+
case "dm":
|
|
1050
|
+
return hooks.dm !== void 0;
|
|
1051
|
+
default:
|
|
1052
|
+
return true;
|
|
1025
1053
|
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1054
|
+
}
|
|
1055
|
+
function getLiveServices(hooks, domains, disabled) {
|
|
1056
|
+
const liveDomains = new Set(domains);
|
|
1057
|
+
return Object.keys(hooks.services ?? {}).filter((name) => {
|
|
1058
|
+
if (disabled.has(name)) return false;
|
|
1059
|
+
return !SERVICE_ONLY_DOMAINS.includes(name) || liveDomains.has(name);
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
function intersectInOrder(available, requested) {
|
|
1063
|
+
const requestedSet = new Set(requested);
|
|
1064
|
+
return available.filter((entry) => requestedSet.has(entry));
|
|
1065
|
+
}
|
|
1066
|
+
function freezeList(values) {
|
|
1067
|
+
return Object.freeze([...values]);
|
|
1032
1068
|
}
|
|
1033
1069
|
|
|
1034
1070
|
// src/shell-ready.ts
|
|
1035
|
-
|
|
1071
|
+
function createShellReadyState() {
|
|
1072
|
+
const initSent = /* @__PURE__ */ new WeakMap();
|
|
1073
|
+
const sessionRegistration = /* @__PURE__ */ new Map();
|
|
1074
|
+
const environments = /* @__PURE__ */ new Map();
|
|
1075
|
+
return {
|
|
1076
|
+
initSent,
|
|
1077
|
+
sessionRegistration,
|
|
1078
|
+
environments,
|
|
1079
|
+
isDomainAllowed(windowId, domain) {
|
|
1080
|
+
return environments.get(windowId)?.capabilities.domains.includes(domain) ?? false;
|
|
1081
|
+
},
|
|
1082
|
+
clear() {
|
|
1083
|
+
sessionRegistration.clear();
|
|
1084
|
+
environments.clear();
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1036
1088
|
function handleShellReady({
|
|
1037
1089
|
hooks,
|
|
1038
1090
|
origin,
|
|
1039
1091
|
runtime,
|
|
1092
|
+
state,
|
|
1040
1093
|
sourceRegistrationId,
|
|
1041
1094
|
sourceWindow,
|
|
1042
1095
|
windowId
|
|
1043
1096
|
}) {
|
|
1044
|
-
|
|
1045
|
-
hooks,
|
|
1046
|
-
origin,
|
|
1047
|
-
runtime,
|
|
1048
|
-
sourceRegistrationId,
|
|
1049
|
-
sourceWindow,
|
|
1050
|
-
windowId
|
|
1051
|
-
});
|
|
1052
|
-
if (initSent.get(sourceWindow) === sourceRegistrationId) {
|
|
1097
|
+
if (state.initSent.get(sourceWindow) === sourceRegistrationId) {
|
|
1053
1098
|
return;
|
|
1054
1099
|
}
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1100
|
+
const identity = resolveNip5dIdentity(hooks, windowId, sourceWindow);
|
|
1101
|
+
if (!identity) return;
|
|
1102
|
+
registerNip5dSessionIfNeeded({ origin, runtime, state, sourceRegistrationId, windowId, identity });
|
|
1103
|
+
const environment = originRegistry.getEnvironment(sourceWindow) ?? resolveShellEnvironment(hooks, identity);
|
|
1104
|
+
state.environments.set(windowId, environment);
|
|
1105
|
+
postShellInit(sourceWindow, environment);
|
|
1106
|
+
state.initSent.set(sourceWindow, sourceRegistrationId);
|
|
1058
1107
|
}
|
|
1059
1108
|
function registerNip5dSessionIfNeeded({
|
|
1060
|
-
hooks,
|
|
1061
1109
|
origin,
|
|
1062
1110
|
runtime,
|
|
1063
|
-
|
|
1111
|
+
state,
|
|
1112
|
+
sourceRegistrationId,
|
|
1113
|
+
windowId,
|
|
1114
|
+
identity
|
|
1064
1115
|
}) {
|
|
1065
|
-
|
|
1116
|
+
const existing = runtime.sessionRegistry.getEntryByWindowId(windowId);
|
|
1117
|
+
const previousRegistration = state.sessionRegistration.get(windowId);
|
|
1118
|
+
if (existing && previousRegistration === sourceRegistrationId) {
|
|
1066
1119
|
return;
|
|
1067
1120
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
return;
|
|
1121
|
+
if (existing && previousRegistration !== void 0) {
|
|
1122
|
+
runtime.sessionRegistry.unregister(windowId);
|
|
1071
1123
|
}
|
|
1124
|
+
if (existing && previousRegistration === void 0) return;
|
|
1072
1125
|
const entry = {
|
|
1073
1126
|
pubkey: "",
|
|
1074
1127
|
windowId,
|
|
@@ -1081,33 +1134,30 @@ function registerNip5dSessionIfNeeded({
|
|
|
1081
1134
|
provenance: "nip-5d"
|
|
1082
1135
|
};
|
|
1083
1136
|
runtime.sessionRegistry.register(windowId, entry);
|
|
1137
|
+
state.sessionRegistration.set(windowId, sourceRegistrationId);
|
|
1084
1138
|
}
|
|
1085
|
-
function resolveNip5dIdentity(hooks, windowId) {
|
|
1139
|
+
function resolveNip5dIdentity(hooks, windowId, sourceWindow) {
|
|
1086
1140
|
const hookIdentity = hooks.onNip5dIframeCreate?.(windowId);
|
|
1087
1141
|
if (hookIdentity !== null && hookIdentity !== void 0) {
|
|
1088
|
-
return {
|
|
1142
|
+
return Object.freeze({
|
|
1089
1143
|
dTag: hookIdentity.dTag,
|
|
1090
1144
|
aggregateHash: hookIdentity.aggregateHash
|
|
1091
|
-
};
|
|
1092
|
-
}
|
|
1093
|
-
const win = originRegistry.getIframeWindow(windowId);
|
|
1094
|
-
if (!win) {
|
|
1095
|
-
return null;
|
|
1145
|
+
});
|
|
1096
1146
|
}
|
|
1097
|
-
const originIdentity = originRegistry.getIdentity(
|
|
1147
|
+
const originIdentity = originRegistry.getIdentity(sourceWindow);
|
|
1098
1148
|
if (!originIdentity) {
|
|
1099
1149
|
return null;
|
|
1100
1150
|
}
|
|
1101
|
-
return {
|
|
1151
|
+
return Object.freeze({
|
|
1102
1152
|
dTag: originIdentity.dTag,
|
|
1103
1153
|
aggregateHash: originIdentity.aggregateHash
|
|
1104
|
-
};
|
|
1154
|
+
});
|
|
1105
1155
|
}
|
|
1106
|
-
function postShellInit(win,
|
|
1156
|
+
function postShellInit(win, environment) {
|
|
1107
1157
|
const initMsg = {
|
|
1108
1158
|
type: "shell.init",
|
|
1109
|
-
capabilities,
|
|
1110
|
-
services
|
|
1159
|
+
capabilities: environment.capabilities,
|
|
1160
|
+
services: environment.services
|
|
1111
1161
|
};
|
|
1112
1162
|
win.postMessage(initMsg, "*");
|
|
1113
1163
|
}
|
|
@@ -1123,6 +1173,7 @@ function reportUnrouted(hooks, event, reason) {
|
|
|
1123
1173
|
}
|
|
1124
1174
|
}
|
|
1125
1175
|
function createShellBridge(hooks) {
|
|
1176
|
+
const shellReadyState = createShellReadyState();
|
|
1126
1177
|
const runtimeHooks = adaptHooks(hooks, {
|
|
1127
1178
|
originRegistry,
|
|
1128
1179
|
manifestCache,
|
|
@@ -1130,11 +1181,16 @@ function createShellBridge(hooks) {
|
|
|
1130
1181
|
audioManager,
|
|
1131
1182
|
nappKeyRegistry
|
|
1132
1183
|
});
|
|
1133
|
-
const runtime = createRuntime(
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1184
|
+
const runtime = createRuntime({
|
|
1185
|
+
...runtimeHooks,
|
|
1186
|
+
isDomainAllowed: shellReadyState.isDomainAllowed
|
|
1187
|
+
});
|
|
1188
|
+
function publishToEligibleNapplets(envelope, domain, capability) {
|
|
1189
|
+
for (const entry of runtime.sessionRegistry.getAllEntries()) {
|
|
1190
|
+
const environment = shellReadyState.environments.get(entry.windowId);
|
|
1191
|
+
if (!environment?.capabilities.domains.includes(domain)) continue;
|
|
1192
|
+
if (!runtime.aclState.check(entry.pubkey, entry.dTag, entry.aggregateHash, capability)) continue;
|
|
1193
|
+
const win = originRegistry.getIframeWindow(entry.windowId);
|
|
1138
1194
|
if (!win) continue;
|
|
1139
1195
|
win.postMessage(envelope, "*");
|
|
1140
1196
|
}
|
|
@@ -1158,6 +1214,7 @@ function createShellBridge(hooks) {
|
|
|
1158
1214
|
hooks,
|
|
1159
1215
|
origin: event.origin,
|
|
1160
1216
|
runtime,
|
|
1217
|
+
state: shellReadyState,
|
|
1161
1218
|
sourceRegistrationId: originRegistry.getRegistrationId(sourceWindow) ?? 0,
|
|
1162
1219
|
sourceWindow,
|
|
1163
1220
|
windowId
|
|
@@ -1171,17 +1228,18 @@ function createShellBridge(hooks) {
|
|
|
1171
1228
|
},
|
|
1172
1229
|
destroy() {
|
|
1173
1230
|
runtime.destroy();
|
|
1231
|
+
shellReadyState.clear();
|
|
1174
1232
|
},
|
|
1175
1233
|
registerConsentHandler(handler) {
|
|
1176
1234
|
runtime.registerConsentHandler(handler);
|
|
1177
1235
|
},
|
|
1178
1236
|
publishTheme(theme) {
|
|
1179
1237
|
const envelope = { type: "theme.changed", theme };
|
|
1180
|
-
|
|
1238
|
+
publishToEligibleNapplets(envelope, "theme", "theme:read");
|
|
1181
1239
|
},
|
|
1182
1240
|
publishIdentityChanged(pubkey) {
|
|
1183
1241
|
const envelope = { type: "identity.changed", pubkey };
|
|
1184
|
-
|
|
1242
|
+
publishToEligibleNapplets(envelope, "identity", "identity:read");
|
|
1185
1243
|
},
|
|
1186
1244
|
get runtime() {
|
|
1187
1245
|
return runtime;
|
|
@@ -1276,6 +1334,41 @@ function nappletNamespacePrelude(domains) {
|
|
|
1276
1334
|
function fire(message) {
|
|
1277
1335
|
post(message);
|
|
1278
1336
|
}
|
|
1337
|
+
function normalizeConventionUri(topic, hasExplicitPayload) {
|
|
1338
|
+
if (typeof topic !== "string") throw new TypeError("INC topic must be text");
|
|
1339
|
+
if (!topic.startsWith("napplet:")) return { topic };
|
|
1340
|
+
if (topic.includes("#")) throw new TypeError("Convention topics cannot contain fragments");
|
|
1341
|
+
const queryStart = topic.indexOf("?");
|
|
1342
|
+
if (queryStart === -1) {
|
|
1343
|
+
return { topic };
|
|
1344
|
+
}
|
|
1345
|
+
if (hasExplicitPayload) {
|
|
1346
|
+
throw new TypeError("Convention queries cannot be combined with an explicit payload");
|
|
1347
|
+
}
|
|
1348
|
+
const values = {};
|
|
1349
|
+
const names = /* @__PURE__ */ new Set();
|
|
1350
|
+
const query = topic.slice(queryStart + 1);
|
|
1351
|
+
if (query) {
|
|
1352
|
+
for (const pair of query.split("&")) {
|
|
1353
|
+
const separator = pair.indexOf("=");
|
|
1354
|
+
if (separator < 0) {
|
|
1355
|
+
throw new TypeError("Convention query parameters must use name=value form");
|
|
1356
|
+
}
|
|
1357
|
+
let name;
|
|
1358
|
+
let value;
|
|
1359
|
+
try {
|
|
1360
|
+
name = decodeURIComponent(pair.slice(0, separator));
|
|
1361
|
+
value = decodeURIComponent(pair.slice(separator + 1));
|
|
1362
|
+
} catch {
|
|
1363
|
+
throw new TypeError("Convention query contains malformed percent encoding");
|
|
1364
|
+
}
|
|
1365
|
+
if (names.has(name)) throw new TypeError("Convention query names must be unique");
|
|
1366
|
+
names.add(name);
|
|
1367
|
+
values[name] = value;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
return { topic: topic.slice(0, queryStart), payload: values };
|
|
1371
|
+
}
|
|
1279
1372
|
function resultOrError(msg) {
|
|
1280
1373
|
return msg;
|
|
1281
1374
|
}
|
|
@@ -1315,11 +1408,13 @@ function nappletNamespacePrelude(domains) {
|
|
|
1315
1408
|
if (typeof incoming !== "object" || incoming === null) return;
|
|
1316
1409
|
const message = incoming;
|
|
1317
1410
|
if (message.type !== "shell.init") return;
|
|
1318
|
-
const
|
|
1319
|
-
|
|
1320
|
-
);
|
|
1411
|
+
const rawCapabilities = message.capabilities;
|
|
1412
|
+
const rawDomains = typeof rawCapabilities === "object" && rawCapabilities !== null ? rawCapabilities.domains : void 0;
|
|
1413
|
+
if (!Array.isArray(rawDomains)) return;
|
|
1414
|
+
const domains2 = freezeStrings(rawDomains);
|
|
1415
|
+
const services = freezeStrings(Array.isArray(message.services) ? message.services : []);
|
|
1321
1416
|
environment = Object.freeze({
|
|
1322
|
-
capabilities:
|
|
1417
|
+
capabilities: Object.freeze({ domains: domains2 }),
|
|
1323
1418
|
services
|
|
1324
1419
|
});
|
|
1325
1420
|
off();
|
|
@@ -1327,20 +1422,18 @@ function nappletNamespacePrelude(domains) {
|
|
|
1327
1422
|
for (const handler of readyHandlers) handler(environment);
|
|
1328
1423
|
readyHandlers.clear();
|
|
1329
1424
|
});
|
|
1330
|
-
function
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
if (typeof protocols === "object" && protocols !== null) {
|
|
1338
|
-
const supported = protocols[domain];
|
|
1339
|
-
if (Array.isArray(supported) && supported.includes(protocol)) return true;
|
|
1340
|
-
}
|
|
1341
|
-
return Array.isArray(capabilities.naps) && capabilities.naps.includes(`${domain}:${protocol}`);
|
|
1425
|
+
function freezeStrings(values) {
|
|
1426
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1427
|
+
const strings = [];
|
|
1428
|
+
for (const value of values) {
|
|
1429
|
+
if (typeof value !== "string" || value.length === 0 || seen.has(value)) continue;
|
|
1430
|
+
seen.add(value);
|
|
1431
|
+
strings.push(value);
|
|
1342
1432
|
}
|
|
1343
|
-
return
|
|
1433
|
+
return Object.freeze(strings);
|
|
1434
|
+
}
|
|
1435
|
+
function supports(domain) {
|
|
1436
|
+
return typeof domain === "string" && environment?.capabilities.domains.includes(domain) === true;
|
|
1344
1437
|
}
|
|
1345
1438
|
const shell2 = {
|
|
1346
1439
|
ready: () => ready,
|
|
@@ -1424,38 +1517,205 @@ function nappletNamespacePrelude(domains) {
|
|
|
1424
1517
|
};
|
|
1425
1518
|
}
|
|
1426
1519
|
function makeInc() {
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1520
|
+
const topicStates = /* @__PURE__ */ new Map();
|
|
1521
|
+
const maxRetainedEvents = 32;
|
|
1522
|
+
const channelStates = /* @__PURE__ */ new Map();
|
|
1523
|
+
const openedHandlers = /* @__PURE__ */ new Set();
|
|
1524
|
+
const pendingOpened = [];
|
|
1525
|
+
const unopenedEvents = /* @__PURE__ */ new Map();
|
|
1526
|
+
const unopenedClosed = /* @__PURE__ */ new Map();
|
|
1527
|
+
function stableSubscriptionTopic(topic) {
|
|
1528
|
+
if (typeof topic !== "string") throw new TypeError("INC topic must be text");
|
|
1529
|
+
if (topic.includes("?") || topic.includes("#")) {
|
|
1530
|
+
throw new TypeError("INC subscriptions require a queryless topic identity");
|
|
1531
|
+
}
|
|
1532
|
+
return topic;
|
|
1533
|
+
}
|
|
1534
|
+
function closeChannelState(state, closed, notifyShell) {
|
|
1535
|
+
if (state.closed) return;
|
|
1536
|
+
state.closed = closed;
|
|
1537
|
+
if (notifyShell) fire({ type: "inc.channel.close", channelId: state.channelId });
|
|
1538
|
+
for (const handler of state.closedHandlers) handler(closed);
|
|
1539
|
+
}
|
|
1540
|
+
function makeChannelHandle(state) {
|
|
1541
|
+
return {
|
|
1542
|
+
id: state.channelId,
|
|
1543
|
+
peer: state.peer,
|
|
1544
|
+
emit(payload) {
|
|
1545
|
+
if (state.closed) return;
|
|
1546
|
+
fire({ type: "inc.channel.emit", channelId: state.channelId, ...payload === void 0 ? {} : { payload } });
|
|
1547
|
+
},
|
|
1548
|
+
on(handler) {
|
|
1549
|
+
if (typeof handler !== "function") throw new TypeError("INC channel event handler must be a function");
|
|
1550
|
+
if (state.closed) return subscriptionHandle(() => void 0);
|
|
1551
|
+
state.eventHandlers.add(handler);
|
|
1552
|
+
const pending = state.pendingEvents.splice(0);
|
|
1553
|
+
for (const event of pending) handler(event);
|
|
1554
|
+
return subscriptionHandle(() => state.eventHandlers.delete(handler));
|
|
1555
|
+
},
|
|
1556
|
+
onClosed(handler) {
|
|
1557
|
+
if (typeof handler !== "function") throw new TypeError("INC channel close handler must be a function");
|
|
1558
|
+
state.closedHandlers.add(handler);
|
|
1559
|
+
if (state.closed) handler(state.closed);
|
|
1560
|
+
return subscriptionHandle(() => state.closedHandlers.delete(handler));
|
|
1561
|
+
},
|
|
1562
|
+
close() {
|
|
1563
|
+
closeChannelState(state, { channelId: state.channelId }, true);
|
|
1436
1564
|
}
|
|
1437
|
-
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
function materializeChannel(channelId, peer) {
|
|
1568
|
+
const existing = channelStates.get(channelId);
|
|
1569
|
+
if (existing) return existing;
|
|
1570
|
+
const state = {
|
|
1571
|
+
channelId,
|
|
1572
|
+
peer,
|
|
1573
|
+
eventHandlers: /* @__PURE__ */ new Set(),
|
|
1574
|
+
closedHandlers: /* @__PURE__ */ new Set(),
|
|
1575
|
+
pendingEvents: unopenedEvents.get(channelId) ?? []
|
|
1576
|
+
};
|
|
1577
|
+
unopenedEvents.delete(channelId);
|
|
1578
|
+
channelStates.set(channelId, state);
|
|
1579
|
+
const closed = unopenedClosed.get(channelId);
|
|
1580
|
+
unopenedClosed.delete(channelId);
|
|
1581
|
+
if (closed) closeChannelState(state, closed, false);
|
|
1582
|
+
return state;
|
|
1583
|
+
}
|
|
1584
|
+
function deliverOpened(state) {
|
|
1585
|
+
const handle = makeChannelHandle(state);
|
|
1586
|
+
if (openedHandlers.size > 0) {
|
|
1587
|
+
for (const handler of openedHandlers) handler(handle);
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
pendingOpened.push(state);
|
|
1591
|
+
}
|
|
1592
|
+
function receiveChannelEvent(channelId, sender, payloadPresent, payload) {
|
|
1593
|
+
const event = payloadPresent ? { channelId, sender, payload } : { channelId, sender };
|
|
1594
|
+
const state = channelStates.get(channelId);
|
|
1595
|
+
if (!state) {
|
|
1596
|
+
const pending = unopenedEvents.get(channelId) ?? [];
|
|
1597
|
+
if (pending.length >= maxRetainedEvents) {
|
|
1598
|
+
unopenedEvents.delete(channelId);
|
|
1599
|
+
unopenedClosed.set(channelId, { channelId, reason: "buffer overflow" });
|
|
1600
|
+
fire({ type: "inc.channel.close", channelId });
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
pending.push(event);
|
|
1604
|
+
unopenedEvents.set(channelId, pending);
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
if (state.closed) return;
|
|
1608
|
+
if (state.eventHandlers.size > 0) {
|
|
1609
|
+
for (const handler of state.eventHandlers) handler(event);
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
if (state.pendingEvents.length >= maxRetainedEvents) {
|
|
1613
|
+
closeChannelState(state, { channelId, reason: "buffer overflow" }, true);
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
state.pendingEvents.push(event);
|
|
1617
|
+
}
|
|
1618
|
+
listen((event) => {
|
|
1619
|
+
if (!isParentMessage(event)) return;
|
|
1620
|
+
const msg = event.data;
|
|
1621
|
+
if (typeof msg !== "object" || msg === null || typeof msg.type !== "string") return;
|
|
1622
|
+
if (msg.type === "inc.channel.opened" && typeof msg.channelId === "string" && typeof msg.peer === "string") {
|
|
1623
|
+
deliverOpened(materializeChannel(msg.channelId, msg.peer));
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
if (msg.type === "inc.channel.event" && typeof msg.channelId === "string" && typeof msg.sender === "string") {
|
|
1627
|
+
receiveChannelEvent(msg.channelId, msg.sender, Object.prototype.hasOwnProperty.call(msg, "payload"), msg.payload);
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
if (msg.type === "inc.channel.closed" && typeof msg.channelId === "string") {
|
|
1631
|
+
const closed = typeof msg.reason === "string" ? { channelId: msg.channelId, reason: msg.reason } : { channelId: msg.channelId };
|
|
1632
|
+
const state = channelStates.get(msg.channelId);
|
|
1633
|
+
if (state) closeChannelState(state, closed, false);
|
|
1634
|
+
else unopenedClosed.set(msg.channelId, closed);
|
|
1635
|
+
}
|
|
1636
|
+
});
|
|
1637
|
+
return {
|
|
1638
|
+
emit(topic, payload) {
|
|
1639
|
+
const transposed = normalizeConventionUri(topic, arguments.length > 1);
|
|
1640
|
+
fire({
|
|
1641
|
+
type: "inc.emit",
|
|
1642
|
+
topic: transposed.topic,
|
|
1643
|
+
...transposed.payload === void 0 && payload === void 0 ? {} : { payload: transposed.payload ?? payload }
|
|
1644
|
+
});
|
|
1438
1645
|
},
|
|
1439
1646
|
on(topic, callback) {
|
|
1440
|
-
const
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1647
|
+
const stableTopic = stableSubscriptionTopic(topic);
|
|
1648
|
+
if (typeof callback !== "function") throw new TypeError("INC subscription handler must be a function");
|
|
1649
|
+
let state = topicStates.get(stableTopic);
|
|
1650
|
+
if (!state) {
|
|
1651
|
+
const handlers = /* @__PURE__ */ new Set();
|
|
1652
|
+
const subscriptionId = id();
|
|
1653
|
+
const offEvent = listen((event) => {
|
|
1654
|
+
if (!isParentMessage(event)) return;
|
|
1655
|
+
const msg = event.data;
|
|
1656
|
+
if (typeof msg !== "object" || msg === null || msg.type !== "inc.event" || msg.topic !== stableTopic || typeof msg.sender !== "string") return;
|
|
1657
|
+
const delivered = Object.prototype.hasOwnProperty.call(msg, "payload") ? { topic: stableTopic, sender: msg.sender, payload: msg.payload } : { topic: stableTopic, sender: msg.sender };
|
|
1658
|
+
for (const handler of handlers) handler(delivered);
|
|
1452
1659
|
});
|
|
1453
|
-
|
|
1454
|
-
|
|
1660
|
+
const offResult = listen((event) => {
|
|
1661
|
+
if (!isParentMessage(event)) return;
|
|
1662
|
+
const msg = event.data;
|
|
1663
|
+
if (typeof msg !== "object" || msg === null || msg.type !== "inc.subscribe.result" || msg.id !== subscriptionId) return;
|
|
1664
|
+
offResult();
|
|
1665
|
+
if (typeof msg.error === "string" && msg.error) {
|
|
1666
|
+
offEvent();
|
|
1667
|
+
topicStates.delete(stableTopic);
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
state = { handlers, offEvent, offResult };
|
|
1671
|
+
topicStates.set(stableTopic, state);
|
|
1672
|
+
fire({ type: "inc.subscribe", id: subscriptionId, topic: stableTopic });
|
|
1673
|
+
}
|
|
1674
|
+
state.handlers.add(callback);
|
|
1455
1675
|
return subscriptionHandle(() => {
|
|
1456
|
-
|
|
1457
|
-
|
|
1676
|
+
const current = topicStates.get(stableTopic);
|
|
1677
|
+
if (!current) return;
|
|
1678
|
+
current.handlers.delete(callback);
|
|
1679
|
+
if (current.handlers.size !== 0) return;
|
|
1680
|
+
current.offEvent();
|
|
1681
|
+
current.offResult();
|
|
1682
|
+
topicStates.delete(stableTopic);
|
|
1683
|
+
fire({ type: "inc.unsubscribe", topic: stableTopic });
|
|
1458
1684
|
});
|
|
1685
|
+
},
|
|
1686
|
+
channel: {
|
|
1687
|
+
open(targetDTag) {
|
|
1688
|
+
return request(
|
|
1689
|
+
{ type: "inc.channel.open", target: targetDTag },
|
|
1690
|
+
"inc.channel.open.result",
|
|
1691
|
+
(msg) => {
|
|
1692
|
+
if (typeof msg.channelId !== "string" || typeof msg.peer !== "string") {
|
|
1693
|
+
throw new Error("inc.channel.open.result missing channel identity");
|
|
1694
|
+
}
|
|
1695
|
+
return makeChannelHandle(materializeChannel(msg.channelId, msg.peer));
|
|
1696
|
+
}
|
|
1697
|
+
);
|
|
1698
|
+
},
|
|
1699
|
+
onOpened(handler) {
|
|
1700
|
+
if (typeof handler !== "function") throw new TypeError("INC opened handler must be a function");
|
|
1701
|
+
openedHandlers.add(handler);
|
|
1702
|
+
const pending = pendingOpened.splice(0);
|
|
1703
|
+
for (const state of pending) handler(makeChannelHandle(state));
|
|
1704
|
+
return subscriptionHandle(() => openedHandlers.delete(handler));
|
|
1705
|
+
},
|
|
1706
|
+
list() {
|
|
1707
|
+
return request(
|
|
1708
|
+
{ type: "inc.channel.list" },
|
|
1709
|
+
"inc.channel.list.result",
|
|
1710
|
+
(msg) => Object.freeze((Array.isArray(msg.channels) ? msg.channels : []).flatMap((channel) => channel && typeof channel === "object" && typeof channel.id === "string" && typeof channel.peer === "string" ? [Object.freeze({
|
|
1711
|
+
id: channel.id,
|
|
1712
|
+
peer: channel.peer
|
|
1713
|
+
})] : []))
|
|
1714
|
+
);
|
|
1715
|
+
},
|
|
1716
|
+
broadcast(payload) {
|
|
1717
|
+
fire({ type: "inc.channel.broadcast", ...payload === void 0 ? {} : { payload } });
|
|
1718
|
+
}
|
|
1459
1719
|
}
|
|
1460
1720
|
};
|
|
1461
1721
|
}
|
|
@@ -1493,14 +1753,22 @@ function nappletNamespacePrelude(domains) {
|
|
|
1493
1753
|
return { ...makeScope(), instance: makeScope("instance") };
|
|
1494
1754
|
}
|
|
1495
1755
|
function makeIdentity() {
|
|
1496
|
-
const read = (type, field, fallback) => request(
|
|
1497
|
-
{ type },
|
|
1756
|
+
const read = (type, field, fallback, payload = {}) => request(
|
|
1757
|
+
{ type, ...payload },
|
|
1498
1758
|
`${type}.result`,
|
|
1499
1759
|
(msg) => Object.prototype.hasOwnProperty.call(msg, field) ? msg[field] : fallback,
|
|
1500
1760
|
{ rejectOnError: type !== "identity.getPublicKey" }
|
|
1501
1761
|
);
|
|
1502
|
-
return {
|
|
1762
|
+
return Object.freeze({
|
|
1503
1763
|
getPublicKey: () => read("identity.getPublicKey", "pubkey", ""),
|
|
1764
|
+
getRelays: () => read("identity.getRelays", "relays", {}),
|
|
1765
|
+
getProfile: () => read("identity.getProfile", "profile", null),
|
|
1766
|
+
getFollows: () => read("identity.getFollows", "pubkeys", []),
|
|
1767
|
+
getList: (listType) => read("identity.getList", "entries", [], { listType }),
|
|
1768
|
+
getZaps: () => read("identity.getZaps", "zaps", []),
|
|
1769
|
+
getMutes: () => read("identity.getMutes", "pubkeys", []),
|
|
1770
|
+
getBlocked: () => read("identity.getBlocked", "pubkeys", []),
|
|
1771
|
+
getBadges: () => read("identity.getBadges", "badges", []),
|
|
1504
1772
|
onChanged(handler) {
|
|
1505
1773
|
const off = listen((event) => {
|
|
1506
1774
|
if (!isParentMessage(event)) return;
|
|
@@ -1509,20 +1777,8 @@ function nappletNamespacePrelude(domains) {
|
|
|
1509
1777
|
if (typeof msg.pubkey === "string") handler(msg.pubkey);
|
|
1510
1778
|
});
|
|
1511
1779
|
return subscriptionHandle(off);
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
getProfile: () => read("identity.getProfile", "profile", null),
|
|
1515
|
-
getFollows: () => read("identity.getFollows", "pubkeys", []),
|
|
1516
|
-
getList: (listType) => request(
|
|
1517
|
-
{ type: "identity.getList", listType },
|
|
1518
|
-
"identity.getList.result",
|
|
1519
|
-
(msg) => Array.isArray(msg.entries) ? msg.entries : []
|
|
1520
|
-
),
|
|
1521
|
-
getZaps: () => read("identity.getZaps", "zaps", []),
|
|
1522
|
-
getMutes: () => read("identity.getMutes", "pubkeys", []),
|
|
1523
|
-
getBlocked: () => read("identity.getBlocked", "pubkeys", []),
|
|
1524
|
-
getBadges: () => read("identity.getBadges", "badges", [])
|
|
1525
|
-
};
|
|
1780
|
+
}
|
|
1781
|
+
});
|
|
1526
1782
|
}
|
|
1527
1783
|
function makeTheme() {
|
|
1528
1784
|
return {
|
|
@@ -1961,18 +2217,69 @@ function nappletNamespacePrelude(domains) {
|
|
|
1961
2217
|
};
|
|
1962
2218
|
}
|
|
1963
2219
|
function makeIntent() {
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
"
|
|
1967
|
-
|
|
1968
|
-
|
|
2220
|
+
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
2221
|
+
const normalizeRequest = (value) => {
|
|
2222
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2223
|
+
throw new TypeError("Intent request must be an object");
|
|
2224
|
+
}
|
|
2225
|
+
const supplied = value;
|
|
2226
|
+
if (hasOwn(supplied, "sender")) {
|
|
2227
|
+
throw new TypeError("Intent callers cannot supply sender");
|
|
2228
|
+
}
|
|
2229
|
+
const allowed2 = ["archetype", "action", "convention", "payload", "handler", "behavior"];
|
|
2230
|
+
if (Object.keys(supplied).some((field) => !allowed2.includes(field))) {
|
|
2231
|
+
throw new TypeError("Intent request contains unsupported fields");
|
|
2232
|
+
}
|
|
2233
|
+
if (typeof supplied.archetype !== "string" || supplied.archetype.length === 0) {
|
|
2234
|
+
throw new TypeError("Intent request requires an archetype");
|
|
2235
|
+
}
|
|
2236
|
+
const action = supplied.action === void 0 ? "open" : supplied.action;
|
|
2237
|
+
if (typeof action !== "string" || action.length === 0) {
|
|
2238
|
+
throw new TypeError("Intent action must be a non-empty string");
|
|
2239
|
+
}
|
|
2240
|
+
if (supplied.convention !== void 0 && (typeof supplied.convention !== "string" || !/^napplet:[^/?#\s]+\/[^/?#\s]+$/.test(supplied.convention))) {
|
|
2241
|
+
throw new TypeError("Intent convention must be queryless");
|
|
2242
|
+
}
|
|
2243
|
+
if (supplied.handler !== void 0 && (typeof supplied.handler !== "string" || supplied.handler.length === 0)) {
|
|
2244
|
+
throw new TypeError("Intent handler must be a non-empty string");
|
|
2245
|
+
}
|
|
2246
|
+
const behaviorValue = supplied?.behavior;
|
|
2247
|
+
const behavior = behaviorValue && typeof behaviorValue === "object" ? behaviorValue : void 0;
|
|
2248
|
+
if (behaviorValue !== void 0 && (!behavior || Array.isArray(behaviorValue) || Object.keys(behavior).some((field) => field !== "focus" && field !== "newWindow" && field !== "reuse") || hasOwn(behavior, "focus") && typeof behavior.focus !== "boolean" || hasOwn(behavior, "newWindow") && typeof behavior.newWindow !== "boolean" || hasOwn(behavior, "reuse") && typeof behavior.reuse !== "boolean")) {
|
|
2249
|
+
throw new TypeError("Intent behavior contains unsupported fields");
|
|
2250
|
+
}
|
|
2251
|
+
const sanitizedBehavior = {
|
|
2252
|
+
...typeof behavior?.focus === "boolean" ? { focus: behavior.focus } : {},
|
|
2253
|
+
...typeof behavior?.newWindow === "boolean" ? { newWindow: behavior.newWindow } : {},
|
|
2254
|
+
...typeof behavior?.reuse === "boolean" ? { reuse: behavior.reuse } : {}
|
|
2255
|
+
};
|
|
2256
|
+
return {
|
|
2257
|
+
archetype: supplied.archetype,
|
|
2258
|
+
action,
|
|
2259
|
+
...typeof supplied.convention === "string" ? { convention: supplied.convention } : {},
|
|
2260
|
+
...hasOwn(supplied, "payload") ? { payload: supplied.payload } : {},
|
|
2261
|
+
...typeof supplied.handler === "string" ? { handler: supplied.handler } : {},
|
|
2262
|
+
...Object.keys(sanitizedBehavior).length === 0 ? {} : { behavior: sanitizedBehavior }
|
|
2263
|
+
};
|
|
2264
|
+
};
|
|
2265
|
+
const invoke = (value) => {
|
|
2266
|
+
const normalized = normalizeRequest(value);
|
|
2267
|
+
return request(
|
|
2268
|
+
{
|
|
2269
|
+
type: "intent.invoke",
|
|
2270
|
+
request: normalized
|
|
2271
|
+
},
|
|
2272
|
+
"intent.invoke.result",
|
|
2273
|
+
(msg) => fieldOrThrow(msg, "result", "intent.invoke.result missing result")
|
|
2274
|
+
);
|
|
2275
|
+
};
|
|
1969
2276
|
return {
|
|
1970
2277
|
invoke,
|
|
1971
2278
|
open: (archetype, payload, opts) => invoke({
|
|
1972
2279
|
archetype,
|
|
1973
2280
|
action: "open",
|
|
1974
|
-
payload,
|
|
1975
|
-
...opts
|
|
2281
|
+
...payload === void 0 ? {} : { payload },
|
|
2282
|
+
...opts
|
|
1976
2283
|
}),
|
|
1977
2284
|
available: (archetype) => request(
|
|
1978
2285
|
{ type: "intent.available", archetype },
|
|
@@ -2123,14 +2430,40 @@ function nappletNamespacePrelude(domains) {
|
|
|
2123
2430
|
};
|
|
2124
2431
|
}
|
|
2125
2432
|
const shell = makeShell();
|
|
2433
|
+
let inc;
|
|
2434
|
+
let identity;
|
|
2435
|
+
let theme;
|
|
2436
|
+
let intent;
|
|
2437
|
+
function makeProtectedInc(existing) {
|
|
2438
|
+
const extensions = existing && typeof existing === "object" ? existing : {};
|
|
2439
|
+
inc ??= makeInc();
|
|
2440
|
+
return { ...extensions, ...inc };
|
|
2441
|
+
}
|
|
2442
|
+
function makeProtectedIdentity() {
|
|
2443
|
+
identity ??= makeIdentity();
|
|
2444
|
+
return identity;
|
|
2445
|
+
}
|
|
2446
|
+
function makeProtectedTheme() {
|
|
2447
|
+
theme ??= Object.freeze(makeTheme());
|
|
2448
|
+
return theme;
|
|
2449
|
+
}
|
|
2450
|
+
function makeProtectedIntent() {
|
|
2451
|
+
intent ??= Object.freeze(makeIntent());
|
|
2452
|
+
return intent;
|
|
2453
|
+
}
|
|
2454
|
+
function isProtectedDomain(domain) {
|
|
2455
|
+
return domain === "shell" || domain === "inc" || domain === "identity" || domain === "theme" || domain === "intent";
|
|
2456
|
+
}
|
|
2126
2457
|
function makeDomain(domain, existing) {
|
|
2127
2458
|
if (domain === "shell") return shell;
|
|
2459
|
+
if (domain === "inc") return makeProtectedInc(existing);
|
|
2460
|
+
if (domain === "identity") return makeProtectedIdentity();
|
|
2461
|
+
if (domain === "theme") return makeProtectedTheme();
|
|
2462
|
+
if (domain === "intent") return makeProtectedIntent();
|
|
2128
2463
|
if (existing && typeof existing === "object" && Object.keys(existing).length > 0) return existing;
|
|
2129
2464
|
switch (domain) {
|
|
2130
2465
|
case "relay":
|
|
2131
2466
|
return makeRelay();
|
|
2132
|
-
case "inc":
|
|
2133
|
-
return makeInc();
|
|
2134
2467
|
case "storage":
|
|
2135
2468
|
return makeStorage();
|
|
2136
2469
|
case "keys":
|
|
@@ -2139,10 +2472,6 @@ function nappletNamespacePrelude(domains) {
|
|
|
2139
2472
|
return makeMedia();
|
|
2140
2473
|
case "notify":
|
|
2141
2474
|
return makeNotify();
|
|
2142
|
-
case "identity":
|
|
2143
|
-
return makeIdentity();
|
|
2144
|
-
case "theme":
|
|
2145
|
-
return makeTheme();
|
|
2146
2475
|
case "config":
|
|
2147
2476
|
return makeConfig();
|
|
2148
2477
|
case "resource":
|
|
@@ -2153,8 +2482,6 @@ function nappletNamespacePrelude(domains) {
|
|
|
2153
2482
|
return makeOutbox();
|
|
2154
2483
|
case "upload":
|
|
2155
2484
|
return makeUpload();
|
|
2156
|
-
case "intent":
|
|
2157
|
-
return makeIntent();
|
|
2158
2485
|
case "webrtc":
|
|
2159
2486
|
return makeWebrtc();
|
|
2160
2487
|
case "ble":
|
|
@@ -2184,10 +2511,22 @@ function nappletNamespacePrelude(domains) {
|
|
|
2184
2511
|
},
|
|
2185
2512
|
defineProperty(obj, prop, descriptor) {
|
|
2186
2513
|
if (typeof prop !== "string" || !allowed.has(prop)) return true;
|
|
2514
|
+
if (isProtectedDomain(prop)) {
|
|
2515
|
+
return Reflect.defineProperty(obj, prop, {
|
|
2516
|
+
value: makeDomain(prop, descriptor.value),
|
|
2517
|
+
enumerable: true,
|
|
2518
|
+
configurable: true,
|
|
2519
|
+
writable: true
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
2187
2522
|
return Reflect.defineProperty(obj, prop, {
|
|
2188
2523
|
...descriptor,
|
|
2189
2524
|
value: makeDomain(prop, descriptor.value)
|
|
2190
2525
|
});
|
|
2526
|
+
},
|
|
2527
|
+
deleteProperty(obj, prop) {
|
|
2528
|
+
if (typeof prop === "string" && allowed.has(prop) && isProtectedDomain(prop)) return true;
|
|
2529
|
+
return Reflect.deleteProperty(obj, prop);
|
|
2191
2530
|
}
|
|
2192
2531
|
});
|
|
2193
2532
|
}
|
|
@@ -2217,7 +2556,7 @@ function nappletNamespacePrelude(domains) {
|
|
|
2217
2556
|
root = buildNappletNamespace(value);
|
|
2218
2557
|
},
|
|
2219
2558
|
enumerable: false,
|
|
2220
|
-
configurable:
|
|
2559
|
+
configurable: false
|
|
2221
2560
|
});
|
|
2222
2561
|
}
|
|
2223
2562
|
|
|
@@ -2234,9 +2573,10 @@ function createIdentityProxy(deps) {
|
|
|
2234
2573
|
dispatch(windowId, envelope) {
|
|
2235
2574
|
deps.runtime.handleMessage(windowId, envelope);
|
|
2236
2575
|
},
|
|
2237
|
-
emit(
|
|
2238
|
-
|
|
2239
|
-
|
|
2576
|
+
emit(_windowId, _envelope) {
|
|
2577
|
+
throw new Error(
|
|
2578
|
+
"Direct identity proxy emit is prohibited; use ShellBridge.publishIdentityChanged()"
|
|
2579
|
+
);
|
|
2240
2580
|
}
|
|
2241
2581
|
};
|
|
2242
2582
|
}
|
|
@@ -2247,9 +2587,10 @@ function createThemeProxy(deps) {
|
|
|
2247
2587
|
dispatch(windowId, envelope) {
|
|
2248
2588
|
deps.runtime.handleMessage(windowId, envelope);
|
|
2249
2589
|
},
|
|
2250
|
-
emit(
|
|
2251
|
-
|
|
2252
|
-
|
|
2590
|
+
emit(_windowId, _envelope) {
|
|
2591
|
+
throw new Error(
|
|
2592
|
+
"Direct theme proxy emit is prohibited; use ShellBridge.publishTheme()"
|
|
2593
|
+
);
|
|
2253
2594
|
}
|
|
2254
2595
|
};
|
|
2255
2596
|
}
|
|
@@ -2312,6 +2653,7 @@ export {
|
|
|
2312
2653
|
nappKeyRegistry,
|
|
2313
2654
|
originRegistry,
|
|
2314
2655
|
renderNappletNamespacePrelude,
|
|
2656
|
+
resolveShellEnvironment,
|
|
2315
2657
|
sessionRegistry
|
|
2316
2658
|
};
|
|
2317
2659
|
//# sourceMappingURL=index.js.map
|