@kehto/paja 0.15.0 → 0.15.2
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 -3
- package/dist/browser-host.js +441 -441
- package/dist/browser-host.js.map +1 -1
- package/dist/{chunk-DTRRHXYY.js → chunk-32QBJDAR.js} +2 -2
- package/dist/chunk-32QBJDAR.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-DTRRHXYY.js.map +0 -1
package/dist/browser-host.js
CHANGED
|
@@ -2447,11 +2447,8 @@ function createFirewallGate(config) {
|
|
|
2447
2447
|
if (decision === "prompt") fireConsent(windowId, napplet);
|
|
2448
2448
|
return "drop";
|
|
2449
2449
|
}
|
|
2450
|
-
const
|
|
2451
|
-
|
|
2452
|
-
if (type) {
|
|
2453
|
-
hooks.sendToNapplet(windowId, { type, id, error: `firewall: ${reason}` });
|
|
2454
|
-
}
|
|
2450
|
+
const denial = createDenialResponse(envelope, `firewall: ${reason}`);
|
|
2451
|
+
if (denial) hooks.sendToNapplet(windowId, denial);
|
|
2455
2452
|
hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope });
|
|
2456
2453
|
if (decision === "prompt") {
|
|
2457
2454
|
fireConsent(windowId, napplet);
|
|
@@ -2473,6 +2470,19 @@ function denialResponseType(envelope) {
|
|
|
2473
2470
|
if (envelope.type.startsWith("inc.")) return null;
|
|
2474
2471
|
return `${envelope.type}.error`;
|
|
2475
2472
|
}
|
|
2473
|
+
function createDenialResponse(envelope, error) {
|
|
2474
|
+
const id = envelope.id ?? "";
|
|
2475
|
+
if (envelope.type === "outbox.query") {
|
|
2476
|
+
return {
|
|
2477
|
+
type: "outbox.query.result",
|
|
2478
|
+
id,
|
|
2479
|
+
events: [],
|
|
2480
|
+
error
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2483
|
+
const type = denialResponseType(envelope);
|
|
2484
|
+
return type ? { type, id, error } : null;
|
|
2485
|
+
}
|
|
2476
2486
|
function createMessageHandler(hooks, sessionRegistry2, enforceNap, dispatchNapEnvelope, firewallGate) {
|
|
2477
2487
|
return (windowId, msg) => {
|
|
2478
2488
|
if (typeof msg !== "object" || msg === null || !("type" in msg)) return;
|
|
@@ -2497,12 +2507,9 @@ function createMessageHandler(hooks, sessionRegistry2, enforceNap, dispatchNapEn
|
|
|
2497
2507
|
hooks.sendToNapplet(windowId, canonicalResult);
|
|
2498
2508
|
return;
|
|
2499
2509
|
}
|
|
2500
|
-
const id = envelope.id ?? "";
|
|
2501
2510
|
const error = formatDenialReason(result2.capability);
|
|
2502
|
-
const
|
|
2503
|
-
if (
|
|
2504
|
-
hooks.sendToNapplet(windowId, { type, id, error });
|
|
2505
|
-
}
|
|
2511
|
+
const denial = createDenialResponse(envelope, error);
|
|
2512
|
+
if (denial) hooks.sendToNapplet(windowId, denial);
|
|
2506
2513
|
return;
|
|
2507
2514
|
}
|
|
2508
2515
|
}
|
|
@@ -19935,97 +19942,442 @@ function forwardPajaHotkey(event) {
|
|
|
19935
19942
|
}));
|
|
19936
19943
|
}
|
|
19937
19944
|
|
|
19938
|
-
// src/
|
|
19939
|
-
var
|
|
19940
|
-
|
|
19941
|
-
|
|
19942
|
-
|
|
19943
|
-
|
|
19944
|
-
|
|
19945
|
-
|
|
19946
|
-
|
|
19947
|
-
|
|
19948
|
-
|
|
19949
|
-
|
|
19945
|
+
// src/runtime-resolver.ts
|
|
19946
|
+
var PAJA_NAPPLET_MANIFEST_KINDS = NAPPLET_KINDS;
|
|
19947
|
+
function decodePajaPointer(value) {
|
|
19948
|
+
const trimmed = value.trim();
|
|
19949
|
+
const decoded = decode(trimmed);
|
|
19950
|
+
if (decoded.type === "naddr") {
|
|
19951
|
+
return {
|
|
19952
|
+
type: "naddr",
|
|
19953
|
+
value: trimmed,
|
|
19954
|
+
identifier: decoded.data.identifier,
|
|
19955
|
+
pubkey: decoded.data.pubkey,
|
|
19956
|
+
kind: decoded.data.kind,
|
|
19957
|
+
relays: [...decoded.data.relays ?? []]
|
|
19958
|
+
};
|
|
19959
|
+
}
|
|
19960
|
+
if (decoded.type === "nevent") {
|
|
19961
|
+
return {
|
|
19962
|
+
type: "nevent",
|
|
19963
|
+
value: trimmed,
|
|
19964
|
+
id: decoded.data.id,
|
|
19965
|
+
...decoded.data.author ? { author: decoded.data.author } : {},
|
|
19966
|
+
...decoded.data.kind ? { kind: decoded.data.kind } : {},
|
|
19967
|
+
relays: [...decoded.data.relays ?? []]
|
|
19968
|
+
};
|
|
19969
|
+
}
|
|
19970
|
+
throw new Error(`Expected naddr or nevent pointer, got ${decoded.type}.`);
|
|
19950
19971
|
}
|
|
19951
|
-
function
|
|
19972
|
+
async function resolvePajaPointer(value, options = {}) {
|
|
19973
|
+
const pointer = decodePajaPointer(value);
|
|
19974
|
+
const relays = uniqueRelayUrls([...pointer.relays, ...options.relays ?? []]);
|
|
19975
|
+
if (relays.length === 0) {
|
|
19976
|
+
throw new Error("Pointer has no relay hints or enabled configured live relays.");
|
|
19977
|
+
}
|
|
19978
|
+
const pool = options.pool ?? new SimplePool();
|
|
19979
|
+
const ownsPool = options.pool === void 0;
|
|
19952
19980
|
try {
|
|
19953
|
-
|
|
19954
|
-
|
|
19955
|
-
|
|
19981
|
+
const event = await resolvePointerEvent(pointer, pool, relays, normalizeMaxWaitMs(options.maxWaitMs));
|
|
19982
|
+
const blossomServers = [...options.blossomServers ?? []];
|
|
19983
|
+
const resolved = await resolveNapplet({
|
|
19984
|
+
event,
|
|
19985
|
+
cache: await openNappletArtifactCache({ requireStorageEstimate: false }),
|
|
19986
|
+
fetchBlob: (sha256Hex, servers) => fetchBlob([...servers, ...blossomServers], sha256Hex, async (url) => {
|
|
19987
|
+
const response = await (options.fetcher ?? fetch)(url);
|
|
19988
|
+
if (!response.ok) throw new Error(`[paja-runtime] blob ${url}: ${response.status}`);
|
|
19989
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
19990
|
+
})
|
|
19991
|
+
});
|
|
19992
|
+
return {
|
|
19993
|
+
pointer,
|
|
19994
|
+
event,
|
|
19995
|
+
relays,
|
|
19996
|
+
blossomServers,
|
|
19997
|
+
dTag: resolved.dTag,
|
|
19998
|
+
aggregateHash: resolved.aggregateHash,
|
|
19999
|
+
indexHtml: resolved.indexHtml,
|
|
20000
|
+
manifest: resolved.manifest
|
|
20001
|
+
};
|
|
20002
|
+
} finally {
|
|
20003
|
+
if (ownsPool) {
|
|
20004
|
+
pool.close?.(relays);
|
|
20005
|
+
pool.destroy?.();
|
|
20006
|
+
}
|
|
19956
20007
|
}
|
|
19957
20008
|
}
|
|
19958
|
-
function
|
|
19959
|
-
const
|
|
19960
|
-
|
|
19961
|
-
|
|
20009
|
+
function injectPajaRuntimeCsp(html, origins) {
|
|
20010
|
+
const grantedOrigins = [...new Set(origins)].sort();
|
|
20011
|
+
const connectSrc = grantedOrigins.length > 0 ? `connect-src ${grantedOrigins.join(" ")}` : "connect-src 'none'";
|
|
20012
|
+
const value = [
|
|
20013
|
+
"default-src 'none'",
|
|
20014
|
+
"script-src 'unsafe-inline' 'wasm-unsafe-eval'",
|
|
20015
|
+
"style-src 'unsafe-inline'",
|
|
20016
|
+
"img-src data: blob:",
|
|
20017
|
+
"font-src data:",
|
|
20018
|
+
connectSrc,
|
|
20019
|
+
"worker-src 'none'",
|
|
20020
|
+
"child-src 'none'",
|
|
20021
|
+
"frame-src 'none'",
|
|
20022
|
+
"media-src 'none'",
|
|
20023
|
+
"object-src 'none'",
|
|
20024
|
+
"manifest-src 'none'",
|
|
20025
|
+
"prefetch-src 'none'",
|
|
20026
|
+
"base-uri 'none'",
|
|
20027
|
+
"form-action 'none'",
|
|
20028
|
+
"frame-ancestors 'self'"
|
|
20029
|
+
].join("; ");
|
|
20030
|
+
const meta = `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(value)}">`;
|
|
20031
|
+
if (/<head[^>]*>/i.test(html)) {
|
|
20032
|
+
return html.replace(/<head[^>]*>/i, (open) => `${open}${meta}`);
|
|
19962
20033
|
}
|
|
19963
|
-
if (
|
|
19964
|
-
|
|
19965
|
-
return JSON.stringify(detail);
|
|
19966
|
-
} catch {
|
|
19967
|
-
return "";
|
|
20034
|
+
if (/<html[^>]*>/i.test(html)) {
|
|
20035
|
+
return html.replace(/<html[^>]*>/i, (open) => `${open}<head>${meta}</head>`);
|
|
19968
20036
|
}
|
|
20037
|
+
return `${meta}${html}`;
|
|
19969
20038
|
}
|
|
19970
|
-
function
|
|
19971
|
-
|
|
19972
|
-
const
|
|
19973
|
-
|
|
19974
|
-
|
|
19975
|
-
if (
|
|
20039
|
+
async function resolvePointerEvent(pointer, pool, relays, maxWaitMs) {
|
|
20040
|
+
const filter = pointer.type === "naddr" ? addressableFilter(pointer) : eventFilter(pointer);
|
|
20041
|
+
const result2 = await queryPointerRelays(pool, relays, filter, maxWaitMs);
|
|
20042
|
+
const event = result2.events.filter((candidate) => matchesPointer(pointer, candidate)).sort((a, b) => b.created_at - a.created_at)[0];
|
|
20043
|
+
if (!event) {
|
|
20044
|
+
if (result2.timedOut) {
|
|
20045
|
+
throw new Error(
|
|
20046
|
+
`Relay resolution timed out after ${maxWaitMs}ms for ${pointer.type} pointer across ${relays.length} relays before complete EOSE.`
|
|
20047
|
+
);
|
|
20048
|
+
}
|
|
20049
|
+
const failures = relayFailureReasons(result2.reasons);
|
|
20050
|
+
if (result2.error || failures.length > 0) {
|
|
20051
|
+
const detail = result2.error instanceof Error ? result2.error.message : result2.error === void 0 ? failures.join("; ") : String(result2.error);
|
|
20052
|
+
throw new Error(`Relay query failed for ${pointer.type} pointer before complete EOSE: ${detail}`);
|
|
20053
|
+
}
|
|
20054
|
+
throw new Error(
|
|
20055
|
+
`All ${relays.length} relays reached EOSE without a matching ${pointer.type} napplet manifest event.`
|
|
20056
|
+
);
|
|
19976
20057
|
}
|
|
19977
|
-
|
|
20058
|
+
if (!isNappletManifestKind(event.kind)) {
|
|
20059
|
+
throw new Error(`Pointer resolved kind ${event.kind}; expected ${formatNappletManifestKinds()}.`);
|
|
20060
|
+
}
|
|
20061
|
+
return event;
|
|
19978
20062
|
}
|
|
19979
|
-
function
|
|
19980
|
-
|
|
20063
|
+
function queryPointerRelays(pool, relays, filter, maxWaitMs) {
|
|
20064
|
+
if (pool.subscribeManyEose) {
|
|
20065
|
+
return queryPointerRelaySubscription(pool, relays, filter, maxWaitMs);
|
|
20066
|
+
}
|
|
20067
|
+
return queryPointerRelaySync(pool, relays, filter, maxWaitMs);
|
|
19981
20068
|
}
|
|
19982
|
-
function
|
|
19983
|
-
return {
|
|
19984
|
-
|
|
19985
|
-
|
|
19986
|
-
|
|
19987
|
-
|
|
20069
|
+
function queryPointerRelaySubscription(pool, relays, filter, maxWaitMs) {
|
|
20070
|
+
return new Promise((resolve) => {
|
|
20071
|
+
const events = [];
|
|
20072
|
+
let settled = false;
|
|
20073
|
+
let subscription;
|
|
20074
|
+
const finish = (result2) => {
|
|
20075
|
+
if (settled) return;
|
|
20076
|
+
settled = true;
|
|
20077
|
+
clearTimeout(deadline);
|
|
20078
|
+
resolve({ events, ...result2 });
|
|
20079
|
+
};
|
|
20080
|
+
const deadline = setTimeout(() => {
|
|
20081
|
+
finish({ reasons: [], timedOut: true });
|
|
20082
|
+
void subscription?.close("Paja pointer relay deadline exceeded");
|
|
20083
|
+
}, maxWaitMs);
|
|
20084
|
+
try {
|
|
20085
|
+
subscription = pool.subscribeManyEose([...relays], filter, {
|
|
20086
|
+
maxWait: maxWaitMs,
|
|
20087
|
+
label: "kehto-paja-runtime",
|
|
20088
|
+
onevent(event) {
|
|
20089
|
+
events.push(event);
|
|
20090
|
+
},
|
|
20091
|
+
onclose(reasons) {
|
|
20092
|
+
finish({ reasons, timedOut: false });
|
|
20093
|
+
}
|
|
20094
|
+
});
|
|
20095
|
+
} catch (error) {
|
|
20096
|
+
finish({ reasons: [], timedOut: false, error });
|
|
20097
|
+
}
|
|
20098
|
+
});
|
|
19988
20099
|
}
|
|
19989
|
-
function
|
|
19990
|
-
|
|
19991
|
-
|
|
19992
|
-
|
|
19993
|
-
|
|
19994
|
-
|
|
19995
|
-
|
|
19996
|
-
|
|
19997
|
-
|
|
19998
|
-
|
|
20100
|
+
function queryPointerRelaySync(pool, relays, filter, maxWaitMs) {
|
|
20101
|
+
return new Promise((resolve) => {
|
|
20102
|
+
let settled = false;
|
|
20103
|
+
const finish = (result2) => {
|
|
20104
|
+
if (settled) return;
|
|
20105
|
+
settled = true;
|
|
20106
|
+
clearTimeout(deadline);
|
|
20107
|
+
resolve(result2);
|
|
20108
|
+
};
|
|
20109
|
+
const deadline = setTimeout(() => {
|
|
20110
|
+
finish({ events: [], reasons: [], timedOut: true });
|
|
20111
|
+
}, maxWaitMs);
|
|
20112
|
+
void pool.querySync([...relays], filter, {
|
|
20113
|
+
maxWait: maxWaitMs,
|
|
20114
|
+
label: "kehto-paja-runtime"
|
|
20115
|
+
}).then(
|
|
20116
|
+
(events) => finish({ events, reasons: [], timedOut: false }),
|
|
20117
|
+
(error) => finish({ events: [], reasons: [], timedOut: false, error })
|
|
20118
|
+
);
|
|
19999
20119
|
});
|
|
20000
|
-
|
|
20001
|
-
|
|
20120
|
+
}
|
|
20121
|
+
function relayFailureReasons(reasons) {
|
|
20122
|
+
return [...new Set(reasons.map((reason) => reason.trim()).filter((reason) => reason.length > 0 && reason !== "closed automatically on eose").map((reason) => reason.slice(0, 160)))].slice(0, 3);
|
|
20123
|
+
}
|
|
20124
|
+
function normalizeMaxWaitMs(value) {
|
|
20125
|
+
if (value === void 0) return 5e3;
|
|
20126
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
20127
|
+
throw new Error("Pointer relay deadline must be a positive number of milliseconds.");
|
|
20002
20128
|
}
|
|
20003
|
-
|
|
20129
|
+
return Math.floor(value);
|
|
20004
20130
|
}
|
|
20005
|
-
function
|
|
20006
|
-
|
|
20007
|
-
|
|
20008
|
-
proxyContexts.set(existing, { state, windowId });
|
|
20009
|
-
return existing;
|
|
20131
|
+
function addressableFilter(pointer) {
|
|
20132
|
+
if (pointer.kind !== NAPPLET_KIND_NAMED) {
|
|
20133
|
+
throw new Error(`naddr kind ${pointer.kind} is not a named NIP-5D napplet manifest kind.`);
|
|
20010
20134
|
}
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
|
|
20014
|
-
|
|
20015
|
-
|
|
20016
|
-
|
|
20017
|
-
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
|
|
20024
|
-
|
|
20025
|
-
|
|
20026
|
-
|
|
20027
|
-
|
|
20028
|
-
|
|
20135
|
+
return {
|
|
20136
|
+
kinds: [NAPPLET_KIND_NAMED],
|
|
20137
|
+
authors: [pointer.pubkey],
|
|
20138
|
+
"#d": [pointer.identifier],
|
|
20139
|
+
limit: 1
|
|
20140
|
+
};
|
|
20141
|
+
}
|
|
20142
|
+
function eventFilter(pointer) {
|
|
20143
|
+
return {
|
|
20144
|
+
ids: [pointer.id],
|
|
20145
|
+
...pointer.kind ? { kinds: [pointer.kind] } : {},
|
|
20146
|
+
...pointer.author ? { authors: [pointer.author] } : {},
|
|
20147
|
+
limit: 1
|
|
20148
|
+
};
|
|
20149
|
+
}
|
|
20150
|
+
function matchesPointer(pointer, event) {
|
|
20151
|
+
if (pointer.type === "nevent") {
|
|
20152
|
+
if (event.id !== pointer.id) return false;
|
|
20153
|
+
if (pointer.author && event.pubkey !== pointer.author) return false;
|
|
20154
|
+
if (pointer.kind && event.kind !== pointer.kind) return false;
|
|
20155
|
+
return true;
|
|
20156
|
+
}
|
|
20157
|
+
return event.kind === pointer.kind && event.pubkey === pointer.pubkey && event.tags.some((tag) => tag[0] === "d" && tag[1] === pointer.identifier);
|
|
20158
|
+
}
|
|
20159
|
+
function uniqueRelayUrls(values) {
|
|
20160
|
+
const out = [];
|
|
20161
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20162
|
+
for (const value of values) {
|
|
20163
|
+
const url = normalizeRelayUrl2(value);
|
|
20164
|
+
if (!url || seen.has(url)) continue;
|
|
20165
|
+
seen.add(url);
|
|
20166
|
+
out.push(url);
|
|
20167
|
+
}
|
|
20168
|
+
return out;
|
|
20169
|
+
}
|
|
20170
|
+
function normalizeRelayUrl2(value) {
|
|
20171
|
+
const raw = value.trim();
|
|
20172
|
+
if (!raw) return void 0;
|
|
20173
|
+
try {
|
|
20174
|
+
const url = new URL(raw);
|
|
20175
|
+
if (url.protocol !== "wss:" && url.protocol !== "ws:") return void 0;
|
|
20176
|
+
url.hash = "";
|
|
20177
|
+
url.search = "";
|
|
20178
|
+
return url.href.replace(/\/$/, "");
|
|
20179
|
+
} catch {
|
|
20180
|
+
return void 0;
|
|
20181
|
+
}
|
|
20182
|
+
}
|
|
20183
|
+
function escapeAttribute(value) {
|
|
20184
|
+
return value.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
20185
|
+
}
|
|
20186
|
+
function formatNappletManifestKinds() {
|
|
20187
|
+
return PAJA_NAPPLET_MANIFEST_KINDS.join(" / ");
|
|
20188
|
+
}
|
|
20189
|
+
|
|
20190
|
+
// src/browser-target-frame.ts
|
|
20191
|
+
function resolvePajaFrameEnvironment(hooks, identity) {
|
|
20192
|
+
if (!identity.dTag || !identity.aggregateHash) {
|
|
20193
|
+
throw new Error("Paja frame identity requires a dTag and aggregate hash");
|
|
20194
|
+
}
|
|
20195
|
+
return resolveShellEnvironment(hooks, identity);
|
|
20196
|
+
}
|
|
20197
|
+
function getTargetIdentity(config, resolvedTarget) {
|
|
20198
|
+
return {
|
|
20199
|
+
pubkey: "",
|
|
20200
|
+
dTag: resolvedTarget?.dTag ?? config.window.dTag,
|
|
20201
|
+
aggregateHash: resolvedTarget?.aggregateHash ?? config.window.aggregateHash
|
|
20202
|
+
};
|
|
20203
|
+
}
|
|
20204
|
+
function getTargetOriginIdentity(config, resolvedTarget) {
|
|
20205
|
+
const target = getTargetIdentity(config, resolvedTarget);
|
|
20206
|
+
return Object.freeze({ dTag: target.dTag, aggregateHash: target.aggregateHash });
|
|
20207
|
+
}
|
|
20208
|
+
function registerFrameForGeneration(frame, config, generation, identity, environment, windowId = `${config.window.id}:${generation}`) {
|
|
20209
|
+
const win = frame.contentWindow;
|
|
20210
|
+
if (!win) return null;
|
|
20211
|
+
originRegistry.register(win, windowId, identity);
|
|
20212
|
+
originRegistry.setEnvironment(win, environment);
|
|
20213
|
+
return windowId;
|
|
20214
|
+
}
|
|
20215
|
+
async function navigateFrame(frame, config, generation, adapter, resolvedTarget, windowId, isCurrent, onRegistered) {
|
|
20216
|
+
const identity = getTargetOriginIdentity(config, resolvedTarget);
|
|
20217
|
+
const environment = resolvePajaFrameEnvironment(adapter, identity);
|
|
20218
|
+
const domains = environment.capabilities.domains;
|
|
20219
|
+
if (config.target.mode === "runtime-pointer") {
|
|
20220
|
+
if (!resolvedTarget) {
|
|
20221
|
+
frame.removeAttribute("src");
|
|
20222
|
+
frame.srcdoc = "<!doctype html><html><body></body></html>";
|
|
20223
|
+
return null;
|
|
20224
|
+
}
|
|
20225
|
+
if (isCurrent && !isCurrent()) return null;
|
|
20226
|
+
const registeredWindowId2 = registerFrameForGeneration(frame, config, generation, identity, environment, windowId);
|
|
20227
|
+
onRegistered?.(registeredWindowId2);
|
|
20228
|
+
frame.removeAttribute("src");
|
|
20229
|
+
frame.srcdoc = injectNappletNamespacePrelude(
|
|
20230
|
+
injectPajaRuntimeCsp(
|
|
20231
|
+
resolvedTarget.indexHtml,
|
|
20232
|
+
connectOrigins([...resolvedTarget.relays, ...resolvedTarget.blossomServers])
|
|
20233
|
+
),
|
|
20234
|
+
{ domains }
|
|
20235
|
+
);
|
|
20236
|
+
return registeredWindowId2;
|
|
20237
|
+
}
|
|
20238
|
+
const html = await fetchTargetHtml();
|
|
20239
|
+
if (isCurrent && !isCurrent()) return null;
|
|
20240
|
+
const registeredWindowId = registerFrameForGeneration(frame, config, generation, identity, environment, windowId);
|
|
20241
|
+
onRegistered?.(registeredWindowId);
|
|
20242
|
+
frame.removeAttribute("src");
|
|
20243
|
+
frame.srcdoc = injectNappletNamespacePrelude(
|
|
20244
|
+
injectBaseHref(html, config.target.url),
|
|
20245
|
+
{ domains }
|
|
20246
|
+
);
|
|
20247
|
+
return registeredWindowId;
|
|
20248
|
+
}
|
|
20249
|
+
function renderTargetErrorHtml(error) {
|
|
20250
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20251
|
+
return `<!doctype html><html><body><pre>${escapeHtml(message)}</pre></body></html>`;
|
|
20252
|
+
}
|
|
20253
|
+
function connectOrigins(urls) {
|
|
20254
|
+
const out = /* @__PURE__ */ new Set();
|
|
20255
|
+
for (const value of urls) {
|
|
20256
|
+
try {
|
|
20257
|
+
const url = new URL(value);
|
|
20258
|
+
if (url.protocol === "wss:" || url.protocol === "ws:") {
|
|
20259
|
+
out.add(value.replace(/\/$/, ""));
|
|
20260
|
+
} else {
|
|
20261
|
+
out.add(url.origin);
|
|
20262
|
+
}
|
|
20263
|
+
} catch {
|
|
20264
|
+
}
|
|
20265
|
+
}
|
|
20266
|
+
return [...out];
|
|
20267
|
+
}
|
|
20268
|
+
async function fetchTargetHtml() {
|
|
20269
|
+
const response = await fetch(new URL("./__kehto/target.html", window.location.href), {
|
|
20270
|
+
cache: "no-store",
|
|
20271
|
+
headers: {
|
|
20272
|
+
accept: "text/html, application/xhtml+xml;q=0.9, */*;q=0.8"
|
|
20273
|
+
}
|
|
20274
|
+
});
|
|
20275
|
+
if (!response.ok) {
|
|
20276
|
+
throw new Error(`Paja target fetch failed: ${response.status} ${response.statusText}`);
|
|
20277
|
+
}
|
|
20278
|
+
return response.text();
|
|
20279
|
+
}
|
|
20280
|
+
function injectBaseHref(html, targetUrl) {
|
|
20281
|
+
const base = `<base href="${escapeAttribute2(targetUrl)}">`;
|
|
20282
|
+
if (/<head[^>]*>/i.test(html)) {
|
|
20283
|
+
return html.replace(/<head[^>]*>/i, (open) => `${open}${base}`);
|
|
20284
|
+
}
|
|
20285
|
+
if (/<html[^>]*>/i.test(html)) {
|
|
20286
|
+
return html.replace(/<html[^>]*>/i, (open) => `${open}<head>${base}</head>`);
|
|
20287
|
+
}
|
|
20288
|
+
return `${base}${html}`;
|
|
20289
|
+
}
|
|
20290
|
+
function escapeAttribute2(value) {
|
|
20291
|
+
return escapeHtml(value).replaceAll('"', """);
|
|
20292
|
+
}
|
|
20293
|
+
function escapeHtml(value) {
|
|
20294
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
20295
|
+
}
|
|
20296
|
+
|
|
20297
|
+
// src/browser-devtools.ts
|
|
20298
|
+
var PAJA_LOG_LIMIT = 500;
|
|
20299
|
+
var proxyToReal = /* @__PURE__ */ new WeakMap();
|
|
20300
|
+
var realToProxy = /* @__PURE__ */ new WeakMap();
|
|
20301
|
+
var proxyContexts = /* @__PURE__ */ new WeakMap();
|
|
20302
|
+
function isNappletMessage(value) {
|
|
20303
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string";
|
|
20304
|
+
}
|
|
20305
|
+
function describeMessage(raw) {
|
|
20306
|
+
if (isNappletMessage(raw)) return raw.type;
|
|
20307
|
+
if (Array.isArray(raw) && typeof raw[0] === "string") return raw[0];
|
|
20308
|
+
return "unknown";
|
|
20309
|
+
}
|
|
20310
|
+
function previewMessage(raw) {
|
|
20311
|
+
try {
|
|
20312
|
+
return JSON.stringify(raw);
|
|
20313
|
+
} catch (error) {
|
|
20314
|
+
return `unserializable:${error instanceof Error ? error.message : String(error)}`;
|
|
20315
|
+
}
|
|
20316
|
+
}
|
|
20317
|
+
function compactMessageDetail(raw) {
|
|
20318
|
+
const detail = {};
|
|
20319
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
20320
|
+
if (key !== "type" && key !== "id" && value !== void 0) detail[key] = value;
|
|
20321
|
+
}
|
|
20322
|
+
if (Object.keys(detail).length === 0) return "";
|
|
20323
|
+
try {
|
|
20324
|
+
return JSON.stringify(detail);
|
|
20325
|
+
} catch {
|
|
20326
|
+
return "";
|
|
20327
|
+
}
|
|
20328
|
+
}
|
|
20329
|
+
function describeMessageDetail(raw) {
|
|
20330
|
+
if (!isNappletMessage(raw)) return "";
|
|
20331
|
+
const value = raw;
|
|
20332
|
+
for (const key of ["error", "message", "reason"]) {
|
|
20333
|
+
const detail = value[key];
|
|
20334
|
+
if (typeof detail === "string" && detail.length > 0) return detail;
|
|
20335
|
+
}
|
|
20336
|
+
return raw.type.endsWith(".error") ? compactMessageDetail(value) : "";
|
|
20337
|
+
}
|
|
20338
|
+
function domainEnabled(simulation, domain) {
|
|
20339
|
+
return simulation.capabilities.domains[domain] === true;
|
|
20340
|
+
}
|
|
20341
|
+
function appendPajaMessageLog(state, direction, raw, windowId) {
|
|
20342
|
+
if (!state) return;
|
|
20343
|
+
state.messageLog.push({
|
|
20344
|
+
index: state.messageLog.length,
|
|
20345
|
+
timestamp: Date.now(),
|
|
20346
|
+
direction,
|
|
20347
|
+
type: describeMessage(raw),
|
|
20348
|
+
windowId,
|
|
20349
|
+
preview: previewMessage(raw),
|
|
20350
|
+
detail: describeMessageDetail(raw)
|
|
20351
|
+
});
|
|
20352
|
+
if (state.messageLog.length > PAJA_LOG_LIMIT) {
|
|
20353
|
+
state.messageLog.splice(0, state.messageLog.length - PAJA_LOG_LIMIT);
|
|
20354
|
+
}
|
|
20355
|
+
renderPajaMessageLog(state);
|
|
20356
|
+
}
|
|
20357
|
+
function createPajaPostMessageProxy(realWin, state, windowId) {
|
|
20358
|
+
const existing = realToProxy.get(realWin);
|
|
20359
|
+
if (existing) {
|
|
20360
|
+
proxyContexts.set(existing, { state, windowId });
|
|
20361
|
+
return existing;
|
|
20362
|
+
}
|
|
20363
|
+
const proxy = new Proxy(realWin, {
|
|
20364
|
+
get(target, prop) {
|
|
20365
|
+
if (prop === "postMessage") {
|
|
20366
|
+
return (msg, targetOrigin, transfer) => {
|
|
20367
|
+
const context = proxyContexts.get(proxy);
|
|
20368
|
+
appendPajaMessageLog(
|
|
20369
|
+
context?.state ?? state,
|
|
20370
|
+
"shell->napplet",
|
|
20371
|
+
redactPajaMessageForLog(msg),
|
|
20372
|
+
context?.windowId ?? windowId
|
|
20373
|
+
);
|
|
20374
|
+
return target.postMessage(msg, targetOrigin, transfer);
|
|
20375
|
+
};
|
|
20376
|
+
}
|
|
20377
|
+
try {
|
|
20378
|
+
const val = Reflect.get(target, prop, target);
|
|
20379
|
+
return typeof val === "function" ? val.bind(target) : val;
|
|
20380
|
+
} catch {
|
|
20029
20381
|
return void 0;
|
|
20030
20382
|
}
|
|
20031
20383
|
}
|
|
@@ -20134,7 +20486,7 @@ function renderInterfaceControls(state) {
|
|
|
20134
20486
|
function renderAclControls(state, bridge) {
|
|
20135
20487
|
const container = document.getElementById("acl-controls");
|
|
20136
20488
|
if (!container) return;
|
|
20137
|
-
const identity = getTargetIdentity(state.config);
|
|
20489
|
+
const identity = getTargetIdentity(state.config, state.resolvedTarget);
|
|
20138
20490
|
container.replaceChildren(...ALL_CAPABILITIES.map((capability) => {
|
|
20139
20491
|
const allowed2 = bridge?.runtime.aclState.check(
|
|
20140
20492
|
identity.pubkey,
|
|
@@ -21391,251 +21743,6 @@ function runtimeTabWindowId(config, tab) {
|
|
|
21391
21743
|
return `${config.window.id}:${tab.id}:${tab.generation}`;
|
|
21392
21744
|
}
|
|
21393
21745
|
|
|
21394
|
-
// src/runtime-resolver.ts
|
|
21395
|
-
var PAJA_NAPPLET_MANIFEST_KINDS = NAPPLET_KINDS;
|
|
21396
|
-
function decodePajaPointer(value) {
|
|
21397
|
-
const trimmed = value.trim();
|
|
21398
|
-
const decoded = decode(trimmed);
|
|
21399
|
-
if (decoded.type === "naddr") {
|
|
21400
|
-
return {
|
|
21401
|
-
type: "naddr",
|
|
21402
|
-
value: trimmed,
|
|
21403
|
-
identifier: decoded.data.identifier,
|
|
21404
|
-
pubkey: decoded.data.pubkey,
|
|
21405
|
-
kind: decoded.data.kind,
|
|
21406
|
-
relays: [...decoded.data.relays ?? []]
|
|
21407
|
-
};
|
|
21408
|
-
}
|
|
21409
|
-
if (decoded.type === "nevent") {
|
|
21410
|
-
return {
|
|
21411
|
-
type: "nevent",
|
|
21412
|
-
value: trimmed,
|
|
21413
|
-
id: decoded.data.id,
|
|
21414
|
-
...decoded.data.author ? { author: decoded.data.author } : {},
|
|
21415
|
-
...decoded.data.kind ? { kind: decoded.data.kind } : {},
|
|
21416
|
-
relays: [...decoded.data.relays ?? []]
|
|
21417
|
-
};
|
|
21418
|
-
}
|
|
21419
|
-
throw new Error(`Expected naddr or nevent pointer, got ${decoded.type}.`);
|
|
21420
|
-
}
|
|
21421
|
-
async function resolvePajaPointer(value, options = {}) {
|
|
21422
|
-
const pointer = decodePajaPointer(value);
|
|
21423
|
-
const relays = uniqueRelayUrls([...pointer.relays, ...options.relays ?? []]);
|
|
21424
|
-
if (relays.length === 0) {
|
|
21425
|
-
throw new Error("Pointer has no relay hints or enabled configured live relays.");
|
|
21426
|
-
}
|
|
21427
|
-
const pool = options.pool ?? new SimplePool();
|
|
21428
|
-
const ownsPool = options.pool === void 0;
|
|
21429
|
-
try {
|
|
21430
|
-
const event = await resolvePointerEvent(pointer, pool, relays, normalizeMaxWaitMs(options.maxWaitMs));
|
|
21431
|
-
const blossomServers = [...options.blossomServers ?? []];
|
|
21432
|
-
const resolved = await resolveNapplet({
|
|
21433
|
-
event,
|
|
21434
|
-
cache: await openNappletArtifactCache({ requireStorageEstimate: false }),
|
|
21435
|
-
fetchBlob: (sha256Hex, servers) => fetchBlob([...servers, ...blossomServers], sha256Hex, async (url) => {
|
|
21436
|
-
const response = await (options.fetcher ?? fetch)(url);
|
|
21437
|
-
if (!response.ok) throw new Error(`[paja-runtime] blob ${url}: ${response.status}`);
|
|
21438
|
-
return new Uint8Array(await response.arrayBuffer());
|
|
21439
|
-
})
|
|
21440
|
-
});
|
|
21441
|
-
return {
|
|
21442
|
-
pointer,
|
|
21443
|
-
event,
|
|
21444
|
-
relays,
|
|
21445
|
-
blossomServers,
|
|
21446
|
-
dTag: resolved.dTag,
|
|
21447
|
-
aggregateHash: resolved.aggregateHash,
|
|
21448
|
-
indexHtml: resolved.indexHtml,
|
|
21449
|
-
manifest: resolved.manifest
|
|
21450
|
-
};
|
|
21451
|
-
} finally {
|
|
21452
|
-
if (ownsPool) {
|
|
21453
|
-
pool.close?.(relays);
|
|
21454
|
-
pool.destroy?.();
|
|
21455
|
-
}
|
|
21456
|
-
}
|
|
21457
|
-
}
|
|
21458
|
-
function injectPajaRuntimeCsp(html, origins) {
|
|
21459
|
-
const grantedOrigins = [...new Set(origins)].sort();
|
|
21460
|
-
const connectSrc = grantedOrigins.length > 0 ? `connect-src ${grantedOrigins.join(" ")}` : "connect-src 'none'";
|
|
21461
|
-
const value = [
|
|
21462
|
-
"default-src 'none'",
|
|
21463
|
-
"script-src 'unsafe-inline'",
|
|
21464
|
-
"style-src 'unsafe-inline'",
|
|
21465
|
-
"img-src data: blob:",
|
|
21466
|
-
"font-src data:",
|
|
21467
|
-
connectSrc,
|
|
21468
|
-
"worker-src 'none'",
|
|
21469
|
-
"child-src 'none'",
|
|
21470
|
-
"frame-src 'none'",
|
|
21471
|
-
"media-src 'none'",
|
|
21472
|
-
"object-src 'none'",
|
|
21473
|
-
"manifest-src 'none'",
|
|
21474
|
-
"prefetch-src 'none'",
|
|
21475
|
-
"base-uri 'none'",
|
|
21476
|
-
"form-action 'none'",
|
|
21477
|
-
"frame-ancestors 'self'"
|
|
21478
|
-
].join("; ");
|
|
21479
|
-
const meta = `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(value)}">`;
|
|
21480
|
-
if (/<head[^>]*>/i.test(html)) {
|
|
21481
|
-
return html.replace(/<head[^>]*>/i, (open) => `${open}${meta}`);
|
|
21482
|
-
}
|
|
21483
|
-
if (/<html[^>]*>/i.test(html)) {
|
|
21484
|
-
return html.replace(/<html[^>]*>/i, (open) => `${open}<head>${meta}</head>`);
|
|
21485
|
-
}
|
|
21486
|
-
return `${meta}${html}`;
|
|
21487
|
-
}
|
|
21488
|
-
async function resolvePointerEvent(pointer, pool, relays, maxWaitMs) {
|
|
21489
|
-
const filter = pointer.type === "naddr" ? addressableFilter(pointer) : eventFilter(pointer);
|
|
21490
|
-
const result2 = await queryPointerRelays(pool, relays, filter, maxWaitMs);
|
|
21491
|
-
const event = result2.events.filter((candidate) => matchesPointer(pointer, candidate)).sort((a, b) => b.created_at - a.created_at)[0];
|
|
21492
|
-
if (!event) {
|
|
21493
|
-
if (result2.timedOut) {
|
|
21494
|
-
throw new Error(
|
|
21495
|
-
`Relay resolution timed out after ${maxWaitMs}ms for ${pointer.type} pointer across ${relays.length} relays before complete EOSE.`
|
|
21496
|
-
);
|
|
21497
|
-
}
|
|
21498
|
-
const failures = relayFailureReasons(result2.reasons);
|
|
21499
|
-
if (result2.error || failures.length > 0) {
|
|
21500
|
-
const detail = result2.error instanceof Error ? result2.error.message : result2.error === void 0 ? failures.join("; ") : String(result2.error);
|
|
21501
|
-
throw new Error(`Relay query failed for ${pointer.type} pointer before complete EOSE: ${detail}`);
|
|
21502
|
-
}
|
|
21503
|
-
throw new Error(
|
|
21504
|
-
`All ${relays.length} relays reached EOSE without a matching ${pointer.type} napplet manifest event.`
|
|
21505
|
-
);
|
|
21506
|
-
}
|
|
21507
|
-
if (!isNappletManifestKind(event.kind)) {
|
|
21508
|
-
throw new Error(`Pointer resolved kind ${event.kind}; expected ${formatNappletManifestKinds()}.`);
|
|
21509
|
-
}
|
|
21510
|
-
return event;
|
|
21511
|
-
}
|
|
21512
|
-
function queryPointerRelays(pool, relays, filter, maxWaitMs) {
|
|
21513
|
-
if (pool.subscribeManyEose) {
|
|
21514
|
-
return queryPointerRelaySubscription(pool, relays, filter, maxWaitMs);
|
|
21515
|
-
}
|
|
21516
|
-
return queryPointerRelaySync(pool, relays, filter, maxWaitMs);
|
|
21517
|
-
}
|
|
21518
|
-
function queryPointerRelaySubscription(pool, relays, filter, maxWaitMs) {
|
|
21519
|
-
return new Promise((resolve) => {
|
|
21520
|
-
const events = [];
|
|
21521
|
-
let settled = false;
|
|
21522
|
-
let subscription;
|
|
21523
|
-
const finish = (result2) => {
|
|
21524
|
-
if (settled) return;
|
|
21525
|
-
settled = true;
|
|
21526
|
-
clearTimeout(deadline);
|
|
21527
|
-
resolve({ events, ...result2 });
|
|
21528
|
-
};
|
|
21529
|
-
const deadline = setTimeout(() => {
|
|
21530
|
-
finish({ reasons: [], timedOut: true });
|
|
21531
|
-
void subscription?.close("Paja pointer relay deadline exceeded");
|
|
21532
|
-
}, maxWaitMs);
|
|
21533
|
-
try {
|
|
21534
|
-
subscription = pool.subscribeManyEose([...relays], filter, {
|
|
21535
|
-
maxWait: maxWaitMs,
|
|
21536
|
-
label: "kehto-paja-runtime",
|
|
21537
|
-
onevent(event) {
|
|
21538
|
-
events.push(event);
|
|
21539
|
-
},
|
|
21540
|
-
onclose(reasons) {
|
|
21541
|
-
finish({ reasons, timedOut: false });
|
|
21542
|
-
}
|
|
21543
|
-
});
|
|
21544
|
-
} catch (error) {
|
|
21545
|
-
finish({ reasons: [], timedOut: false, error });
|
|
21546
|
-
}
|
|
21547
|
-
});
|
|
21548
|
-
}
|
|
21549
|
-
function queryPointerRelaySync(pool, relays, filter, maxWaitMs) {
|
|
21550
|
-
return new Promise((resolve) => {
|
|
21551
|
-
let settled = false;
|
|
21552
|
-
const finish = (result2) => {
|
|
21553
|
-
if (settled) return;
|
|
21554
|
-
settled = true;
|
|
21555
|
-
clearTimeout(deadline);
|
|
21556
|
-
resolve(result2);
|
|
21557
|
-
};
|
|
21558
|
-
const deadline = setTimeout(() => {
|
|
21559
|
-
finish({ events: [], reasons: [], timedOut: true });
|
|
21560
|
-
}, maxWaitMs);
|
|
21561
|
-
void pool.querySync([...relays], filter, {
|
|
21562
|
-
maxWait: maxWaitMs,
|
|
21563
|
-
label: "kehto-paja-runtime"
|
|
21564
|
-
}).then(
|
|
21565
|
-
(events) => finish({ events, reasons: [], timedOut: false }),
|
|
21566
|
-
(error) => finish({ events: [], reasons: [], timedOut: false, error })
|
|
21567
|
-
);
|
|
21568
|
-
});
|
|
21569
|
-
}
|
|
21570
|
-
function relayFailureReasons(reasons) {
|
|
21571
|
-
return [...new Set(reasons.map((reason) => reason.trim()).filter((reason) => reason.length > 0 && reason !== "closed automatically on eose").map((reason) => reason.slice(0, 160)))].slice(0, 3);
|
|
21572
|
-
}
|
|
21573
|
-
function normalizeMaxWaitMs(value) {
|
|
21574
|
-
if (value === void 0) return 5e3;
|
|
21575
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
21576
|
-
throw new Error("Pointer relay deadline must be a positive number of milliseconds.");
|
|
21577
|
-
}
|
|
21578
|
-
return Math.floor(value);
|
|
21579
|
-
}
|
|
21580
|
-
function addressableFilter(pointer) {
|
|
21581
|
-
if (pointer.kind !== NAPPLET_KIND_NAMED) {
|
|
21582
|
-
throw new Error(`naddr kind ${pointer.kind} is not a named NIP-5D napplet manifest kind.`);
|
|
21583
|
-
}
|
|
21584
|
-
return {
|
|
21585
|
-
kinds: [NAPPLET_KIND_NAMED],
|
|
21586
|
-
authors: [pointer.pubkey],
|
|
21587
|
-
"#d": [pointer.identifier],
|
|
21588
|
-
limit: 1
|
|
21589
|
-
};
|
|
21590
|
-
}
|
|
21591
|
-
function eventFilter(pointer) {
|
|
21592
|
-
return {
|
|
21593
|
-
ids: [pointer.id],
|
|
21594
|
-
...pointer.kind ? { kinds: [pointer.kind] } : {},
|
|
21595
|
-
...pointer.author ? { authors: [pointer.author] } : {},
|
|
21596
|
-
limit: 1
|
|
21597
|
-
};
|
|
21598
|
-
}
|
|
21599
|
-
function matchesPointer(pointer, event) {
|
|
21600
|
-
if (pointer.type === "nevent") {
|
|
21601
|
-
if (event.id !== pointer.id) return false;
|
|
21602
|
-
if (pointer.author && event.pubkey !== pointer.author) return false;
|
|
21603
|
-
if (pointer.kind && event.kind !== pointer.kind) return false;
|
|
21604
|
-
return true;
|
|
21605
|
-
}
|
|
21606
|
-
return event.kind === pointer.kind && event.pubkey === pointer.pubkey && event.tags.some((tag) => tag[0] === "d" && tag[1] === pointer.identifier);
|
|
21607
|
-
}
|
|
21608
|
-
function uniqueRelayUrls(values) {
|
|
21609
|
-
const out = [];
|
|
21610
|
-
const seen = /* @__PURE__ */ new Set();
|
|
21611
|
-
for (const value of values) {
|
|
21612
|
-
const url = normalizeRelayUrl2(value);
|
|
21613
|
-
if (!url || seen.has(url)) continue;
|
|
21614
|
-
seen.add(url);
|
|
21615
|
-
out.push(url);
|
|
21616
|
-
}
|
|
21617
|
-
return out;
|
|
21618
|
-
}
|
|
21619
|
-
function normalizeRelayUrl2(value) {
|
|
21620
|
-
const raw = value.trim();
|
|
21621
|
-
if (!raw) return void 0;
|
|
21622
|
-
try {
|
|
21623
|
-
const url = new URL(raw);
|
|
21624
|
-
if (url.protocol !== "wss:" && url.protocol !== "ws:") return void 0;
|
|
21625
|
-
url.hash = "";
|
|
21626
|
-
url.search = "";
|
|
21627
|
-
return url.href.replace(/\/$/, "");
|
|
21628
|
-
} catch {
|
|
21629
|
-
return void 0;
|
|
21630
|
-
}
|
|
21631
|
-
}
|
|
21632
|
-
function escapeAttribute(value) {
|
|
21633
|
-
return value.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
21634
|
-
}
|
|
21635
|
-
function formatNappletManifestKinds() {
|
|
21636
|
-
return PAJA_NAPPLET_MANIFEST_KINDS.join(" / ");
|
|
21637
|
-
}
|
|
21638
|
-
|
|
21639
21746
|
// src/browser-intent-host.ts
|
|
21640
21747
|
function createPajaIntentTargetOptions(getState, getContext, effects = {}) {
|
|
21641
21748
|
return {
|
|
@@ -21815,113 +21922,6 @@ function createPajaThemeBroadcastLink() {
|
|
|
21815
21922
|
};
|
|
21816
21923
|
}
|
|
21817
21924
|
|
|
21818
|
-
// src/browser-target-frame.ts
|
|
21819
|
-
function resolvePajaFrameEnvironment(hooks, identity) {
|
|
21820
|
-
if (!identity.dTag || !identity.aggregateHash) {
|
|
21821
|
-
throw new Error("Paja frame identity requires a dTag and aggregate hash");
|
|
21822
|
-
}
|
|
21823
|
-
return resolveShellEnvironment(hooks, identity);
|
|
21824
|
-
}
|
|
21825
|
-
function getTargetIdentity2(config, resolvedTarget) {
|
|
21826
|
-
return {
|
|
21827
|
-
pubkey: "",
|
|
21828
|
-
dTag: resolvedTarget?.dTag ?? config.window.dTag,
|
|
21829
|
-
aggregateHash: resolvedTarget?.aggregateHash ?? config.window.aggregateHash
|
|
21830
|
-
};
|
|
21831
|
-
}
|
|
21832
|
-
function getTargetOriginIdentity(config, resolvedTarget) {
|
|
21833
|
-
const target = getTargetIdentity2(config, resolvedTarget);
|
|
21834
|
-
return Object.freeze({ dTag: target.dTag, aggregateHash: target.aggregateHash });
|
|
21835
|
-
}
|
|
21836
|
-
function registerFrameForGeneration(frame, config, generation, identity, environment, windowId = `${config.window.id}:${generation}`) {
|
|
21837
|
-
const win = frame.contentWindow;
|
|
21838
|
-
if (!win) return null;
|
|
21839
|
-
originRegistry.register(win, windowId, identity);
|
|
21840
|
-
originRegistry.setEnvironment(win, environment);
|
|
21841
|
-
return windowId;
|
|
21842
|
-
}
|
|
21843
|
-
async function navigateFrame(frame, config, generation, adapter, resolvedTarget, windowId, isCurrent, onRegistered) {
|
|
21844
|
-
const identity = getTargetOriginIdentity(config, resolvedTarget);
|
|
21845
|
-
const environment = resolvePajaFrameEnvironment(adapter, identity);
|
|
21846
|
-
const domains = environment.capabilities.domains;
|
|
21847
|
-
if (config.target.mode === "runtime-pointer") {
|
|
21848
|
-
if (!resolvedTarget) {
|
|
21849
|
-
frame.removeAttribute("src");
|
|
21850
|
-
frame.srcdoc = "<!doctype html><html><body></body></html>";
|
|
21851
|
-
return null;
|
|
21852
|
-
}
|
|
21853
|
-
if (isCurrent && !isCurrent()) return null;
|
|
21854
|
-
const registeredWindowId2 = registerFrameForGeneration(frame, config, generation, identity, environment, windowId);
|
|
21855
|
-
onRegistered?.(registeredWindowId2);
|
|
21856
|
-
frame.removeAttribute("src");
|
|
21857
|
-
frame.srcdoc = injectNappletNamespacePrelude(
|
|
21858
|
-
injectPajaRuntimeCsp(
|
|
21859
|
-
resolvedTarget.indexHtml,
|
|
21860
|
-
connectOrigins([...resolvedTarget.relays, ...resolvedTarget.blossomServers])
|
|
21861
|
-
),
|
|
21862
|
-
{ domains }
|
|
21863
|
-
);
|
|
21864
|
-
return registeredWindowId2;
|
|
21865
|
-
}
|
|
21866
|
-
const html = await fetchTargetHtml();
|
|
21867
|
-
if (isCurrent && !isCurrent()) return null;
|
|
21868
|
-
const registeredWindowId = registerFrameForGeneration(frame, config, generation, identity, environment, windowId);
|
|
21869
|
-
onRegistered?.(registeredWindowId);
|
|
21870
|
-
frame.removeAttribute("src");
|
|
21871
|
-
frame.srcdoc = injectNappletNamespacePrelude(
|
|
21872
|
-
injectBaseHref(html, config.target.url),
|
|
21873
|
-
{ domains }
|
|
21874
|
-
);
|
|
21875
|
-
return registeredWindowId;
|
|
21876
|
-
}
|
|
21877
|
-
function renderTargetErrorHtml(error) {
|
|
21878
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
21879
|
-
return `<!doctype html><html><body><pre>${escapeHtml(message)}</pre></body></html>`;
|
|
21880
|
-
}
|
|
21881
|
-
function connectOrigins(urls) {
|
|
21882
|
-
const out = /* @__PURE__ */ new Set();
|
|
21883
|
-
for (const value of urls) {
|
|
21884
|
-
try {
|
|
21885
|
-
const url = new URL(value);
|
|
21886
|
-
if (url.protocol === "wss:" || url.protocol === "ws:") {
|
|
21887
|
-
out.add(value.replace(/\/$/, ""));
|
|
21888
|
-
} else {
|
|
21889
|
-
out.add(url.origin);
|
|
21890
|
-
}
|
|
21891
|
-
} catch {
|
|
21892
|
-
}
|
|
21893
|
-
}
|
|
21894
|
-
return [...out];
|
|
21895
|
-
}
|
|
21896
|
-
async function fetchTargetHtml() {
|
|
21897
|
-
const response = await fetch(new URL("./__kehto/target.html", window.location.href), {
|
|
21898
|
-
cache: "no-store",
|
|
21899
|
-
headers: {
|
|
21900
|
-
accept: "text/html, application/xhtml+xml;q=0.9, */*;q=0.8"
|
|
21901
|
-
}
|
|
21902
|
-
});
|
|
21903
|
-
if (!response.ok) {
|
|
21904
|
-
throw new Error(`Paja target fetch failed: ${response.status} ${response.statusText}`);
|
|
21905
|
-
}
|
|
21906
|
-
return response.text();
|
|
21907
|
-
}
|
|
21908
|
-
function injectBaseHref(html, targetUrl) {
|
|
21909
|
-
const base = `<base href="${escapeAttribute2(targetUrl)}">`;
|
|
21910
|
-
if (/<head[^>]*>/i.test(html)) {
|
|
21911
|
-
return html.replace(/<head[^>]*>/i, (open) => `${open}${base}`);
|
|
21912
|
-
}
|
|
21913
|
-
if (/<html[^>]*>/i.test(html)) {
|
|
21914
|
-
return html.replace(/<html[^>]*>/i, (open) => `${open}<head>${base}</head>`);
|
|
21915
|
-
}
|
|
21916
|
-
return `${base}${html}`;
|
|
21917
|
-
}
|
|
21918
|
-
function escapeAttribute2(value) {
|
|
21919
|
-
return escapeHtml(value).replaceAll('"', """);
|
|
21920
|
-
}
|
|
21921
|
-
function escapeHtml(value) {
|
|
21922
|
-
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
21923
|
-
}
|
|
21924
|
-
|
|
21925
21925
|
// src/browser-target-diagnostics.ts
|
|
21926
21926
|
async function reportTargetCorsDiagnostic(state) {
|
|
21927
21927
|
let diagnostic;
|
|
@@ -22710,7 +22710,7 @@ function createPajaBrowserState(context) {
|
|
|
22710
22710
|
setRuntimeDomainEnabled(this, context, domain, enabled);
|
|
22711
22711
|
},
|
|
22712
22712
|
setAclCapability(capability, enabled) {
|
|
22713
|
-
const identity =
|
|
22713
|
+
const identity = getTargetIdentity(config, this.resolvedTarget);
|
|
22714
22714
|
if (enabled) bridge.runtime.aclState.grant(identity.pubkey, identity.dTag, identity.aggregateHash, capability);
|
|
22715
22715
|
else bridge.runtime.aclState.revoke(identity.pubkey, identity.dTag, identity.aggregateHash, capability);
|
|
22716
22716
|
bridge.runtime.aclState.persist();
|
|
@@ -22784,7 +22784,7 @@ async function installPajaHost() {
|
|
|
22784
22784
|
);
|
|
22785
22785
|
const getWindowIdentity = (windowId) => {
|
|
22786
22786
|
const tabTarget = stateRef?.tabs.find((tab) => tab.windowId === windowId)?.resolvedTarget;
|
|
22787
|
-
return
|
|
22787
|
+
return getTargetIdentity(config, tabTarget ?? stateRef?.resolvedTarget);
|
|
22788
22788
|
};
|
|
22789
22789
|
const notifyController = createPajaNotifyController({
|
|
22790
22790
|
confirm: confirmationController.confirm,
|