@kehto/services 0.8.0 → 0.10.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 +13 -13
- package/dist/index.d.ts +382 -25
- package/dist/index.js +247 -6
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1366,7 +1366,7 @@ var DEFAULT_THEME = {
|
|
|
1366
1366
|
primary: "#7aa2f7"
|
|
1367
1367
|
}
|
|
1368
1368
|
// fonts, background, title intentionally undefined — all optional per
|
|
1369
|
-
// @napplet/
|
|
1369
|
+
// @napplet/nap/theme Theme interface.
|
|
1370
1370
|
};
|
|
1371
1371
|
function createThemeService(options = {}) {
|
|
1372
1372
|
let currentTheme = options.initialTheme ?? DEFAULT_THEME;
|
|
@@ -2396,6 +2396,244 @@ function toErrorMessage3(err) {
|
|
|
2396
2396
|
return "upload failed";
|
|
2397
2397
|
}
|
|
2398
2398
|
|
|
2399
|
+
// src/intent-service.ts
|
|
2400
|
+
var INTENT_SERVICE_VERSION = "1.0.0";
|
|
2401
|
+
var INTENT_DESCRIPTOR = {
|
|
2402
|
+
name: "intent",
|
|
2403
|
+
version: INTENT_SERVICE_VERSION,
|
|
2404
|
+
description: "NAP-INTENT archetype intent dispatch \u2014 invoke/available/handlers"
|
|
2405
|
+
};
|
|
2406
|
+
function toErrorMessage4(err) {
|
|
2407
|
+
if (err instanceof Error) return err.message;
|
|
2408
|
+
if (typeof err === "string") return err;
|
|
2409
|
+
return "intent request failed";
|
|
2410
|
+
}
|
|
2411
|
+
function createIntentService(options) {
|
|
2412
|
+
if (!options || typeof options.resolver !== "object" || options.resolver === null) {
|
|
2413
|
+
throw new Error("createIntentService: options.resolver is required");
|
|
2414
|
+
}
|
|
2415
|
+
const { resolver } = options;
|
|
2416
|
+
const windows = /* @__PURE__ */ new Map();
|
|
2417
|
+
resolver.onChanged?.((availability) => {
|
|
2418
|
+
for (const send of windows.values()) {
|
|
2419
|
+
send({ type: "intent.changed", availability });
|
|
2420
|
+
}
|
|
2421
|
+
});
|
|
2422
|
+
function settle(call, send, resultType, id, onValue) {
|
|
2423
|
+
let pending;
|
|
2424
|
+
try {
|
|
2425
|
+
pending = Promise.resolve(call());
|
|
2426
|
+
} catch (err) {
|
|
2427
|
+
send({ type: resultType, id, error: toErrorMessage4(err) });
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
pending.then((value) => send(onValue(value))).catch((err) => send({ type: resultType, id, error: toErrorMessage4(err) }));
|
|
2431
|
+
}
|
|
2432
|
+
function handleInvoke(windowId, msg, send) {
|
|
2433
|
+
const m = msg;
|
|
2434
|
+
const id = m.id ?? "";
|
|
2435
|
+
const request = m.request;
|
|
2436
|
+
if (!request || typeof request !== "object" || typeof request.archetype !== "string" || request.archetype.length === 0) {
|
|
2437
|
+
send({ type: "intent.invoke.result", id, error: "invalid request" });
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
settle(
|
|
2441
|
+
() => resolver.invoke(request, { windowId }),
|
|
2442
|
+
send,
|
|
2443
|
+
"intent.invoke.result",
|
|
2444
|
+
id,
|
|
2445
|
+
(result) => ({ type: "intent.invoke.result", id, result })
|
|
2446
|
+
);
|
|
2447
|
+
}
|
|
2448
|
+
function handleAvailable(msg, send) {
|
|
2449
|
+
const m = msg;
|
|
2450
|
+
const id = m.id ?? "";
|
|
2451
|
+
if (typeof m.archetype !== "string" || m.archetype.length === 0) {
|
|
2452
|
+
send({ type: "intent.available.result", id, error: "invalid archetype" });
|
|
2453
|
+
return;
|
|
2454
|
+
}
|
|
2455
|
+
const archetype = m.archetype;
|
|
2456
|
+
settle(
|
|
2457
|
+
() => resolver.available(archetype),
|
|
2458
|
+
send,
|
|
2459
|
+
"intent.available.result",
|
|
2460
|
+
id,
|
|
2461
|
+
(availability) => ({ type: "intent.available.result", id, availability })
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2464
|
+
function handleHandlers(msg, send) {
|
|
2465
|
+
const m = msg;
|
|
2466
|
+
const id = m.id ?? "";
|
|
2467
|
+
settle(
|
|
2468
|
+
() => resolver.handlers(),
|
|
2469
|
+
send,
|
|
2470
|
+
"intent.handlers.result",
|
|
2471
|
+
id,
|
|
2472
|
+
(handlers) => ({ type: "intent.handlers.result", id, handlers })
|
|
2473
|
+
);
|
|
2474
|
+
}
|
|
2475
|
+
return {
|
|
2476
|
+
descriptor: INTENT_DESCRIPTOR,
|
|
2477
|
+
handleMessage(windowId, message, send) {
|
|
2478
|
+
windows.set(windowId, send);
|
|
2479
|
+
switch (message.type) {
|
|
2480
|
+
case "intent.invoke":
|
|
2481
|
+
handleInvoke(windowId, message, send);
|
|
2482
|
+
return;
|
|
2483
|
+
case "intent.available":
|
|
2484
|
+
handleAvailable(message, send);
|
|
2485
|
+
return;
|
|
2486
|
+
case "intent.handlers":
|
|
2487
|
+
handleHandlers(message, send);
|
|
2488
|
+
return;
|
|
2489
|
+
default:
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
},
|
|
2493
|
+
onWindowDestroyed(windowId) {
|
|
2494
|
+
windows.delete(windowId);
|
|
2495
|
+
}
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
// src/catalog-intent-resolver.ts
|
|
2500
|
+
function candidatesFor(catalog, archetype, defaultHandler) {
|
|
2501
|
+
const candidates = [];
|
|
2502
|
+
for (const entry of catalog) {
|
|
2503
|
+
const support = entry.archetypes[archetype];
|
|
2504
|
+
if (!support) continue;
|
|
2505
|
+
candidates.push({
|
|
2506
|
+
dTag: entry.dTag,
|
|
2507
|
+
...entry.title === void 0 ? {} : { title: entry.title },
|
|
2508
|
+
actions: support.actions,
|
|
2509
|
+
protocols: support.protocols,
|
|
2510
|
+
...entry.dTag === defaultHandler ? { isDefault: true } : {}
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
return candidates;
|
|
2514
|
+
}
|
|
2515
|
+
function fail(archetype, action, error) {
|
|
2516
|
+
return { ok: false, archetype, action, handled: false, error };
|
|
2517
|
+
}
|
|
2518
|
+
function createCatalogIntentResolver(options) {
|
|
2519
|
+
if (!options || typeof options.loadCatalog !== "function") {
|
|
2520
|
+
throw new Error("createCatalogIntentResolver: options.loadCatalog is required");
|
|
2521
|
+
}
|
|
2522
|
+
if (!options.windows || typeof options.windows.open !== "function") {
|
|
2523
|
+
throw new Error("createCatalogIntentResolver: options.windows is required");
|
|
2524
|
+
}
|
|
2525
|
+
const { loadCatalog, windows, getDefaultHandler, chooseHandler, defaultProtocol } = options;
|
|
2526
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2527
|
+
async function availabilityFor(archetype) {
|
|
2528
|
+
const catalog = await loadCatalog();
|
|
2529
|
+
const def = getDefaultHandler?.(archetype);
|
|
2530
|
+
const candidates = candidatesFor(catalog, archetype, def);
|
|
2531
|
+
return {
|
|
2532
|
+
archetype,
|
|
2533
|
+
available: candidates.length > 0,
|
|
2534
|
+
candidates,
|
|
2535
|
+
hasDefault: def !== void 0 && candidates.some((c) => c.dTag === def)
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
async function pickHandler(archetype, candidates, preference, callerWindowId) {
|
|
2539
|
+
if (typeof preference === "string" && preference !== "default" && preference !== "choose") {
|
|
2540
|
+
return preference;
|
|
2541
|
+
}
|
|
2542
|
+
if (preference === "choose") {
|
|
2543
|
+
const picked = await chooseHandler?.(archetype, candidates, callerWindowId);
|
|
2544
|
+
return picked ?? null;
|
|
2545
|
+
}
|
|
2546
|
+
const def = getDefaultHandler?.(archetype);
|
|
2547
|
+
if (def !== void 0 && candidates.some((c) => c.dTag === def)) return def;
|
|
2548
|
+
if (candidates.length === 1) return candidates[0].dTag;
|
|
2549
|
+
if (chooseHandler) {
|
|
2550
|
+
const picked = await chooseHandler(archetype, candidates, callerWindowId);
|
|
2551
|
+
return picked ?? null;
|
|
2552
|
+
}
|
|
2553
|
+
return candidates[0].dTag;
|
|
2554
|
+
}
|
|
2555
|
+
async function invoke(request, context) {
|
|
2556
|
+
const archetype = request.archetype;
|
|
2557
|
+
const action = request.action ?? "open";
|
|
2558
|
+
const catalog = await loadCatalog();
|
|
2559
|
+
const candidates = candidatesFor(catalog, archetype, getDefaultHandler?.(archetype));
|
|
2560
|
+
if (candidates.length === 0) return fail(archetype, action, "no handler");
|
|
2561
|
+
const pickedDTag = await pickHandler(archetype, candidates, request.handler, context.windowId);
|
|
2562
|
+
if (pickedDTag === null) return fail(archetype, action, "user cancelled");
|
|
2563
|
+
const handler = candidates.find((c) => c.dTag === pickedDTag);
|
|
2564
|
+
if (!handler) return fail(archetype, action, "no handler");
|
|
2565
|
+
if (!handler.actions.includes(action)) return fail(archetype, action, "unsupported action");
|
|
2566
|
+
const protocol = request.protocol ?? defaultProtocol?.(archetype) ?? handler.protocols[0];
|
|
2567
|
+
if (protocol !== void 0 && handler.protocols.length > 0 && !handler.protocols.includes(protocol)) {
|
|
2568
|
+
return fail(archetype, action, "unsupported protocol");
|
|
2569
|
+
}
|
|
2570
|
+
let windowId;
|
|
2571
|
+
try {
|
|
2572
|
+
const opened = await windows.open({
|
|
2573
|
+
dTag: handler.dTag,
|
|
2574
|
+
archetype,
|
|
2575
|
+
action,
|
|
2576
|
+
...protocol === void 0 ? {} : { protocol },
|
|
2577
|
+
...request.payload === void 0 ? {} : { payload: request.payload },
|
|
2578
|
+
...request.behavior === void 0 ? {} : { behavior: request.behavior },
|
|
2579
|
+
callerWindowId: context.windowId
|
|
2580
|
+
});
|
|
2581
|
+
windowId = opened.windowId;
|
|
2582
|
+
} catch {
|
|
2583
|
+
return fail(archetype, action, "invoke failed");
|
|
2584
|
+
}
|
|
2585
|
+
return {
|
|
2586
|
+
ok: true,
|
|
2587
|
+
archetype,
|
|
2588
|
+
action,
|
|
2589
|
+
handled: true,
|
|
2590
|
+
handler: handler.dTag,
|
|
2591
|
+
windowId,
|
|
2592
|
+
...protocol === void 0 ? {} : { protocol }
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
async function handlers() {
|
|
2596
|
+
const catalog = await loadCatalog();
|
|
2597
|
+
const archetypes = /* @__PURE__ */ new Set();
|
|
2598
|
+
for (const entry of catalog) {
|
|
2599
|
+
for (const slug of Object.keys(entry.archetypes)) archetypes.add(slug);
|
|
2600
|
+
}
|
|
2601
|
+
return Promise.all([...archetypes].map((a) => availabilityFor(a)));
|
|
2602
|
+
}
|
|
2603
|
+
return {
|
|
2604
|
+
invoke,
|
|
2605
|
+
available: availabilityFor,
|
|
2606
|
+
handlers,
|
|
2607
|
+
onChanged(listener) {
|
|
2608
|
+
listeners.add(listener);
|
|
2609
|
+
return () => listeners.delete(listener);
|
|
2610
|
+
},
|
|
2611
|
+
notifyChanged(archetype) {
|
|
2612
|
+
if (listeners.size === 0) return;
|
|
2613
|
+
void availabilityFor(archetype).then((availability) => {
|
|
2614
|
+
for (const listener of listeners) listener(availability);
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
// src/manifest-intent-catalog.ts
|
|
2621
|
+
var DEFAULT_ACTIONS = ["open"];
|
|
2622
|
+
function manifestToIntentCatalogEntry(manifest) {
|
|
2623
|
+
const archetypes = {};
|
|
2624
|
+
for (const { slug, nap } of manifest.archetypes) {
|
|
2625
|
+
archetypes[slug] = {
|
|
2626
|
+
actions: [...DEFAULT_ACTIONS],
|
|
2627
|
+
protocols: nap ? [nap] : []
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2630
|
+
return {
|
|
2631
|
+
dTag: manifest.dTag,
|
|
2632
|
+
...manifest.title === void 0 ? {} : { title: manifest.title },
|
|
2633
|
+
archetypes
|
|
2634
|
+
};
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2399
2637
|
// src/cvm-service.ts
|
|
2400
2638
|
var CVM_SERVICE_VERSION = "1.0.0";
|
|
2401
2639
|
var CVM_DESCRIPTOR = {
|
|
@@ -2438,7 +2676,7 @@ function createCvmService(options) {
|
|
|
2438
2676
|
const m = msg;
|
|
2439
2677
|
const id = m.id ?? "";
|
|
2440
2678
|
void transport.discover(m.query).then((servers) => send({ type: "cvm.discover.result", id, servers })).catch(
|
|
2441
|
-
(err) => send({ type: "cvm.discover.result", id, servers: [], error:
|
|
2679
|
+
(err) => send({ type: "cvm.discover.result", id, servers: [], error: toErrorMessage5(err) })
|
|
2442
2680
|
);
|
|
2443
2681
|
}
|
|
2444
2682
|
function handleRequest(windowId, msg, send) {
|
|
@@ -2454,7 +2692,7 @@ function createCvmService(options) {
|
|
|
2454
2692
|
}
|
|
2455
2693
|
openSession(windowId, m.server, send);
|
|
2456
2694
|
void transport.request(m.server, m.message, m.options).then((message) => send({ type: "cvm.request.result", id, message })).catch(
|
|
2457
|
-
(err) => send({ type: "cvm.request.result", id, error:
|
|
2695
|
+
(err) => send({ type: "cvm.request.result", id, error: toErrorMessage5(err) })
|
|
2458
2696
|
);
|
|
2459
2697
|
}
|
|
2460
2698
|
function handleClose(windowId, msg, send) {
|
|
@@ -2465,7 +2703,7 @@ function createCvmService(options) {
|
|
|
2465
2703
|
return;
|
|
2466
2704
|
}
|
|
2467
2705
|
closeSession(windowId, m.server.pubkey);
|
|
2468
|
-
void transport.close(m.server).then(() => send({ type: "cvm.close.result", id })).catch((err) => send({ type: "cvm.close.result", id, error:
|
|
2706
|
+
void transport.close(m.server).then(() => send({ type: "cvm.close.result", id })).catch((err) => send({ type: "cvm.close.result", id, error: toErrorMessage5(err) }));
|
|
2469
2707
|
}
|
|
2470
2708
|
return {
|
|
2471
2709
|
descriptor: CVM_DESCRIPTOR,
|
|
@@ -2496,7 +2734,7 @@ function createCvmService(options) {
|
|
|
2496
2734
|
}
|
|
2497
2735
|
};
|
|
2498
2736
|
}
|
|
2499
|
-
function
|
|
2737
|
+
function toErrorMessage5(err) {
|
|
2500
2738
|
if (err instanceof Error) return err.message;
|
|
2501
2739
|
if (typeof err === "string") return err;
|
|
2502
2740
|
return "cvm request failed";
|
|
@@ -2505,11 +2743,13 @@ export {
|
|
|
2505
2743
|
createAudioService,
|
|
2506
2744
|
createBrowserMediaBridge,
|
|
2507
2745
|
createCacheService,
|
|
2746
|
+
createCatalogIntentResolver,
|
|
2508
2747
|
createConfigService,
|
|
2509
2748
|
createCoordinatedRelay,
|
|
2510
2749
|
createCvmService,
|
|
2511
2750
|
createHttpUploader,
|
|
2512
2751
|
createIdentityService,
|
|
2752
|
+
createIntentService,
|
|
2513
2753
|
createKeysService,
|
|
2514
2754
|
createMediaService,
|
|
2515
2755
|
createNotificationService,
|
|
@@ -2519,6 +2759,7 @@ export {
|
|
|
2519
2759
|
createRelayPoolService,
|
|
2520
2760
|
createResourceService,
|
|
2521
2761
|
createThemeService,
|
|
2522
|
-
createUploadService
|
|
2762
|
+
createUploadService,
|
|
2763
|
+
manifestToIntentCatalogEntry
|
|
2523
2764
|
};
|
|
2524
2765
|
//# sourceMappingURL=index.js.map
|