@kehto/paja 0.16.1 → 0.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -6
- package/dist/browser-host.js +109 -10
- package/dist/browser-host.js.map +1 -1
- package/dist/{chunk-32QBJDAR.js → chunk-AY3UYDP2.js} +47 -2
- package/dist/chunk-AY3UYDP2.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-32QBJDAR.js.map +0 -1
package/README.md
CHANGED
|
@@ -186,7 +186,8 @@ When `PAJA_UPLOAD_SERVERS` is set, the runtime enables the Blossom upload rail
|
|
|
186
186
|
and also makes those servers explicit NAP-RESOURCE fallbacks. It is not needed
|
|
187
187
|
for read-side discovery: resources learned through OUTBOX use their event hints
|
|
188
188
|
and hinted-author/publisher server lists first, then the active shell user's
|
|
189
|
-
published list
|
|
189
|
+
published list, the current window's verified pointer-manifest servers, and
|
|
190
|
+
finally configured runtime fallbacks.
|
|
190
191
|
|
|
191
192
|
## Installed intent handlers and delivery
|
|
192
193
|
|
|
@@ -308,7 +309,9 @@ Paja's developer-runtime policy accepts arbitrary `http:` and `https:` resource
|
|
|
308
309
|
URLs so a normal remote image does not look broken merely because its origin was
|
|
309
310
|
not pre-granted. Paja resolves those URLs with browser `fetch`, omits credentials
|
|
310
311
|
and referrer data, caps responses at 10 MiB, and classifies MIME from returned
|
|
311
|
-
bytes.
|
|
312
|
+
bytes. Its byte classifier recognizes checksum-valid Game Boy ROM headers as
|
|
313
|
+
`application/vnd.nintendo.gb-rom`; it never trusts a server-supplied media type.
|
|
314
|
+
Browser network and CORS rules still apply: an unreadable response is the
|
|
312
315
|
canonical `network-error`, while any CORS-readable response is returned as NAP
|
|
313
316
|
bytes.
|
|
314
317
|
|
|
@@ -322,8 +325,8 @@ without prefetching bytes. Resolution tries request and
|
|
|
322
325
|
event-local server hints first, then lazily queries hinted authors' and the
|
|
323
326
|
event publisher's newest BUD-03 kind `10063` lists through the verified
|
|
324
327
|
NIP-65-aware OUTBOX router, then queries the active shell user's BUD-03 list
|
|
325
|
-
through that same router, then uses
|
|
326
|
-
upload-runtime fallbacks. The user-list
|
|
328
|
+
through that same router, then uses the current window's verified
|
|
329
|
+
pointer-manifest servers, followed by upload-runtime fallbacks. The user-list
|
|
327
330
|
lookup works independently of upload mode; an upload runtime may reuse the same
|
|
328
331
|
servers when present. ROM-specific event and publisher locations retain
|
|
329
332
|
priority over the user/runtime fallbacks. The combined list is capped at eight
|
|
@@ -331,8 +334,14 @@ candidates. The only accepted identifier is `blossom:sha256:<hex>`;
|
|
|
331
334
|
Paja refuses redirects, verifies the requested SHA-256, and permits plain-HTTP
|
|
332
335
|
transport only for configured loopback development defaults. Browser-only Paja
|
|
333
336
|
cannot pin DNS results, so production runtimes must add the draft's DNS-time
|
|
334
|
-
private-address checks.
|
|
335
|
-
[NAP-RESOURCE at `
|
|
337
|
+
private-address checks. The current
|
|
338
|
+
[NAP-RESOURCE draft at `fa6bcc6`](https://github.com/napplet/naps/blob/fa6bcc6935aa19e7b70ab2a2c721dafca77c78e1/naps/NAP-RESOURCE.md)
|
|
339
|
+
assigns fetching and policy to the runtime and defines no wire-level Blossom
|
|
340
|
+
server-hint field. Retaining verified manifest servers per window is therefore
|
|
341
|
+
Paja host policy, informed by the current
|
|
342
|
+
[NIP-5D draft at `24711d9`](https://github.com/nostr-protocol/nips/blob/24711d9c47bbdd07908bf1d52bf677d9cbc530f0/5D.md),
|
|
343
|
+
which defines manifest `server` tags. The existing request-server compatibility
|
|
344
|
+
path comes from the earlier NAP-RESOURCE draft at `9511232`, with the merged package implementation
|
|
336
345
|
[`napplet/web#206@19e0029b`](https://github.com/napplet/web/pull/206) released as
|
|
337
346
|
`@napplet/core`/`@napplet/nap` 0.32.0, `@napplet/shim` 0.30.0, and
|
|
338
347
|
`@napplet/sdk` 0.28.0. Publisher discovery follows
|
package/dist/browser-host.js
CHANGED
|
@@ -16869,6 +16869,56 @@ var PAJA_RESOURCE_MAX_URLS = 100;
|
|
|
16869
16869
|
var PAJA_RESOURCE_MAX_SERVERS = 8;
|
|
16870
16870
|
var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
16871
16871
|
var BLOSSOM_RESOURCE_PATTERN = /^blossom:sha256:([0-9a-f]{64})$/i;
|
|
16872
|
+
var GAME_BOY_NINTENDO_LOGO = [
|
|
16873
|
+
206,
|
|
16874
|
+
237,
|
|
16875
|
+
102,
|
|
16876
|
+
102,
|
|
16877
|
+
204,
|
|
16878
|
+
13,
|
|
16879
|
+
0,
|
|
16880
|
+
11,
|
|
16881
|
+
3,
|
|
16882
|
+
115,
|
|
16883
|
+
0,
|
|
16884
|
+
131,
|
|
16885
|
+
0,
|
|
16886
|
+
12,
|
|
16887
|
+
0,
|
|
16888
|
+
13,
|
|
16889
|
+
0,
|
|
16890
|
+
8,
|
|
16891
|
+
17,
|
|
16892
|
+
31,
|
|
16893
|
+
136,
|
|
16894
|
+
137,
|
|
16895
|
+
0,
|
|
16896
|
+
14,
|
|
16897
|
+
220,
|
|
16898
|
+
204,
|
|
16899
|
+
110,
|
|
16900
|
+
230,
|
|
16901
|
+
221,
|
|
16902
|
+
221,
|
|
16903
|
+
217,
|
|
16904
|
+
153,
|
|
16905
|
+
187,
|
|
16906
|
+
187,
|
|
16907
|
+
103,
|
|
16908
|
+
99,
|
|
16909
|
+
110,
|
|
16910
|
+
14,
|
|
16911
|
+
236,
|
|
16912
|
+
204,
|
|
16913
|
+
221,
|
|
16914
|
+
220,
|
|
16915
|
+
153,
|
|
16916
|
+
159,
|
|
16917
|
+
187,
|
|
16918
|
+
185,
|
|
16919
|
+
51,
|
|
16920
|
+
62
|
|
16921
|
+
];
|
|
16872
16922
|
function createPajaResourceFetch(options = {}) {
|
|
16873
16923
|
return async (value, init) => {
|
|
16874
16924
|
if (init.signal.aborted) throw new DOMException("Resource request cancelled", "AbortError");
|
|
@@ -17127,8 +17177,17 @@ function sniffSafeResourceMime(bytes) {
|
|
|
17127
17177
|
if (ascii(bytes, 4, 8) === "ftyp") return "video/mp4";
|
|
17128
17178
|
if (ascii(bytes, 0, 4) === "wOFF") return "font/woff";
|
|
17129
17179
|
if (ascii(bytes, 0, 4) === "wOF2") return "font/woff2";
|
|
17180
|
+
if (isGameBoyRom(bytes)) return "application/vnd.nintendo.gb-rom";
|
|
17130
17181
|
return sniffSafeTextMime(bytes);
|
|
17131
17182
|
}
|
|
17183
|
+
function isGameBoyRom(bytes) {
|
|
17184
|
+
if (bytes.byteLength < 336 || !startsWith(bytes, GAME_BOY_NINTENDO_LOGO, 260)) return false;
|
|
17185
|
+
let checksum = 0;
|
|
17186
|
+
for (let index = 308; index <= 332; index += 1) {
|
|
17187
|
+
checksum = checksum - (bytes[index] ?? 0) - 1 & 255;
|
|
17188
|
+
}
|
|
17189
|
+
return checksum === bytes[333];
|
|
17190
|
+
}
|
|
17132
17191
|
function sniffSafeTextMime(bytes) {
|
|
17133
17192
|
let text;
|
|
17134
17193
|
try {
|
|
@@ -17161,6 +17220,7 @@ function createPajaBlossomEventResolver(options) {
|
|
|
17161
17220
|
const now2 = options.now ?? Date.now;
|
|
17162
17221
|
const ttl = Math.max(0, options.serverListTtlMs ?? DEFAULT_SERVER_LIST_TTL_MS);
|
|
17163
17222
|
const windows = /* @__PURE__ */ new Map();
|
|
17223
|
+
const windowServers = /* @__PURE__ */ new Map();
|
|
17164
17224
|
const serverLists = /* @__PURE__ */ new Map();
|
|
17165
17225
|
const pendingServerLists = /* @__PURE__ */ new Map();
|
|
17166
17226
|
function observe(windowId, results) {
|
|
@@ -17238,9 +17298,20 @@ function createPajaBlossomEventResolver(options) {
|
|
|
17238
17298
|
);
|
|
17239
17299
|
for (const list of lists) appendUnique(servers, list);
|
|
17240
17300
|
}
|
|
17301
|
+
if (windowId) appendUnique(servers, windowServers.get(windowId) ?? []);
|
|
17241
17302
|
appendUnique(servers, options.getConfiguredServers());
|
|
17242
17303
|
return servers.slice(0, PAJA_RESOURCE_MAX_SERVERS);
|
|
17243
17304
|
}
|
|
17305
|
+
function setWindowServers(windowId, servers) {
|
|
17306
|
+
if (!windowId) return;
|
|
17307
|
+
const normalized = [];
|
|
17308
|
+
for (const value of servers) {
|
|
17309
|
+
const server = normalizePublicBlossomServer(value);
|
|
17310
|
+
if (server) appendUnique(normalized, [server]);
|
|
17311
|
+
}
|
|
17312
|
+
if (normalized.length > 0) windowServers.set(windowId, normalized);
|
|
17313
|
+
else windowServers.delete(windowId);
|
|
17314
|
+
}
|
|
17244
17315
|
async function discoverServerList(pubkey2) {
|
|
17245
17316
|
const cached = serverLists.get(pubkey2);
|
|
17246
17317
|
if (cached && cached.expiresAt > now2()) return [...cached.servers];
|
|
@@ -17285,7 +17356,11 @@ function createPajaBlossomEventResolver(options) {
|
|
|
17285
17356
|
observe,
|
|
17286
17357
|
decorate,
|
|
17287
17358
|
getServers,
|
|
17288
|
-
|
|
17359
|
+
setWindowServers,
|
|
17360
|
+
clearWindow(windowId) {
|
|
17361
|
+
windows.delete(windowId);
|
|
17362
|
+
windowServers.delete(windowId);
|
|
17363
|
+
}
|
|
17289
17364
|
};
|
|
17290
17365
|
}
|
|
17291
17366
|
function extractEventResourceLocations(event) {
|
|
@@ -20181,7 +20256,11 @@ function createDevServices(backend, getSimulation, relayConfig, onThemeService,
|
|
|
20181
20256
|
return wasWebrtcEnabled !== webrtcEnabled || dmChanged;
|
|
20182
20257
|
}
|
|
20183
20258
|
refreshAvailability();
|
|
20184
|
-
return {
|
|
20259
|
+
return {
|
|
20260
|
+
services,
|
|
20261
|
+
refreshAvailability,
|
|
20262
|
+
setWindowBlossomServers: blossomEventResolver.setWindowServers
|
|
20263
|
+
};
|
|
20185
20264
|
}
|
|
20186
20265
|
function createPajaAdapter(config, getSimulation, onThemeService, onThemeBroadcast, confirmRequest, signerProvider, getIdentity, onEnvironmentChanged, intentHost, userActivation, notifyOptions, configOptions) {
|
|
20187
20266
|
const resolveIdentity = (windowId) => getIdentity?.(windowId) ?? {
|
|
@@ -20249,6 +20328,7 @@ function createPajaAdapter(config, getSimulation, onThemeService, onThemeBroadca
|
|
|
20249
20328
|
});
|
|
20250
20329
|
return {
|
|
20251
20330
|
ready,
|
|
20331
|
+
setWindowBlossomServers: serviceBundle.setWindowBlossomServers,
|
|
20252
20332
|
relayPool: createPajaRelayHooks(relayBackend, getSimulation, relayConfig),
|
|
20253
20333
|
relayConfig,
|
|
20254
20334
|
windowManager: { createWindow: () => null },
|
|
@@ -20341,7 +20421,7 @@ async function resolvePajaPointer(value, options = {}) {
|
|
|
20341
20421
|
const ownsPool = options.pool === void 0;
|
|
20342
20422
|
try {
|
|
20343
20423
|
const event = await resolvePointerEvent(pointer, pool, relays, normalizeMaxWaitMs(options.maxWaitMs));
|
|
20344
|
-
const blossomServers =
|
|
20424
|
+
const blossomServers = resolvePointerBlossomServers(event, options.blossomServers ?? []);
|
|
20345
20425
|
const resolved = await resolveNapplet({
|
|
20346
20426
|
event,
|
|
20347
20427
|
cache: await openNappletArtifactCache({ requireStorageEstimate: false }),
|
|
@@ -20368,6 +20448,20 @@ async function resolvePajaPointer(value, options = {}) {
|
|
|
20368
20448
|
}
|
|
20369
20449
|
}
|
|
20370
20450
|
}
|
|
20451
|
+
function resolvePointerBlossomServers(event, configured) {
|
|
20452
|
+
const servers = [];
|
|
20453
|
+
for (const value of event.tags.flatMap(
|
|
20454
|
+
(tag) => tag[0] === "server" && tag[1] ? [tag[1]] : []
|
|
20455
|
+
)) {
|
|
20456
|
+
const normalized = normalizePublicBlossomServer(value);
|
|
20457
|
+
if (normalized && !servers.includes(normalized)) servers.push(normalized);
|
|
20458
|
+
}
|
|
20459
|
+
for (const value of configured) {
|
|
20460
|
+
const server = normalizePublicBlossomServer(value) ?? value;
|
|
20461
|
+
if (!servers.includes(server)) servers.push(server);
|
|
20462
|
+
}
|
|
20463
|
+
return servers;
|
|
20464
|
+
}
|
|
20371
20465
|
function injectPajaRuntimeCsp(html, origins) {
|
|
20372
20466
|
const grantedOrigins = [...new Set(origins)].sort();
|
|
20373
20467
|
const connectSrc = grantedOrigins.length > 0 ? `connect-src ${grantedOrigins.join(" ")}` : "connect-src 'none'";
|
|
@@ -21793,6 +21887,9 @@ function resolvedTargetKey(target) {
|
|
|
21793
21887
|
function runtimeTabGenerationId(tab) {
|
|
21794
21888
|
return `${tab.id}:${tab.generation}`;
|
|
21795
21889
|
}
|
|
21890
|
+
function bindRuntimeTabBlossomServers(adapter, windowId, resolvedTarget) {
|
|
21891
|
+
adapter.setWindowBlossomServers(windowId, resolvedTarget.blossomServers);
|
|
21892
|
+
}
|
|
21796
21893
|
function getActiveTab(state) {
|
|
21797
21894
|
return state.tabs.find((tab) => tab.id === state.activeTabId) ?? null;
|
|
21798
21895
|
}
|
|
@@ -22070,23 +22167,25 @@ function startRuntimeTabNavigation(tab, state, context) {
|
|
|
22070
22167
|
tab.status = "booting";
|
|
22071
22168
|
context.setStatus(state, "booting");
|
|
22072
22169
|
renderRuntimeTabs(state);
|
|
22170
|
+
const windowId = runtimeTabWindowId(context.config, tab);
|
|
22171
|
+
bindRuntimeTabBlossomServers(context.adapter, windowId, tab.resolvedTarget);
|
|
22073
22172
|
void context.navigateFrame(
|
|
22074
22173
|
tab.frame,
|
|
22075
22174
|
context.config,
|
|
22076
22175
|
generation,
|
|
22077
22176
|
context.adapter,
|
|
22078
22177
|
tab.resolvedTarget,
|
|
22079
|
-
|
|
22178
|
+
windowId,
|
|
22080
22179
|
() => tab.generation === generation,
|
|
22081
|
-
(
|
|
22180
|
+
(windowId2) => {
|
|
22082
22181
|
if (tab.generation !== generation) return;
|
|
22083
|
-
tab.windowId =
|
|
22084
|
-
if (state.activeTabId === tab.id) context.runtime.currentWindowId =
|
|
22182
|
+
tab.windowId = windowId2;
|
|
22183
|
+
if (state.activeTabId === tab.id) context.runtime.currentWindowId = windowId2;
|
|
22085
22184
|
}
|
|
22086
|
-
).then((
|
|
22185
|
+
).then((windowId2) => {
|
|
22087
22186
|
if (tab.generation !== generation) return;
|
|
22088
|
-
tab.windowId =
|
|
22089
|
-
if (state.activeTabId === tab.id) context.runtime.currentWindowId =
|
|
22187
|
+
tab.windowId = windowId2;
|
|
22188
|
+
if (state.activeTabId === tab.id) context.runtime.currentWindowId = windowId2;
|
|
22090
22189
|
}).catch((error) => {
|
|
22091
22190
|
if (tab.generation !== generation) return;
|
|
22092
22191
|
tab.frame.removeAttribute("src");
|