@kehto/shell 0.23.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/dist/index.d.ts +11 -3
- package/dist/index.js +104 -13
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ Current draft behaviors this package enforces:
|
|
|
34
34
|
when the runtime can identify it. The shell forwards this optional context
|
|
35
35
|
without imposing a consent policy; host runtimes may ignore it or use it to
|
|
36
36
|
resolve their own identity-scoped signer decisions.
|
|
37
|
-
- `prepareNappletSrcdoc()` inserts validated CSP
|
|
37
|
+
- `prepareNappletSrcdoc()` inserts validated CSP and the runtime namespace outside verified artifact bytes. It exposes only the host-selected bare domains. Optional `shell` installs its parent-bound receiver before readiness; routing uses trusted iframe creation identity independently of that exchange. See the living [NAP-SHELL](https://github.com/napplet/naps/blob/master/naps/NAP-SHELL.md).
|
|
38
38
|
- The injected resource projection follows draft NAP-RESOURCE `9511232f69313aa7953d110e35d32cc28d506f66`: `bytes(url, { servers? })` carries advisory Blossom locations and `bytesMany(requests)` keeps each `{ url, servers? }` entry intact.
|
|
39
39
|
- `window.napplet.shell.supports(domain)` answers synchronously and locally from the cached first `shell.init` environment. It returns `false` before `shell.init`, for unknown values, and for domains that are not live and granted to that napplet; it never sends a support-query message.
|
|
40
40
|
- Five optional per-domain proxies — `createIdentityProxy`, `createThemeProxy`, `createKeysProxy`, `createMediaProxy`, `createNotifyProxy` — can be composed between napplet and runtime to intercept request traffic per NAP. They are NOT wired by default. Identity/theme proxy `emit()` compatibility members fail closed; hosts must deliver automatic changes through `ShellBridge.publishIdentityChanged()` / `publishTheme()`, which enforce live session, granted domain, and current ACL.
|
|
@@ -198,7 +198,7 @@ automatic changes are accepted only when their `MessageEvent.source` is
|
|
|
198
198
|
the canonical operations.
|
|
199
199
|
|
|
200
200
|
`ShellBridge` delivers `identity.changed` and `theme.changed` exactly once per
|
|
201
|
-
eligible recipient: a live authenticated
|
|
201
|
+
eligible recipient: a live authenticated source-bound session, its frozen
|
|
202
202
|
environment includes the relevant domain, and its current recipient read
|
|
203
203
|
capability is still granted. Identity sign-out is `pubkey: ""`. Theme updates
|
|
204
204
|
arrive only after the service has stored the complete color state. These are
|
|
@@ -283,3 +283,7 @@ contract, as documented in the conformance policy.
|
|
|
283
283
|
The shell normalizer also powers Paja intent links. See the
|
|
284
284
|
[Paja guide](https://kehto.github.io/web/docs/packages/paja.html#nap-intent-links-and-lifecycle)
|
|
285
285
|
for outer URL encoding, review, routing, and verified launcher behavior.
|
|
286
|
+
|
|
287
|
+
## GAMEPAD integration
|
|
288
|
+
|
|
289
|
+
This non-normative guidance defers to the living [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). Services export `createGamepadService` and `browserGamepadOptions`; shell exports `denyNappletControllerAccess`. Paja and the playground register one host reader and deny native gamepad/HID/USB paths before frame execution. Source-bound subscriptions receive immediate full snapshots; only the visible host's input owner gets live controls. Focus loss supplies neutral controls and timestamp zero; unsubscribe, window destruction, and service removal release polling. Non-browser or denied native readers report the proposal's unavailable reasons. See [Controller input](https://github.com/kehto/web/blob/main/docs/gamepad.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -327,7 +327,7 @@ interface UnroutedMessageInfo {
|
|
|
327
327
|
/**
|
|
328
328
|
* Static capability set sent to napplet iframes through the shell.ready /
|
|
329
329
|
* shell.init handshake. Optional runtime domain presence is injected before
|
|
330
|
-
* authored code, while
|
|
330
|
+
* authored code, while optional NAP-SHELL caches this environment for local
|
|
331
331
|
* capability queries.
|
|
332
332
|
*
|
|
333
333
|
* NAP-SHELL needs only a truthful set of bare domain names. The shim answers
|
|
@@ -584,6 +584,14 @@ interface ShellBridge {
|
|
|
584
584
|
}
|
|
585
585
|
declare function createShellBridge(hooks: ShellAdapter): ShellBridge;
|
|
586
586
|
|
|
587
|
+
/**
|
|
588
|
+
* Restrict native controller/device APIs before assigning a napplet document.
|
|
589
|
+
* @param frame Host-owned iframe, before its authored code executes.
|
|
590
|
+
* @returns Nothing; unrelated Permissions-Policy entries are retained.
|
|
591
|
+
* @example denyNappletControllerAccess(frame);
|
|
592
|
+
*/
|
|
593
|
+
declare function denyNappletControllerAccess(frame: Pick<HTMLIFrameElement, 'getAttribute' | 'setAttribute'>): void;
|
|
594
|
+
|
|
587
595
|
/**
|
|
588
596
|
* The injected implementation must remain one self-contained function: the
|
|
589
597
|
* renderer serializes it ahead of verified artifact scripts without changing
|
|
@@ -605,7 +613,7 @@ declare function createShellBridge(hooks: ShellAdapter): ShellBridge;
|
|
|
605
613
|
interface NappletNamespacePreludeOptions {
|
|
606
614
|
/**
|
|
607
615
|
* Optional bare NAP domain names the shell exposes to this napplet.
|
|
608
|
-
*
|
|
616
|
+
* Shell is injected only when included in the exposed domains.
|
|
609
617
|
*/
|
|
610
618
|
domains: readonly string[];
|
|
611
619
|
}
|
|
@@ -1560,4 +1568,4 @@ type ResourceInbound = ResourceInfoRequest | ResourceBytesRequest | ResourceByte
|
|
|
1560
1568
|
*/
|
|
1561
1569
|
type ResourceOutbound = ResourceInfoResult | ResourceInfoError | ResourceBytesResult | ResourceBytesError | ResourceBytesManyResult | ResourceBytesManyError;
|
|
1562
1570
|
|
|
1563
|
-
export { type AclCheckEvent, type AclEntry, type AclStore, type AudioManager, type AudioSource, type AuthHooks, type BleHooks, type BrowserDeps, type CapabilityHooks, type CommonHooks, type ConfigHooks, type CryptoHooks, type DmHooks, type HotkeyHooks, type IdentityProxy, type IdentityProxyDeps, type IntentHooks, type KeysProxy, type KeysProxyDeps, type LinkHooks, type ListsHooks, type ManifestCache, type ManifestCacheEntry, type MediaProxy, type MediaProxyDeps, type NappletCspDirective, type NappletCspDirectives, type NappletCspOptions, type NappletNamespacePreludeOptions, type NappletSrcdocOptions, type NormalizedIntentUri, type NotifyProxy, type NotifyProxyDeps, type OriginIdentity, type OriginRegistry, type ProxyOriginRegistry, type RelayConfigHooks, type RelayPoolHooks, type RelayPoolLike, type ResourceBytesError, type ResourceBytesManyError, type ResourceBytesManyItem, type ResourceBytesManyRequest, type ResourceBytesManyResult, type ResourceBytesRequest, type ResourceBytesResult, type ResourceCancelRequest, type ResourceErrorCode, type ResourceInbound, type ResourceInfoError, type ResourceInfoRequest, type ResourceInfoResult, type ResourceOutbound, type ResourceRequestId, type SerialHooks, type SessionRegistry, type ShellAdapter, type ShellBridge, type ShellCapabilities, type ShellEnvironment, type ThemeProxy, type ThemeProxyDeps, type UnroutedMessageInfo, type UploadBackendLike, type UploadHooks, type WebrtcHooks, type WindowManagerHooks, type WorkerRelayHooks, type WorkerRelayLike, adaptHooks, audioManager, buildNappletCsp, buildShellCapabilities, createIdentityProxy, createKeysProxy, createMediaProxy, createNotifyProxy, createShellBridge, createThemeProxy, injectNappletCsp, injectNappletNamespacePrelude, manifestCache, nappKeyRegistry, normalizeIntentUri, originRegistry, prepareNappletSrcdoc, renderNappletCspMeta, renderNappletNamespacePrelude, resolveShellEnvironment, sessionRegistry };
|
|
1571
|
+
export { type AclCheckEvent, type AclEntry, type AclStore, type AudioManager, type AudioSource, type AuthHooks, type BleHooks, type BrowserDeps, type CapabilityHooks, type CommonHooks, type ConfigHooks, type CryptoHooks, type DmHooks, type HotkeyHooks, type IdentityProxy, type IdentityProxyDeps, type IntentHooks, type KeysProxy, type KeysProxyDeps, type LinkHooks, type ListsHooks, type ManifestCache, type ManifestCacheEntry, type MediaProxy, type MediaProxyDeps, type NappletCspDirective, type NappletCspDirectives, type NappletCspOptions, type NappletNamespacePreludeOptions, type NappletSrcdocOptions, type NormalizedIntentUri, type NotifyProxy, type NotifyProxyDeps, type OriginIdentity, type OriginRegistry, type ProxyOriginRegistry, type RelayConfigHooks, type RelayPoolHooks, type RelayPoolLike, type ResourceBytesError, type ResourceBytesManyError, type ResourceBytesManyItem, type ResourceBytesManyRequest, type ResourceBytesManyResult, type ResourceBytesRequest, type ResourceBytesResult, type ResourceCancelRequest, type ResourceErrorCode, type ResourceInbound, type ResourceInfoError, type ResourceInfoRequest, type ResourceInfoResult, type ResourceOutbound, type ResourceRequestId, type SerialHooks, type SessionRegistry, type ShellAdapter, type ShellBridge, type ShellCapabilities, type ShellEnvironment, type ThemeProxy, type ThemeProxyDeps, type UnroutedMessageInfo, type UploadBackendLike, type UploadHooks, type WebrtcHooks, type WindowManagerHooks, type WorkerRelayHooks, type WorkerRelayLike, adaptHooks, audioManager, buildNappletCsp, buildShellCapabilities, createIdentityProxy, createKeysProxy, createMediaProxy, createNotifyProxy, createShellBridge, createThemeProxy, denyNappletControllerAccess, injectNappletCsp, injectNappletNamespacePrelude, manifestCache, nappKeyRegistry, normalizeIntentUri, originRegistry, prepareNappletSrcdoc, renderNappletCspMeta, renderNappletNamespacePrelude, resolveShellEnvironment, sessionRegistry };
|
package/dist/index.js
CHANGED
|
@@ -976,6 +976,7 @@ var audioManager = {
|
|
|
976
976
|
|
|
977
977
|
// src/shell-init.ts
|
|
978
978
|
var RUNTIME_NATIVE_DOMAINS = [
|
|
979
|
+
"shell",
|
|
979
980
|
"relay",
|
|
980
981
|
"identity",
|
|
981
982
|
"storage",
|
|
@@ -1000,7 +1001,8 @@ var SERVICE_ONLY_DOMAINS = [
|
|
|
1000
1001
|
"webrtc",
|
|
1001
1002
|
"dm",
|
|
1002
1003
|
"count",
|
|
1003
|
-
"fs"
|
|
1004
|
+
"fs",
|
|
1005
|
+
"gamepad"
|
|
1004
1006
|
];
|
|
1005
1007
|
function buildShellCapabilities(hooks) {
|
|
1006
1008
|
const disabled = new Set(hooks.capabilities?.disabledDomains ?? []);
|
|
@@ -1096,14 +1098,21 @@ function handleShellReady({
|
|
|
1096
1098
|
if (state.initSent.get(sourceWindow) === sourceRegistrationId) {
|
|
1097
1099
|
return;
|
|
1098
1100
|
}
|
|
1099
|
-
const
|
|
1100
|
-
if (!
|
|
1101
|
-
registerNip5dSessionIfNeeded({ origin, runtime, state, sourceRegistrationId, windowId, identity });
|
|
1102
|
-
const environment = originRegistry.getEnvironment(sourceWindow) ?? resolveShellEnvironment(hooks, identity);
|
|
1103
|
-
state.environments.set(windowId, environment);
|
|
1101
|
+
const environment = registerSourceSession({ hooks, origin, runtime, state, sourceRegistrationId, sourceWindow, windowId });
|
|
1102
|
+
if (!environment?.capabilities.domains.includes("shell")) return;
|
|
1104
1103
|
postShellInit(sourceWindow, environment);
|
|
1105
1104
|
state.initSent.set(sourceWindow, sourceRegistrationId);
|
|
1106
1105
|
}
|
|
1106
|
+
function registerSourceSession(options) {
|
|
1107
|
+
const { hooks, origin, runtime, state, sourceRegistrationId, sourceWindow, windowId } = options;
|
|
1108
|
+
const identity = resolveNip5dIdentity(hooks, windowId, sourceWindow, runtime.sessionRegistry.getEntryByWindowId(windowId));
|
|
1109
|
+
if (!identity) return null;
|
|
1110
|
+
registerNip5dSessionIfNeeded({ origin, runtime, state, sourceRegistrationId, windowId, identity });
|
|
1111
|
+
const existing = state.environments.get(windowId);
|
|
1112
|
+
const environment = originRegistry.getEnvironment(sourceWindow) ?? existing ?? resolveShellEnvironment(hooks, identity);
|
|
1113
|
+
state.environments.set(windowId, environment);
|
|
1114
|
+
return environment;
|
|
1115
|
+
}
|
|
1107
1116
|
function registerNip5dSessionIfNeeded({
|
|
1108
1117
|
origin,
|
|
1109
1118
|
runtime,
|
|
@@ -1118,9 +1127,14 @@ function registerNip5dSessionIfNeeded({
|
|
|
1118
1127
|
return;
|
|
1119
1128
|
}
|
|
1120
1129
|
if (existing && previousRegistration !== void 0) {
|
|
1130
|
+
runtime.destroyWindow(windowId);
|
|
1121
1131
|
runtime.sessionRegistry.unregister(windowId);
|
|
1132
|
+
state.environments.delete(windowId);
|
|
1133
|
+
}
|
|
1134
|
+
if (existing && previousRegistration === void 0) {
|
|
1135
|
+
state.sessionRegistration.set(windowId, sourceRegistrationId);
|
|
1136
|
+
return;
|
|
1122
1137
|
}
|
|
1123
|
-
if (existing && previousRegistration === void 0) return;
|
|
1124
1138
|
const entry = {
|
|
1125
1139
|
pubkey: "",
|
|
1126
1140
|
windowId,
|
|
@@ -1135,7 +1149,7 @@ function registerNip5dSessionIfNeeded({
|
|
|
1135
1149
|
runtime.sessionRegistry.register(windowId, entry);
|
|
1136
1150
|
state.sessionRegistration.set(windowId, sourceRegistrationId);
|
|
1137
1151
|
}
|
|
1138
|
-
function resolveNip5dIdentity(hooks, windowId, sourceWindow) {
|
|
1152
|
+
function resolveNip5dIdentity(hooks, windowId, sourceWindow, existingSession) {
|
|
1139
1153
|
const hookIdentity = hooks.onNip5dIframeCreate?.(windowId);
|
|
1140
1154
|
if (hookIdentity !== null && hookIdentity !== void 0) {
|
|
1141
1155
|
return Object.freeze({
|
|
@@ -1145,7 +1159,8 @@ function resolveNip5dIdentity(hooks, windowId, sourceWindow) {
|
|
|
1145
1159
|
}
|
|
1146
1160
|
const originIdentity = originRegistry.getIdentity(sourceWindow);
|
|
1147
1161
|
if (!originIdentity) {
|
|
1148
|
-
return null;
|
|
1162
|
+
if (existingSession?.provenance !== "nip-5d" || typeof existingSession.dTag !== "string" || typeof existingSession.aggregateHash !== "string") return null;
|
|
1163
|
+
return Object.freeze({ dTag: existingSession.dTag, aggregateHash: existingSession.aggregateHash });
|
|
1149
1164
|
}
|
|
1150
1165
|
return Object.freeze({
|
|
1151
1166
|
dTag: originIdentity.dTag,
|
|
@@ -1208,6 +1223,15 @@ function createShellBridge(hooks) {
|
|
|
1208
1223
|
}
|
|
1209
1224
|
const msg = event.data;
|
|
1210
1225
|
if (typeof msg !== "object" || msg === null || typeof msg.type !== "string") return;
|
|
1226
|
+
if (!registerSourceSession({
|
|
1227
|
+
hooks,
|
|
1228
|
+
origin: event.origin,
|
|
1229
|
+
runtime,
|
|
1230
|
+
state: shellReadyState,
|
|
1231
|
+
sourceRegistrationId: originRegistry.getRegistrationId(sourceWindow) ?? 0,
|
|
1232
|
+
sourceWindow,
|
|
1233
|
+
windowId
|
|
1234
|
+
})) return;
|
|
1211
1235
|
if (msg.type === "shell.ready") {
|
|
1212
1236
|
handleShellReady({
|
|
1213
1237
|
hooks,
|
|
@@ -1246,6 +1270,13 @@ function createShellBridge(hooks) {
|
|
|
1246
1270
|
};
|
|
1247
1271
|
}
|
|
1248
1272
|
|
|
1273
|
+
// src/gamepad-policy.ts
|
|
1274
|
+
var CONTROLLER_FEATURES = ["gamepad", "hid", "usb"];
|
|
1275
|
+
function denyNappletControllerAccess(frame) {
|
|
1276
|
+
const remaining = (frame.getAttribute("allow") ?? "").split(";").map((entry) => entry.trim()).filter((entry) => entry && !CONTROLLER_FEATURES.some((feature) => entry === feature || entry.startsWith(`${feature} `)));
|
|
1277
|
+
frame.setAttribute("allow", [...remaining, ...CONTROLLER_FEATURES.map((feature) => `${feature} 'none'`)].join("; "));
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1249
1280
|
// src/intent-uri.ts
|
|
1250
1281
|
function createIntentUriNormalizer() {
|
|
1251
1282
|
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
@@ -1439,7 +1470,7 @@ function scriptJson(value) {
|
|
|
1439
1470
|
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1440
1471
|
}
|
|
1441
1472
|
function renderNappletNamespacePrelude(options) {
|
|
1442
|
-
const domains = uniqueBareDomains(
|
|
1473
|
+
const domains = uniqueBareDomains(options.domains);
|
|
1443
1474
|
return `<script data-kehto-nip5d-injection>(${nappletNamespacePrelude.toString()})(${scriptJson(domains)}, (${createIntentUriNormalizer.toString()})());</script>`;
|
|
1444
1475
|
}
|
|
1445
1476
|
function injectNappletNamespacePrelude(html, options) {
|
|
@@ -1628,7 +1659,7 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
1628
1659
|
return Object.freeze(strings);
|
|
1629
1660
|
}
|
|
1630
1661
|
function supports(domain) {
|
|
1631
|
-
return typeof domain === "string" &&
|
|
1662
|
+
return typeof domain === "string" && !!target.napplet && Object.prototype.hasOwnProperty.call(target.napplet, domain);
|
|
1632
1663
|
}
|
|
1633
1664
|
const shell2 = {
|
|
1634
1665
|
ready: () => ready,
|
|
@@ -2709,6 +2740,63 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2709
2740
|
})
|
|
2710
2741
|
};
|
|
2711
2742
|
}
|
|
2743
|
+
function makeGamepad() {
|
|
2744
|
+
let state = { available: true, focused: false, pads: [] };
|
|
2745
|
+
const handlers = /* @__PURE__ */ new Set();
|
|
2746
|
+
const copy = (value) => ({
|
|
2747
|
+
available: value.available,
|
|
2748
|
+
focused: value.focused,
|
|
2749
|
+
...value.reason === void 0 ? {} : { reason: value.reason },
|
|
2750
|
+
pads: value.pads.map((pad) => pad === null ? null : {
|
|
2751
|
+
index: pad.index,
|
|
2752
|
+
id: pad.id,
|
|
2753
|
+
mapping: pad.mapping,
|
|
2754
|
+
connected: true,
|
|
2755
|
+
timestamp: pad.timestamp,
|
|
2756
|
+
axes: [...pad.axes],
|
|
2757
|
+
buttons: pad.buttons.map(({ value: value2, pressed, touched }) => ({ value: value2, pressed, touched }))
|
|
2758
|
+
})
|
|
2759
|
+
});
|
|
2760
|
+
const numberIn = (value, min, max) => typeof value === "number" && Number.isFinite(value) && value >= min && value <= max;
|
|
2761
|
+
const valid = (value) => {
|
|
2762
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2763
|
+
const candidate = value;
|
|
2764
|
+
if (typeof candidate.available !== "boolean" || typeof candidate.focused !== "boolean" || !Array.isArray(candidate.pads)) return false;
|
|
2765
|
+
if (candidate.available ? candidate.reason !== void 0 : candidate.focused || !["blocked", "unavailable"].includes(candidate.reason ?? "")) return false;
|
|
2766
|
+
return Array.from(candidate.pads).every((pad) => {
|
|
2767
|
+
if (pad === null) return true;
|
|
2768
|
+
if (!pad || typeof pad !== "object" || !Number.isInteger(pad.index) || pad.index < 0 || typeof pad.id !== "string" || typeof pad.mapping !== "string" || pad.connected !== true || !Number.isFinite(pad.timestamp) || !Array.isArray(pad.axes) || !Array.isArray(pad.buttons)) return false;
|
|
2769
|
+
if (!candidate.focused && pad.timestamp !== 0) return false;
|
|
2770
|
+
if (!Array.from(pad.axes).every((axis) => numberIn(axis, -1, 1) && (candidate.focused || axis === 0))) return false;
|
|
2771
|
+
return Array.from(pad.buttons).every((button) => button && numberIn(button.value, 0, 1) && typeof button.pressed === "boolean" && typeof button.touched === "boolean" && (candidate.focused || button.value === 0 && !button.pressed && !button.touched));
|
|
2772
|
+
});
|
|
2773
|
+
};
|
|
2774
|
+
listen((event) => {
|
|
2775
|
+
if (!isParentMessage(event)) return;
|
|
2776
|
+
const incoming = event.data;
|
|
2777
|
+
if (!incoming || incoming.type !== "gamepad.state" || "id" in incoming || !valid(incoming)) return;
|
|
2778
|
+
state = copy(incoming);
|
|
2779
|
+
const snapshot = copy(state);
|
|
2780
|
+
const recipients = Array.from(handlers);
|
|
2781
|
+
for (const entry of recipients) if (handlers.has(entry)) entry.handler(copy(snapshot));
|
|
2782
|
+
});
|
|
2783
|
+
const api = {
|
|
2784
|
+
get available() {
|
|
2785
|
+
return state.available;
|
|
2786
|
+
},
|
|
2787
|
+
get focused() {
|
|
2788
|
+
return state.focused;
|
|
2789
|
+
},
|
|
2790
|
+
getGamepads: () => copy(state).pads,
|
|
2791
|
+
onChange(handler) {
|
|
2792
|
+
const entry = { handler };
|
|
2793
|
+
handlers.add(entry);
|
|
2794
|
+
return subscriptionHandle(() => handlers.delete(entry));
|
|
2795
|
+
}
|
|
2796
|
+
};
|
|
2797
|
+
post({ type: "gamepad.subscribe" });
|
|
2798
|
+
return api;
|
|
2799
|
+
}
|
|
2712
2800
|
function makeDm() {
|
|
2713
2801
|
const req = (type, payload) => request({ type, ...payload }, `${type}.result`, withoutEnvelope, { rejectOnError: false });
|
|
2714
2802
|
return {
|
|
@@ -2755,7 +2843,8 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2755
2843
|
};
|
|
2756
2844
|
}
|
|
2757
2845
|
const intentDelivery = allowed.has("intent") ? makeIntentDelivery() : void 0;
|
|
2758
|
-
const shell = makeShell();
|
|
2846
|
+
const shell = allowed.has("shell") ? makeShell() : void 0;
|
|
2847
|
+
let gamepad;
|
|
2759
2848
|
let inc;
|
|
2760
2849
|
let identity;
|
|
2761
2850
|
let theme;
|
|
@@ -2778,9 +2867,10 @@ function nappletNamespacePrelude(domains, normalizeIntentUri2) {
|
|
|
2778
2867
|
return intent;
|
|
2779
2868
|
}
|
|
2780
2869
|
function isProtectedDomain(domain) {
|
|
2781
|
-
return domain === "shell" || domain === "inc" || domain === "identity" || domain === "theme" || domain === "intent";
|
|
2870
|
+
return domain === "gamepad" || domain === "shell" || domain === "inc" || domain === "identity" || domain === "theme" || domain === "intent";
|
|
2782
2871
|
}
|
|
2783
2872
|
function makeDomain(domain, existing) {
|
|
2873
|
+
if (domain === "gamepad") return gamepad ??= makeGamepad();
|
|
2784
2874
|
if (domain === "shell") return shell;
|
|
2785
2875
|
if (domain === "inc") return makeProtectedInc(existing);
|
|
2786
2876
|
if (domain === "identity") return makeProtectedIdentity();
|
|
@@ -3094,6 +3184,7 @@ export {
|
|
|
3094
3184
|
createNotifyProxy,
|
|
3095
3185
|
createShellBridge,
|
|
3096
3186
|
createThemeProxy,
|
|
3187
|
+
denyNappletControllerAccess,
|
|
3097
3188
|
formatDenialReason,
|
|
3098
3189
|
injectNappletCsp,
|
|
3099
3190
|
injectNappletNamespacePrelude,
|