@kehto/paja 0.9.0 → 0.11.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 +36 -9
- package/dist/browser-host.js +642 -339
- package/dist/browser-host.js.map +1 -1
- package/dist/{chunk-OWVINAW7.js → chunk-EOCGC7A2.js} +30 -36
- package/dist/{chunk-OWVINAW7.js.map → chunk-EOCGC7A2.js.map} +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +17 -19
- package/dist/index.js +1 -1
- package/package.json +11 -11
package/dist/browser-host.js
CHANGED
|
@@ -262,7 +262,7 @@ function uploadMap(action) {
|
|
|
262
262
|
return { senderCap: "upload:write", recipientCap: null };
|
|
263
263
|
}
|
|
264
264
|
function intentMap(action) {
|
|
265
|
-
if (action === "changed" || action === "
|
|
265
|
+
if (action === "changed" || action === "invoke.result" || action === "available.result" || action === "handlers.result") {
|
|
266
266
|
return { senderCap: null, recipientCap: "intent:read" };
|
|
267
267
|
}
|
|
268
268
|
if (action === "invoke") return { senderCap: "intent:write", recipientCap: null };
|
|
@@ -623,7 +623,7 @@ function deserialize2(json) {
|
|
|
623
623
|
return defaultConfig();
|
|
624
624
|
}
|
|
625
625
|
|
|
626
|
-
// ../../node_modules/.pnpm/@napplet+core@0.
|
|
626
|
+
// ../../node_modules/.pnpm/@napplet+core@0.31.0/node_modules/@napplet/core/dist/index.js
|
|
627
627
|
function createDispatch() {
|
|
628
628
|
const handlers = /* @__PURE__ */ new Map();
|
|
629
629
|
function registerNap2(domain, handler) {
|
|
@@ -1599,17 +1599,29 @@ function isRuntimeOwnedIdentityOrThemeRequest(type) {
|
|
|
1599
1599
|
function createRuntimeOwnedResultEnvelope(type, id) {
|
|
1600
1600
|
return Object.assign({ type }, { id });
|
|
1601
1601
|
}
|
|
1602
|
+
function intentRequestDetails(message) {
|
|
1603
|
+
const request = message.request;
|
|
1604
|
+
return {
|
|
1605
|
+
archetype: typeof request?.archetype === "string" ? request.archetype : "",
|
|
1606
|
+
action: typeof request?.action === "string" && request.action.length > 0 ? request.action : "open"
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
function createIntentInvokeRejection(message, error) {
|
|
1610
|
+
const id = message.id ?? "";
|
|
1611
|
+
const { archetype, action } = intentRequestDetails(message);
|
|
1612
|
+
return {
|
|
1613
|
+
type: "intent.invoke.result",
|
|
1614
|
+
id,
|
|
1615
|
+
result: { ok: false, archetype, action, handled: false, error }
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1602
1618
|
function isIdentityOrThemeMessage(message) {
|
|
1603
1619
|
return message.type.startsWith("identity.") || message.type.startsWith("theme.");
|
|
1604
1620
|
}
|
|
1605
1621
|
function createIntentPolicyDenial(message) {
|
|
1606
1622
|
const id = message.id ?? "";
|
|
1607
1623
|
if (message.type === "intent.invoke") {
|
|
1608
|
-
return
|
|
1609
|
-
type: "intent.invoke.result",
|
|
1610
|
-
id,
|
|
1611
|
-
result: { ok: false, error: "invoke rejected" }
|
|
1612
|
-
};
|
|
1624
|
+
return createIntentInvokeRejection(message, "invoke rejected");
|
|
1613
1625
|
}
|
|
1614
1626
|
if (message.type === "intent.available" || message.type === "intent.handlers") {
|
|
1615
1627
|
return {
|
|
@@ -1620,6 +1632,29 @@ function createIntentPolicyDenial(message) {
|
|
|
1620
1632
|
}
|
|
1621
1633
|
return void 0;
|
|
1622
1634
|
}
|
|
1635
|
+
function createIntentServiceUnavailableResult(message) {
|
|
1636
|
+
const id = message.id ?? "";
|
|
1637
|
+
if (message.type === "intent.invoke") {
|
|
1638
|
+
return createIntentInvokeRejection(message, "no handler");
|
|
1639
|
+
}
|
|
1640
|
+
if (message.type === "intent.available") {
|
|
1641
|
+
const archetype = message.archetype;
|
|
1642
|
+
return {
|
|
1643
|
+
type: "intent.available.result",
|
|
1644
|
+
id,
|
|
1645
|
+
availability: {
|
|
1646
|
+
archetype: typeof archetype === "string" ? archetype : "",
|
|
1647
|
+
available: false,
|
|
1648
|
+
candidates: [],
|
|
1649
|
+
hasDefault: false
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
if (message.type === "intent.handlers") {
|
|
1654
|
+
return { type: "intent.handlers.result", id, handlers: [] };
|
|
1655
|
+
}
|
|
1656
|
+
return void 0;
|
|
1657
|
+
}
|
|
1623
1658
|
function createCanonicalDomainResult(message) {
|
|
1624
1659
|
if (!isRuntimeOwnedIdentityOrThemeRequest(message.type)) return void 0;
|
|
1625
1660
|
const id = message.id ?? "";
|
|
@@ -1766,6 +1801,7 @@ function createIncRuntime(hooks, sessionRegistry2, authorizeChannelOpen) {
|
|
|
1766
1801
|
},
|
|
1767
1802
|
revokeWindow(windowId) {
|
|
1768
1803
|
revokeWindowChannels(state, hooks, windowId);
|
|
1804
|
+
removeWindowSubscriptions(state, windowId);
|
|
1769
1805
|
},
|
|
1770
1806
|
clear() {
|
|
1771
1807
|
state.subscriptions.clear();
|
|
@@ -1851,7 +1887,12 @@ function handleEmit(state, hooks, sessionRegistry2, windowId, m) {
|
|
|
1851
1887
|
if (!subscribers) return;
|
|
1852
1888
|
for (const subscriberWindowId of subscribers) {
|
|
1853
1889
|
if (subscriberWindowId !== windowId) {
|
|
1854
|
-
hooks.sendToNapplet(subscriberWindowId, {
|
|
1890
|
+
hooks.sendToNapplet(subscriberWindowId, {
|
|
1891
|
+
type: "inc.event",
|
|
1892
|
+
topic,
|
|
1893
|
+
sender,
|
|
1894
|
+
...m.payload === void 0 ? {} : { payload: m.payload }
|
|
1895
|
+
});
|
|
1855
1896
|
}
|
|
1856
1897
|
}
|
|
1857
1898
|
}
|
|
@@ -1914,7 +1955,12 @@ function handleChannelEmit(state, hooks, sessionRegistry2, windowId, m) {
|
|
|
1914
1955
|
const peer = peerOf(state, m.channelId ?? "", windowId);
|
|
1915
1956
|
const sender = sessionRegistry2.getEntryByWindowId(windowId)?.dTag;
|
|
1916
1957
|
if (peer && sender) {
|
|
1917
|
-
hooks.sendToNapplet(peer, {
|
|
1958
|
+
hooks.sendToNapplet(peer, {
|
|
1959
|
+
type: "inc.channel.event",
|
|
1960
|
+
channelId: m.channelId ?? "",
|
|
1961
|
+
sender,
|
|
1962
|
+
...m.payload === void 0 ? {} : { payload: m.payload }
|
|
1963
|
+
});
|
|
1918
1964
|
}
|
|
1919
1965
|
}
|
|
1920
1966
|
function handleChannelBroadcast(state, hooks, sessionRegistry2, windowId, m) {
|
|
@@ -1924,7 +1970,12 @@ function handleChannelBroadcast(state, hooks, sessionRegistry2, windowId, m) {
|
|
|
1924
1970
|
for (const channelId of channels) {
|
|
1925
1971
|
const peer = peerOf(state, channelId, windowId);
|
|
1926
1972
|
if (peer) {
|
|
1927
|
-
hooks.sendToNapplet(peer, {
|
|
1973
|
+
hooks.sendToNapplet(peer, {
|
|
1974
|
+
type: "inc.channel.event",
|
|
1975
|
+
channelId,
|
|
1976
|
+
sender,
|
|
1977
|
+
...m.payload === void 0 ? {} : { payload: m.payload }
|
|
1978
|
+
});
|
|
1928
1979
|
}
|
|
1929
1980
|
}
|
|
1930
1981
|
}
|
|
@@ -2250,7 +2301,13 @@ function handleServiceOnlyMessage(context, name, windowId, msg) {
|
|
|
2250
2301
|
return;
|
|
2251
2302
|
}
|
|
2252
2303
|
const service = context.serviceRegistry[name];
|
|
2253
|
-
if (!service)
|
|
2304
|
+
if (!service) {
|
|
2305
|
+
if (name === "intent") {
|
|
2306
|
+
const result2 = createIntentServiceUnavailableResult(msg);
|
|
2307
|
+
if (result2) context.hooks.sendToNapplet(windowId, result2);
|
|
2308
|
+
}
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2254
2311
|
service.handleMessage(windowId, msg, (resp) => context.hooks.sendToNapplet(windowId, resp));
|
|
2255
2312
|
}
|
|
2256
2313
|
function createRegisteredServices(serviceRegistry) {
|
|
@@ -2476,6 +2533,10 @@ function createRuntimeInstance(context) {
|
|
|
2476
2533
|
sessionRegistry2.getAllEntries().map((entry) => entry.windowId)
|
|
2477
2534
|
);
|
|
2478
2535
|
},
|
|
2536
|
+
hasCapability(windowId, capability) {
|
|
2537
|
+
const entry = sessionRegistry2.getEntryByWindowId(windowId);
|
|
2538
|
+
return !!entry && aclState.check("", entry.dTag, entry.aggregateHash, capability);
|
|
2539
|
+
},
|
|
2479
2540
|
sendToEligibleNapplet(windowId, message) {
|
|
2480
2541
|
const entry = sessionRegistry2.getEntryByWindowId(windowId);
|
|
2481
2542
|
if (!entry || typeof message.type !== "string") return false;
|
|
@@ -3979,9 +4040,14 @@ function nappletNamespacePrelude(domains) {
|
|
|
3979
4040
|
function fire(message) {
|
|
3980
4041
|
post(message);
|
|
3981
4042
|
}
|
|
4043
|
+
function isConventionTopic(topic) {
|
|
4044
|
+
const pathEnd = topic.search(/[?#]/);
|
|
4045
|
+
const path = pathEnd === -1 ? topic : topic.slice(0, pathEnd);
|
|
4046
|
+
return /^napplet:[^/?#]+\/[^/?#]+$/.test(path);
|
|
4047
|
+
}
|
|
3982
4048
|
function normalizeConventionUri(topic, hasExplicitPayload) {
|
|
3983
4049
|
if (typeof topic !== "string") throw new TypeError("INC topic must be text");
|
|
3984
|
-
if (!topic
|
|
4050
|
+
if (!isConventionTopic(topic)) return { topic };
|
|
3985
4051
|
if (topic.includes("#")) throw new TypeError("Convention topics cannot contain fragments");
|
|
3986
4052
|
const queryStart = topic.indexOf("?");
|
|
3987
4053
|
if (queryStart === -1) {
|
|
@@ -3990,7 +4056,7 @@ function nappletNamespacePrelude(domains) {
|
|
|
3990
4056
|
if (hasExplicitPayload) {
|
|
3991
4057
|
throw new TypeError("Convention queries cannot be combined with an explicit payload");
|
|
3992
4058
|
}
|
|
3993
|
-
const
|
|
4059
|
+
const entries = [];
|
|
3994
4060
|
const names = /* @__PURE__ */ new Set();
|
|
3995
4061
|
const query = topic.slice(queryStart + 1);
|
|
3996
4062
|
if (query) {
|
|
@@ -4009,10 +4075,10 @@ function nappletNamespacePrelude(domains) {
|
|
|
4009
4075
|
}
|
|
4010
4076
|
if (names.has(name)) throw new TypeError("Convention query names must be unique");
|
|
4011
4077
|
names.add(name);
|
|
4012
|
-
|
|
4078
|
+
entries.push([name, value]);
|
|
4013
4079
|
}
|
|
4014
4080
|
}
|
|
4015
|
-
return { topic: topic.slice(0, queryStart), payload:
|
|
4081
|
+
return { topic: topic.slice(0, queryStart), payload: Object.fromEntries(entries) };
|
|
4016
4082
|
}
|
|
4017
4083
|
function resultOrError(msg) {
|
|
4018
4084
|
return msg;
|
|
@@ -4171,7 +4237,7 @@ function nappletNamespacePrelude(domains) {
|
|
|
4171
4237
|
const unopenedClosed = /* @__PURE__ */ new Map();
|
|
4172
4238
|
function stableSubscriptionTopic(topic) {
|
|
4173
4239
|
if (typeof topic !== "string") throw new TypeError("INC topic must be text");
|
|
4174
|
-
if (topic.includes("?") || topic.includes("#")) {
|
|
4240
|
+
if (isConventionTopic(topic) && (topic.includes("?") || topic.includes("#"))) {
|
|
4175
4241
|
throw new TypeError("INC subscriptions require a queryless topic identity");
|
|
4176
4242
|
}
|
|
4177
4243
|
return topic;
|
|
@@ -4192,7 +4258,6 @@ function nappletNamespacePrelude(domains) {
|
|
|
4192
4258
|
},
|
|
4193
4259
|
on(handler) {
|
|
4194
4260
|
if (typeof handler !== "function") throw new TypeError("INC channel event handler must be a function");
|
|
4195
|
-
if (state.closed) return subscriptionHandle(() => void 0);
|
|
4196
4261
|
state.eventHandlers.add(handler);
|
|
4197
4262
|
const pending = state.pendingEvents.splice(0);
|
|
4198
4263
|
for (const event of pending) handler(event);
|
|
@@ -4862,77 +4927,57 @@ function nappletNamespacePrelude(domains) {
|
|
|
4862
4927
|
};
|
|
4863
4928
|
}
|
|
4864
4929
|
function makeIntent() {
|
|
4865
|
-
const deliveryHandlers = /* @__PURE__ */ new Set();
|
|
4866
|
-
const pendingDeliveries = [];
|
|
4867
4930
|
const hasOwn2 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
4868
|
-
const
|
|
4869
|
-
if (!value || typeof value !== "object" ||
|
|
4870
|
-
|
|
4931
|
+
const normalizeRequest = (value) => {
|
|
4932
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
4933
|
+
throw new TypeError("Intent request must be an object");
|
|
4871
4934
|
}
|
|
4872
|
-
const
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
archetype: delivery.archetype,
|
|
4876
|
-
action: delivery.action,
|
|
4877
|
-
convention: delivery.convention,
|
|
4878
|
-
...hasOwn2(delivery, "payload") ? { payload: delivery.payload } : {}
|
|
4879
|
-
};
|
|
4880
|
-
};
|
|
4881
|
-
listen((event) => {
|
|
4882
|
-
if (!isParentMessage(event)) return;
|
|
4883
|
-
const msg = event.data;
|
|
4884
|
-
if (typeof msg !== "object" || msg === null || msg.type !== "intent.deliver") return;
|
|
4885
|
-
const delivery = normalizeDelivery(msg.delivery);
|
|
4886
|
-
if (!delivery) return;
|
|
4887
|
-
if (deliveryHandlers.size === 0) pendingDeliveries.push(delivery);
|
|
4888
|
-
else for (const handler of deliveryHandlers) handler(delivery);
|
|
4889
|
-
});
|
|
4890
|
-
function normalizeIntentUri(uri, explicitPayload, actionMustBe) {
|
|
4891
|
-
if (typeof uri !== "string" || !uri.startsWith("napplet:")) {
|
|
4892
|
-
throw new TypeError("Intent convention URI must start with napplet:");
|
|
4935
|
+
const supplied = value;
|
|
4936
|
+
if (hasOwn2(supplied, "sender")) {
|
|
4937
|
+
throw new TypeError("Intent callers cannot supply sender");
|
|
4893
4938
|
}
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
if (!match) throw new TypeError("Intent convention URI must be napplet:<archetype>/<intent>");
|
|
4898
|
-
if (actionMustBe !== void 0 && match[2] !== actionMustBe) {
|
|
4899
|
-
throw new TypeError(`Intent convention URI must use the ${actionMustBe} intent`);
|
|
4939
|
+
const allowed22 = ["archetype", "action", "convention", "payload", "handler", "behavior"];
|
|
4940
|
+
if (Object.keys(supplied).some((field) => !allowed22.includes(field))) {
|
|
4941
|
+
throw new TypeError("Intent request contains unsupported fields");
|
|
4900
4942
|
}
|
|
4901
|
-
|
|
4902
|
-
archetype
|
|
4903
|
-
action: match[2],
|
|
4904
|
-
convention: normalized.topic,
|
|
4905
|
-
...normalized.payload === void 0 ? {} : { payload: normalized.payload }
|
|
4906
|
-
};
|
|
4907
|
-
}
|
|
4908
|
-
const invoke = (uri, opts, actionMustBe) => {
|
|
4909
|
-
const supplied = opts && typeof opts === "object" ? opts : void 0;
|
|
4910
|
-
const explicitPayload = supplied?.payload !== void 0;
|
|
4911
|
-
const normalized = normalizeIntentUri(uri, explicitPayload, actionMustBe);
|
|
4912
|
-
if (supplied && hasOwn2(supplied, "sender")) {
|
|
4913
|
-
throw new TypeError("Intent callers cannot supply sender");
|
|
4943
|
+
if (typeof supplied.archetype !== "string" || supplied.archetype.length === 0) {
|
|
4944
|
+
throw new TypeError("Intent request requires an archetype");
|
|
4914
4945
|
}
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4946
|
+
const action = supplied.action === void 0 ? "open" : supplied.action;
|
|
4947
|
+
if (typeof action !== "string" || action.length === 0) {
|
|
4948
|
+
throw new TypeError("Intent action must be a non-empty string");
|
|
4949
|
+
}
|
|
4950
|
+
if (supplied.convention !== void 0 && (typeof supplied.convention !== "string" || !/^napplet:[^/?#\s]+\/[^/?#\s]+$/.test(supplied.convention))) {
|
|
4951
|
+
throw new TypeError("Intent convention must be queryless");
|
|
4952
|
+
}
|
|
4953
|
+
if (supplied.handler !== void 0 && (typeof supplied.handler !== "string" || supplied.handler.length === 0)) {
|
|
4954
|
+
throw new TypeError("Intent handler must be a non-empty string");
|
|
4919
4955
|
}
|
|
4920
4956
|
const behaviorValue = supplied?.behavior;
|
|
4921
4957
|
const behavior = behaviorValue && typeof behaviorValue === "object" ? behaviorValue : void 0;
|
|
4958
|
+
if (behaviorValue !== void 0 && (!behavior || Array.isArray(behaviorValue) || Object.keys(behavior).some((field) => field !== "focus" && field !== "newWindow" && field !== "reuse") || hasOwn2(behavior, "focus") && typeof behavior.focus !== "boolean" || hasOwn2(behavior, "newWindow") && typeof behavior.newWindow !== "boolean" || hasOwn2(behavior, "reuse") && typeof behavior.reuse !== "boolean")) {
|
|
4959
|
+
throw new TypeError("Intent behavior contains unsupported fields");
|
|
4960
|
+
}
|
|
4922
4961
|
const sanitizedBehavior = {
|
|
4923
4962
|
...typeof behavior?.focus === "boolean" ? { focus: behavior.focus } : {},
|
|
4963
|
+
...typeof behavior?.newWindow === "boolean" ? { newWindow: behavior.newWindow } : {},
|
|
4924
4964
|
...typeof behavior?.reuse === "boolean" ? { reuse: behavior.reuse } : {}
|
|
4925
4965
|
};
|
|
4926
|
-
|
|
4966
|
+
return {
|
|
4967
|
+
archetype: supplied.archetype,
|
|
4968
|
+
action,
|
|
4969
|
+
...typeof supplied.convention === "string" ? { convention: supplied.convention } : {},
|
|
4970
|
+
...hasOwn2(supplied, "payload") ? { payload: supplied.payload } : {},
|
|
4971
|
+
...typeof supplied.handler === "string" ? { handler: supplied.handler } : {},
|
|
4972
|
+
...Object.keys(sanitizedBehavior).length === 0 ? {} : { behavior: sanitizedBehavior }
|
|
4973
|
+
};
|
|
4974
|
+
};
|
|
4975
|
+
const invoke = (value) => {
|
|
4976
|
+
const normalized = normalizeRequest(value);
|
|
4927
4977
|
return request(
|
|
4928
4978
|
{
|
|
4929
4979
|
type: "intent.invoke",
|
|
4930
|
-
request:
|
|
4931
|
-
...normalized,
|
|
4932
|
-
...explicitPayload ? { payload: supplied?.payload } : {},
|
|
4933
|
-
...handler === void 0 ? {} : { handler },
|
|
4934
|
-
...Object.keys(sanitizedBehavior).length === 0 ? {} : { behavior: sanitizedBehavior }
|
|
4935
|
-
}
|
|
4980
|
+
request: normalized
|
|
4936
4981
|
},
|
|
4937
4982
|
"intent.invoke.result",
|
|
4938
4983
|
(msg) => fieldOrThrow(msg, "result", "intent.invoke.result missing result")
|
|
@@ -4940,7 +4985,12 @@ function nappletNamespacePrelude(domains) {
|
|
|
4940
4985
|
};
|
|
4941
4986
|
return {
|
|
4942
4987
|
invoke,
|
|
4943
|
-
open: (
|
|
4988
|
+
open: (archetype, payload, opts) => invoke({
|
|
4989
|
+
archetype,
|
|
4990
|
+
action: "open",
|
|
4991
|
+
...payload === void 0 ? {} : { payload },
|
|
4992
|
+
...opts
|
|
4993
|
+
}),
|
|
4944
4994
|
available: (archetype) => request(
|
|
4945
4995
|
{ type: "intent.available", archetype },
|
|
4946
4996
|
"intent.available.result",
|
|
@@ -4955,14 +5005,7 @@ function nappletNamespacePrelude(domains) {
|
|
|
4955
5005
|
"intent.changed",
|
|
4956
5006
|
callback,
|
|
4957
5007
|
(msg) => msg.availability
|
|
4958
|
-
)
|
|
4959
|
-
onDelivery: (callback) => {
|
|
4960
|
-
if (typeof callback !== "function") throw new TypeError("Intent delivery handler must be a function");
|
|
4961
|
-
deliveryHandlers.add(callback);
|
|
4962
|
-
const pending = pendingDeliveries.splice(0);
|
|
4963
|
-
for (const delivery of pending) callback(delivery);
|
|
4964
|
-
return subscriptionHandle(() => deliveryHandlers.delete(callback));
|
|
4965
|
-
}
|
|
5008
|
+
)
|
|
4966
5009
|
};
|
|
4967
5010
|
}
|
|
4968
5011
|
function makeWebrtc() {
|
|
@@ -10741,9 +10784,10 @@ function createOutboxService(options) {
|
|
|
10741
10784
|
if (!options || typeof options.router !== "object" || options.router === null) {
|
|
10742
10785
|
throw new Error("createOutboxService: options.router is required");
|
|
10743
10786
|
}
|
|
10744
|
-
const { router } = options;
|
|
10787
|
+
const { router, getQueryRouter } = options;
|
|
10788
|
+
let runtimeContext;
|
|
10745
10789
|
const subscriptions = /* @__PURE__ */ new Map();
|
|
10746
|
-
function handleQuery(msg, send) {
|
|
10790
|
+
function handleQuery(windowId, msg, send) {
|
|
10747
10791
|
const m = msg;
|
|
10748
10792
|
const id = m.id ?? "";
|
|
10749
10793
|
const filters = normalizeFilters(m.filters);
|
|
@@ -10751,7 +10795,8 @@ function createOutboxService(options) {
|
|
|
10751
10795
|
send({ type: "outbox.query.result", id, events: [], error: "invalid filter" });
|
|
10752
10796
|
return;
|
|
10753
10797
|
}
|
|
10754
|
-
|
|
10798
|
+
const queryRouter = getQueryRouter?.(windowId, runtimeContext) ?? router;
|
|
10799
|
+
void queryRouter.query(filters, sanitizeQueryOptions(m.options)).then(
|
|
10755
10800
|
(result2) => send({
|
|
10756
10801
|
type: "outbox.query.result",
|
|
10757
10802
|
id,
|
|
@@ -10828,13 +10873,19 @@ function createOutboxService(options) {
|
|
|
10828
10873
|
}
|
|
10829
10874
|
return {
|
|
10830
10875
|
descriptor: OUTBOX_DESCRIPTOR,
|
|
10876
|
+
onRegistered(context) {
|
|
10877
|
+
runtimeContext = context;
|
|
10878
|
+
},
|
|
10879
|
+
onUnregistered() {
|
|
10880
|
+
runtimeContext = void 0;
|
|
10881
|
+
},
|
|
10831
10882
|
handleMessage(windowId, message, send) {
|
|
10832
10883
|
switch (message.type) {
|
|
10833
10884
|
case "outbox.getEvent":
|
|
10834
10885
|
handleGetEvent(router, message, send);
|
|
10835
10886
|
return;
|
|
10836
10887
|
case "outbox.query":
|
|
10837
|
-
handleQuery(message, send);
|
|
10888
|
+
handleQuery(windowId, message, send);
|
|
10838
10889
|
return;
|
|
10839
10890
|
case "outbox.subscribe":
|
|
10840
10891
|
handleSubscribe2(windowId, message, send);
|
|
@@ -11721,14 +11772,17 @@ function hasOwn(value, key) {
|
|
|
11721
11772
|
}
|
|
11722
11773
|
function validateIntentRequest(value) {
|
|
11723
11774
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
11724
|
-
return { error: "invalid
|
|
11775
|
+
return { error: "invalid request" };
|
|
11725
11776
|
}
|
|
11726
11777
|
const request = value;
|
|
11727
|
-
if (typeof request.archetype !== "string" || request.archetype.length === 0
|
|
11728
|
-
return { error: "invalid
|
|
11778
|
+
if (typeof request.archetype !== "string" || request.archetype.length === 0) {
|
|
11779
|
+
return { error: "invalid request" };
|
|
11780
|
+
}
|
|
11781
|
+
const action = request.action === void 0 ? "open" : request.action;
|
|
11782
|
+
if (typeof action !== "string" || action.length === 0) {
|
|
11783
|
+
return { error: "invalid request" };
|
|
11729
11784
|
}
|
|
11730
|
-
|
|
11731
|
-
if (!match || match[1] !== request.archetype || match[2] !== request.action) {
|
|
11785
|
+
if (request.convention !== void 0 && (typeof request.convention !== "string" || !/^napplet:[^/?#\s]+\/[^/?#\s]+$/.test(request.convention))) {
|
|
11732
11786
|
return { error: "invalid convention" };
|
|
11733
11787
|
}
|
|
11734
11788
|
const allowedKeys = ["archetype", "action", "convention", "payload", "handler", "behavior"];
|
|
@@ -11744,19 +11798,20 @@ function validateIntentRequest(value) {
|
|
|
11744
11798
|
return { error: "invoke rejected" };
|
|
11745
11799
|
}
|
|
11746
11800
|
const record = request.behavior;
|
|
11747
|
-
if (Object.keys(record).some((key) => key !== "focus" && key !== "reuse") || hasOwn(record, "focus") && typeof record.focus !== "boolean" || hasOwn(record, "reuse") && typeof record.reuse !== "boolean") {
|
|
11801
|
+
if (Object.keys(record).some((key) => key !== "focus" && key !== "newWindow" && key !== "reuse") || hasOwn(record, "focus") && typeof record.focus !== "boolean" || hasOwn(record, "newWindow") && typeof record.newWindow !== "boolean" || hasOwn(record, "reuse") && typeof record.reuse !== "boolean") {
|
|
11748
11802
|
return { error: "invoke rejected" };
|
|
11749
11803
|
}
|
|
11750
11804
|
behavior = {
|
|
11751
11805
|
...record.focus === void 0 ? {} : { focus: record.focus },
|
|
11806
|
+
...record.newWindow === void 0 ? {} : { newWindow: record.newWindow },
|
|
11752
11807
|
...record.reuse === void 0 ? {} : { reuse: record.reuse }
|
|
11753
11808
|
};
|
|
11754
11809
|
}
|
|
11755
11810
|
return {
|
|
11756
11811
|
request: {
|
|
11757
11812
|
archetype: request.archetype,
|
|
11758
|
-
action
|
|
11759
|
-
convention: request.convention,
|
|
11813
|
+
action,
|
|
11814
|
+
...typeof request.convention === "string" ? { convention: request.convention } : {},
|
|
11760
11815
|
...hasOwn(request, "payload") ? { payload: request.payload } : {},
|
|
11761
11816
|
...typeof request.handler === "string" ? { handler: request.handler } : {},
|
|
11762
11817
|
...behavior === void 0 ? {} : { behavior }
|
|
@@ -11773,11 +11828,20 @@ function settleResolverCall(call, send, resultType, id, onValue) {
|
|
|
11773
11828
|
}
|
|
11774
11829
|
pending.then((value) => send(onValue(value))).catch((error) => send({ type: resultType, id, error: toErrorMessage4(error) }));
|
|
11775
11830
|
}
|
|
11776
|
-
function
|
|
11831
|
+
function rejectedResult(request, error) {
|
|
11832
|
+
return {
|
|
11833
|
+
ok: false,
|
|
11834
|
+
archetype: request.archetype,
|
|
11835
|
+
action: request.action ?? "open",
|
|
11836
|
+
handled: false,
|
|
11837
|
+
error
|
|
11838
|
+
};
|
|
11839
|
+
}
|
|
11840
|
+
function rejectInvoke(send, id, request, error = "invoke rejected") {
|
|
11777
11841
|
send({
|
|
11778
11842
|
type: "intent.invoke.result",
|
|
11779
11843
|
id,
|
|
11780
|
-
result:
|
|
11844
|
+
result: rejectedResult(request, error)
|
|
11781
11845
|
});
|
|
11782
11846
|
}
|
|
11783
11847
|
function createIntentService(options) {
|
|
@@ -11792,46 +11856,40 @@ function createIntentService(options) {
|
|
|
11792
11856
|
const id = m.id ?? "";
|
|
11793
11857
|
const validation = validateIntentRequest(m.request);
|
|
11794
11858
|
if ("error" in validation) {
|
|
11795
|
-
|
|
11796
|
-
|
|
11859
|
+
const raw = typeof m.request === "object" && m.request !== null ? m.request : {};
|
|
11860
|
+
rejectInvoke(
|
|
11861
|
+
send,
|
|
11797
11862
|
id,
|
|
11798
|
-
|
|
11799
|
-
|
|
11863
|
+
{
|
|
11864
|
+
archetype: typeof raw.archetype === "string" ? raw.archetype : "",
|
|
11865
|
+
action: typeof raw.action === "string" ? raw.action : "open"
|
|
11866
|
+
},
|
|
11867
|
+
validation.error
|
|
11868
|
+
);
|
|
11800
11869
|
return;
|
|
11801
11870
|
}
|
|
11802
11871
|
const sender = runtimeContext?.resolveDTag(windowId);
|
|
11803
11872
|
if (!sender) {
|
|
11804
|
-
rejectInvoke(send, id);
|
|
11873
|
+
rejectInvoke(send, id, validation.request);
|
|
11805
11874
|
return;
|
|
11806
11875
|
}
|
|
11807
11876
|
let pending;
|
|
11808
11877
|
try {
|
|
11809
11878
|
pending = Promise.resolve(resolver.invoke(validation.request, { sender }));
|
|
11810
11879
|
} catch {
|
|
11811
|
-
rejectInvoke(send, id);
|
|
11880
|
+
rejectInvoke(send, id, validation.request);
|
|
11812
11881
|
return;
|
|
11813
11882
|
}
|
|
11814
11883
|
void pending.then(
|
|
11815
|
-
(
|
|
11816
|
-
if (outcome.result.ok && (!("retained" in outcome) || typeof outcome.retained.start !== "function")) {
|
|
11817
|
-
rejectInvoke(send, id);
|
|
11818
|
-
return;
|
|
11819
|
-
}
|
|
11884
|
+
(result2) => {
|
|
11820
11885
|
send({
|
|
11821
11886
|
type: "intent.invoke.result",
|
|
11822
11887
|
id,
|
|
11823
|
-
result:
|
|
11888
|
+
result: result2
|
|
11824
11889
|
});
|
|
11825
|
-
if (!outcome.result.ok || !("retained" in outcome)) return;
|
|
11826
|
-
try {
|
|
11827
|
-
const started = outcome.retained.start();
|
|
11828
|
-
void Promise.resolve(started).catch(() => {
|
|
11829
|
-
});
|
|
11830
|
-
} catch {
|
|
11831
|
-
}
|
|
11832
11890
|
},
|
|
11833
11891
|
() => {
|
|
11834
|
-
rejectInvoke(send, id);
|
|
11892
|
+
rejectInvoke(send, id, validation.request);
|
|
11835
11893
|
}
|
|
11836
11894
|
).catch(() => {
|
|
11837
11895
|
});
|
|
@@ -11916,128 +11974,134 @@ function candidatesFor(catalog, archetype, defaultHandler) {
|
|
|
11916
11974
|
...entry.title === void 0 ? {} : { title: entry.title },
|
|
11917
11975
|
actions: [...support.actions],
|
|
11918
11976
|
conventions: [...support.conventions],
|
|
11919
|
-
contracts: support.contracts.map((contract) => ({
|
|
11920
|
-
convention: contract.convention,
|
|
11921
|
-
...contract.eventKinds === void 0 ? {} : { eventKinds: [...contract.eventKinds] }
|
|
11922
|
-
})),
|
|
11923
11977
|
...entry.dTag === defaultHandler ? { isDefault: true } : {}
|
|
11924
11978
|
});
|
|
11925
11979
|
}
|
|
11926
11980
|
return candidates;
|
|
11927
11981
|
}
|
|
11928
|
-
function reject(error) {
|
|
11929
|
-
return {
|
|
11982
|
+
function reject(request, error) {
|
|
11983
|
+
return {
|
|
11984
|
+
ok: false,
|
|
11985
|
+
archetype: request.archetype,
|
|
11986
|
+
action: request.action ?? "open",
|
|
11987
|
+
handled: false,
|
|
11988
|
+
error
|
|
11989
|
+
};
|
|
11990
|
+
}
|
|
11991
|
+
async function availabilityForCatalog(options, archetype) {
|
|
11992
|
+
const catalog = await options.loadCatalog();
|
|
11993
|
+
const defaultHandler = options.getDefaultHandler?.(archetype);
|
|
11994
|
+
const candidates = candidatesFor(catalog, archetype, defaultHandler);
|
|
11995
|
+
return {
|
|
11996
|
+
archetype,
|
|
11997
|
+
available: candidates.length > 0,
|
|
11998
|
+
candidates,
|
|
11999
|
+
hasDefault: defaultHandler !== void 0 && candidates.some((candidate) => candidate.dTag === defaultHandler)
|
|
12000
|
+
};
|
|
12001
|
+
}
|
|
12002
|
+
async function chooseCompatible(options, request, candidates, sender) {
|
|
12003
|
+
if (!options.chooseHandler) {
|
|
12004
|
+
return request.handler === "choose" ? { error: "user cancelled" } : { error: "invoke rejected" };
|
|
12005
|
+
}
|
|
12006
|
+
const picked = await options.chooseHandler(request.archetype, candidates, sender);
|
|
12007
|
+
if (picked === void 0) return { error: "user cancelled" };
|
|
12008
|
+
const candidate = candidates.find((item) => item.dTag === picked);
|
|
12009
|
+
return candidate ? { candidate } : { error: "invoke rejected" };
|
|
12010
|
+
}
|
|
12011
|
+
async function pickHandler(options, request, compatible, sender) {
|
|
12012
|
+
const preference = request.handler;
|
|
12013
|
+
if (typeof preference === "string" && preference !== "default" && preference !== "choose") {
|
|
12014
|
+
const candidate = compatible.find((item) => item.dTag === preference);
|
|
12015
|
+
if (!candidate || !options.authorizeExplicitHandler) return { error: "invoke rejected" };
|
|
12016
|
+
let authorized = false;
|
|
12017
|
+
try {
|
|
12018
|
+
authorized = await options.authorizeExplicitHandler(
|
|
12019
|
+
sender,
|
|
12020
|
+
preference,
|
|
12021
|
+
request,
|
|
12022
|
+
candidate
|
|
12023
|
+
);
|
|
12024
|
+
} catch {
|
|
12025
|
+
return { error: "invoke rejected" };
|
|
12026
|
+
}
|
|
12027
|
+
return authorized ? { candidate } : { error: "invoke rejected" };
|
|
12028
|
+
}
|
|
12029
|
+
if (preference === "choose") {
|
|
12030
|
+
return chooseCompatible(options, request, compatible, sender);
|
|
12031
|
+
}
|
|
12032
|
+
const defaultCandidate = compatible.find((candidate) => candidate.isDefault === true);
|
|
12033
|
+
if (preference === "default") {
|
|
12034
|
+
return defaultCandidate ? { candidate: defaultCandidate } : { error: "invoke rejected" };
|
|
12035
|
+
}
|
|
12036
|
+
if (defaultCandidate) return { candidate: defaultCandidate };
|
|
12037
|
+
if (compatible.length === 1) return { candidate: compatible[0] };
|
|
12038
|
+
return chooseCompatible(options, request, compatible, sender);
|
|
12039
|
+
}
|
|
12040
|
+
async function invokeFromCatalog(options, request, context) {
|
|
12041
|
+
const catalog = await options.loadCatalog();
|
|
12042
|
+
const defaultHandler = options.getDefaultHandler?.(request.archetype);
|
|
12043
|
+
const candidates = candidatesFor(catalog, request.archetype, defaultHandler);
|
|
12044
|
+
if (candidates.length === 0) return reject(request, "no handler");
|
|
12045
|
+
const action = request.action ?? "open";
|
|
12046
|
+
const actionCompatible = candidates.filter((candidate) => candidate.actions.includes(action));
|
|
12047
|
+
if (actionCompatible.length === 0) return reject(request, "unsupported action");
|
|
12048
|
+
const compatible = request.convention === void 0 ? actionCompatible : actionCompatible.filter((candidate) => candidate.conventions.includes(request.convention));
|
|
12049
|
+
if (compatible.length === 0) return reject(request, "unsupported convention");
|
|
12050
|
+
const sender = context.sender;
|
|
12051
|
+
if (typeof sender !== "string" || sender.length === 0) {
|
|
12052
|
+
return reject(request, "invoke rejected");
|
|
12053
|
+
}
|
|
12054
|
+
const selected = await pickHandler(options, request, compatible, sender);
|
|
12055
|
+
if ("error" in selected) return reject(request, selected.error);
|
|
12056
|
+
const convention = request.convention ?? selected.candidate.conventions.find((value) => {
|
|
12057
|
+
const match = /^napplet:[^/?#\s]+\/([^/?#\s]+)$/.exec(value);
|
|
12058
|
+
return match?.[1] === action;
|
|
12059
|
+
});
|
|
12060
|
+
if (!convention) return reject(request, "unsupported convention");
|
|
12061
|
+
const behavior = request.behavior === void 0 ? void 0 : Object.freeze({
|
|
12062
|
+
...request.behavior.focus === void 0 ? {} : { focus: request.behavior.focus },
|
|
12063
|
+
...request.behavior.newWindow === void 0 ? {} : { newWindow: request.behavior.newWindow },
|
|
12064
|
+
...request.behavior.reuse === void 0 ? {} : { reuse: request.behavior.reuse }
|
|
12065
|
+
});
|
|
12066
|
+
const params = Object.freeze({
|
|
12067
|
+
handler: selected.candidate.dTag,
|
|
12068
|
+
sender,
|
|
12069
|
+
archetype: request.archetype,
|
|
12070
|
+
action,
|
|
12071
|
+
convention,
|
|
12072
|
+
...request.payload === void 0 ? {} : { payload: request.payload },
|
|
12073
|
+
...behavior === void 0 ? {} : { behavior }
|
|
12074
|
+
});
|
|
12075
|
+
let target;
|
|
12076
|
+
try {
|
|
12077
|
+
target = await options.targets.dispatch(params);
|
|
12078
|
+
} catch {
|
|
12079
|
+
return reject(request, "invoke failed");
|
|
12080
|
+
}
|
|
12081
|
+
if (!target || typeof target.windowId !== "string" || target.windowId.length === 0) {
|
|
12082
|
+
return reject(request, "invoke failed");
|
|
12083
|
+
}
|
|
12084
|
+
return {
|
|
12085
|
+
ok: true,
|
|
12086
|
+
archetype: request.archetype,
|
|
12087
|
+
action,
|
|
12088
|
+
handled: true,
|
|
12089
|
+
handler: selected.candidate.dTag,
|
|
12090
|
+
windowId: target.windowId,
|
|
12091
|
+
convention
|
|
12092
|
+
};
|
|
11930
12093
|
}
|
|
11931
12094
|
function createCatalogIntentResolver(options) {
|
|
11932
12095
|
if (!options || typeof options.loadCatalog !== "function") {
|
|
11933
12096
|
throw new Error("createCatalogIntentResolver: options.loadCatalog is required");
|
|
11934
12097
|
}
|
|
11935
|
-
if (!options.targets || typeof options.targets.
|
|
12098
|
+
if (!options.targets || typeof options.targets.dispatch !== "function") {
|
|
11936
12099
|
throw new Error("createCatalogIntentResolver: options.targets is required");
|
|
11937
12100
|
}
|
|
11938
|
-
const {
|
|
11939
|
-
loadCatalog,
|
|
11940
|
-
targets,
|
|
11941
|
-
getDefaultHandler,
|
|
11942
|
-
chooseHandler,
|
|
11943
|
-
authorizeExplicitHandler
|
|
11944
|
-
} = options;
|
|
11945
12101
|
const listeners = /* @__PURE__ */ new Set();
|
|
11946
|
-
|
|
11947
|
-
const catalog = await loadCatalog();
|
|
11948
|
-
const defaultHandler = getDefaultHandler?.(archetype);
|
|
11949
|
-
const candidates = candidatesFor(catalog, archetype, defaultHandler);
|
|
11950
|
-
return {
|
|
11951
|
-
archetype,
|
|
11952
|
-
available: candidates.length > 0,
|
|
11953
|
-
candidates,
|
|
11954
|
-
hasDefault: defaultHandler !== void 0 && candidates.some((candidate) => candidate.dTag === defaultHandler)
|
|
11955
|
-
};
|
|
11956
|
-
}
|
|
11957
|
-
async function chooseCompatible(request, candidates, sender) {
|
|
11958
|
-
if (!chooseHandler) {
|
|
11959
|
-
return request.handler === "choose" ? { error: "user cancelled" } : { error: "invoke rejected" };
|
|
11960
|
-
}
|
|
11961
|
-
const picked = await chooseHandler(request.archetype, candidates, sender);
|
|
11962
|
-
if (picked === void 0) return { error: "user cancelled" };
|
|
11963
|
-
const candidate = candidates.find((item) => item.dTag === picked);
|
|
11964
|
-
return candidate ? { candidate } : { error: "invoke rejected" };
|
|
11965
|
-
}
|
|
11966
|
-
async function pickHandler(request, compatible, sender) {
|
|
11967
|
-
const preference = request.handler;
|
|
11968
|
-
if (typeof preference === "string" && preference !== "default" && preference !== "choose") {
|
|
11969
|
-
const candidate = compatible.find((item) => item.dTag === preference);
|
|
11970
|
-
if (!candidate || !authorizeExplicitHandler) return { error: "invoke rejected" };
|
|
11971
|
-
let authorized = false;
|
|
11972
|
-
try {
|
|
11973
|
-
authorized = await authorizeExplicitHandler(sender, preference, request, candidate);
|
|
11974
|
-
} catch {
|
|
11975
|
-
return { error: "invoke rejected" };
|
|
11976
|
-
}
|
|
11977
|
-
return authorized ? { candidate } : { error: "invoke rejected" };
|
|
11978
|
-
}
|
|
11979
|
-
if (preference === "choose") {
|
|
11980
|
-
return chooseCompatible(request, compatible, sender);
|
|
11981
|
-
}
|
|
11982
|
-
const defaultCandidate = compatible.find((candidate) => candidate.isDefault === true);
|
|
11983
|
-
if (preference === "default") {
|
|
11984
|
-
return defaultCandidate ? { candidate: defaultCandidate } : { error: "invoke rejected" };
|
|
11985
|
-
}
|
|
11986
|
-
if (defaultCandidate) return { candidate: defaultCandidate };
|
|
11987
|
-
if (compatible.length === 1) return { candidate: compatible[0] };
|
|
11988
|
-
return chooseCompatible(request, compatible, sender);
|
|
11989
|
-
}
|
|
11990
|
-
async function invoke(request, context) {
|
|
11991
|
-
const catalog = await loadCatalog();
|
|
11992
|
-
const defaultHandler = getDefaultHandler?.(request.archetype);
|
|
11993
|
-
const candidates = candidatesFor(catalog, request.archetype, defaultHandler);
|
|
11994
|
-
if (candidates.length === 0) return { result: reject("no handler") };
|
|
11995
|
-
const compatible = candidates.filter((candidate) => candidate.contracts.some((contract) => contract.convention === request.convention));
|
|
11996
|
-
if (compatible.length === 0) return { result: reject("unsupported convention") };
|
|
11997
|
-
const sender = context.sender;
|
|
11998
|
-
if (typeof sender !== "string" || sender.length === 0) {
|
|
11999
|
-
return { result: reject("invoke rejected") };
|
|
12000
|
-
}
|
|
12001
|
-
const selected = await pickHandler(request, compatible, sender);
|
|
12002
|
-
if ("error" in selected) return { result: reject(selected.error) };
|
|
12003
|
-
const delivery = Object.freeze({
|
|
12004
|
-
sender,
|
|
12005
|
-
archetype: request.archetype,
|
|
12006
|
-
action: request.action,
|
|
12007
|
-
convention: request.convention,
|
|
12008
|
-
...request.payload === void 0 ? {} : { payload: request.payload }
|
|
12009
|
-
});
|
|
12010
|
-
const behavior = request.behavior === void 0 ? void 0 : Object.freeze({
|
|
12011
|
-
...request.behavior.focus === void 0 ? {} : { focus: request.behavior.focus },
|
|
12012
|
-
...request.behavior.reuse === void 0 ? {} : { reuse: request.behavior.reuse }
|
|
12013
|
-
});
|
|
12014
|
-
const params = Object.freeze({
|
|
12015
|
-
handler: selected.candidate.dTag,
|
|
12016
|
-
delivery,
|
|
12017
|
-
...behavior === void 0 ? {} : { behavior }
|
|
12018
|
-
});
|
|
12019
|
-
let retained;
|
|
12020
|
-
try {
|
|
12021
|
-
retained = await targets.retain(params);
|
|
12022
|
-
} catch {
|
|
12023
|
-
return { result: reject("invoke rejected") };
|
|
12024
|
-
}
|
|
12025
|
-
if (!retained || typeof retained.start !== "function") {
|
|
12026
|
-
return { result: reject("invoke rejected") };
|
|
12027
|
-
}
|
|
12028
|
-
return {
|
|
12029
|
-
result: {
|
|
12030
|
-
ok: true,
|
|
12031
|
-
archetype: request.archetype,
|
|
12032
|
-
action: request.action,
|
|
12033
|
-
convention: request.convention,
|
|
12034
|
-
handler: selected.candidate.dTag
|
|
12035
|
-
},
|
|
12036
|
-
retained
|
|
12037
|
-
};
|
|
12038
|
-
}
|
|
12102
|
+
const availabilityFor = (archetype) => availabilityForCatalog(options, archetype);
|
|
12039
12103
|
async function handlers() {
|
|
12040
|
-
const catalog = await loadCatalog();
|
|
12104
|
+
const catalog = await options.loadCatalog();
|
|
12041
12105
|
const archetypes = /* @__PURE__ */ new Set();
|
|
12042
12106
|
for (const entry of catalog) {
|
|
12043
12107
|
for (const slug of Object.keys(entry.archetypes)) archetypes.add(slug);
|
|
@@ -12045,7 +12109,7 @@ function createCatalogIntentResolver(options) {
|
|
|
12045
12109
|
return Promise.all([...archetypes].map((archetype) => availabilityFor(archetype)));
|
|
12046
12110
|
}
|
|
12047
12111
|
return {
|
|
12048
|
-
invoke,
|
|
12112
|
+
invoke: (request, context) => invokeFromCatalog(options, request, context),
|
|
12049
12113
|
available: availabilityFor,
|
|
12050
12114
|
handlers,
|
|
12051
12115
|
onChanged(listener) {
|
|
@@ -12060,33 +12124,24 @@ function createCatalogIntentResolver(options) {
|
|
|
12060
12124
|
}
|
|
12061
12125
|
};
|
|
12062
12126
|
}
|
|
12063
|
-
function actionFromConvention(
|
|
12064
|
-
if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
|
|
12065
|
-
throw new TypeError("manifest archetype slug is invalid");
|
|
12066
|
-
}
|
|
12127
|
+
function actionFromConvention(convention) {
|
|
12067
12128
|
const match = /^napplet:([^/?#\s]+)\/([^/?#\s]+)$/.exec(convention);
|
|
12068
|
-
if (!match
|
|
12069
|
-
throw new TypeError("manifest archetype convention is invalid
|
|
12129
|
+
if (!match) {
|
|
12130
|
+
throw new TypeError("manifest archetype convention is invalid");
|
|
12070
12131
|
}
|
|
12071
12132
|
return match[2];
|
|
12072
12133
|
}
|
|
12073
12134
|
function manifestToIntentCatalogEntry(manifest) {
|
|
12074
12135
|
const archetypes = {};
|
|
12075
|
-
for (const { slug, convention
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
throw new TypeError("manifest archetype event kinds must be unsigned safe integers");
|
|
12136
|
+
for (const { slug, convention } of manifest.archetypes) {
|
|
12137
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
|
|
12138
|
+
throw new TypeError("manifest archetype slug is invalid");
|
|
12079
12139
|
}
|
|
12140
|
+
const action = actionFromConvention(convention);
|
|
12080
12141
|
const support = archetypes[slug] ??= {
|
|
12081
12142
|
actions: [],
|
|
12082
|
-
conventions: []
|
|
12083
|
-
contracts: []
|
|
12143
|
+
conventions: []
|
|
12084
12144
|
};
|
|
12085
|
-
const contract = {
|
|
12086
|
-
convention,
|
|
12087
|
-
...eventKinds === void 0 || eventKinds.length === 0 ? {} : { eventKinds: [...eventKinds] }
|
|
12088
|
-
};
|
|
12089
|
-
support.contracts.push(contract);
|
|
12090
12145
|
if (!support.actions.includes(action)) support.actions.push(action);
|
|
12091
12146
|
if (!support.conventions.includes(convention)) support.conventions.push(convention);
|
|
12092
12147
|
}
|
|
@@ -13095,25 +13150,13 @@ var BrowserIntentController = class {
|
|
|
13095
13150
|
}
|
|
13096
13151
|
options;
|
|
13097
13152
|
maxAttempts;
|
|
13098
|
-
|
|
13099
|
-
const
|
|
13100
|
-
let started;
|
|
13101
|
-
let delivered = false;
|
|
13102
|
-
return {
|
|
13103
|
-
start: () => {
|
|
13104
|
-
started ??= this.start(retained, () => delivered, () => {
|
|
13105
|
-
delivered = true;
|
|
13106
|
-
});
|
|
13107
|
-
return started;
|
|
13108
|
-
}
|
|
13109
|
-
};
|
|
13110
|
-
}
|
|
13111
|
-
async start(params, isDelivered, markDelivered) {
|
|
13153
|
+
async dispatch(params) {
|
|
13154
|
+
const dispatch2 = freezeDispatch(params);
|
|
13112
13155
|
let reason = "no-current-target";
|
|
13113
13156
|
for (let attempt = 1; attempt <= this.maxAttempts; attempt += 1) {
|
|
13114
13157
|
let generation;
|
|
13115
13158
|
try {
|
|
13116
|
-
generation = await this.options.openOrReuse(
|
|
13159
|
+
generation = await this.options.openOrReuse(dispatch2, attempt);
|
|
13117
13160
|
} catch {
|
|
13118
13161
|
reason = "open-failed";
|
|
13119
13162
|
continue;
|
|
@@ -13132,17 +13175,18 @@ var BrowserIntentController = class {
|
|
|
13132
13175
|
reason = "no-current-target";
|
|
13133
13176
|
continue;
|
|
13134
13177
|
}
|
|
13135
|
-
if (isDelivered()) return;
|
|
13136
|
-
markDelivered();
|
|
13137
13178
|
try {
|
|
13138
|
-
await this.options.send(generation,
|
|
13139
|
-
|
|
13179
|
+
await this.options.send(generation, dispatch2);
|
|
13180
|
+
const windowId = this.options.getWindowId(generation);
|
|
13181
|
+
if (!windowId) throw new Error("intent target window is unavailable");
|
|
13182
|
+
return { windowId };
|
|
13140
13183
|
} catch {
|
|
13141
|
-
this.options.onTerminal?.(
|
|
13142
|
-
|
|
13184
|
+
this.options.onTerminal?.(dispatch2, "send-failed");
|
|
13185
|
+
throw new Error("intent target send failed");
|
|
13143
13186
|
}
|
|
13144
13187
|
}
|
|
13145
|
-
this.options.onTerminal?.(
|
|
13188
|
+
this.options.onTerminal?.(dispatch2, reason);
|
|
13189
|
+
throw new Error(`intent target ${reason}`);
|
|
13146
13190
|
}
|
|
13147
13191
|
};
|
|
13148
13192
|
function normalizeAttempts(value) {
|
|
@@ -13150,15 +13194,22 @@ function normalizeAttempts(value) {
|
|
|
13150
13194
|
if (!Number.isFinite(value)) throw new TypeError("maxAttempts must be finite");
|
|
13151
13195
|
return Math.min(MAX_INTENT_DELIVERY_ATTEMPTS, Math.max(1, Math.floor(value)));
|
|
13152
13196
|
}
|
|
13153
|
-
function
|
|
13197
|
+
function freezeDispatch(params) {
|
|
13154
13198
|
if (!params || typeof params.handler !== "string" || params.handler.length === 0) {
|
|
13155
|
-
throw new TypeError("Intent
|
|
13199
|
+
throw new TypeError("Intent dispatch requires a handler");
|
|
13156
13200
|
}
|
|
13157
|
-
|
|
13201
|
+
if (typeof params.sender !== "string" || params.sender.length === 0 || typeof params.archetype !== "string" || params.archetype.length === 0 || typeof params.action !== "string" || params.action.length === 0 || typeof params.convention !== "string" || params.convention.length === 0) {
|
|
13202
|
+
throw new TypeError("Intent dispatch requires canonical routing fields");
|
|
13203
|
+
}
|
|
13204
|
+
const payload = freezeValue(params.payload);
|
|
13158
13205
|
const behavior = params.behavior === void 0 ? void 0 : freezeValue({ ...params.behavior });
|
|
13159
13206
|
return Object.freeze({
|
|
13160
13207
|
handler: params.handler,
|
|
13161
|
-
|
|
13208
|
+
sender: params.sender,
|
|
13209
|
+
archetype: params.archetype,
|
|
13210
|
+
action: params.action,
|
|
13211
|
+
convention: params.convention,
|
|
13212
|
+
...params.payload === void 0 ? {} : { payload },
|
|
13162
13213
|
...behavior === void 0 ? {} : { behavior }
|
|
13163
13214
|
});
|
|
13164
13215
|
}
|
|
@@ -13192,10 +13243,10 @@ var InstalledNappletCatalog = class {
|
|
|
13192
13243
|
aggregateHash: resolved.aggregateHash,
|
|
13193
13244
|
pointer: copyPointer(resolved.pointer),
|
|
13194
13245
|
...resolved.manifest.title === void 0 ? {} : { title: resolved.manifest.title },
|
|
13246
|
+
requires: [...resolved.manifest.requires],
|
|
13195
13247
|
archetypes: resolved.manifest.archetypes.map((archetype) => ({
|
|
13196
13248
|
slug: archetype.slug,
|
|
13197
|
-
convention: archetype.convention
|
|
13198
|
-
...archetype.eventKinds === void 0 ? {} : { eventKinds: [...archetype.eventKinds] }
|
|
13249
|
+
convention: archetype.convention
|
|
13199
13250
|
}))
|
|
13200
13251
|
});
|
|
13201
13252
|
this.records.set(record.dTag, record);
|
|
@@ -13231,13 +13282,12 @@ var InstalledNappletCatalog = class {
|
|
|
13231
13282
|
}
|
|
13232
13283
|
/** Return exact handler candidates derived only from installed manifests. */
|
|
13233
13284
|
intentCatalog() {
|
|
13234
|
-
return this.installed().map((record) => manifestToIntentCatalogEntry({
|
|
13285
|
+
return this.installed().filter((record) => record.requires.includes("inc")).map((record) => manifestToIntentCatalogEntry({
|
|
13235
13286
|
dTag: record.dTag,
|
|
13236
13287
|
...record.title === void 0 ? {} : { title: record.title },
|
|
13237
13288
|
archetypes: record.archetypes.map((archetype) => ({
|
|
13238
13289
|
slug: archetype.slug,
|
|
13239
|
-
convention: archetype.convention
|
|
13240
|
-
...archetype.eventKinds === void 0 ? {} : { eventKinds: [...archetype.eventKinds] }
|
|
13290
|
+
convention: archetype.convention
|
|
13241
13291
|
}))
|
|
13242
13292
|
}));
|
|
13243
13293
|
}
|
|
@@ -13273,10 +13323,8 @@ function copyPointer(pointer) {
|
|
|
13273
13323
|
function freezeRecord(record) {
|
|
13274
13324
|
return Object.freeze({
|
|
13275
13325
|
...record,
|
|
13276
|
-
|
|
13277
|
-
|
|
13278
|
-
...archetype.eventKinds === void 0 ? {} : { eventKinds: Object.freeze([...archetype.eventKinds]) }
|
|
13279
|
-
})))
|
|
13326
|
+
requires: Object.freeze([...record.requires]),
|
|
13327
|
+
archetypes: Object.freeze(record.archetypes.map((archetype) => Object.freeze({ ...archetype })))
|
|
13280
13328
|
});
|
|
13281
13329
|
}
|
|
13282
13330
|
|
|
@@ -14528,7 +14576,9 @@ var SimplePool = class extends AbstractSimplePool {
|
|
|
14528
14576
|
// src/browser-relay-runtime.ts
|
|
14529
14577
|
var PAJA_NIP65_RELAY_LIST_KIND = 10002;
|
|
14530
14578
|
var PAJA_CONTACT_LIST_KIND = 3;
|
|
14579
|
+
var PAJA_CONTACT_LIST_CANDIDATE_LIMIT = 64;
|
|
14531
14580
|
var PAJA_LIVE_QUERY_WAIT_MS = 4e3;
|
|
14581
|
+
var contactListCandidateQueries = /* @__PURE__ */ new WeakMap();
|
|
14532
14582
|
function matchesFilter2(event, filter) {
|
|
14533
14583
|
const ids = filter.ids;
|
|
14534
14584
|
if (ids && !ids.some((id) => event.id === id || event.id.startsWith(id))) return false;
|
|
@@ -14588,14 +14638,54 @@ function subscribeLive(pool, relayUrls, filters, next) {
|
|
|
14588
14638
|
}
|
|
14589
14639
|
};
|
|
14590
14640
|
}
|
|
14591
|
-
|
|
14641
|
+
function queryLiveBounded(pool, relayUrls, filter, maxWaitMs, limit, signal) {
|
|
14642
|
+
if (limit === 0 || signal?.aborted) return Promise.resolve([]);
|
|
14643
|
+
return new Promise((resolve) => {
|
|
14644
|
+
const events = [];
|
|
14645
|
+
let settled = false;
|
|
14646
|
+
let closeRequested = false;
|
|
14647
|
+
let closeReason = "paja query limit reached";
|
|
14648
|
+
let subscription;
|
|
14649
|
+
let abort;
|
|
14650
|
+
const finish = () => {
|
|
14651
|
+
if (settled) return;
|
|
14652
|
+
settled = true;
|
|
14653
|
+
if (abort) signal?.removeEventListener("abort", abort);
|
|
14654
|
+
resolve(events);
|
|
14655
|
+
};
|
|
14656
|
+
const close = (reason = closeReason) => {
|
|
14657
|
+
closeRequested = true;
|
|
14658
|
+
closeReason = reason;
|
|
14659
|
+
if (!subscription) return;
|
|
14660
|
+
void Promise.resolve(subscription.close(reason)).catch(() => {
|
|
14661
|
+
});
|
|
14662
|
+
finish();
|
|
14663
|
+
};
|
|
14664
|
+
abort = () => close("paja query aborted");
|
|
14665
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
14666
|
+
subscription = pool.subscribeEose(relayUrls, filter, {
|
|
14667
|
+
label: "kehto-paja-runtime",
|
|
14668
|
+
maxWait: maxWaitMs,
|
|
14669
|
+
onevent(event) {
|
|
14670
|
+
if (events.length >= limit) return;
|
|
14671
|
+
events.push(event);
|
|
14672
|
+
if (events.length === limit) close();
|
|
14673
|
+
},
|
|
14674
|
+
onclose: finish
|
|
14675
|
+
});
|
|
14676
|
+
if (closeRequested) close();
|
|
14677
|
+
});
|
|
14678
|
+
}
|
|
14679
|
+
async function queryLive(pool, relayUrls, filters, maxWaitMs = PAJA_LIVE_QUERY_WAIT_MS, collectUntilLimit = false, signal) {
|
|
14592
14680
|
const activeFilters = filters.length > 0 ? filters : [{}];
|
|
14593
|
-
const batches = await Promise.all(activeFilters.map(
|
|
14594
|
-
|
|
14681
|
+
const batches = await Promise.all(activeFilters.map((filter) => {
|
|
14682
|
+
const limit = typeof filter.limit === "number" && filter.limit >= 0 ? filter.limit : void 0;
|
|
14683
|
+
if (collectUntilLimit && limit !== void 0) return queryLiveBounded(pool, relayUrls, filter, maxWaitMs, limit, signal);
|
|
14684
|
+
return pool.querySync(relayUrls, filter, {
|
|
14595
14685
|
label: "kehto-paja-runtime",
|
|
14596
14686
|
maxWait: maxWaitMs
|
|
14597
|
-
})
|
|
14598
|
-
));
|
|
14687
|
+
});
|
|
14688
|
+
}));
|
|
14599
14689
|
const out = /* @__PURE__ */ new Map();
|
|
14600
14690
|
for (const event of batches.flat()) out.set(event.id, event);
|
|
14601
14691
|
return [...out.values()].sort((a, b) => b.created_at - a.created_at);
|
|
@@ -14616,6 +14706,14 @@ function createPajaRelayBackend(getSimulation, confirmRequest, livePool = new Si
|
|
|
14616
14706
|
for (const event of [...memoryEvents, ...liveEvents]) out.set(event.id, event);
|
|
14617
14707
|
return [...out.values()].sort((a, b) => b.created_at - a.created_at);
|
|
14618
14708
|
};
|
|
14709
|
+
const queryContactListCandidates = async (relayUrls, filters, maxWaitMs, signal) => {
|
|
14710
|
+
const memoryEvents = collectMemoryEvents(events, filters);
|
|
14711
|
+
if (signal?.aborted || getSimulation().relay.mode !== "live" || relayUrls.length === 0) return memoryEvents;
|
|
14712
|
+
const liveEvents = await queryLive(livePool, relayUrls, filters, maxWaitMs, true, signal);
|
|
14713
|
+
const out = /* @__PURE__ */ new Map();
|
|
14714
|
+
for (const event of [...memoryEvents, ...liveEvents]) out.set(event.id, event);
|
|
14715
|
+
return [...out.values()].sort((a, b) => b.created_at - a.created_at);
|
|
14716
|
+
};
|
|
14619
14717
|
function retainPublishedEvent(event) {
|
|
14620
14718
|
events.push(event);
|
|
14621
14719
|
for (const subscriber of subscribers) {
|
|
@@ -14691,6 +14789,7 @@ function createPajaRelayBackend(getSimulation, confirmRequest, livePool = new Si
|
|
|
14691
14789
|
livePool.destroy();
|
|
14692
14790
|
}
|
|
14693
14791
|
};
|
|
14792
|
+
contactListCandidateQueries.set(backend, queryContactListCandidates);
|
|
14694
14793
|
return backend;
|
|
14695
14794
|
}
|
|
14696
14795
|
function toNostrEvent(value) {
|
|
@@ -14730,19 +14829,25 @@ async function getBootstrapRelayUrls(getSimulation, signerProvider) {
|
|
|
14730
14829
|
...await getSignerRelayUrls(signerProvider, "read")
|
|
14731
14830
|
]);
|
|
14732
14831
|
}
|
|
14832
|
+
function createPajaContactListLoader(backend, getSimulation, signerProvider) {
|
|
14833
|
+
return async (pubkey, signal) => {
|
|
14834
|
+
if (!/^[0-9a-fA-F]{64}$/.test(pubkey) || signal?.aborted) return [];
|
|
14835
|
+
const relayUrls = await getBootstrapRelayUrls(getSimulation, signerProvider);
|
|
14836
|
+
if (signal?.aborted) return [];
|
|
14837
|
+
const filters = [{
|
|
14838
|
+
kinds: [PAJA_CONTACT_LIST_KIND],
|
|
14839
|
+
authors: [pubkey],
|
|
14840
|
+
limit: PAJA_CONTACT_LIST_CANDIDATE_LIMIT
|
|
14841
|
+
}];
|
|
14842
|
+
const contactQuery = contactListCandidateQueries.get(backend);
|
|
14843
|
+
const candidates = contactQuery ? await contactQuery(relayUrls, filters, PAJA_LIVE_QUERY_WAIT_MS, signal) : await backend.query(relayUrls, filters, PAJA_LIVE_QUERY_WAIT_MS);
|
|
14844
|
+
if (signal?.aborted) return [];
|
|
14845
|
+
return candidates.filter((event) => event.kind === PAJA_CONTACT_LIST_KIND && event.pubkey.toLowerCase() === pubkey.toLowerCase()).sort((left, right) => right.created_at - left.created_at || left.id.localeCompare(right.id)).slice(0, PAJA_CONTACT_LIST_CANDIDATE_LIMIT);
|
|
14846
|
+
};
|
|
14847
|
+
}
|
|
14733
14848
|
function latestEvent(events, kind, pubkey) {
|
|
14734
14849
|
return events.filter((event) => event.kind === kind && event.pubkey === pubkey).sort((a, b) => b.created_at - a.created_at)[0];
|
|
14735
14850
|
}
|
|
14736
|
-
function contactPubkeys(event) {
|
|
14737
|
-
if (!event) return [];
|
|
14738
|
-
const out = /* @__PURE__ */ new Set();
|
|
14739
|
-
for (const tag of event.tags) {
|
|
14740
|
-
if (tag[0] === "p" && typeof tag[1] === "string" && /^[0-9a-fA-F]{64}$/.test(tag[1])) {
|
|
14741
|
-
out.add(tag[1].toLowerCase());
|
|
14742
|
-
}
|
|
14743
|
-
}
|
|
14744
|
-
return [...out];
|
|
14745
|
-
}
|
|
14746
14851
|
function createPajaRelayListLoader(backend, getSimulation, signerProvider) {
|
|
14747
14852
|
const registry2 = createNip65Registry();
|
|
14748
14853
|
return async (pubkeys) => {
|
|
@@ -14785,24 +14890,210 @@ function createPajaOutboxRelayPool(backend) {
|
|
|
14785
14890
|
}
|
|
14786
14891
|
};
|
|
14787
14892
|
}
|
|
14788
|
-
|
|
14789
|
-
|
|
14893
|
+
|
|
14894
|
+
// src/browser-social-cache.ts
|
|
14895
|
+
var HEX_PUBKEY = /^[0-9a-f]{64}$/i;
|
|
14896
|
+
var FOLLOW_PARSE_YIELD_EVERY = 64;
|
|
14897
|
+
var PROFILE_WARM_YIELD_EVERY = 64;
|
|
14898
|
+
function createPajaSocialCache(options) {
|
|
14899
|
+
let generation = 0;
|
|
14900
|
+
let activeRefreshController;
|
|
14901
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
14902
|
+
async function loadFollows(capturedPubkey) {
|
|
14903
|
+
const normalizedPubkey = normalizePubkey(capturedPubkey);
|
|
14904
|
+
if (!normalizedPubkey) return [];
|
|
14905
|
+
const existing = snapshots.get(normalizedPubkey);
|
|
14906
|
+
if (existing) return [...existing.follows];
|
|
14907
|
+
const capturedGeneration = generation;
|
|
14908
|
+
const follows = await verifiedFollows(normalizedPubkey);
|
|
14909
|
+
const warmed = snapshots.get(normalizedPubkey);
|
|
14910
|
+
if (warmed) return [...warmed.follows];
|
|
14911
|
+
if (generation === capturedGeneration) {
|
|
14912
|
+
snapshots.set(normalizedPubkey, { follows, profiles: /* @__PURE__ */ new Map() });
|
|
14913
|
+
}
|
|
14914
|
+
return [...follows];
|
|
14915
|
+
}
|
|
14916
|
+
async function verifiedFollows(capturedPubkey, isStillCurrent, signal) {
|
|
14917
|
+
const candidates = await options.loadContactList(capturedPubkey, signal);
|
|
14918
|
+
const verified = [];
|
|
14919
|
+
for (const candidate of candidates) {
|
|
14920
|
+
if (isStillCurrent && !isStillCurrent()) return [];
|
|
14921
|
+
if (!isContactCandidate(candidate, capturedPubkey)) continue;
|
|
14922
|
+
try {
|
|
14923
|
+
if (await options.verifyEvent(candidate)) verified.push(candidate);
|
|
14924
|
+
} catch {
|
|
14925
|
+
}
|
|
14926
|
+
}
|
|
14927
|
+
return contactPubkeys(selectReplacement(verified), isStillCurrent);
|
|
14928
|
+
}
|
|
14929
|
+
async function refreshActiveIdentity() {
|
|
14930
|
+
activeRefreshController?.abort();
|
|
14931
|
+
activeRefreshController = void 0;
|
|
14932
|
+
const currentGeneration = ++generation;
|
|
14933
|
+
const capturedPubkey = normalizePubkey(options.getActivePubkey() ?? "");
|
|
14934
|
+
if (!capturedPubkey) return;
|
|
14935
|
+
const controller = new AbortController();
|
|
14936
|
+
activeRefreshController = controller;
|
|
14937
|
+
const isStillCurrent = () => !controller.signal.aborted && isCurrent(capturedPubkey, currentGeneration);
|
|
14938
|
+
try {
|
|
14939
|
+
const follows = await verifiedFollows(capturedPubkey, isStillCurrent, controller.signal);
|
|
14940
|
+
if (!isStillCurrent()) return;
|
|
14941
|
+
const snapshot = { follows, profiles: /* @__PURE__ */ new Map() };
|
|
14942
|
+
snapshots.set(capturedPubkey, snapshot);
|
|
14943
|
+
for (let index = 0; index < follows.length; index += 1) {
|
|
14944
|
+
if (!isStillCurrent()) return;
|
|
14945
|
+
const author = follows[index];
|
|
14946
|
+
let warmed;
|
|
14947
|
+
try {
|
|
14948
|
+
warmed = await options.baseRouter.query(
|
|
14949
|
+
[{ kinds: [0], authors: [author], limit: 1 }],
|
|
14950
|
+
{ authors: [author], limit: 1 }
|
|
14951
|
+
);
|
|
14952
|
+
} catch {
|
|
14953
|
+
}
|
|
14954
|
+
if (!isStillCurrent()) return;
|
|
14955
|
+
if (warmed) {
|
|
14956
|
+
const profile = profileResult(warmed, author);
|
|
14957
|
+
if (profile) snapshot.profiles.set(author, profile);
|
|
14958
|
+
}
|
|
14959
|
+
if ((index + 1) % PROFILE_WARM_YIELD_EVERY === 0 && index + 1 < follows.length) {
|
|
14960
|
+
if (!isStillCurrent()) return;
|
|
14961
|
+
await yieldToEventLoop();
|
|
14962
|
+
if (!isStillCurrent()) return;
|
|
14963
|
+
}
|
|
14964
|
+
}
|
|
14965
|
+
} catch {
|
|
14966
|
+
} finally {
|
|
14967
|
+
if (activeRefreshController === controller) activeRefreshController = void 0;
|
|
14968
|
+
}
|
|
14969
|
+
}
|
|
14970
|
+
function isCurrent(capturedPubkey, currentGeneration) {
|
|
14971
|
+
return generation === currentGeneration && normalizePubkey(options.getActivePubkey() ?? "") === capturedPubkey;
|
|
14972
|
+
}
|
|
14973
|
+
function decorate(router, canReadIdentity) {
|
|
14974
|
+
return {
|
|
14975
|
+
...router.getEvent ? { getEvent: router.getEvent.bind(router) } : {},
|
|
14976
|
+
async query(filters, queryOptions) {
|
|
14977
|
+
const canAugmentAtStart = canReadIdentity?.() ?? true;
|
|
14978
|
+
const activePubkey = normalizePubkey(options.getActivePubkey() ?? "");
|
|
14979
|
+
const snapshot = canAugmentAtStart && activePubkey ? snapshots.get(activePubkey) : void 0;
|
|
14980
|
+
const base = await router.query(filters, queryOptions);
|
|
14981
|
+
if (!canAugmentAtStart || !(canReadIdentity?.() ?? true)) return base;
|
|
14982
|
+
const cached = matchingCachedProfiles(snapshot?.profiles, filters);
|
|
14983
|
+
return mergeResult(base, cached, filters, queryOptions);
|
|
14984
|
+
},
|
|
14985
|
+
subscribe: router.subscribe.bind(router),
|
|
14986
|
+
publish: router.publish.bind(router),
|
|
14987
|
+
resolveRelays: router.resolveRelays.bind(router)
|
|
14988
|
+
};
|
|
14989
|
+
}
|
|
14990
|
+
const unsubscribe = options.subscribeSignerChange?.(() => {
|
|
14991
|
+
void refreshActiveIdentity();
|
|
14992
|
+
});
|
|
14790
14993
|
return {
|
|
14791
|
-
|
|
14792
|
-
|
|
14793
|
-
|
|
14794
|
-
|
|
14795
|
-
|
|
14796
|
-
|
|
14797
|
-
authors: [pubkey],
|
|
14798
|
-
limit: 1
|
|
14799
|
-
}], PAJA_LIVE_QUERY_WAIT_MS);
|
|
14800
|
-
const follows = contactPubkeys(latestEvent(events, PAJA_CONTACT_LIST_KIND, pubkey));
|
|
14801
|
-
followsCache.set(pubkey, follows);
|
|
14802
|
-
return [...follows];
|
|
14994
|
+
getFollows: loadFollows,
|
|
14995
|
+
refreshActiveIdentity,
|
|
14996
|
+
decorate,
|
|
14997
|
+
dispose: () => {
|
|
14998
|
+
activeRefreshController?.abort();
|
|
14999
|
+
unsubscribe?.();
|
|
14803
15000
|
}
|
|
14804
15001
|
};
|
|
14805
15002
|
}
|
|
15003
|
+
function normalizePubkey(value) {
|
|
15004
|
+
return typeof value === "string" && HEX_PUBKEY.test(value) ? value.toLowerCase() : null;
|
|
15005
|
+
}
|
|
15006
|
+
function isContactCandidate(candidate, capturedPubkey) {
|
|
15007
|
+
if (typeof candidate !== "object" || candidate === null) return false;
|
|
15008
|
+
const event = candidate;
|
|
15009
|
+
return event.kind === 3 && normalizePubkey(event.pubkey) === capturedPubkey;
|
|
15010
|
+
}
|
|
15011
|
+
function selectReplacement(events) {
|
|
15012
|
+
return [...events].sort((left, right) => {
|
|
15013
|
+
const timestampOrder = right.created_at - left.created_at;
|
|
15014
|
+
if (timestampOrder !== 0) return timestampOrder;
|
|
15015
|
+
if (left.id === right.id) return 0;
|
|
15016
|
+
return left.id < right.id ? -1 : 1;
|
|
15017
|
+
})[0];
|
|
15018
|
+
}
|
|
15019
|
+
async function contactPubkeys(event, isStillCurrent) {
|
|
15020
|
+
if (!event || !Array.isArray(event.tags)) return [];
|
|
15021
|
+
const follows = /* @__PURE__ */ new Set();
|
|
15022
|
+
for (let index = 0; index < event.tags.length; index += 1) {
|
|
15023
|
+
if (isStillCurrent && !isStillCurrent()) return [];
|
|
15024
|
+
const tag = event.tags[index];
|
|
15025
|
+
if (Array.isArray(tag) && tag[0] === "p") {
|
|
15026
|
+
const pubkey = normalizePubkey(tag[1]);
|
|
15027
|
+
if (pubkey) follows.add(pubkey);
|
|
15028
|
+
}
|
|
15029
|
+
if ((index + 1) % FOLLOW_PARSE_YIELD_EVERY === 0 && index + 1 < event.tags.length) {
|
|
15030
|
+
await yieldToEventLoop();
|
|
15031
|
+
if (isStillCurrent && !isStillCurrent()) return [];
|
|
15032
|
+
}
|
|
15033
|
+
}
|
|
15034
|
+
return [...follows];
|
|
15035
|
+
}
|
|
15036
|
+
function profileResult(result2, author) {
|
|
15037
|
+
let newest;
|
|
15038
|
+
for (const entry of result2.events) {
|
|
15039
|
+
if (entry.event.kind !== 0 || normalizePubkey(entry.event.pubkey) !== author) continue;
|
|
15040
|
+
if (!newest || isNewerProfile(entry, newest)) newest = entry;
|
|
15041
|
+
}
|
|
15042
|
+
return newest;
|
|
15043
|
+
}
|
|
15044
|
+
function isNewerProfile(candidate, existing) {
|
|
15045
|
+
if (candidate.event.created_at !== existing.event.created_at) {
|
|
15046
|
+
return candidate.event.created_at > existing.event.created_at;
|
|
15047
|
+
}
|
|
15048
|
+
return candidate.event.id < existing.event.id;
|
|
15049
|
+
}
|
|
15050
|
+
function yieldToEventLoop() {
|
|
15051
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
15052
|
+
}
|
|
15053
|
+
function matchingCachedProfiles(profiles, filters) {
|
|
15054
|
+
const readyProfiles = profiles ? [...profiles.values()] : [];
|
|
15055
|
+
if (filters.length === 0) return readyProfiles;
|
|
15056
|
+
const matched = /* @__PURE__ */ new Map();
|
|
15057
|
+
for (const filter of filters) {
|
|
15058
|
+
const limit = typeof filter.limit === "number" && filter.limit >= 0 ? filter.limit : void 0;
|
|
15059
|
+
let count = 0;
|
|
15060
|
+
for (const entry of readyProfiles) {
|
|
15061
|
+
if (!matchesAnyFilter2(entry.event, [filter])) continue;
|
|
15062
|
+
if (limit !== void 0 && count >= limit) continue;
|
|
15063
|
+
count += 1;
|
|
15064
|
+
if (!matched.has(entry.event.id)) matched.set(entry.event.id, entry);
|
|
15065
|
+
}
|
|
15066
|
+
}
|
|
15067
|
+
return [...matched.values()];
|
|
15068
|
+
}
|
|
15069
|
+
function mergeResult(base, cached, filters, queryOptions) {
|
|
15070
|
+
const events = /* @__PURE__ */ new Map();
|
|
15071
|
+
for (const entry of base.events) events.set(entry.event.id, entry);
|
|
15072
|
+
for (const entry of cached) {
|
|
15073
|
+
if (!events.has(entry.event.id)) events.set(entry.event.id, entry);
|
|
15074
|
+
}
|
|
15075
|
+
return {
|
|
15076
|
+
events: applyFinalLimits([...events.values()], filters, queryOptions),
|
|
15077
|
+
...base.incomplete === void 0 ? {} : { incomplete: base.incomplete },
|
|
15078
|
+
...base.error === void 0 ? {} : { error: base.error }
|
|
15079
|
+
};
|
|
15080
|
+
}
|
|
15081
|
+
function applyFinalLimits(entries, filters, queryOptions) {
|
|
15082
|
+
const aggregateLimit = typeof queryOptions?.limit === "number" && queryOptions.limit >= 0 ? queryOptions.limit : void 0;
|
|
15083
|
+
const aggregateCapped = aggregateLimit === void 0 ? entries : entries.slice(0, aggregateLimit);
|
|
15084
|
+
if (filters.length === 0) return aggregateCapped;
|
|
15085
|
+
const counts = filters.map(() => 0);
|
|
15086
|
+
return aggregateCapped.filter((entry) => {
|
|
15087
|
+
const matching = filters.flatMap((filter, index) => matchesAnyFilter2(entry.event, [filter]) ? [index] : []);
|
|
15088
|
+
const allowed2 = matching.some((index) => {
|
|
15089
|
+
const limit = filters[index]?.limit;
|
|
15090
|
+
return typeof limit !== "number" || limit < 0 || counts[index] < limit;
|
|
15091
|
+
});
|
|
15092
|
+
if (!allowed2) return false;
|
|
15093
|
+
for (const index of matching) counts[index] += 1;
|
|
15094
|
+
return true;
|
|
15095
|
+
});
|
|
15096
|
+
}
|
|
14806
15097
|
|
|
14807
15098
|
// src/browser-adapter.ts
|
|
14808
15099
|
var DEV_COMMON_PUBKEY = "1".repeat(64);
|
|
@@ -14898,6 +15189,7 @@ function createDefaultIntentHost() {
|
|
|
14898
15189
|
openOrReuse: () => null,
|
|
14899
15190
|
waitForReady: () => void 0,
|
|
14900
15191
|
isCurrent: () => false,
|
|
15192
|
+
getWindowId: () => null,
|
|
14901
15193
|
send: () => void 0
|
|
14902
15194
|
})
|
|
14903
15195
|
};
|
|
@@ -14975,7 +15267,7 @@ function createDevSigner(getSimulation, confirmRequest) {
|
|
|
14975
15267
|
};
|
|
14976
15268
|
}
|
|
14977
15269
|
function getRuntimePubkey(getSimulation, signerProvider) {
|
|
14978
|
-
return
|
|
15270
|
+
return signerProvider?.getPubkey() || (signerProvider?.getMethod() === "dev" ? PAJA_DEV_SIGNER_PUBKEY : "") || getSimulation().identity.pubkey;
|
|
14979
15271
|
}
|
|
14980
15272
|
function createRuntimeSigner(getSimulation, confirmRequest, signerProvider) {
|
|
14981
15273
|
const signer = signerProvider?.getSigner();
|
|
@@ -15012,6 +15304,15 @@ function createDevServices(backend, getSimulation, onThemeService, onThemeBroadc
|
|
|
15012
15304
|
}
|
|
15013
15305
|
})
|
|
15014
15306
|
});
|
|
15307
|
+
const baseOutboxRouter = createOutboxRouter(backend, getSimulation, confirmRequest, signerProvider);
|
|
15308
|
+
const socialCache = createPajaSocialCache({
|
|
15309
|
+
baseRouter: baseOutboxRouter,
|
|
15310
|
+
loadContactList: createPajaContactListLoader(backend, getSimulation, signerProvider),
|
|
15311
|
+
verifyEvent: (event) => verifyEvent(event),
|
|
15312
|
+
getActivePubkey: () => getRuntimePubkey(getSimulation, signerProvider),
|
|
15313
|
+
subscribeSignerChange: signerProvider?.subscribe?.bind(signerProvider)
|
|
15314
|
+
});
|
|
15315
|
+
void socialCache.refreshActiveIdentity();
|
|
15015
15316
|
const services = {
|
|
15016
15317
|
keys: createKeysService(),
|
|
15017
15318
|
resource: createResourceService({
|
|
@@ -15032,7 +15333,13 @@ function createDevServices(backend, getSimulation, onThemeService, onThemeBroadc
|
|
|
15032
15333
|
selectRelayTier: () => getPajaRelayUrls(getSimulation()),
|
|
15033
15334
|
isAvailable: () => backend.isAvailable()
|
|
15034
15335
|
});
|
|
15035
|
-
services.outbox = createOutboxService({
|
|
15336
|
+
services.outbox = createOutboxService({
|
|
15337
|
+
router: baseOutboxRouter,
|
|
15338
|
+
getQueryRouter: (windowId, context) => socialCache.decorate(
|
|
15339
|
+
baseOutboxRouter,
|
|
15340
|
+
() => context?.hasCapability(windowId, "identity:read") ?? false
|
|
15341
|
+
)
|
|
15342
|
+
});
|
|
15036
15343
|
}
|
|
15037
15344
|
if (getSimulation().capabilities.domains.count && typeof backend.count === "function") {
|
|
15038
15345
|
services.count = createCountService({
|
|
@@ -15056,7 +15363,7 @@ function createDevServices(backend, getSimulation, onThemeService, onThemeBroadc
|
|
|
15056
15363
|
if (getSimulation().capabilities.domains.identity) {
|
|
15057
15364
|
services.identity = createIdentityService({
|
|
15058
15365
|
getSigner: () => createRuntimeSigner(getSimulation, confirmRequest, signerProvider),
|
|
15059
|
-
|
|
15366
|
+
getFollows: socialCache.getFollows
|
|
15060
15367
|
});
|
|
15061
15368
|
}
|
|
15062
15369
|
if (getSimulation().notifications.enabled) {
|
|
@@ -18925,7 +19232,7 @@ function aggregateTagValue(tags) {
|
|
|
18925
19232
|
return void 0;
|
|
18926
19233
|
}
|
|
18927
19234
|
|
|
18928
|
-
// ../nip/dist/chunk-
|
|
19235
|
+
// ../nip/dist/chunk-YIG2IRN2.js
|
|
18929
19236
|
var MIB = 1024 * 1024;
|
|
18930
19237
|
var NAPPLET_ARTIFACT_CACHE_NAME = "kehto:napplet-artifacts:v1";
|
|
18931
19238
|
var SCHEMA_VERSION = 1;
|
|
@@ -19390,28 +19697,13 @@ function archetypesFromTags(tags) {
|
|
|
19390
19697
|
"archetype slug must match the convention archetype"
|
|
19391
19698
|
);
|
|
19392
19699
|
}
|
|
19393
|
-
|
|
19394
|
-
|
|
19395
|
-
|
|
19396
|
-
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
}
|
|
19401
|
-
const kind = Number(match[1]);
|
|
19402
|
-
if (!Number.isSafeInteger(kind) || kind < 0) {
|
|
19403
|
-
throw new NappletResolutionError(
|
|
19404
|
-
"invalid-manifest",
|
|
19405
|
-
"archetype event kinds must be unsigned safe integers"
|
|
19406
|
-
);
|
|
19407
|
-
}
|
|
19408
|
-
return kind;
|
|
19409
|
-
});
|
|
19410
|
-
out.push({
|
|
19411
|
-
slug,
|
|
19412
|
-
convention,
|
|
19413
|
-
...eventKinds.length === 0 ? {} : { eventKinds }
|
|
19414
|
-
});
|
|
19700
|
+
if (tag.length !== 3) {
|
|
19701
|
+
throw new NappletResolutionError(
|
|
19702
|
+
"invalid-manifest",
|
|
19703
|
+
"archetype tags must contain exactly slug and convention"
|
|
19704
|
+
);
|
|
19705
|
+
}
|
|
19706
|
+
out.push({ slug, convention });
|
|
19415
19707
|
}
|
|
19416
19708
|
return out;
|
|
19417
19709
|
}
|
|
@@ -19427,10 +19719,14 @@ function parseNappletManifest(event) {
|
|
|
19427
19719
|
if (!aggregateHash) {
|
|
19428
19720
|
throw new NappletResolutionError("invalid-manifest", "manifest has no aggregate x tag");
|
|
19429
19721
|
}
|
|
19722
|
+
const dTag = firstTagValue(event.tags, "d");
|
|
19723
|
+
if (event.kind !== NAPPLET_KIND_NAMED && dTag !== void 0) {
|
|
19724
|
+
throw new NappletResolutionError("invalid-manifest", "only named manifests may carry a d tag");
|
|
19725
|
+
}
|
|
19430
19726
|
return {
|
|
19431
19727
|
kind: event.kind,
|
|
19432
19728
|
pubkey: event.pubkey,
|
|
19433
|
-
dTag:
|
|
19729
|
+
dTag: dTag ?? "",
|
|
19434
19730
|
paths,
|
|
19435
19731
|
aggregateHash,
|
|
19436
19732
|
servers: allTagValues(event.tags, "server"),
|
|
@@ -19773,7 +20069,7 @@ function createPajaIntentTargetOptions(getState, getContext, effects = {}) {
|
|
|
19773
20069
|
const current = state.tabs.find(
|
|
19774
20070
|
(tab2) => matchesInstalledNappletRecord(record, tab2.resolvedTarget) && isCurrentRuntimeTabGeneration(state, context, tab2)
|
|
19775
20071
|
);
|
|
19776
|
-
if (current && params.behavior?.reuse !== false) {
|
|
20072
|
+
if (current && params.behavior?.newWindow !== true && params.behavior?.reuse !== false) {
|
|
19777
20073
|
return bindPajaIntentGeneration(current, record, context.runtime);
|
|
19778
20074
|
}
|
|
19779
20075
|
const resolved = await resolvePajaPointer(record.pointer.value, pajaPointerResolverOptions(context));
|
|
@@ -19810,7 +20106,12 @@ function createPajaIntentTargetOptions(getState, getContext, effects = {}) {
|
|
|
19810
20106
|
}
|
|
19811
20107
|
return true;
|
|
19812
20108
|
},
|
|
19813
|
-
|
|
20109
|
+
getWindowId(generation) {
|
|
20110
|
+
const state = getState();
|
|
20111
|
+
const tab = state ? findRuntimeTabGeneration(state, generation) : null;
|
|
20112
|
+
return tab?.windowId ?? null;
|
|
20113
|
+
},
|
|
20114
|
+
send(generation, params) {
|
|
19814
20115
|
const state = getState();
|
|
19815
20116
|
const context = getContext();
|
|
19816
20117
|
const tab = state && context ? findRuntimeTabGeneration(state, generation) : null;
|
|
@@ -19823,8 +20124,10 @@ function createPajaIntentTargetOptions(getState, getContext, effects = {}) {
|
|
|
19823
20124
|
throw new Error("intent target source is no longer registered");
|
|
19824
20125
|
}
|
|
19825
20126
|
createPajaPostMessageProxy(source, state, tab.windowId).postMessage({
|
|
19826
|
-
type: "
|
|
19827
|
-
|
|
20127
|
+
type: "inc.event",
|
|
20128
|
+
topic: params.convention,
|
|
20129
|
+
sender: params.sender,
|
|
20130
|
+
...params.payload === void 0 ? {} : { payload: params.payload }
|
|
19828
20131
|
}, "*");
|
|
19829
20132
|
}
|
|
19830
20133
|
};
|
|
@@ -19841,12 +20144,12 @@ function pajaPointerResolverOptions(context) {
|
|
|
19841
20144
|
}
|
|
19842
20145
|
function recordSupportsDelivery(record, params) {
|
|
19843
20146
|
return record.archetypes.some(
|
|
19844
|
-
(archetype) => archetype.slug === params.
|
|
20147
|
+
(archetype) => archetype.slug === params.archetype && archetype.convention === params.convention
|
|
19845
20148
|
);
|
|
19846
20149
|
}
|
|
19847
20150
|
function resolvedSupportsDelivery(resolved, params) {
|
|
19848
20151
|
return resolved.manifest.archetypes.some(
|
|
19849
|
-
(archetype) => archetype.slug === params.
|
|
20152
|
+
(archetype) => archetype.slug === params.archetype && archetype.convention === params.convention
|
|
19850
20153
|
);
|
|
19851
20154
|
}
|
|
19852
20155
|
function findRuntimeTabGeneration(state, generation) {
|