@kehto/services 0.19.0 → 0.20.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/dist/index.d.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { ServiceHandler, Signer, RelayEventResult, ServiceRuntimeContext } from '@kehto/runtime';
2
2
  import { IdentityGetProfileMessage, IdentityGetProfileResultMessage, IdentityGetFollowsMessage, IdentityGetFollowsResultMessage, IdentityGetListMessage, IdentityGetListResultMessage, IdentityGetZapsMessage, IdentityGetZapsResultMessage, IdentityGetMutesMessage, IdentityGetMutesResultMessage, IdentityGetBlockedMessage, IdentityGetBlockedResultMessage, IdentityGetBadgesMessage, IdentityGetBadgesResultMessage } from '@napplet/nap/identity/types';
3
- import { NostrFilter, NostrEvent, EventTemplate, IntentRequest, IntentResult, IntentAvailability, IntentBehavior, IntentCandidate, LinkOpenOptions, LinkOpenResult, ListSupport, ListRef, ListItem, ListOptions, ListMutationResult, SerialOpenRequest, SerialOpenResult, BleEvent, BleOpenRequest, BleOpenResult, BleService, BleAttribute, BleWriteOptions, WebrtcEvent, WebrtcOpenRequest, WebrtcOpenResult, CommonProfileTarget, CommonProfileResult, CommonFollowsResult, CommonActionResult, CommonReaction, CommonReportTarget, CommonReportReason } from '@napplet/core';
4
- export { IntentAvailability, IntentBehavior, IntentCandidate, IntentHandlerPreference, IntentOpenOptions, IntentRequest, IntentResult } from '@napplet/core';
3
+ import { NostrFilter, NostrEvent, EventTemplate, IntentRequest, IntentResult, IntentAvailability, IntentBehavior, IntentCandidate, LinkOpenOptions, LinkOpenResult, ListSupport, ListRef, ListItem, ListOptions, ListMutationResult, SerialEvent, SerialOpenRequest, SerialOpenResult, BleEvent, BleOpenRequest, BleOpenResult, BleService, BleAttribute, BleWriteOptions, WebrtcEvent, WebrtcOpenRequest, WebrtcOpenResult, CommonProfileTarget, CommonProfileResult, CommonFollowsResult, CommonActionResult, CommonReaction, CommonReportTarget, CommonReportReason, DmStatus, DmConversationQuery, DmConversationPage, DmMessageQuery, DmMessagePage, DmSendRequest, DmSendResult, DmSubscribeRequest, DmMessage, DmSubscription, DmOk, DmHexPubkey, FsInfo, FsPickOptions, FsPickResult, FsMetadata, FsDirectoryEntry, FsReadOptions, FsReadResult, FsWriteOptions, FsWriteResult, FsMkdirOptions, FsWatchOptions, FsChange, FsError } from '@napplet/core';
4
+ export { DmConversation, DmConversationPage, DmConversationQuery, DmHexPubkey, DmMessage, DmMessagePage, DmMessageQuery, DmMessageStatus, DmOk, DmPeer, DmSendRequest, DmSendResult, DmStatus, DmSubscribeRequest, DmSubscription, DmTimestamp, IntentAvailability, IntentBehavior, IntentCandidate, IntentHandlerPreference, IntentOpenOptions, IntentRequest, IntentResult } from '@napplet/core';
5
5
  import { MediaMetadata, MediaAction } from '@napplet/nap/media/types';
6
6
  export { MediaAction } from '@napplet/nap/media/types';
7
- import { NotifySendMessage } from '@napplet/nap/notify/types';
7
+ import { NotifyActionMessage, NotifyClickedMessage, NotifyDismissedMessage, NotifySendMessage, NotifyChannelRegisterMessage, NotifyControl } from '@napplet/nap/notify/types';
8
8
  import { Theme, ThemeChangedMessage } from '@napplet/nap/theme/types';
9
- import { ConfigSchemaErrorCode, ConfigValues, NappletConfigSchema } from '@napplet/nap/config/types';
10
- export { a as CvmCloseMessage, b as CvmCloseResultMessage, c as CvmDiscoverMessage, d as CvmDiscoverQuery, e as CvmDiscoverResultMessage, f as CvmEventMessage, g as CvmInboundMessage, h as CvmOutboundMessage, i as CvmRequestMessage, j as CvmRequestOptions, k as CvmRequestResultMessage, l as CvmServer, m as CvmServerRef, n as CvmService, o as CvmServiceOptions, C as CvmTransport, p as CvmTransportError, M as McpContentBlock, q as McpMessage, r as McpTool, s as McpToolResult, t as createCvmService } from './cvm-service-DKIzbiDw.js';
9
+ import { ConfigSchemaErrorCode, NappletConfigSchema, ConfigValues } from '@napplet/nap/config/types';
10
+ export { a as CvmCloseMessage, b as CvmCloseResultMessage, c as CvmDiscoverMessage, d as CvmDiscoverQuery, e as CvmDiscoverResultMessage, f as CvmEventMessage, g as CvmInboundMessage, h as CvmOutboundMessage, i as CvmRequestMessage, j as CvmRequestOptions, k as CvmRequestResultMessage, l as CvmServer, m as CvmServerRef, n as CvmService, o as CvmServiceOptions, C as CvmTransport, p as CvmTransportError, M as McpContentBlock, q as McpMessage, r as McpTool, s as McpToolResult, t as createCvmService } from './cvm-service-t5syG4tU.js';
11
11
 
12
12
  /**
13
13
  * @kehto/services — Shared types for reference service implementations.
@@ -260,11 +260,11 @@ interface RelayPoolServiceOptions {
260
260
  * Subscribe to events matching filters. Returns handle with unsubscribe().
261
261
  *
262
262
  * @param filters - NIP-01 filter objects
263
- * @param callback - Receives matching events or 'EOSE'
263
+ * @param callback - Receives matching events or 'EOSE' plus optional observed relay URLs
264
264
  * @param relayUrls - Optional relay URL hints
265
265
  * @returns Handle to cancel the subscription
266
266
  */
267
- subscribe(filters: NostrFilter[], callback: (item: NostrEvent | 'EOSE') => void, relayUrls?: string[]): {
267
+ subscribe(filters: NostrFilter[], callback: (item: NostrEvent | 'EOSE', observedRelayUrls?: string[]) => void, relayUrls?: string[]): {
268
268
  unsubscribe(): void;
269
269
  };
270
270
  /**
@@ -1014,81 +1014,62 @@ declare function createMediaService(options?: MediaServiceOptions): ServiceHandl
1014
1014
  };
1015
1015
 
1016
1016
  /**
1017
- * notify-service.ts — NIP-5D notify NAP reference service (stub-level).
1018
- *
1019
- * Handles the 5 napplet -> shell request types from `@napplet/nap/notify`:
1020
- * - `notify.send` -> `notify.send.result` (shell-assigned id)
1021
- * - `notify.dismiss` -> fire-and-forget
1022
- * - `notify.badge` -> fire-and-forget
1023
- * - `notify.channel.register` -> fire-and-forget
1024
- * - `notify.permission.request` -> `notify.permission.result { granted }`
1025
- *
1026
- * Stub-level: no real Notification API calls, no real channel registry.
1027
- * Host apps wire a real backend via
1028
- * `runtime.registerService('notify', realHandler)`.
1017
+ * NAP-NOTIFY reference service.
1029
1018
  *
1030
- * `notify-service.ts` is the canonical `@napplet/nap/notify` NIP-5D path.
1031
- * It lives alongside the direct notification state registry in
1032
- * `notification-service.ts`; neither service interprets an `inc.emit` topic.
1033
- * If the host app registers this service via
1034
- * `runtime.registerService('notify', ...)`, @napplet/nap/notify messages
1035
- * land here.
1036
- *
1037
- * Shell -> napplet push messages (`notify.action`, `notify.clicked`,
1038
- * `notify.dismissed`, `notify.controls`) are not emitted by this stub —
1039
- * they are the host app's responsibility and are deferred to a future plan.
1019
+ * The service owns protocol correlation and per-window lifecycle while a host
1020
+ * backend owns presentation, permission, badges, and channels.
1040
1021
  */
1041
1022
 
1042
- /**
1043
- * Optional configuration for `createNotifyService`.
1044
- *
1045
- * @example
1046
- * ```ts
1047
- * const notify = createNotifyService({
1048
- * generateId: () => crypto.randomUUID(),
1049
- * defaultGrant: false,
1050
- * onSend: (windowId, msg) => console.log(`napplet ${windowId} sent ${msg.title}`),
1051
- * });
1052
- * runtime.registerService('notify', notify);
1053
- * ```
1054
- */
1023
+ /** User-interaction events emitted by a host notification presentation. */
1024
+ type NotifyInteractionMessage = NotifyActionMessage | NotifyClickedMessage | NotifyDismissedMessage;
1025
+ /** Host presentation request for one accepted notification. */
1026
+ interface NotifyPresentation {
1027
+ /** Window that requested the notification. */
1028
+ readonly windowId: string;
1029
+ /** Shell-assigned notification identifier. */
1030
+ readonly notificationId: string;
1031
+ /** Original, untrusted notification payload. */
1032
+ readonly message: NotifySendMessage;
1033
+ /** Route a host-side user interaction back to the requesting napplet. */
1034
+ readonly emit: (message: NotifyInteractionMessage) => void;
1035
+ }
1036
+ /** Host hooks required to turn NAP-NOTIFY messages into observable behavior. */
1055
1037
  interface NotifyServiceOptions {
1056
- /**
1057
- * Generate a shell-assigned notification ID for `notify.send.result`.
1058
- * Default: a monotonically increasing `shell-<n>` counter.
1059
- */
1038
+ /** Generate a shell-assigned notification identifier. */
1060
1039
  generateId?: () => string;
1061
- /**
1062
- * Default permission grant for `notify.permission.request`.
1063
- * Host apps that want real permission prompts should replace this
1064
- * service with a real backend via `runtime.registerService`.
1065
- * Default: `true`.
1066
- */
1067
- defaultGrant?: boolean;
1068
- /**
1069
- * Called synchronously when a napplet dispatches `notify.send`.
1070
- * Intended for host-app plumbing (UI toast, logging, etc.) without
1071
- * requiring a full backend replacement.
1072
- */
1073
- onSend?: (windowId: string, payload: NotifySendMessage) => void;
1074
- }
1075
- /**
1076
- * Create a stub-level notify service handler.
1077
- *
1078
- * Answers the 5 napplet->shell request types from `@napplet/nap/notify`.
1079
- * Does NOT implement a real backend (no DOM Notification API, no channel
1080
- * registry, no permission prompt). Host apps replace this via
1081
- * `runtime.registerService('notify', realHandler)` when a real backend is
1082
- * needed.
1083
- *
1084
- * @param options - Optional service configuration (see NotifyServiceOptions)
1085
- * @returns A ServiceHandler to register with the runtime under domain `notify`
1040
+ /** Render or otherwise deliver a notification. Missing backends fail closed. */
1041
+ present?: (presentation: NotifyPresentation) => void | Promise<void>;
1042
+ /** Remove an active notification from host presentation. */
1043
+ dismiss?: (windowId: string, notificationId: string) => void | Promise<void>;
1044
+ /** Display or clear the requesting napplet's badge count. */
1045
+ setBadge?: (windowId: string, count: number) => void | Promise<void>;
1046
+ /** Persist a channel registration in host-owned state. */
1047
+ registerChannel?: (windowId: string, channel: NotifyChannelRegisterMessage) => void | Promise<void>;
1048
+ /** Apply policy or prompt the user for notification permission. */
1049
+ requestPermission?: (windowId: string, channel?: string) => boolean | Promise<boolean>;
1050
+ /** Release host-owned state when the runtime destroys a napplet window. */
1051
+ destroyWindow?: (windowId: string) => void | Promise<void>;
1052
+ /** Notification controls implemented by the host backend. */
1053
+ controls?: readonly NotifyControl[];
1054
+ /** Observe asynchronous fire-and-forget backend failures. */
1055
+ onError?: (error: unknown) => void;
1056
+ }
1057
+ /**
1058
+ * Create a NAP-NOTIFY service backed by host-owned notification behavior.
1059
+ *
1060
+ * A backendless instance is safe to register for conformance testing: send
1061
+ * requests return an error and permission requests return `granted: false`
1062
+ * instead of manufacturing successful delivery.
1063
+ *
1064
+ * @param options - Host presentation, policy, and lifecycle hooks.
1065
+ * @returns Runtime service handler for the `notify` domain.
1086
1066
  *
1087
1067
  * @example
1088
1068
  * ```ts
1089
- * import { createNotifyService } from '@kehto/services';
1090
- *
1091
- * const notify = createNotifyService();
1069
+ * const notify = createNotifyService({
1070
+ * present: ({ message }) => showToast(message.title, message.body),
1071
+ * requestPermission: () => promptUser(),
1072
+ * });
1092
1073
  * runtime.registerService('notify', notify);
1093
1074
  * ```
1094
1075
  */
@@ -1198,6 +1179,19 @@ interface ThemeService {
1198
1179
  */
1199
1180
  declare function createThemeService(options?: ThemeServiceOptions): ThemeService;
1200
1181
 
1182
+ /** Result of validating a NAP-CONFIG Core Subset schema. */
1183
+ type ConfigSchemaValidation = {
1184
+ ok: true;
1185
+ } | {
1186
+ ok: false;
1187
+ code: ConfigSchemaErrorCode;
1188
+ error: string;
1189
+ };
1190
+ /** Validate the complete bounded NAP-CONFIG Core Subset recursively. */
1191
+ declare function validateConfigSchema(schema: unknown): ConfigSchemaValidation;
1192
+ /** Apply schema defaults and drop values that do not validate. */
1193
+ declare function resolveConfigValues(schema: NappletConfigSchema, values: ConfigValues): ConfigValues;
1194
+
1201
1195
  /**
1202
1196
  * config-service.ts — NAP-CONFIG reference service (9th NAP domain, v1.7 Phase 39).
1203
1197
  *
@@ -1219,44 +1213,29 @@ declare function createThemeService(options?: ThemeServiceOptions): ThemeService
1219
1213
  * (H-07 in PITFALLS.md) — such state belongs in NAP-STORAGE.
1220
1214
  * ──────────────────────────────────────────────────────────────────────────────────
1221
1215
  *
1222
- * Host integration: provide `getValues()` returning the current
1223
- * `ConfigValues` snapshot. Call the returned `publishValues(newValues)`
1224
- * whenever the configuration changes — the service fans the new snapshot
1225
- * out to every napplet that has an active `config.subscribe`.
1216
+ * Host integration: provide `getValues(windowId)` and `saveValues(windowId,
1217
+ * values)` backed by storage scoped to the source window's napplet identity.
1218
+ * The service validates and defaults every snapshot before delivery.
1226
1219
  *
1227
- * Optional: provide `registerSchema` to accept napplet-declared schemas at
1228
- * runtime (the ref impl does a minimal shape check using the Core Subset
1229
- * validator; use `ajv` in host impls that need strict draft-07 conformance).
1230
- * Provide `openSettings` to open a shell-side UI for the napplet (no
1231
- * response envelope — fire-and-forget UI hook).
1220
+ * Provide `openSettings` to render the shell-owned settings UI. Optional
1221
+ * hooks may add host policy around schema acceptance and lifecycle cleanup.
1232
1222
  *
1233
1223
  * @example
1234
1224
  * ```ts
1235
1225
  * import { createConfigService } from '@kehto/services';
1236
1226
  *
1237
- * const configFixtures = { theme: 'dark', density: 'compact', recentSearches: [] };
1227
+ * const configFixtures = new Map<string, ConfigValues>();
1238
1228
  * const config = createConfigService({
1239
- * getValues: () => ({ ...configFixtures }),
1229
+ * getValues: (windowId) => ({ ...configFixtures.get(windowId) }),
1230
+ * saveValues: (windowId, values) => configFixtures.set(windowId, values),
1240
1231
  * });
1241
1232
  * runtime.registerService('config', config.handler);
1242
1233
  *
1243
1234
  * // Later, when shell-side values change:
1244
- * configFixtures.theme = 'light';
1245
- * config.publishValues({ ...configFixtures });
1235
+ * config.publishValues({ theme: 'light' }, 'window-1');
1246
1236
  * ```
1247
1237
  */
1248
1238
 
1249
- /**
1250
- * Shape returned by a successful `registerSchema` result (ok=true) or a
1251
- * rejection (ok=false + code + error). Mirrors the wire envelope fields.
1252
- */
1253
- type ConfigSchemaValidation = {
1254
- ok: true;
1255
- } | {
1256
- ok: false;
1257
- code: ConfigSchemaErrorCode;
1258
- error: string;
1259
- };
1260
1239
  /**
1261
1240
  * Configuration options for `createConfigService` (options-as-bridge
1262
1241
  * per v1.6 Decision 18).
@@ -1264,8 +1243,9 @@ type ConfigSchemaValidation = {
1264
1243
  * @example
1265
1244
  * ```ts
1266
1245
  * const config = createConfigService({
1267
- * getValues: () => ({ theme: 'dark', density: 'compact' }),
1268
- * openSettings: (windowId, section) => showSettingsPanel(windowId, section),
1246
+ * getValues: (windowId) => loadScopedValues(windowId),
1247
+ * saveValues: (windowId, values) => saveScopedValues(windowId, values),
1248
+ * openSettings: (windowId, section, context) => showSettingsPanel(windowId, section, context),
1269
1249
  * });
1270
1250
  * ```
1271
1251
  */
@@ -1275,7 +1255,9 @@ interface ConfigServiceOptions {
1275
1255
  * Called on every `config.get` and at every `config.subscribe` initial push.
1276
1256
  * Implementations should return a fresh object (not a mutable reference).
1277
1257
  */
1278
- getValues(): ConfigValues;
1258
+ getValues(windowId: string): ConfigValues;
1259
+ /** Persist one shell-validated full snapshot after settings UI commit. */
1260
+ saveValues?: (windowId: string, values: ConfigValues) => void;
1279
1261
  /**
1280
1262
  * Optional: receive notification when a napplet subscribes to config updates.
1281
1263
  * Fire-and-forget — the service tracks the subscription internally regardless.
@@ -1288,9 +1270,9 @@ interface ConfigServiceOptions {
1288
1270
  /**
1289
1271
  * Optional: validate and store a napplet-provided schema.
1290
1272
  *
1291
- * If omitted, the ref impl runs its own Core Subset check (hand-coded
1292
- * validator; 30-50 lines) and returns ok/reject. Hosts that need strict
1293
- * draft-07 conformance should provide an ajv-backed implementation.
1273
+ * The reference implementation always runs its complete bounded Core Subset
1274
+ * validator first. This hook may add host policy such as version acceptance
1275
+ * or durable schema persistence, but cannot weaken protocol validation.
1294
1276
  *
1295
1277
  * Return shape mirrors `config.registerSchema.result` wire envelope
1296
1278
  * (minus the `id` — the dispatch layer correlates).
@@ -1299,10 +1281,21 @@ interface ConfigServiceOptions {
1299
1281
  /**
1300
1282
  * Optional: open the shell-side settings UI for this napplet.
1301
1283
  * Fire-and-forget — no response envelope per the wire spec.
1302
- * If omitted, `config.openSettings` is silently dropped (D10 allows
1303
- * the config-demo napplet to function without a settings UI).
1284
+ * If omitted, `config.openSettings` is silently dropped. A conformant host
1285
+ * must supply this hook before advertising the CONFIG domain.
1304
1286
  */
1305
- openSettings?: (windowId: string, section: string | undefined) => void;
1287
+ openSettings?: (windowId: string, section: string | undefined, context: ConfigSettingsContext) => void;
1288
+ /** Release host-owned transient UI or state for a destroyed window. */
1289
+ onWindowDestroyed?: (windowId: string) => void;
1290
+ }
1291
+ /** Shell-owned settings state and commit boundary passed to host UI code. */
1292
+ interface ConfigSettingsContext {
1293
+ /** Accepted schema for this live napplet window. */
1294
+ readonly schema: NappletConfigSchema;
1295
+ /** Current validated/defaulted values. */
1296
+ readonly values: ConfigValues;
1297
+ /** Validate, persist, and publish a full settings snapshot. */
1298
+ commit(values: ConfigValues): ConfigValues;
1306
1299
  }
1307
1300
  /**
1308
1301
  * NAP-CONFIG reference service bundle — `handler` to register with the
@@ -1319,8 +1312,14 @@ interface ConfigService {
1319
1312
  * push from correlated `config.get` response).
1320
1313
  *
1321
1314
  * @param values - The new configuration snapshot (full object, not a diff)
1315
+ * @param windowId - Optional source window scope. Omit only for a host-wide
1316
+ * update that should be independently validated for every subscriber.
1322
1317
  */
1323
- publishValues(values: ConfigValues): void;
1318
+ publishValues(values: ConfigValues, windowId?: string): void;
1319
+ /** Return the accepted schema for one live window, or null. */
1320
+ getSchema(windowId: string): NappletConfigSchema | null;
1321
+ /** Return the current validated/defaulted values for one live window, or null. */
1322
+ getValues(windowId: string): ConfigValues | null;
1324
1323
  }
1325
1324
  /**
1326
1325
  * Create a NAP-CONFIG reference service.
@@ -1348,13 +1347,14 @@ interface ConfigService {
1348
1347
  * import { createConfigService } from '@kehto/services';
1349
1348
  *
1350
1349
  * const config = createConfigService({
1351
- * getValues: () => ({ theme: 'dark', density: 'compact' }),
1352
- * openSettings: (windowId, section) => openSettingsUI(section),
1350
+ * getValues: (windowId) => loadScopedValues(windowId),
1351
+ * saveValues: (windowId, values) => saveScopedValues(windowId, values),
1352
+ * openSettings: (windowId, section, context) => openSettingsUI(section, context),
1353
1353
  * });
1354
1354
  * runtime.registerService('config', config.handler);
1355
1355
  *
1356
- * // Push a live update to all subscribers:
1357
- * config.publishValues({ theme: 'light', density: 'compact' });
1356
+ * // Push one externally-originated scoped update:
1357
+ * config.publishValues({ theme: 'light', density: 'compact' }, 'window-1');
1358
1358
  * ```
1359
1359
  */
1360
1360
  declare function createConfigService(options: ConfigServiceOptions): ConfigService;
@@ -1364,8 +1364,7 @@ declare function createConfigService(options: ConfigServiceOptions): ConfigServi
1364
1364
  *
1365
1365
  * Shell-side reference implementation for the canonical NAP-RESOURCE wire
1366
1366
  * protocol (`internal-resource.ts` in @kehto/shell/src/types; kehto-internal
1367
- * model per PROJECT.md Decision #31. Kehto keeps legacy single-fetch fields
1368
- * for existing callers and also emits the current NAP-RESOURCE fields.
1367
+ * model per PROJECT.md Decision #31.
1369
1368
  * Handles:
1370
1369
  * Inbound: resource.info, resource.bytes, resource.bytesMany, resource.cancel
1371
1370
  * Outbound: resource.info.result, resource.info.error,
@@ -1375,16 +1374,10 @@ declare function createConfigService(options: ConfigServiceOptions): ConfigServi
1375
1374
  * ──────────────────────── SCOPE BOUNDARY (RESOURCE-01) ────────────────────────
1376
1375
  * NAP-RESOURCE is an **authenticated fetch proxy** — read-only, atomic.
1377
1376
  *
1378
- * This service is NOT responsible for:
1379
- * - Streaming / chunked responses (host-app concern)
1380
- * - Response caching / conditional requests (host-app concern)
1381
- * - Upload / POST body construction (NAP-RESOURCE v1.7 is read-only)
1382
- * - Redirect limits, MIME sniffing, SVG rasterization (host-fetch concern)
1383
- * - Private-IP blocking, SSRF mitigation (host-provided-fetch responsibility)
1384
- *
1385
- * These belong to the host-app's `fetch` implementation per D7 and
1386
- * SHELL-RESOURCE-POLICY.md (Phase 40 Plan 40-03). Kehto ships a reference
1387
- * service; production hardening is the host app's concern.
1377
+ * The host fetch boundary performs scheme-specific I/O and MUST return only a
1378
+ * policy-checked, byte-classified response. This service independently enforces
1379
+ * identity, scheme disclosure, origin grants, bulk limits, response-size caps,
1380
+ * cancellation, and the current wire shape. It never forwards upstream headers.
1388
1381
  * ──────────────────────────────────────────────────────────────────────────────
1389
1382
  *
1390
1383
  * Host integration: provide `fetch`, `isOriginGranted`, `getConnectGrants`,
@@ -1437,12 +1430,13 @@ type ResourceInfoProvider = ResourceInfo | ((context: ResourceInfoContext) => Re
1437
1430
  */
1438
1431
  interface ResourceServiceOptions {
1439
1432
  /**
1440
- * Host-supplied fetch implementation. Receives the URL, a partial init
1441
- * (method, headers, signal), and must return a `Response`-compatible promise.
1433
+ * Host-supplied policy fetch implementation. Receives the URL, a partial init
1434
+ * (method, headers, signal), and returns a sanitized `Response`.
1442
1435
  *
1443
- * The host's `fetch` is the ONLY place to implement redirect limits, MIME
1444
- * sniffing, SVG rasterization, private-IP / SSRF blocking, etc.
1445
- * This service does NOT filter: it proxies transparently.
1436
+ * The returned `content-type` MUST be derived from inspected output bytes,
1437
+ * never an upstream header. The host boundary also owns redirect-by-redirect
1438
+ * private-address checks, SVG rasterization, and scheme-specific integrity.
1439
+ * Throw `ResourceServiceError` to preserve a protocol error classification.
1446
1440
  *
1447
1441
  * @param url - The URL from the resource.bytes request
1448
1442
  * @param init - Method, headers (from napplet), and an AbortSignal
@@ -1494,7 +1488,7 @@ interface ResourceServiceOptions {
1494
1488
  *
1495
1489
  * Provide a static snapshot or a resolver when the shell wants to disclose
1496
1490
  * configured schemes and coarse limits. Omit to expose the reference
1497
- * service's conservative default (`https` enabled, no numeric limits).
1491
+ * service's fail-closed default (no enabled schemes or numeric limits).
1498
1492
  */
1499
1493
  resourceInfo?: ResourceInfoProvider;
1500
1494
  }
@@ -1503,6 +1497,16 @@ interface ResourceServiceOptions {
1503
1497
  * Exported for host apps that need to type-annotate the handler reference.
1504
1498
  */
1505
1499
  type ResourceService = ServiceHandler;
1500
+ type ResourceErrorCode = 'invalid-request' | 'not-found' | 'blocked-by-policy' | 'timeout' | 'too-large' | 'unsupported-scheme' | 'decode-failed' | 'network-error' | 'quota-exceeded';
1501
+ /** A host policy failure with a stable NAP-RESOURCE error classification. */
1502
+ declare class ResourceServiceError extends Error {
1503
+ readonly code: ResourceErrorCode;
1504
+ /**
1505
+ * @param code - Canonical NAP-RESOURCE error code.
1506
+ * @param message - Diagnostic detail safe to return to the napplet.
1507
+ */
1508
+ constructor(code: ResourceErrorCode, message: string);
1509
+ }
1506
1510
  /**
1507
1511
  * Create a NAP-RESOURCE reference service.
1508
1512
  *
@@ -2445,6 +2449,8 @@ declare function createListsService(options?: ListsServiceOptions): ServiceHandl
2445
2449
  interface SerialServiceContext {
2446
2450
  /** Window id of the requesting napplet. */
2447
2451
  windowId: string;
2452
+ /** Emit a runtime-owned serial event back to the requesting napplet. */
2453
+ emit(event: SerialEvent): void;
2448
2454
  }
2449
2455
  /** Options for {@link createSerialService}. */
2450
2456
  interface SerialServiceOptions {
@@ -2575,94 +2581,14 @@ interface CommonServiceOptions {
2575
2581
  */
2576
2582
  declare function createCommonService(options?: CommonServiceOptions): ServiceHandler;
2577
2583
 
2578
- /** Hex Nostr public key. */
2579
- type DmHexPubkey = string;
2580
- /** Unix timestamp in seconds. */
2581
- type DmTimestamp = number;
2582
- /** Current runtime direct-message availability. */
2583
- interface DmStatus {
2584
- available: boolean;
2585
- ownerPubkey?: DmHexPubkey;
2586
- implementations: string[];
2587
- capabilities: string[];
2588
- }
2589
- /** Query parameters for normalized DM conversation summaries. */
2590
- interface DmConversationQuery {
2591
- cursor?: string;
2592
- limit?: number;
2593
- }
2594
- /** Public peer metadata safe for napplet display. */
2595
- interface DmPeer {
2596
- pubkey: DmHexPubkey;
2597
- label?: string;
2598
- avatar?: string;
2599
- }
2600
- /** A normalized direct or group conversation summary. */
2601
- interface DmConversation {
2602
- id: string;
2603
- kind: 'direct' | 'group';
2604
- participants: DmPeer[];
2605
- subject?: string;
2606
- unread: number;
2607
- updatedAt?: DmTimestamp;
2608
- }
2609
- /** Page of normalized conversation summaries. */
2610
- interface DmConversationPage {
2611
- conversations: DmConversation[];
2612
- cursor?: string;
2613
- }
2614
- /** Query parameters for message history within one conversation. */
2615
- interface DmMessageQuery {
2616
- conversationId: string;
2617
- cursor?: string;
2618
- limit?: number;
2619
- }
2620
- /** Runtime-normalized delivery state for a DM message. */
2621
- type DmMessageStatus = 'sent' | 'delivered' | 'received' | 'failed';
2622
- /** Normalized cleartext message visible to the napplet by runtime policy. */
2623
- interface DmMessage {
2624
- id: string;
2625
- conversationId: string;
2626
- senderPubkey: DmHexPubkey;
2627
- createdAt: DmTimestamp;
2628
- content: string;
2629
- status: DmMessageStatus;
2630
- }
2631
- /** Page of normalized messages for one conversation. */
2632
- interface DmMessagePage {
2633
- messages: DmMessage[];
2634
- cursor?: string;
2635
- }
2636
- /** Request to send a direct message. */
2637
- interface DmSendRequest {
2638
- conversationId?: string;
2639
- recipients: DmHexPubkey[];
2640
- content: string;
2641
- clientMessageId?: string;
2642
- }
2643
- /** Result of a runtime-mediated send. */
2644
- interface DmSendResult {
2645
- ok: boolean;
2646
- message: DmMessage;
2647
- }
2648
- /** Request to start live DM delivery. */
2649
- interface DmSubscribeRequest {
2650
- conversationId?: string;
2651
- }
2652
- /** Runtime-assigned live subscription identity. */
2653
- interface DmSubscription {
2654
- subscriptionId: string;
2655
- }
2656
- /** Generic boolean acknowledgement used by `dm.unsubscribe`. */
2657
- interface DmOk {
2658
- ok: boolean;
2659
- }
2660
2584
  /** Relay pool contract for DM adapters. */
2661
2585
  interface DmRelayPool {
2662
2586
  subscribe(filters: NostrFilter[], callback: (item: NostrEvent | 'EOSE') => void, relayUrls?: string[]): {
2663
2587
  unsubscribe(): void;
2664
2588
  };
2665
2589
  publish(event: NostrEvent): void | Promise<void>;
2590
+ /** Query persisted encrypted messages before serving normalized history. */
2591
+ query?(filters: NostrFilter[], relayUrls?: string[]): Promise<NostrEvent[]>;
2666
2592
  selectRelayTier(filters: NostrFilter[]): string[];
2667
2593
  isAvailable(): boolean;
2668
2594
  }
@@ -2730,6 +2656,8 @@ interface Nip17DmAdapterOptions {
2730
2656
  relays?: string[];
2731
2657
  /** Optional normalized message store. Defaults to an in-memory store. */
2732
2658
  store?: DmMemoryStore;
2659
+ /** Runtime policy invoked once before encrypted relay publication. */
2660
+ authorizeSend?(request: DmSendRequest): boolean | Promise<boolean>;
2733
2661
  }
2734
2662
  /**
2735
2663
  * Create a concrete NIP-17 NAP-DM adapter.
@@ -2860,4 +2788,60 @@ declare function createCordnRelayCoordinatorClient(options: CordnRelayCoordinato
2860
2788
  /** Create a Cordn/ContextVM-backed NAP-DM adapter. */
2861
2789
  declare function createCordnDmAdapter(options: CordnDmAdapterOptions): DmAdapter;
2862
2790
 
2863
- export { type BleServiceContext, type BleServiceOptions, type CacheServiceOptions, type CatalogIntentResolver, type CatalogIntentResolverOptions, type CommonServiceContext, type CommonServiceOptions, type ConfigSchemaValidation, type ConfigService, type ConfigServiceOptions, type CoordinatedRelayOptions, type CordnCodecResult, type CordnCoordinatorClient, type CordnCoordinatorSubscription, type CordnDmAdapterOptions, type CordnDmClient, type CordnGroupMessage, type CordnRelayCoordinatorOptions, type CountRequest, type CountResult, type CountServiceOptions, type DmAdapter, type DmConversation, type DmConversationPage, type DmConversationQuery, type DmHexPubkey, DmMemoryStore, type DmMessage, type DmMessagePage, type DmMessageQuery, type DmMessageStatus, type DmOk, type DmPeer, type DmRelayPool, type DmSendRequest, type DmSendResult, type DmService, type DmServiceOptions, type DmStatus, type DmSubscribeRequest, type DmSubscription, type DmTimestamp, type HostCacheBridge, type HostKeyEvent, type HostKeysBridge, type HostMediaBridge, type HttpUploaderOptions, type HttpUploaderRails, type IdentityServiceOptions, type IntentArchetypeSupport, type IntentCatalogEntry, type IntentDispatchParams, type IntentResolver, type IntentResolverContext, type IntentServiceOptions, type IntentTargetController, type IntentTargetDispatch, type KeysServiceOptions, type LinkOpenContext, type LinkServiceOptions, type ListsServiceContext, type ListsServiceOptions, type ManifestArchetypeInput, type MaybePromise, type MediaMetadataLike, type MediaPlaybackOwner, type MediaServiceOptions, type MediaSessionCreateOptions, type MediaSessionTarget, type MediaSourceRef, type NdrDmAdapterOptions, type NdrRelayTransport, type NdrRelayTransportOptions, type NdrRumorLike, type NdrRuntimeLike, type Nip17DmAdapterOptions, type NostrTag, type Notification, type NotificationServiceOptions, type NotifyServiceOptions, type OutboxEventOptions, type OutboxEventResult, type OutboxPublishOptions, type OutboxPublishResult, type OutboxQueryOptions, type OutboxQueryStream, type OutboxQueryStreamSink, type OutboxRelayPlan, type OutboxRelayPool, type OutboxResult, type OutboxRouter, type OutboxRouterSubscription, type OutboxServiceOptions, type OutboxSubscribeOptions, type OutboxSubscriptionSink, type OutboxTarget, type RailServerConfig, type RelayListEntry, type RelayPoolOutboxRouterOptions, type RelayPoolServiceOptions, type ResourceInfo, type ResourceInfoContext, type ResourceInfoProvider, type ResourceSchemeInfo, type ResourceService, type ResourceServiceOptions, type SerialServiceContext, type SerialServiceOptions, type SignEvent, type StreamingOutboxRouter, type ThemeService, type ThemeServiceOptions, type UploadDimensions, type UploadInfo, type UploadInfoContext, type UploadInfoProvider, type UploadRail, type UploadRailInfo, type UploadRequest, type UploadResult, type UploadServiceOptions, type UploadState, type UploadStatus, type Uploader, type UploaderContext, type WebrtcServiceContext, type WebrtcServiceOptions, createBleService, createBrowserMediaBridge, createCacheService, createCatalogIntentResolver, createCommonService, createConfigService, createCoordinatedRelay, createCordnDmAdapter, createCordnRelayCoordinatorClient, createCountService, createDmService, createHttpUploader, createIdentityService, createIntentService, createKeysService, createLinkService, createListsService, createMediaService, createNdrDmAdapter, createNdrRelayTransport, createNip17DmAdapter, createNotificationService, createNotifyService, createOutboxService, createRelayPoolOutboxRouter, createRelayPoolService, createResourceService, createSerialService, createThemeService, createUploadService, createWebrtcService, manifestToIntentCatalogEntry };
2791
+ /**
2792
+ * NAP-FS service boundary for runtime-owned virtual filesystems.
2793
+ *
2794
+ * The service owns wire correlation and per-window watch identity. Backends own
2795
+ * virtual-path policy, authorization, persistence, picker mediation, and I/O.
2796
+ */
2797
+
2798
+ /** Change details emitted by a backend before the service adds its scoped watch id. */
2799
+ type FsBackendChange = Omit<FsChange, 'watchId'>;
2800
+ /** Active backend watch handle. */
2801
+ interface FsBackendWatch {
2802
+ /** Stop the backing watch. Must be idempotent. */
2803
+ close(): void | Promise<void>;
2804
+ }
2805
+ /** Filesystem operations supplied to the NAP-FS service. */
2806
+ interface FsBackend {
2807
+ info(windowId: string): FsInfo | Promise<FsInfo>;
2808
+ pickFile(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;
2809
+ pickFiles(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;
2810
+ pickDirectory(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;
2811
+ pickSaveFile(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;
2812
+ stat(windowId: string, path: string): FsMetadata | Promise<FsMetadata>;
2813
+ list(windowId: string, path: string): FsDirectoryEntry[] | Promise<FsDirectoryEntry[]>;
2814
+ read(windowId: string, path: string, options?: FsReadOptions): FsReadResult | Promise<FsReadResult>;
2815
+ write(windowId: string, path: string, data: string, options?: FsWriteOptions): FsWriteResult | Promise<FsWriteResult>;
2816
+ mkdir(windowId: string, path: string, options?: FsMkdirOptions): void | Promise<void>;
2817
+ remove(windowId: string, path: string, recursive?: boolean): void | Promise<void>;
2818
+ move(windowId: string, fromPath: string, toPath: string): void | Promise<void>;
2819
+ watch(windowId: string, path: string, options: FsWatchOptions | undefined, onChange: (change: FsBackendChange) => void): FsBackendWatch | Promise<FsBackendWatch>;
2820
+ /** Release session-scoped backend state for a destroyed runtime window. */
2821
+ onWindowDestroyed?(windowId: string): void;
2822
+ /** Release all backend resources. */
2823
+ close?(): void;
2824
+ }
2825
+ /** Error with a closed NAP-FS wire reason. */
2826
+ declare class FsServiceError extends Error {
2827
+ readonly code: FsError;
2828
+ constructor(code: FsError, message?: FsError);
2829
+ }
2830
+ /** Options for {@link createFsService}. */
2831
+ interface FsServiceOptions {
2832
+ backend: FsBackend;
2833
+ }
2834
+ /** Created NAP-FS service. */
2835
+ interface FsService extends ServiceHandler {
2836
+ /** Close watches and backend resources. */
2837
+ dispose(): void;
2838
+ }
2839
+ /**
2840
+ * Create a NAP-FS wire service over a runtime-owned backend.
2841
+ *
2842
+ * @param options - Filesystem backend.
2843
+ * @returns Service handler for `runtime.registerService('fs', handler)`.
2844
+ */
2845
+ declare function createFsService(options: FsServiceOptions): FsService;
2846
+
2847
+ export { type BleServiceContext, type BleServiceOptions, type CacheServiceOptions, type CatalogIntentResolver, type CatalogIntentResolverOptions, type CommonServiceContext, type CommonServiceOptions, type ConfigSchemaValidation, type ConfigService, type ConfigServiceOptions, type ConfigSettingsContext, type CoordinatedRelayOptions, type CordnCodecResult, type CordnCoordinatorClient, type CordnCoordinatorSubscription, type CordnDmAdapterOptions, type CordnDmClient, type CordnGroupMessage, type CordnRelayCoordinatorOptions, type CountRequest, type CountResult, type CountServiceOptions, type DmAdapter, DmMemoryStore, type DmRelayPool, type DmService, type DmServiceOptions, type FsBackend, type FsBackendChange, type FsBackendWatch, type FsService, FsServiceError, type FsServiceOptions, type HostCacheBridge, type HostKeyEvent, type HostKeysBridge, type HostMediaBridge, type HttpUploaderOptions, type HttpUploaderRails, type IdentityServiceOptions, type IntentArchetypeSupport, type IntentCatalogEntry, type IntentDispatchParams, type IntentResolver, type IntentResolverContext, type IntentServiceOptions, type IntentTargetController, type IntentTargetDispatch, type KeysServiceOptions, type LinkOpenContext, type LinkServiceOptions, type ListsServiceContext, type ListsServiceOptions, type ManifestArchetypeInput, type MaybePromise, type MediaMetadataLike, type MediaPlaybackOwner, type MediaServiceOptions, type MediaSessionCreateOptions, type MediaSessionTarget, type MediaSourceRef, type NdrDmAdapterOptions, type NdrRelayTransport, type NdrRelayTransportOptions, type NdrRumorLike, type NdrRuntimeLike, type Nip17DmAdapterOptions, type NostrTag, type Notification, type NotificationServiceOptions, type NotifyInteractionMessage, type NotifyPresentation, type NotifyServiceOptions, type OutboxEventOptions, type OutboxEventResult, type OutboxPublishOptions, type OutboxPublishResult, type OutboxQueryOptions, type OutboxQueryStream, type OutboxQueryStreamSink, type OutboxRelayPlan, type OutboxRelayPool, type OutboxResult, type OutboxRouter, type OutboxRouterSubscription, type OutboxServiceOptions, type OutboxSubscribeOptions, type OutboxSubscriptionSink, type OutboxTarget, type RailServerConfig, type RelayListEntry, type RelayPoolOutboxRouterOptions, type RelayPoolServiceOptions, type ResourceErrorCode, type ResourceInfo, type ResourceInfoContext, type ResourceInfoProvider, type ResourceSchemeInfo, type ResourceService, ResourceServiceError, type ResourceServiceOptions, type SerialServiceContext, type SerialServiceOptions, type SignEvent, type StreamingOutboxRouter, type ThemeService, type ThemeServiceOptions, type UploadDimensions, type UploadInfo, type UploadInfoContext, type UploadInfoProvider, type UploadRail, type UploadRailInfo, type UploadRequest, type UploadResult, type UploadServiceOptions, type UploadState, type UploadStatus, type Uploader, type UploaderContext, type WebrtcServiceContext, type WebrtcServiceOptions, createBleService, createBrowserMediaBridge, createCacheService, createCatalogIntentResolver, createCommonService, createConfigService, createCoordinatedRelay, createCordnDmAdapter, createCordnRelayCoordinatorClient, createCountService, createDmService, createFsService, createHttpUploader, createIdentityService, createIntentService, createKeysService, createLinkService, createListsService, createMediaService, createNdrDmAdapter, createNdrRelayTransport, createNip17DmAdapter, createNotificationService, createNotifyService, createOutboxService, createRelayPoolOutboxRouter, createRelayPoolService, createResourceService, createSerialService, createThemeService, createUploadService, createWebrtcService, manifestToIntentCatalogEntry, resolveConfigValues, validateConfigSchema };