@kehto/services 0.16.5 → 0.17.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 +98 -18
- package/dist/index.d.ts +147 -282
- package/dist/index.js +392 -294
- package/dist/index.js.map +1 -1
- package/package.json +7 -8
package/dist/index.js
CHANGED
|
@@ -1,71 +1,3 @@
|
|
|
1
|
-
// src/audio-service.ts
|
|
2
|
-
var AUDIO_SERVICE_VERSION = "1.0.0";
|
|
3
|
-
function createAudioService(options) {
|
|
4
|
-
const sources = /* @__PURE__ */ new Map();
|
|
5
|
-
const onChange = options?.onChange;
|
|
6
|
-
function notify2() {
|
|
7
|
-
onChange?.(new Map(sources));
|
|
8
|
-
}
|
|
9
|
-
const descriptor = {
|
|
10
|
-
name: "audio",
|
|
11
|
-
version: AUDIO_SERVICE_VERSION,
|
|
12
|
-
description: "Audio source registry \u2014 tracks active audio sources per napplet window"
|
|
13
|
-
};
|
|
14
|
-
return {
|
|
15
|
-
descriptor,
|
|
16
|
-
handleMessage(windowId, message, send) {
|
|
17
|
-
if (message.type !== "inc.emit") return;
|
|
18
|
-
const incMessage = message;
|
|
19
|
-
const topic = typeof incMessage.topic === "string" ? incMessage.topic : void 0;
|
|
20
|
-
if (!topic?.startsWith("audio:")) return;
|
|
21
|
-
const action = topic.slice(6);
|
|
22
|
-
const payload = incMessage.payload && typeof incMessage.payload === "object" ? incMessage.payload : {};
|
|
23
|
-
switch (action) {
|
|
24
|
-
case "register": {
|
|
25
|
-
const nappletClass = typeof payload.nappletClass === "string" ? payload.nappletClass : "";
|
|
26
|
-
const title = typeof payload.title === "string" ? payload.title : "";
|
|
27
|
-
sources.set(windowId, { windowId, nappletClass, title, muted: false });
|
|
28
|
-
notify2();
|
|
29
|
-
break;
|
|
30
|
-
}
|
|
31
|
-
case "unregister": {
|
|
32
|
-
if (sources.delete(windowId)) {
|
|
33
|
-
notify2();
|
|
34
|
-
}
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
case "state-changed": {
|
|
38
|
-
const source = sources.get(windowId);
|
|
39
|
-
if (!source) return;
|
|
40
|
-
if (typeof payload.title === "string") {
|
|
41
|
-
source.title = payload.title;
|
|
42
|
-
}
|
|
43
|
-
notify2();
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
case "mute": {
|
|
47
|
-
const targetWindowId = typeof payload.windowId === "string" ? payload.windowId : windowId;
|
|
48
|
-
const muted = payload.muted === true;
|
|
49
|
-
const source = sources.get(targetWindowId);
|
|
50
|
-
if (source) {
|
|
51
|
-
source.muted = muted;
|
|
52
|
-
notify2();
|
|
53
|
-
}
|
|
54
|
-
send({ type: "inc.event", topic: "napplet:audio-muted", payload: { muted } });
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
default:
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
onWindowDestroyed(windowId) {
|
|
62
|
-
if (sources.delete(windowId)) {
|
|
63
|
-
notify2();
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
1
|
// src/notification-service.ts
|
|
70
2
|
var NOTIFICATION_SERVICE_VERSION = "1.0.0";
|
|
71
3
|
var DEFAULT_MAX_PER_WINDOW = 100;
|
|
@@ -97,14 +29,11 @@ function enforceLimit(store, list) {
|
|
|
97
29
|
list.shift();
|
|
98
30
|
}
|
|
99
31
|
}
|
|
100
|
-
function findById(store, id) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return void 0;
|
|
32
|
+
function findById(store, windowId, id) {
|
|
33
|
+
const list = store.notifications.get(windowId);
|
|
34
|
+
const index = list?.findIndex((notification) => notification.id === id) ?? -1;
|
|
35
|
+
if (!list || index === -1) return void 0;
|
|
36
|
+
return [list[index], index];
|
|
108
37
|
}
|
|
109
38
|
function createNotification(store, windowId, title, body) {
|
|
110
39
|
const notification = {
|
|
@@ -121,20 +50,20 @@ function createNotification(store, windowId, title, body) {
|
|
|
121
50
|
notify(store);
|
|
122
51
|
return notification;
|
|
123
52
|
}
|
|
124
|
-
function dismissNotification(store, id) {
|
|
125
|
-
const found = findById(store, id);
|
|
53
|
+
function dismissNotification(store, windowId, id) {
|
|
54
|
+
const found = findById(store, windowId, id);
|
|
126
55
|
if (!found) return;
|
|
127
|
-
const [
|
|
128
|
-
const list = store.notifications.get(
|
|
56
|
+
const [, index] = found;
|
|
57
|
+
const list = store.notifications.get(windowId);
|
|
129
58
|
if (!list) return;
|
|
130
59
|
list.splice(index, 1);
|
|
131
|
-
if (list.length === 0) store.notifications.delete(
|
|
60
|
+
if (list.length === 0) store.notifications.delete(windowId);
|
|
132
61
|
notify(store);
|
|
133
62
|
}
|
|
134
|
-
function markNotificationRead(store, id) {
|
|
135
|
-
const found = findById(store, id);
|
|
63
|
+
function markNotificationRead(store, windowId, id) {
|
|
64
|
+
const found = findById(store, windowId, id);
|
|
136
65
|
if (!found) return;
|
|
137
|
-
const [
|
|
66
|
+
const [notification] = found;
|
|
138
67
|
if (!notification.read) {
|
|
139
68
|
notification.read = true;
|
|
140
69
|
notify(store);
|
|
@@ -151,12 +80,12 @@ function handleNotifyEnvelope(store, windowId, action, msg, send) {
|
|
|
151
80
|
}
|
|
152
81
|
case "dismiss": {
|
|
153
82
|
const notifId = typeof msg.notificationId === "string" ? msg.notificationId : "";
|
|
154
|
-
if (notifId) dismissNotification(store, notifId);
|
|
83
|
+
if (notifId) dismissNotification(store, windowId, notifId);
|
|
155
84
|
break;
|
|
156
85
|
}
|
|
157
86
|
case "read": {
|
|
158
87
|
const notifId = typeof msg.notificationId === "string" ? msg.notificationId : "";
|
|
159
|
-
if (notifId) markNotificationRead(store, notifId);
|
|
88
|
+
if (notifId) markNotificationRead(store, windowId, notifId);
|
|
160
89
|
break;
|
|
161
90
|
}
|
|
162
91
|
case "list": {
|
|
@@ -168,34 +97,6 @@ function handleNotifyEnvelope(store, windowId, action, msg, send) {
|
|
|
168
97
|
break;
|
|
169
98
|
}
|
|
170
99
|
}
|
|
171
|
-
function handleIncNotification(store, windowId, action, payload, send) {
|
|
172
|
-
switch (action) {
|
|
173
|
-
case "create": {
|
|
174
|
-
const title = typeof payload.title === "string" ? payload.title : "";
|
|
175
|
-
const body = typeof payload.body === "string" ? payload.body : "";
|
|
176
|
-
const notification = createNotification(store, windowId, title, body);
|
|
177
|
-
send({ type: "inc.event", topic: "notifications:created", payload: { id: notification.id } });
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
case "dismiss": {
|
|
181
|
-
const id = typeof payload.id === "string" ? payload.id : "";
|
|
182
|
-
if (id) dismissNotification(store, id);
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
case "read": {
|
|
186
|
-
const id = typeof payload.id === "string" ? payload.id : "";
|
|
187
|
-
if (id) markNotificationRead(store, id);
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
case "list": {
|
|
191
|
-
const windowNotifs = store.notifications.get(windowId) ?? [];
|
|
192
|
-
send({ type: "inc.event", topic: "notifications:listed", payload: { notifications: windowNotifs } });
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
default:
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
100
|
function createNotificationService(options) {
|
|
200
101
|
const store = {
|
|
201
102
|
notifications: /* @__PURE__ */ new Map(),
|
|
@@ -203,23 +104,21 @@ function createNotificationService(options) {
|
|
|
203
104
|
maxPerWindow: options?.maxPerWindow ?? DEFAULT_MAX_PER_WINDOW
|
|
204
105
|
};
|
|
205
106
|
const descriptor = {
|
|
206
|
-
name: "
|
|
107
|
+
name: "notify",
|
|
207
108
|
version: NOTIFICATION_SERVICE_VERSION,
|
|
208
109
|
description: "Notification state registry \u2014 tracks notifications per napplet window"
|
|
209
110
|
};
|
|
210
111
|
return {
|
|
211
112
|
descriptor,
|
|
212
113
|
handleMessage(windowId, message, send) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const payload = msg.payload ?? {};
|
|
222
|
-
handleIncNotification(store, windowId, topic.slice(14), payload, send);
|
|
114
|
+
if (!message.type.startsWith("notify.")) return;
|
|
115
|
+
handleNotifyEnvelope(
|
|
116
|
+
store,
|
|
117
|
+
windowId,
|
|
118
|
+
message.type.slice(7),
|
|
119
|
+
message,
|
|
120
|
+
send
|
|
121
|
+
);
|
|
223
122
|
},
|
|
224
123
|
onWindowDestroyed(windowId) {
|
|
225
124
|
if (store.notifications.delete(windowId)) {
|
|
@@ -231,16 +130,16 @@ function createNotificationService(options) {
|
|
|
231
130
|
|
|
232
131
|
// src/identity-service.ts
|
|
233
132
|
var IDENTITY_SERVICE_VERSION = "1.0.0";
|
|
234
|
-
function sendProviderError(send, result2, fallback,
|
|
133
|
+
function sendProviderError(send, result2, fallback, _err) {
|
|
235
134
|
send({
|
|
236
135
|
...result2,
|
|
237
|
-
error:
|
|
136
|
+
error: fallback
|
|
238
137
|
});
|
|
239
138
|
}
|
|
240
139
|
async function getCurrentPubkey(options) {
|
|
241
|
-
const currentSigner = options.getSigner();
|
|
242
|
-
if (!currentSigner?.getPublicKey) return "";
|
|
243
140
|
try {
|
|
141
|
+
const currentSigner = options.getSigner();
|
|
142
|
+
if (!currentSigner?.getPublicKey) return "";
|
|
244
143
|
return await currentSigner.getPublicKey() ?? "";
|
|
245
144
|
} catch {
|
|
246
145
|
return "";
|
|
@@ -253,46 +152,29 @@ function sendOptionalProviderResult(options, send, fallbackResult, errorFallback
|
|
|
253
152
|
}
|
|
254
153
|
Promise.resolve(getCurrentPubkey(options)).then((pubkey) => buildResult(pubkey)).then((result2) => send(result2)).catch((err2) => sendProviderError(send, fallbackResult, errorFallback, err2));
|
|
255
154
|
}
|
|
256
|
-
function sendIdentityError(send, id, typeBase, error) {
|
|
257
|
-
send({ type: `${typeBase}.error`, id, error });
|
|
258
|
-
}
|
|
259
|
-
function sendSignerError(send, id, typeBase, fallback, err2) {
|
|
260
|
-
sendIdentityError(send, id, typeBase, err2?.message ?? fallback);
|
|
261
|
-
}
|
|
262
155
|
function handleGetPublicKey(options, id, send) {
|
|
263
|
-
|
|
264
|
-
if (!signer) {
|
|
265
|
-
const result2 = {
|
|
266
|
-
type: "identity.getPublicKey.result",
|
|
267
|
-
id,
|
|
268
|
-
pubkey: ""
|
|
269
|
-
};
|
|
270
|
-
send(result2);
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
Promise.resolve(signer.getPublicKey?.()).then((pubkey) => {
|
|
156
|
+
Promise.resolve().then(() => options.getSigner()).then(async (signer) => signer?.getPublicKey ? signer.getPublicKey() : "").catch(() => "").then((pubkey) => {
|
|
274
157
|
const result2 = {
|
|
275
158
|
type: "identity.getPublicKey.result",
|
|
276
159
|
id,
|
|
277
160
|
pubkey: pubkey ?? ""
|
|
278
161
|
};
|
|
279
162
|
send(result2);
|
|
280
|
-
})
|
|
163
|
+
});
|
|
281
164
|
}
|
|
282
165
|
function handleGetRelays(options, id, send) {
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
Promise.resolve(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
relays
|
|
166
|
+
const fallbackResult = {
|
|
167
|
+
type: "identity.getRelays.result",
|
|
168
|
+
id,
|
|
169
|
+
relays: {}
|
|
170
|
+
};
|
|
171
|
+
Promise.resolve().then(() => options.getSigner()).then(async (signer) => {
|
|
172
|
+
if (!signer?.getRelays) return fallbackResult;
|
|
173
|
+
return {
|
|
174
|
+
...fallbackResult,
|
|
175
|
+
relays: await signer.getRelays() ?? {}
|
|
293
176
|
};
|
|
294
|
-
|
|
295
|
-
}).catch((err2) => sendSignerError(send, id, "identity.getRelays", "getRelays failed", err2));
|
|
177
|
+
}).catch(() => ({ ...fallbackResult, error: "identity.getRelays failed" })).then(send);
|
|
296
178
|
}
|
|
297
179
|
function handleReadProvider(options, id, message, send) {
|
|
298
180
|
switch (message.type) {
|
|
@@ -301,11 +183,11 @@ function handleReadProvider(options, id, message, send) {
|
|
|
301
183
|
options,
|
|
302
184
|
send,
|
|
303
185
|
{ type: "identity.getProfile.result", id, profile: null },
|
|
304
|
-
"getProfile failed",
|
|
186
|
+
"identity.getProfile failed",
|
|
305
187
|
options.getProfile ? async (pubkey) => ({
|
|
306
188
|
type: "identity.getProfile.result",
|
|
307
189
|
id,
|
|
308
|
-
profile: await options.getProfile(pubkey, message)
|
|
190
|
+
profile: await options.getProfile(pubkey, message) ?? null
|
|
309
191
|
}) : void 0
|
|
310
192
|
);
|
|
311
193
|
return true;
|
|
@@ -314,11 +196,11 @@ function handleReadProvider(options, id, message, send) {
|
|
|
314
196
|
options,
|
|
315
197
|
send,
|
|
316
198
|
{ type: "identity.getFollows.result", id, pubkeys: [] },
|
|
317
|
-
"getFollows failed",
|
|
199
|
+
"identity.getFollows failed",
|
|
318
200
|
options.getFollows ? async (pubkey) => ({
|
|
319
201
|
type: "identity.getFollows.result",
|
|
320
202
|
id,
|
|
321
|
-
pubkeys: await options.getFollows(pubkey, message)
|
|
203
|
+
pubkeys: await options.getFollows(pubkey, message) ?? []
|
|
322
204
|
}) : void 0
|
|
323
205
|
);
|
|
324
206
|
return true;
|
|
@@ -330,11 +212,11 @@ function handleReadProvider(options, id, message, send) {
|
|
|
330
212
|
options,
|
|
331
213
|
send,
|
|
332
214
|
{ type: "identity.getZaps.result", id, zaps: [] },
|
|
333
|
-
"getZaps failed",
|
|
215
|
+
"identity.getZaps failed",
|
|
334
216
|
options.getZaps ? async (pubkey) => ({
|
|
335
217
|
type: "identity.getZaps.result",
|
|
336
218
|
id,
|
|
337
|
-
zaps: await options.getZaps(pubkey, message)
|
|
219
|
+
zaps: await options.getZaps(pubkey, message) ?? []
|
|
338
220
|
}) : void 0
|
|
339
221
|
);
|
|
340
222
|
return true;
|
|
@@ -343,11 +225,11 @@ function handleReadProvider(options, id, message, send) {
|
|
|
343
225
|
options,
|
|
344
226
|
send,
|
|
345
227
|
{ type: "identity.getMutes.result", id, pubkeys: [] },
|
|
346
|
-
"getMutes failed",
|
|
228
|
+
"identity.getMutes failed",
|
|
347
229
|
options.getMutes ? async (pubkey) => ({
|
|
348
230
|
type: "identity.getMutes.result",
|
|
349
231
|
id,
|
|
350
|
-
pubkeys: await options.getMutes(pubkey, message)
|
|
232
|
+
pubkeys: await options.getMutes(pubkey, message) ?? []
|
|
351
233
|
}) : void 0
|
|
352
234
|
);
|
|
353
235
|
return true;
|
|
@@ -356,11 +238,11 @@ function handleReadProvider(options, id, message, send) {
|
|
|
356
238
|
options,
|
|
357
239
|
send,
|
|
358
240
|
{ type: "identity.getBlocked.result", id, pubkeys: [] },
|
|
359
|
-
"getBlocked failed",
|
|
241
|
+
"identity.getBlocked failed",
|
|
360
242
|
options.getBlocked ? async (pubkey) => ({
|
|
361
243
|
type: "identity.getBlocked.result",
|
|
362
244
|
id,
|
|
363
|
-
pubkeys: await options.getBlocked(pubkey, message)
|
|
245
|
+
pubkeys: await options.getBlocked(pubkey, message) ?? []
|
|
364
246
|
}) : void 0
|
|
365
247
|
);
|
|
366
248
|
return true;
|
|
@@ -369,11 +251,11 @@ function handleReadProvider(options, id, message, send) {
|
|
|
369
251
|
options,
|
|
370
252
|
send,
|
|
371
253
|
{ type: "identity.getBadges.result", id, badges: [] },
|
|
372
|
-
"getBadges failed",
|
|
254
|
+
"identity.getBadges failed",
|
|
373
255
|
options.getBadges ? async (pubkey) => ({
|
|
374
256
|
type: "identity.getBadges.result",
|
|
375
257
|
id,
|
|
376
|
-
badges: await options.getBadges(pubkey, message)
|
|
258
|
+
badges: await options.getBadges(pubkey, message) ?? []
|
|
377
259
|
}) : void 0
|
|
378
260
|
);
|
|
379
261
|
return true;
|
|
@@ -386,11 +268,11 @@ function handleGetList(options, id, message, send) {
|
|
|
386
268
|
options,
|
|
387
269
|
send,
|
|
388
270
|
{ type: "identity.getList.result", id, entries: [] },
|
|
389
|
-
"getList failed",
|
|
271
|
+
"identity.getList failed",
|
|
390
272
|
options.getList ? async (pubkey) => ({
|
|
391
273
|
type: "identity.getList.result",
|
|
392
274
|
id,
|
|
393
|
-
entries: await options.getList(message.listType, pubkey, message)
|
|
275
|
+
entries: await options.getList(message.listType, pubkey, message) ?? []
|
|
394
276
|
}) : void 0
|
|
395
277
|
);
|
|
396
278
|
}
|
|
@@ -404,9 +286,7 @@ function handleIdentityServiceMessage(options, message, send) {
|
|
|
404
286
|
handleGetRelays(options, id, send);
|
|
405
287
|
return;
|
|
406
288
|
default:
|
|
407
|
-
|
|
408
|
-
sendIdentityError(send, id, message.type, `Unknown identity method: ${message.type}`);
|
|
409
|
-
}
|
|
289
|
+
handleReadProvider(options, id, message, send);
|
|
410
290
|
}
|
|
411
291
|
}
|
|
412
292
|
function createIdentityService(options) {
|
|
@@ -430,6 +310,37 @@ import { createRelayEventResultWithHints } from "@kehto/runtime";
|
|
|
430
310
|
var EOSE_FALLBACK_MS = 15e3;
|
|
431
311
|
function createRelayPoolService(options) {
|
|
432
312
|
const tracked = /* @__PURE__ */ new Map();
|
|
313
|
+
function publishEvent(relayMessage, resultType, send) {
|
|
314
|
+
const id = typeof relayMessage.id === "string" ? relayMessage.id : "";
|
|
315
|
+
const event = relayMessage.event;
|
|
316
|
+
if (!event || typeof event !== "object") {
|
|
317
|
+
send({ type: resultType, id, ok: false, error: "invalid event" });
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (!options.isAvailable()) {
|
|
321
|
+
send({ type: resultType, id, ok: false, error: "no relay pool available" });
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
void (async () => {
|
|
325
|
+
try {
|
|
326
|
+
await options.publish(event);
|
|
327
|
+
send({
|
|
328
|
+
type: resultType,
|
|
329
|
+
id,
|
|
330
|
+
ok: true,
|
|
331
|
+
event,
|
|
332
|
+
eventId: event.id
|
|
333
|
+
});
|
|
334
|
+
} catch (error) {
|
|
335
|
+
send({
|
|
336
|
+
type: resultType,
|
|
337
|
+
id,
|
|
338
|
+
ok: false,
|
|
339
|
+
error: error instanceof Error && error.message ? error.message : "publish failed"
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
})();
|
|
343
|
+
}
|
|
433
344
|
return {
|
|
434
345
|
descriptor: {
|
|
435
346
|
name: "relay-pool",
|
|
@@ -489,17 +400,11 @@ function createRelayPoolService(options) {
|
|
|
489
400
|
return;
|
|
490
401
|
}
|
|
491
402
|
if (message.type === "relay.publish") {
|
|
492
|
-
|
|
493
|
-
if (event && typeof event === "object" && options.isAvailable()) {
|
|
494
|
-
options.publish(event);
|
|
495
|
-
}
|
|
403
|
+
publishEvent(relayMessage, "relay.publish.result", send);
|
|
496
404
|
return;
|
|
497
405
|
}
|
|
498
406
|
if (message.type === "relay.publishEncrypted") {
|
|
499
|
-
|
|
500
|
-
if (event && typeof event === "object" && options.isAvailable()) {
|
|
501
|
-
options.publish(event);
|
|
502
|
-
}
|
|
407
|
+
publishEvent(relayMessage, "relay.publishEncrypted.result", send);
|
|
503
408
|
return;
|
|
504
409
|
}
|
|
505
410
|
},
|
|
@@ -651,6 +556,48 @@ function isPlainObject(value) {
|
|
|
651
556
|
// src/coordinated-relay.ts
|
|
652
557
|
import { createRelayEventResult as createRelayEventResult2, createRelayEventResultWithHints as createRelayEventResultWithHints2 } from "@kehto/runtime";
|
|
653
558
|
var DEFAULT_EOSE_TIMEOUT_MS = 15e3;
|
|
559
|
+
function publishCoordinatedEvent(options, relayMessage, send) {
|
|
560
|
+
const id = typeof relayMessage.id === "string" ? relayMessage.id : "";
|
|
561
|
+
const event = relayMessage.event;
|
|
562
|
+
if (!event || typeof event !== "object") {
|
|
563
|
+
send({ type: "relay.publish.result", id, ok: false, error: "invalid event" });
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
if (!options.relayPool.isAvailable()) {
|
|
567
|
+
send({
|
|
568
|
+
type: "relay.publish.result",
|
|
569
|
+
id,
|
|
570
|
+
ok: false,
|
|
571
|
+
error: "no relay pool available"
|
|
572
|
+
});
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
void (async () => {
|
|
576
|
+
try {
|
|
577
|
+
await options.relayPool.publish(event);
|
|
578
|
+
if (options.cache.isAvailable()) {
|
|
579
|
+
try {
|
|
580
|
+
options.cache.store(event);
|
|
581
|
+
} catch {
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
send({
|
|
585
|
+
type: "relay.publish.result",
|
|
586
|
+
id,
|
|
587
|
+
ok: true,
|
|
588
|
+
event,
|
|
589
|
+
eventId: event.id
|
|
590
|
+
});
|
|
591
|
+
} catch (error) {
|
|
592
|
+
send({
|
|
593
|
+
type: "relay.publish.result",
|
|
594
|
+
id,
|
|
595
|
+
ok: false,
|
|
596
|
+
error: error instanceof Error && error.message ? error.message : "publish failed"
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
})();
|
|
600
|
+
}
|
|
654
601
|
function createCoordinatedRelay(options) {
|
|
655
602
|
const timeoutMs = options.eoseTimeoutMs ?? DEFAULT_EOSE_TIMEOUT_MS;
|
|
656
603
|
const subs = /* @__PURE__ */ new Map();
|
|
@@ -758,17 +705,7 @@ function createCoordinatedRelay(options) {
|
|
|
758
705
|
return;
|
|
759
706
|
}
|
|
760
707
|
if (message.type === "relay.publish") {
|
|
761
|
-
|
|
762
|
-
if (!event || typeof event !== "object") return;
|
|
763
|
-
if (options.relayPool.isAvailable()) {
|
|
764
|
-
options.relayPool.publish(event);
|
|
765
|
-
}
|
|
766
|
-
if (options.cache.isAvailable()) {
|
|
767
|
-
try {
|
|
768
|
-
options.cache.store(event);
|
|
769
|
-
} catch {
|
|
770
|
-
}
|
|
771
|
-
}
|
|
708
|
+
publishCoordinatedEvent(options, relayMessage, send);
|
|
772
709
|
return;
|
|
773
710
|
}
|
|
774
711
|
},
|
|
@@ -1628,8 +1565,15 @@ var DEFAULT_THEME = {
|
|
|
1628
1565
|
// fonts, background, title intentionally undefined — all optional per
|
|
1629
1566
|
// @napplet/nap/theme Theme interface.
|
|
1630
1567
|
};
|
|
1568
|
+
function isCompleteTheme(theme) {
|
|
1569
|
+
const colors = theme?.colors;
|
|
1570
|
+
return typeof colors?.background === "string" && typeof colors.text === "string" && typeof colors.primary === "string";
|
|
1571
|
+
}
|
|
1572
|
+
function normalizeTheme(theme) {
|
|
1573
|
+
return isCompleteTheme(theme) ? theme : DEFAULT_THEME;
|
|
1574
|
+
}
|
|
1631
1575
|
function createThemeService(options = {}) {
|
|
1632
|
-
let currentTheme = options.initialTheme
|
|
1576
|
+
let currentTheme = normalizeTheme(options.initialTheme);
|
|
1633
1577
|
const descriptor = {
|
|
1634
1578
|
name: "theme",
|
|
1635
1579
|
version: THEME_SERVICE_VERSION,
|
|
@@ -1648,19 +1592,15 @@ function createThemeService(options = {}) {
|
|
|
1648
1592
|
send(result2);
|
|
1649
1593
|
return;
|
|
1650
1594
|
}
|
|
1651
|
-
|
|
1652
|
-
type: `${message.type}.error`,
|
|
1653
|
-
id,
|
|
1654
|
-
error: `Unknown theme method: ${message.type}`
|
|
1655
|
-
});
|
|
1595
|
+
return;
|
|
1656
1596
|
},
|
|
1657
1597
|
// Theme service has no per-window state to clean up.
|
|
1658
1598
|
onWindowDestroyed(_windowId) {
|
|
1659
1599
|
}
|
|
1660
1600
|
};
|
|
1661
1601
|
function publishTheme(theme) {
|
|
1662
|
-
currentTheme = theme;
|
|
1663
|
-
const envelope = { type: "theme.changed", theme };
|
|
1602
|
+
currentTheme = normalizeTheme(theme);
|
|
1603
|
+
const envelope = { type: "theme.changed", theme: currentTheme };
|
|
1664
1604
|
options.onBroadcast?.(envelope);
|
|
1665
1605
|
return envelope;
|
|
1666
1606
|
}
|
|
@@ -3226,42 +3166,125 @@ function toErrorMessage4(err2) {
|
|
|
3226
3166
|
if (typeof err2 === "string") return err2;
|
|
3227
3167
|
return "intent request failed";
|
|
3228
3168
|
}
|
|
3169
|
+
function hasOwn(value, key) {
|
|
3170
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
3171
|
+
}
|
|
3172
|
+
function validateIntentRequest(value) {
|
|
3173
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3174
|
+
return { error: "invalid convention" };
|
|
3175
|
+
}
|
|
3176
|
+
const request = value;
|
|
3177
|
+
if (typeof request.archetype !== "string" || request.archetype.length === 0 || typeof request.action !== "string" || request.action.length === 0 || typeof request.convention !== "string" || request.convention.length === 0) {
|
|
3178
|
+
return { error: "invalid convention" };
|
|
3179
|
+
}
|
|
3180
|
+
const match = /^napplet:([^/?#\s]+)\/([^/?#\s]+)$/.exec(request.convention);
|
|
3181
|
+
if (!match || match[1] !== request.archetype || match[2] !== request.action) {
|
|
3182
|
+
return { error: "invalid convention" };
|
|
3183
|
+
}
|
|
3184
|
+
const allowedKeys = ["archetype", "action", "convention", "payload", "handler", "behavior"];
|
|
3185
|
+
if (Object.keys(request).some((key) => !allowedKeys.includes(key))) {
|
|
3186
|
+
return { error: "invoke rejected" };
|
|
3187
|
+
}
|
|
3188
|
+
if (hasOwn(request, "handler") && request.handler !== void 0 && (typeof request.handler !== "string" || request.handler.length === 0)) {
|
|
3189
|
+
return { error: "invoke rejected" };
|
|
3190
|
+
}
|
|
3191
|
+
let behavior;
|
|
3192
|
+
if (hasOwn(request, "behavior") && request.behavior !== void 0) {
|
|
3193
|
+
if (typeof request.behavior !== "object" || request.behavior === null || Array.isArray(request.behavior)) {
|
|
3194
|
+
return { error: "invoke rejected" };
|
|
3195
|
+
}
|
|
3196
|
+
const record = request.behavior;
|
|
3197
|
+
if (Object.keys(record).some((key) => key !== "focus" && key !== "reuse") || hasOwn(record, "focus") && typeof record.focus !== "boolean" || hasOwn(record, "reuse") && typeof record.reuse !== "boolean") {
|
|
3198
|
+
return { error: "invoke rejected" };
|
|
3199
|
+
}
|
|
3200
|
+
behavior = {
|
|
3201
|
+
...record.focus === void 0 ? {} : { focus: record.focus },
|
|
3202
|
+
...record.reuse === void 0 ? {} : { reuse: record.reuse }
|
|
3203
|
+
};
|
|
3204
|
+
}
|
|
3205
|
+
return {
|
|
3206
|
+
request: {
|
|
3207
|
+
archetype: request.archetype,
|
|
3208
|
+
action: request.action,
|
|
3209
|
+
convention: request.convention,
|
|
3210
|
+
...hasOwn(request, "payload") ? { payload: request.payload } : {},
|
|
3211
|
+
...typeof request.handler === "string" ? { handler: request.handler } : {},
|
|
3212
|
+
...behavior === void 0 ? {} : { behavior }
|
|
3213
|
+
}
|
|
3214
|
+
};
|
|
3215
|
+
}
|
|
3216
|
+
function settleResolverCall(call, send, resultType, id, onValue) {
|
|
3217
|
+
let pending;
|
|
3218
|
+
try {
|
|
3219
|
+
pending = Promise.resolve(call());
|
|
3220
|
+
} catch (error) {
|
|
3221
|
+
send({ type: resultType, id, error: toErrorMessage4(error) });
|
|
3222
|
+
return;
|
|
3223
|
+
}
|
|
3224
|
+
pending.then((value) => send(onValue(value))).catch((error) => send({ type: resultType, id, error: toErrorMessage4(error) }));
|
|
3225
|
+
}
|
|
3226
|
+
function rejectInvoke(send, id) {
|
|
3227
|
+
send({
|
|
3228
|
+
type: "intent.invoke.result",
|
|
3229
|
+
id,
|
|
3230
|
+
result: { ok: false, error: "invoke rejected" }
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3229
3233
|
function createIntentService(options) {
|
|
3230
3234
|
if (!options || typeof options.resolver !== "object" || options.resolver === null) {
|
|
3231
3235
|
throw new Error("createIntentService: options.resolver is required");
|
|
3232
3236
|
}
|
|
3233
3237
|
const { resolver } = options;
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
for (const send of windows.values()) {
|
|
3237
|
-
send({ type: "intent.changed", availability });
|
|
3238
|
-
}
|
|
3239
|
-
});
|
|
3240
|
-
function settle6(call, send, resultType, id, onValue) {
|
|
3241
|
-
let pending;
|
|
3242
|
-
try {
|
|
3243
|
-
pending = Promise.resolve(call());
|
|
3244
|
-
} catch (err2) {
|
|
3245
|
-
send({ type: resultType, id, error: toErrorMessage4(err2) });
|
|
3246
|
-
return;
|
|
3247
|
-
}
|
|
3248
|
-
pending.then((value) => send(onValue(value))).catch((err2) => send({ type: resultType, id, error: toErrorMessage4(err2) }));
|
|
3249
|
-
}
|
|
3238
|
+
let runtimeContext;
|
|
3239
|
+
let unsubscribeChanged;
|
|
3250
3240
|
function handleInvoke(windowId, msg, send) {
|
|
3251
3241
|
const m = msg;
|
|
3252
3242
|
const id = m.id ?? "";
|
|
3253
|
-
const
|
|
3254
|
-
if (
|
|
3255
|
-
send({
|
|
3243
|
+
const validation = validateIntentRequest(m.request);
|
|
3244
|
+
if ("error" in validation) {
|
|
3245
|
+
send({
|
|
3246
|
+
type: "intent.invoke.result",
|
|
3247
|
+
id,
|
|
3248
|
+
result: { ok: false, error: validation.error }
|
|
3249
|
+
});
|
|
3256
3250
|
return;
|
|
3257
3251
|
}
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
send,
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3252
|
+
const sender = runtimeContext?.resolveDTag(windowId);
|
|
3253
|
+
if (!sender) {
|
|
3254
|
+
rejectInvoke(send, id);
|
|
3255
|
+
return;
|
|
3256
|
+
}
|
|
3257
|
+
let pending;
|
|
3258
|
+
try {
|
|
3259
|
+
pending = Promise.resolve(resolver.invoke(validation.request, { sender }));
|
|
3260
|
+
} catch {
|
|
3261
|
+
rejectInvoke(send, id);
|
|
3262
|
+
return;
|
|
3263
|
+
}
|
|
3264
|
+
void pending.then(
|
|
3265
|
+
(outcome) => {
|
|
3266
|
+
if (outcome.result.ok && (!("retained" in outcome) || typeof outcome.retained.start !== "function")) {
|
|
3267
|
+
rejectInvoke(send, id);
|
|
3268
|
+
return;
|
|
3269
|
+
}
|
|
3270
|
+
send({
|
|
3271
|
+
type: "intent.invoke.result",
|
|
3272
|
+
id,
|
|
3273
|
+
result: outcome.result
|
|
3274
|
+
});
|
|
3275
|
+
if (!outcome.result.ok || !("retained" in outcome)) return;
|
|
3276
|
+
try {
|
|
3277
|
+
const started = outcome.retained.start();
|
|
3278
|
+
void Promise.resolve(started).catch(() => {
|
|
3279
|
+
});
|
|
3280
|
+
} catch {
|
|
3281
|
+
}
|
|
3282
|
+
},
|
|
3283
|
+
() => {
|
|
3284
|
+
rejectInvoke(send, id);
|
|
3285
|
+
}
|
|
3286
|
+
).catch(() => {
|
|
3287
|
+
});
|
|
3265
3288
|
}
|
|
3266
3289
|
function handleAvailable(msg, send) {
|
|
3267
3290
|
const m = msg;
|
|
@@ -3271,7 +3294,7 @@ function createIntentService(options) {
|
|
|
3271
3294
|
return;
|
|
3272
3295
|
}
|
|
3273
3296
|
const archetype = m.archetype;
|
|
3274
|
-
|
|
3297
|
+
settleResolverCall(
|
|
3275
3298
|
() => resolver.available(archetype),
|
|
3276
3299
|
send,
|
|
3277
3300
|
"intent.available.result",
|
|
@@ -3282,7 +3305,7 @@ function createIntentService(options) {
|
|
|
3282
3305
|
function handleHandlers(msg, send) {
|
|
3283
3306
|
const m = msg;
|
|
3284
3307
|
const id = m.id ?? "";
|
|
3285
|
-
|
|
3308
|
+
settleResolverCall(
|
|
3286
3309
|
() => resolver.handlers(),
|
|
3287
3310
|
send,
|
|
3288
3311
|
"intent.handlers.result",
|
|
@@ -3292,8 +3315,31 @@ function createIntentService(options) {
|
|
|
3292
3315
|
}
|
|
3293
3316
|
return {
|
|
3294
3317
|
descriptor: INTENT_DESCRIPTOR,
|
|
3318
|
+
onRegistered(context) {
|
|
3319
|
+
try {
|
|
3320
|
+
unsubscribeChanged?.();
|
|
3321
|
+
} catch {
|
|
3322
|
+
}
|
|
3323
|
+
runtimeContext = context;
|
|
3324
|
+
unsubscribeChanged = resolver.onChanged?.((availability) => {
|
|
3325
|
+
const current = runtimeContext;
|
|
3326
|
+
if (!current) return;
|
|
3327
|
+
const message = { type: "intent.changed", availability };
|
|
3328
|
+
for (const windowId of current.listWindowIds()) {
|
|
3329
|
+
current.sendToEligibleNapplet(windowId, message);
|
|
3330
|
+
}
|
|
3331
|
+
});
|
|
3332
|
+
},
|
|
3333
|
+
onUnregistered() {
|
|
3334
|
+
const unsubscribe = unsubscribeChanged;
|
|
3335
|
+
unsubscribeChanged = void 0;
|
|
3336
|
+
runtimeContext = void 0;
|
|
3337
|
+
try {
|
|
3338
|
+
unsubscribe?.();
|
|
3339
|
+
} catch {
|
|
3340
|
+
}
|
|
3341
|
+
},
|
|
3295
3342
|
handleMessage(windowId, message, send) {
|
|
3296
|
-
windows.set(windowId, send);
|
|
3297
3343
|
switch (message.type) {
|
|
3298
3344
|
case "intent.invoke":
|
|
3299
3345
|
handleInvoke(windowId, message, send);
|
|
@@ -3307,9 +3353,6 @@ function createIntentService(options) {
|
|
|
3307
3353
|
default:
|
|
3308
3354
|
return;
|
|
3309
3355
|
}
|
|
3310
|
-
},
|
|
3311
|
-
onWindowDestroyed(windowId) {
|
|
3312
|
-
windows.delete(windowId);
|
|
3313
3356
|
}
|
|
3314
3357
|
};
|
|
3315
3358
|
}
|
|
@@ -3323,91 +3366,126 @@ function candidatesFor(catalog, archetype, defaultHandler) {
|
|
|
3323
3366
|
candidates.push({
|
|
3324
3367
|
dTag: entry.dTag,
|
|
3325
3368
|
...entry.title === void 0 ? {} : { title: entry.title },
|
|
3326
|
-
actions: support.actions,
|
|
3327
|
-
|
|
3369
|
+
actions: [...support.actions],
|
|
3370
|
+
conventions: [...support.conventions],
|
|
3371
|
+
contracts: support.contracts.map((contract) => ({
|
|
3372
|
+
convention: contract.convention,
|
|
3373
|
+
...contract.eventKinds === void 0 ? {} : { eventKinds: [...contract.eventKinds] }
|
|
3374
|
+
})),
|
|
3328
3375
|
...entry.dTag === defaultHandler ? { isDefault: true } : {}
|
|
3329
3376
|
});
|
|
3330
3377
|
}
|
|
3331
3378
|
return candidates;
|
|
3332
3379
|
}
|
|
3333
|
-
function
|
|
3334
|
-
return { ok: false,
|
|
3380
|
+
function reject(error) {
|
|
3381
|
+
return { ok: false, error };
|
|
3335
3382
|
}
|
|
3336
3383
|
function createCatalogIntentResolver(options) {
|
|
3337
3384
|
if (!options || typeof options.loadCatalog !== "function") {
|
|
3338
3385
|
throw new Error("createCatalogIntentResolver: options.loadCatalog is required");
|
|
3339
3386
|
}
|
|
3340
|
-
if (!options.
|
|
3341
|
-
throw new Error("createCatalogIntentResolver: options.
|
|
3387
|
+
if (!options.targets || typeof options.targets.retain !== "function") {
|
|
3388
|
+
throw new Error("createCatalogIntentResolver: options.targets is required");
|
|
3342
3389
|
}
|
|
3343
|
-
const {
|
|
3390
|
+
const {
|
|
3391
|
+
loadCatalog,
|
|
3392
|
+
targets,
|
|
3393
|
+
getDefaultHandler,
|
|
3394
|
+
chooseHandler,
|
|
3395
|
+
authorizeExplicitHandler
|
|
3396
|
+
} = options;
|
|
3344
3397
|
const listeners = /* @__PURE__ */ new Set();
|
|
3345
3398
|
async function availabilityFor(archetype) {
|
|
3346
3399
|
const catalog = await loadCatalog();
|
|
3347
|
-
const
|
|
3348
|
-
const candidates = candidatesFor(catalog, archetype,
|
|
3400
|
+
const defaultHandler = getDefaultHandler?.(archetype);
|
|
3401
|
+
const candidates = candidatesFor(catalog, archetype, defaultHandler);
|
|
3349
3402
|
return {
|
|
3350
3403
|
archetype,
|
|
3351
3404
|
available: candidates.length > 0,
|
|
3352
3405
|
candidates,
|
|
3353
|
-
hasDefault:
|
|
3406
|
+
hasDefault: defaultHandler !== void 0 && candidates.some((candidate) => candidate.dTag === defaultHandler)
|
|
3354
3407
|
};
|
|
3355
3408
|
}
|
|
3356
|
-
async function
|
|
3409
|
+
async function chooseCompatible(request, candidates, sender) {
|
|
3410
|
+
if (!chooseHandler) {
|
|
3411
|
+
return request.handler === "choose" ? { error: "user cancelled" } : { error: "invoke rejected" };
|
|
3412
|
+
}
|
|
3413
|
+
const picked = await chooseHandler(request.archetype, candidates, sender);
|
|
3414
|
+
if (picked === void 0) return { error: "user cancelled" };
|
|
3415
|
+
const candidate = candidates.find((item) => item.dTag === picked);
|
|
3416
|
+
return candidate ? { candidate } : { error: "invoke rejected" };
|
|
3417
|
+
}
|
|
3418
|
+
async function pickHandler(request, compatible, sender) {
|
|
3419
|
+
const preference = request.handler;
|
|
3357
3420
|
if (typeof preference === "string" && preference !== "default" && preference !== "choose") {
|
|
3358
|
-
|
|
3421
|
+
const candidate = compatible.find((item) => item.dTag === preference);
|
|
3422
|
+
if (!candidate || !authorizeExplicitHandler) return { error: "invoke rejected" };
|
|
3423
|
+
let authorized = false;
|
|
3424
|
+
try {
|
|
3425
|
+
authorized = await authorizeExplicitHandler(sender, preference, request, candidate);
|
|
3426
|
+
} catch {
|
|
3427
|
+
return { error: "invoke rejected" };
|
|
3428
|
+
}
|
|
3429
|
+
return authorized ? { candidate } : { error: "invoke rejected" };
|
|
3359
3430
|
}
|
|
3360
3431
|
if (preference === "choose") {
|
|
3361
|
-
|
|
3362
|
-
return picked ?? null;
|
|
3432
|
+
return chooseCompatible(request, compatible, sender);
|
|
3363
3433
|
}
|
|
3364
|
-
const
|
|
3365
|
-
if (
|
|
3366
|
-
|
|
3367
|
-
if (chooseHandler) {
|
|
3368
|
-
const picked = await chooseHandler(archetype, candidates, callerWindowId);
|
|
3369
|
-
return picked ?? null;
|
|
3434
|
+
const defaultCandidate = compatible.find((candidate) => candidate.isDefault === true);
|
|
3435
|
+
if (preference === "default") {
|
|
3436
|
+
return defaultCandidate ? { candidate: defaultCandidate } : { error: "invoke rejected" };
|
|
3370
3437
|
}
|
|
3371
|
-
return
|
|
3438
|
+
if (defaultCandidate) return { candidate: defaultCandidate };
|
|
3439
|
+
if (compatible.length === 1) return { candidate: compatible[0] };
|
|
3440
|
+
return chooseCompatible(request, compatible, sender);
|
|
3372
3441
|
}
|
|
3373
3442
|
async function invoke(request, context) {
|
|
3374
|
-
const archetype = request.archetype;
|
|
3375
|
-
const action = request.action ?? "open";
|
|
3376
3443
|
const catalog = await loadCatalog();
|
|
3377
|
-
const
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
if (
|
|
3384
|
-
|
|
3385
|
-
if (protocol !== void 0 && handler.protocols.length > 0 && !handler.protocols.includes(protocol)) {
|
|
3386
|
-
return fail(archetype, action, "unsupported protocol");
|
|
3444
|
+
const defaultHandler = getDefaultHandler?.(request.archetype);
|
|
3445
|
+
const candidates = candidatesFor(catalog, request.archetype, defaultHandler);
|
|
3446
|
+
if (candidates.length === 0) return { result: reject("no handler") };
|
|
3447
|
+
const compatible = candidates.filter((candidate) => candidate.contracts.some((contract) => contract.convention === request.convention));
|
|
3448
|
+
if (compatible.length === 0) return { result: reject("unsupported convention") };
|
|
3449
|
+
const sender = context.sender;
|
|
3450
|
+
if (typeof sender !== "string" || sender.length === 0) {
|
|
3451
|
+
return { result: reject("invoke rejected") };
|
|
3387
3452
|
}
|
|
3388
|
-
|
|
3453
|
+
const selected = await pickHandler(request, compatible, sender);
|
|
3454
|
+
if ("error" in selected) return { result: reject(selected.error) };
|
|
3455
|
+
const delivery = Object.freeze({
|
|
3456
|
+
sender,
|
|
3457
|
+
archetype: request.archetype,
|
|
3458
|
+
action: request.action,
|
|
3459
|
+
convention: request.convention,
|
|
3460
|
+
...request.payload === void 0 ? {} : { payload: request.payload }
|
|
3461
|
+
});
|
|
3462
|
+
const behavior = request.behavior === void 0 ? void 0 : Object.freeze({
|
|
3463
|
+
...request.behavior.focus === void 0 ? {} : { focus: request.behavior.focus },
|
|
3464
|
+
...request.behavior.reuse === void 0 ? {} : { reuse: request.behavior.reuse }
|
|
3465
|
+
});
|
|
3466
|
+
const params = Object.freeze({
|
|
3467
|
+
handler: selected.candidate.dTag,
|
|
3468
|
+
delivery,
|
|
3469
|
+
...behavior === void 0 ? {} : { behavior }
|
|
3470
|
+
});
|
|
3471
|
+
let retained;
|
|
3389
3472
|
try {
|
|
3390
|
-
|
|
3391
|
-
dTag: handler.dTag,
|
|
3392
|
-
archetype,
|
|
3393
|
-
action,
|
|
3394
|
-
...protocol === void 0 ? {} : { protocol },
|
|
3395
|
-
...request.payload === void 0 ? {} : { payload: request.payload },
|
|
3396
|
-
...request.behavior === void 0 ? {} : { behavior: request.behavior },
|
|
3397
|
-
callerWindowId: context.windowId
|
|
3398
|
-
});
|
|
3399
|
-
windowId = opened.windowId;
|
|
3473
|
+
retained = await targets.retain(params);
|
|
3400
3474
|
} catch {
|
|
3401
|
-
return
|
|
3475
|
+
return { result: reject("invoke rejected") };
|
|
3476
|
+
}
|
|
3477
|
+
if (!retained || typeof retained.start !== "function") {
|
|
3478
|
+
return { result: reject("invoke rejected") };
|
|
3402
3479
|
}
|
|
3403
3480
|
return {
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3481
|
+
result: {
|
|
3482
|
+
ok: true,
|
|
3483
|
+
archetype: request.archetype,
|
|
3484
|
+
action: request.action,
|
|
3485
|
+
convention: request.convention,
|
|
3486
|
+
handler: selected.candidate.dTag
|
|
3487
|
+
},
|
|
3488
|
+
retained
|
|
3411
3489
|
};
|
|
3412
3490
|
}
|
|
3413
3491
|
async function handlers() {
|
|
@@ -3416,7 +3494,7 @@ function createCatalogIntentResolver(options) {
|
|
|
3416
3494
|
for (const entry of catalog) {
|
|
3417
3495
|
for (const slug of Object.keys(entry.archetypes)) archetypes.add(slug);
|
|
3418
3496
|
}
|
|
3419
|
-
return Promise.all([...archetypes].map((
|
|
3497
|
+
return Promise.all([...archetypes].map((archetype) => availabilityFor(archetype)));
|
|
3420
3498
|
}
|
|
3421
3499
|
return {
|
|
3422
3500
|
invoke,
|
|
@@ -3436,14 +3514,35 @@ function createCatalogIntentResolver(options) {
|
|
|
3436
3514
|
}
|
|
3437
3515
|
|
|
3438
3516
|
// src/manifest-intent-catalog.ts
|
|
3439
|
-
|
|
3517
|
+
function actionFromConvention(slug, convention) {
|
|
3518
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
|
|
3519
|
+
throw new TypeError("manifest archetype slug is invalid");
|
|
3520
|
+
}
|
|
3521
|
+
const match = /^napplet:([^/?#\s]+)\/([^/?#\s]+)$/.exec(convention);
|
|
3522
|
+
if (!match || match[1] !== slug) {
|
|
3523
|
+
throw new TypeError("manifest archetype convention is invalid or mismatched");
|
|
3524
|
+
}
|
|
3525
|
+
return match[2];
|
|
3526
|
+
}
|
|
3440
3527
|
function manifestToIntentCatalogEntry(manifest) {
|
|
3441
3528
|
const archetypes = {};
|
|
3442
|
-
for (const { slug,
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3529
|
+
for (const { slug, convention, eventKinds } of manifest.archetypes) {
|
|
3530
|
+
const action = actionFromConvention(slug, convention);
|
|
3531
|
+
if (eventKinds?.some((kind) => !Number.isSafeInteger(kind) || kind < 0)) {
|
|
3532
|
+
throw new TypeError("manifest archetype event kinds must be unsigned safe integers");
|
|
3533
|
+
}
|
|
3534
|
+
const support = archetypes[slug] ??= {
|
|
3535
|
+
actions: [],
|
|
3536
|
+
conventions: [],
|
|
3537
|
+
contracts: []
|
|
3538
|
+
};
|
|
3539
|
+
const contract = {
|
|
3540
|
+
convention,
|
|
3541
|
+
...eventKinds === void 0 || eventKinds.length === 0 ? {} : { eventKinds: [...eventKinds] }
|
|
3446
3542
|
};
|
|
3543
|
+
support.contracts.push(contract);
|
|
3544
|
+
if (!support.actions.includes(action)) support.actions.push(action);
|
|
3545
|
+
if (!support.conventions.includes(convention)) support.conventions.push(convention);
|
|
3447
3546
|
}
|
|
3448
3547
|
return {
|
|
3449
3548
|
dTag: manifest.dTag,
|
|
@@ -4932,7 +5031,6 @@ function createCordnDmAdapter(options) {
|
|
|
4932
5031
|
}
|
|
4933
5032
|
export {
|
|
4934
5033
|
DmMemoryStore,
|
|
4935
|
-
createAudioService,
|
|
4936
5034
|
createBleService,
|
|
4937
5035
|
createBrowserMediaBridge,
|
|
4938
5036
|
createCacheService,
|