@econ-v1/ports 6.7.0 → 6.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/dist/app-data.d.ts +39 -0
  2. package/dist/app-data.d.ts.map +1 -0
  3. package/dist/client-ui-preference.d.ts +23 -0
  4. package/dist/client-ui-preference.d.ts.map +1 -0
  5. package/dist/clipboard.d.ts +41 -0
  6. package/dist/clipboard.d.ts.map +1 -0
  7. package/dist/clock.d.ts +8 -8
  8. package/dist/clock.d.ts.map +1 -1
  9. package/dist/credential-handoff.d.ts +57 -0
  10. package/dist/credential-handoff.d.ts.map +1 -0
  11. package/dist/crypto.d.ts +4 -4
  12. package/dist/crypto.d.ts.map +1 -1
  13. package/dist/id.d.ts +2 -2
  14. package/dist/id.d.ts.map +1 -1
  15. package/dist/identity.d.ts +31 -25
  16. package/dist/identity.d.ts.map +1 -1
  17. package/dist/index.d.ts +226 -30
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/install-environment.d.ts +22 -32
  20. package/dist/install-environment.d.ts.map +1 -1
  21. package/dist/lcd-tree-source.d.ts +54 -0
  22. package/dist/lcd-tree-source.d.ts.map +1 -0
  23. package/dist/legacy-credential.d.ts +30 -0
  24. package/dist/legacy-credential.d.ts.map +1 -0
  25. package/dist/media-preferences.d.ts +2 -44
  26. package/dist/media-preferences.d.ts.map +1 -1
  27. package/dist/network/certificate-trust.d.ts +31 -0
  28. package/dist/network/certificate-trust.d.ts.map +1 -0
  29. package/dist/network/client-node-origin.d.ts +3 -15
  30. package/dist/network/client-node-origin.d.ts.map +1 -1
  31. package/dist/network/home-node-candidate-store.d.ts +4 -32
  32. package/dist/network/home-node-candidate-store.d.ts.map +1 -1
  33. package/dist/network/home-node-candidate.d.ts +9 -7
  34. package/dist/network/home-node-candidate.d.ts.map +1 -1
  35. package/dist/network/l402-payment.d.ts +11 -28
  36. package/dist/network/l402-payment.d.ts.map +1 -1
  37. package/dist/network/node-discovery.d.ts +5 -16
  38. package/dist/network/node-discovery.d.ts.map +1 -1
  39. package/dist/network/webrtc-signaling.d.ts +36 -52
  40. package/dist/network/webrtc-signaling.d.ts.map +1 -1
  41. package/dist/network.d.ts +65 -40
  42. package/dist/network.d.ts.map +1 -1
  43. package/dist/offline-readiness.d.ts +122 -0
  44. package/dist/offline-readiness.d.ts.map +1 -0
  45. package/dist/onboarding-access-code.d.ts +30 -0
  46. package/dist/onboarding-access-code.d.ts.map +1 -0
  47. package/dist/onboarding-checkpoint.d.ts +26 -0
  48. package/dist/onboarding-checkpoint.d.ts.map +1 -0
  49. package/dist/onboarding-presence.d.ts +12 -0
  50. package/dist/onboarding-presence.d.ts.map +1 -0
  51. package/dist/owner-claim-session.d.ts +60 -0
  52. package/dist/owner-claim-session.d.ts.map +1 -0
  53. package/dist/owner-claim.d.ts +22 -0
  54. package/dist/owner-claim.d.ts.map +1 -0
  55. package/dist/repositories/conversation-repository.d.ts +21 -20
  56. package/dist/repositories/conversation-repository.d.ts.map +1 -1
  57. package/dist/repositories/durable-event.d.ts +8 -8
  58. package/dist/repositories/durable-event.d.ts.map +1 -1
  59. package/dist/repositories/node-app-memory-repository.d.ts +168 -0
  60. package/dist/repositories/node-app-memory-repository.d.ts.map +1 -0
  61. package/dist/repositories/outbox.d.ts +33 -20
  62. package/dist/repositories/outbox.d.ts.map +1 -1
  63. package/dist/repositories/repository-mutation.d.ts +3 -1
  64. package/dist/repositories/repository-mutation.d.ts.map +1 -1
  65. package/dist/repositories/revisioned-repository.d.ts +14 -12
  66. package/dist/repositories/revisioned-repository.d.ts.map +1 -1
  67. package/dist/repositories/settings.d.ts +10 -10
  68. package/dist/repositories/settings.d.ts.map +1 -1
  69. package/dist/repositories/sync-ledger.d.ts +11 -11
  70. package/dist/repositories/sync-ledger.d.ts.map +1 -1
  71. package/dist/repositories/wallet-mirror-repository.d.ts +22 -13
  72. package/dist/repositories/wallet-mirror-repository.d.ts.map +1 -1
  73. package/dist/repositories.d.ts +52 -17
  74. package/dist/repositories.d.ts.map +1 -1
  75. package/dist/scheduler.d.ts +4 -4
  76. package/dist/scheduler.d.ts.map +1 -1
  77. package/dist/session-flag.d.ts +48 -0
  78. package/dist/session-flag.d.ts.map +1 -0
  79. package/dist/shell-cache-reset.d.ts +4 -0
  80. package/dist/shell-cache-reset.d.ts.map +1 -0
  81. package/dist/shell-version.d.ts +8 -0
  82. package/dist/shell-version.d.ts.map +1 -0
  83. package/dist/storage.d.ts +76 -34
  84. package/dist/storage.d.ts.map +1 -1
  85. package/dist/theme.d.ts +2 -24
  86. package/dist/theme.d.ts.map +1 -1
  87. package/package.json +14 -10
  88. package/src/app-data.js +42 -0
  89. package/src/client-ui-preference.js +23 -0
  90. package/src/clipboard.js +41 -0
  91. package/src/clock.js +13 -0
  92. package/src/credential-handoff.js +68 -0
  93. package/src/crypto.js +9 -0
  94. package/src/id.js +5 -0
  95. package/src/identity.js +33 -0
  96. package/src/index.js +250 -0
  97. package/src/install-environment.js +44 -0
  98. package/src/lcd-tree-source.js +56 -0
  99. package/src/legacy-credential.js +30 -0
  100. package/src/media-preferences.js +40 -0
  101. package/src/network/certificate-trust.js +25 -0
  102. package/src/network/client-node-origin.js +7 -0
  103. package/src/network/home-node-candidate-store.js +6 -0
  104. package/src/network/home-node-candidate.js +9 -0
  105. package/src/network/l402-payment.js +20 -0
  106. package/src/network/node-discovery.js +13 -0
  107. package/src/network/webrtc-signaling.js +40 -0
  108. package/src/network.js +33 -0
  109. package/src/offline-readiness.js +65 -0
  110. package/src/onboarding-access-code.js +21 -0
  111. package/src/onboarding-checkpoint.js +26 -0
  112. package/src/onboarding-presence.js +15 -0
  113. package/src/owner-claim-session.js +55 -0
  114. package/src/owner-claim.js +22 -0
  115. package/src/repositories/conversation-repository.js +20 -0
  116. package/src/repositories/durable-event.js +8 -0
  117. package/src/repositories/node-app-memory-repository.js +221 -0
  118. package/src/repositories/outbox.js +27 -0
  119. package/src/repositories/repository-mutation.js +8 -0
  120. package/src/repositories/revisioned-repository.js +17 -0
  121. package/src/repositories/settings.js +10 -0
  122. package/src/repositories/sync-ledger.js +11 -0
  123. package/src/repositories/wallet-mirror-repository.js +73 -0
  124. package/src/repositories.js +22 -0
  125. package/src/scheduler.js +6 -0
  126. package/src/session-flag.js +48 -0
  127. package/src/shell-cache-reset.js +3 -0
  128. package/src/shell-version.js +4 -0
  129. package/src/storage.js +60 -0
  130. package/src/theme.js +21 -0
  131. package/dist/clock.js +0 -2
  132. package/dist/clock.js.map +0 -1
  133. package/dist/crypto.js +0 -2
  134. package/dist/crypto.js.map +0 -1
  135. package/dist/id.js +0 -2
  136. package/dist/id.js.map +0 -1
  137. package/dist/identity.js +0 -2
  138. package/dist/identity.js.map +0 -1
  139. package/dist/index.js +0 -9
  140. package/dist/index.js.map +0 -1
  141. package/dist/install-environment.js +0 -26
  142. package/dist/install-environment.js.map +0 -1
  143. package/dist/media-preferences.js +0 -2
  144. package/dist/media-preferences.js.map +0 -1
  145. package/dist/network/client-node-origin.js +0 -18
  146. package/dist/network/client-node-origin.js.map +0 -1
  147. package/dist/network/home-node-candidate-store.js +0 -2
  148. package/dist/network/home-node-candidate-store.js.map +0 -1
  149. package/dist/network/home-node-candidate.js +0 -2
  150. package/dist/network/home-node-candidate.js.map +0 -1
  151. package/dist/network/l402-payment.js +0 -2
  152. package/dist/network/l402-payment.js.map +0 -1
  153. package/dist/network/node-discovery.js +0 -13
  154. package/dist/network/node-discovery.js.map +0 -1
  155. package/dist/network/webrtc-signaling.js +0 -30
  156. package/dist/network/webrtc-signaling.js.map +0 -1
  157. package/dist/network.js +0 -2
  158. package/dist/network.js.map +0 -1
  159. package/dist/repositories/conversation-repository.js +0 -2
  160. package/dist/repositories/conversation-repository.js.map +0 -1
  161. package/dist/repositories/durable-event.js +0 -2
  162. package/dist/repositories/durable-event.js.map +0 -1
  163. package/dist/repositories/outbox.js +0 -9
  164. package/dist/repositories/outbox.js.map +0 -1
  165. package/dist/repositories/repository-mutation.js +0 -2
  166. package/dist/repositories/repository-mutation.js.map +0 -1
  167. package/dist/repositories/revisioned-repository.js +0 -2
  168. package/dist/repositories/revisioned-repository.js.map +0 -1
  169. package/dist/repositories/settings.js +0 -2
  170. package/dist/repositories/settings.js.map +0 -1
  171. package/dist/repositories/sync-ledger.js +0 -2
  172. package/dist/repositories/sync-ledger.js.map +0 -1
  173. package/dist/repositories/wallet-mirror-repository.js +0 -53
  174. package/dist/repositories/wallet-mirror-repository.js.map +0 -1
  175. package/dist/repositories.js +0 -2
  176. package/dist/repositories.js.map +0 -1
  177. package/dist/scheduler.js +0 -2
  178. package/dist/scheduler.js.map +0 -1
  179. package/dist/storage.js +0 -14
  180. package/dist/storage.js.map +0 -1
  181. package/dist/theme.js +0 -2
  182. package/dist/theme.js.map +0 -1
@@ -0,0 +1,56 @@
1
+ // @ts-check
2
+ /**
3
+ * The seam through which the LCD pane obtains the widget tree the node's round display is
4
+ * currently showing (design §8.1).
5
+ *
6
+ * The pane is a `client/ui` surface and `client/ui` may never call `fetch` (rule B2, enforced
7
+ * by `client/scripts/check-boundaries.mjs`), so the two HTTP calls that produce a tree —
8
+ * `POST /lcd/screens/:id/state` then `GET /lcd/screens/:id/preview` against
9
+ * `modules/node-app-lcd`'s debug server — live in `client/adapters-web` behind this port.
10
+ * `client/domain` holds the port and never learns there is a network at all.
11
+ *
12
+ * `screen()` returns a CLOSED OUTCOME UNION and never rejects. That is not stylistic: the
13
+ * only consumer is a live query, and `LiveQueryEngine.#enqueue` does
14
+ * `void run.catch(() => undefined)` — a rejected `run()` never reaches the subscriber, so a
15
+ * throwing source would freeze the pane on a stale screen with no error and no retry. The
16
+ * failure must be data.
17
+ *
18
+ * Nothing here carries node error text. `"unreachable"` covers offline, DNS failure, a 5xx,
19
+ * a malformed body and a missing dev proxy alike; `"not-registered"` is specifically "this
20
+ * node's LCD app has no screen with that id", which is the normal answer for a screen whose
21
+ * Rust `build()` has not been written yet.
22
+ */
23
+ /**
24
+ * For every composition without a reachable LCD daemon — production (where the pane is not
25
+ * shipped at all), tests, and any headless kernel. Reporting `"unreachable"` is the honest
26
+ * answer and drives the pane's §8.4 placeholder; it is also the SAFE direction, because the
27
+ * pane is never on onboarding's critical path and the device pane continues regardless.
28
+ * @returns {LcdTreeSourcePort}
29
+ */
30
+ export function createNullLcdTreeSource() {
31
+ return { screen: async () => ({ kind: "unreachable" }) };
32
+ }
33
+ /** An RFC-7396 JSON Merge Patch applied to the screen's registry state before its tree is
34
+ * built. Scalars only: every built-in screen's `initial_state` is a flat scalar map.
35
+ * @typedef {Readonly<Record<string, number | string>>} LcdScreenStatePatch
36
+ */
37
+ /**
38
+ * @typedef {| { readonly kind: "not-registered" }
39
+ * | { readonly kind: "tree"; readonly tree: LcdTreeNode }
40
+ * | { readonly kind: "unreachable" }} LcdTreeOutcome
41
+ */
42
+ /**
43
+ * The serde-flattened `Node` the LCD engine emits: `{ id, type, ...props, children }`, where
44
+ * the props are spread onto the node itself and `type` is the discriminant
45
+ * (`modules/node-app-lcd/src/lcd/engine/components/node.rs`). Deliberately loose — a renderer
46
+ * switches on `type` and reads the props it knows, and a screen growing a field must not
47
+ * break this seam.
48
+ * @typedef {Readonly<Record<string, unknown>> & object} LcdTreeNode
49
+ * @property {readonly LcdTreeNode[]} [children]
50
+ * @property {string} id
51
+ * @property {string} type
52
+ */
53
+ /**
54
+ * @typedef {object} LcdTreeSourcePort
55
+ * @property {(screenId: string, state: LcdScreenStatePatch) => Promise<LcdTreeOutcome>} screen
56
+ */
@@ -0,0 +1,30 @@
1
+ // @ts-check
2
+ export {};
3
+ /**
4
+ * Whether this browser holds a credential the LEGACY `system/ui` SPA can sign in with.
5
+ *
6
+ * `"unknown"` is a first-class answer, not an error: a storage-partitioned or
7
+ * privacy-locked browser genuinely cannot be asked. Callers MUST treat it like `"absent"`
8
+ * for any safety decision — the only consequence of over-warning is one extra click,
9
+ * whereas under-warning is the failure this port exists to prevent.
10
+ * @typedef {"absent" | "present" | "unknown"} LegacyCredentialStatus
11
+ */
12
+ /**
13
+ * Answers "would the classic UI recognise this browser?" WITHOUT bridging the two
14
+ * authentication systems.
15
+ *
16
+ * The two UIs deliberately do not share an identity: the legacy SPA signs in with a
17
+ * secp256k1 device key in its own IndexedDB (migrated once from a BIP39 seed —
18
+ * `system/ui/src/lib/deviceKey.ts`), while the Client Node PWA uses a paired device
19
+ * identity + device session it never converts back into a seed. `client/kernel`'s
20
+ * `LegacyStorageReader` imports legacy state INTO the PWA for the v1 cutover and nothing
21
+ * writes the reverse, so a PWA-onboarded browser has no legacy credential at all.
22
+ *
23
+ * This port therefore reports PRESENCE ONLY. It never reads, returns, derives, or
24
+ * provisions key material — implementations are expected to test for the record's
25
+ * existence without deserializing its value. Answering this question is what lets the
26
+ * "Switch to the classic UI" control warn honestly instead of dropping a PWA-onboarded
27
+ * user onto a sign-in screen they cannot satisfy.
28
+ * @typedef {object} LegacyCredentialProbePort
29
+ * @property {() => Promise<LegacyCredentialStatus>} status
30
+ */
@@ -0,0 +1,40 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Boundary port for the two NON-THEME media queries the shell reads (#1687).
5
+ *
6
+ * `client/ui` may never call `matchMedia` directly (boundary rule B2,
7
+ * `client/scripts/check-boundaries.mjs`) — this is the narrow surface
8
+ * `client/adapters-web`'s `createBrowserMediaPreferences()` implements against the real
9
+ * global, and the composition root (`system/pwa/src/main.ts`) registers it (via
10
+ * `client/ui`'s `configureMediaPreferences`) before the shell's first paint.
11
+ *
12
+ * Deliberately SEPARATE from `ThemeEnvironmentPort` rather than folded into it, even
13
+ * though both end up calling `matchMedia` in their adapters:
14
+ *
15
+ * - `ThemeEnvironmentPort` models the *theme preference* as a whole — a value with
16
+ * PERSISTENCE (`getStoredTheme`/`setStoredTheme` over `localStorage`) where the media
17
+ * query is only the fallback when nothing is stored, plus a live `change`
18
+ * subscription because a theme flip must repaint mid-session. Its concern is
19
+ * `prefers-color-scheme`.
20
+ * - The queries here are neither theme nor persisted nor live: `prefers-reduced-motion`
21
+ * (`stages/waves-stage.ts`, `stages/waves-canvas.ts`) is an accessibility/motion
22
+ * preference, and `(hover: hover) and (pointer: fine)` (`shell/pmenu.ts`) is a
23
+ * pointer-capability probe. Both are read ONCE, at mount/connect time, matching the
24
+ * original skin's own one-shot checks — neither has a stored override to consult nor
25
+ * a subscription to tear down.
26
+ *
27
+ * Folding them into `ThemeEnvironmentPort` would make a port named for theme answer
28
+ * questions about motion and pointers, and would oblige every fake in the test suite (and
29
+ * `theme-environment.ts`'s `NOOP_THEME_ENVIRONMENT`) to stub three unrelated concerns to
30
+ * exercise one. Two narrow ports, one adapter technology.
31
+ *
32
+ * Both methods are synchronous and side-effect-free, for the same reason
33
+ * `ThemeEnvironmentPort`'s are: they are read during `connectedCallback`/`mount`, before
34
+ * the kernel connection (`KernelRpcClient`, an async `SharedWorker`/leader-tab bridge)
35
+ * exists, so they can never be mediated through `@econ-v1/rpc` the way ordinary UI
36
+ * reads are (rule S4).
37
+ */
38
+ /** @typedef {{ prefersReducedMotion(): boolean, hasFinePointer(): boolean }} MediaPreferencesPort */
39
+
40
+ export {};
@@ -0,0 +1,25 @@
1
+ // @ts-check
2
+ export {};
3
+ /** @typedef {"android" | "apple-mobile" | "apple-mac" | "firefox" | "windows" | "linux" | "unknown"} CertificateTrustPlatform */
4
+ /**
5
+ * `untrusted` and `unreachable` are deliberately separate outcomes, not one
6
+ * failure bucket. They look identical to the probe's caller — neither unlocks
7
+ * the shell — but they have opposite remedies, and telling a person to install
8
+ * a certificate authority when the real problem is that their node stopped
9
+ * answering sends them somewhere they cannot succeed. `untrusted` means the
10
+ * node answered and the browser refused the chain; `unreachable` means the
11
+ * request never produced a response at all (DNS failure, connection refused,
12
+ * a certificate the browser rejects before any HTTP exchange, or a timeout).
13
+ * @typedef {| { readonly status: "trusted"; readonly origin: string; readonly fingerprint?: string }
14
+ * | { readonly status: "untrusted"; readonly origin: string; readonly fingerprint?: string }
15
+ * | { readonly status: "unreachable"; readonly origin: string }} CertificateTrustProbeResult
16
+ */
17
+ /**
18
+ * @typedef {object} CertificateTrustProbePort
19
+ * @property {(origin: string, signal?: CancellationSignal) => Promise<CertificateTrustProbeResult>} probe
20
+ */
21
+ /**
22
+ * @typedef {object} PlatformTrustPort
23
+ * @property {() => CertificateTrustPlatform} detect
24
+ */
25
+ /** @import { CancellationSignal } from '../network.js' */
@@ -0,0 +1,7 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * The Client Node PWA's cloud distribution origin. This static origin is never itself a
5
+ * dialable home node.
6
+ */
7
+ export const DEFAULT_CLIENT_NODE_ORIGIN = "https://client-node.economy1.cloud";
@@ -0,0 +1,6 @@
1
+ // @ts-check
2
+
3
+ /** @typedef {{ readonly address: string, readonly id: string }} StoredHomeNodeCandidate */
4
+ /** @typedef {{ read(): StoredHomeNodeCandidate | undefined, write(candidate: StoredHomeNodeCandidate): void, clear(): void }} HomeNodeCandidateStorePort */
5
+
6
+ export {};
@@ -0,0 +1,9 @@
1
+ // @ts-check
2
+
3
+ /** @typedef {import("../network.js").CancellationSignal} CancellationSignal */
4
+ /** @typedef {import("../network.js").HomeNodeClient} HomeNodeClient */
5
+ /** @typedef {Exclude<import("../network.js").TransportPath, "offline">} HomeNodePath */
6
+ /** @typedef {{ readonly address: string, readonly id: string, readonly path: HomeNodePath }} HomeNodeCandidate */
7
+ /** @typedef {{ dial(candidate: HomeNodeCandidate, signal: CancellationSignal): Promise<HomeNodeClient> }} HomeNodeDialer */
8
+
9
+ export {};
@@ -0,0 +1,20 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * The domain-level shape of an L402 payment challenge. Never log `macaroon`
5
+ * or `invoice`: both are credential/payment-secret material.
6
+ *
7
+ * @typedef {{ readonly amountSats: number, readonly description: string, readonly invoice: string, readonly macaroon: string }} L402PaymentChallenge
8
+ */
9
+
10
+ /** @typedef {{ readonly preimage: string }} L402PaymentResult */
11
+
12
+ /**
13
+ * Pluggable port supplied by the kernel or composition root to pay an L402
14
+ * challenge surfaced by a client-RPC carrier.
15
+ *
16
+ * @typedef {object} L402PaymentHandler
17
+ * @property {(challenge: L402PaymentChallenge) => Promise<L402PaymentResult>} pay
18
+ */
19
+
20
+ export {};
@@ -0,0 +1,13 @@
1
+ // @ts-check
2
+
3
+ /** @typedef {{ readonly address: string, readonly id: string, readonly path: "lan-direct" }} NodeDiscoveryCandidate */
4
+ /**
5
+ * @typedef {
6
+ * | { readonly status: "verified", readonly candidate: NodeDiscoveryCandidate }
7
+ * | { readonly status: "reached-but-invalid", readonly reason: string }
8
+ * | { readonly status: "unreachable-or-untrusted" }
9
+ * } NodeDiscoveryResult
10
+ */
11
+ /** @typedef {{ discover(rawAddress: string): Promise<NodeDiscoveryResult> }} NodeAddressDiscoverer */
12
+
13
+ export {};
@@ -0,0 +1,40 @@
1
+ // @ts-check
2
+
3
+ /** @typedef {import("../network.js").CancellationSignal} CancellationSignal */
4
+
5
+ export const WEBRTC_SIGNAL_VERSION = 1;
6
+
7
+ /** Maximum serialized size of one signaling WebSocket text frame. */
8
+ export const MAX_WEBRTC_SIGNAL_MESSAGE_BYTES = 32 * 1024;
9
+ /** Maximum SDP body size (offer, answer, or restart). */
10
+ export const MAX_SDP_BYTES = 16 * 1024;
11
+ export const MIN_OFFER_NONCE_BYTES = 16;
12
+ export const MAX_OFFER_NONCE_BYTES = 128;
13
+ /** Maximum DTLS fingerprint size: `<algorithm> <hex-pairs>`. */
14
+ export const MAX_DTLS_FINGERPRINT_BYTES = 200;
15
+ /** Maximum size of one trickle-ICE candidate line. */
16
+ export const MAX_ICE_CANDIDATE_BYTES = 1024;
17
+ export const MAX_SDP_MID_BYTES = 64;
18
+ /** Maximum number of trickle-ICE candidates accepted for one bound offer. */
19
+ export const MAX_ICE_CANDIDATES_PER_OFFER = 32;
20
+ export const MAX_CLOSE_REASON_BYTES = 256;
21
+
22
+ /** @typedef {{ readonly headers: Readonly<Record<string, string>>, readonly type: "setup", readonly version: number }} WebRtcSignalSetup */
23
+ /** @typedef {{ readonly dtls_fingerprint: string, readonly offer_nonce: string, readonly sdp: string, readonly type: "offer", readonly version: number }} WebRtcOffer */
24
+ /** @typedef {{ readonly dtls_fingerprint: string, readonly offer_nonce: string, readonly sdp: string, readonly type: "answer" }} WebRtcAnswer */
25
+ /** @typedef {{ readonly candidate: string, readonly offer_nonce: string, readonly sdp_mid?: string, readonly sdp_mline_index?: number, readonly type: "candidate" }} WebRtcIceCandidate */
26
+ /** @typedef {{ readonly dtls_fingerprint: string, readonly offer_nonce: string, readonly previous_offer_nonce: string, readonly sdp: string, readonly type: "restart", readonly version: number }} WebRtcRestart */
27
+ /** @typedef {{ readonly offer_nonce?: string, readonly reason?: string, readonly type: "close" }} WebRtcClose */
28
+ /** @typedef {WebRtcAnswer | WebRtcClose | WebRtcIceCandidate | WebRtcOffer | WebRtcRestart | WebRtcSignalSetup} WebRtcSignalMessage */
29
+
30
+ /**
31
+ * Browser-side signaling transport contract. No peer connection or
32
+ * DataChannel is created here.
33
+ *
34
+ * @typedef {object} WebRtcSignalingPort
35
+ * @property {(close?: { readonly offer_nonce?: string, readonly reason?: string }) => Promise<void>} close
36
+ * @property {(setupHeaders: Readonly<Record<string, string>>, signal?: CancellationSignal) => Promise<void>} connect
37
+ * @property {(candidate: { readonly candidate: string, readonly offer_nonce: string, readonly sdp_mid?: string, readonly sdp_mline_index?: number }) => Promise<void>} sendCandidate
38
+ * @property {(offer: { readonly dtls_fingerprint: string, readonly offer_nonce: string, readonly sdp: string }) => Promise<WebRtcAnswer>} sendOffer
39
+ * @property {(restart: { readonly dtls_fingerprint: string, readonly offer_nonce: string, readonly previous_offer_nonce: string, readonly sdp: string }) => Promise<WebRtcAnswer>} sendRestart
40
+ */
package/src/network.js ADDED
@@ -0,0 +1,33 @@
1
+ // @ts-check
2
+
3
+ /** @typedef {import("./network/home-node-candidate.js").HomeNodeCandidate} HomeNodeCandidate */
4
+ /** @typedef {import("./repositories/repository-mutation.js").RepositoryJsonValue} RepositoryJsonValue */
5
+
6
+ /** @typedef {"iroh-relay" | "lan-direct" | "lan-webtransport" | "offline" | "wan-direct" | "webrtc-direct"} TransportPath */
7
+ /** @typedef {"lan-direct" | "wan-direct"} HttpTransportPath */
8
+ /** @typedef {"lan-direct" | "lan-webtransport" | "wan-direct"} DirectTransportPath */
9
+ /** @typedef {HomeNodeCandidate & { readonly identityBinding?: "bootstrap", readonly path: HttpTransportPath }} HttpHomeNodeCandidate */
10
+ /** @typedef {{ readonly algorithm: "sha-256", readonly current: string, readonly next: string }} WebTransportCertificateHashes */
11
+ /** @typedef {HomeNodeCandidate & { readonly certificateHashes?: WebTransportCertificateHashes, readonly path: "lan-webtransport" }} WebTransportHomeNodeCandidate */
12
+ /** @typedef {HttpHomeNodeCandidate | WebTransportHomeNodeCandidate} DirectHomeNodeCandidate */
13
+ /** @typedef {"no-network" | "node-unreachable" | "session-required"} TransportOfflineReason */
14
+ /** @typedef {{ readonly changedAt: string, readonly path: "lan-direct" | "lan-webtransport" | "wan-direct" | "iroh-relay" | "webrtc-direct", readonly status: "connected" } | { readonly attempt: number, readonly changedAt: string, readonly path: "offline", readonly status: "connecting" } | { readonly attempt: number, readonly changedAt: string, readonly nextRetryAt: string, readonly path: "offline", readonly status: "reconnecting" } | { readonly changedAt: string, readonly path: "offline", readonly reason?: TransportOfflineReason, readonly status: "offline" }} TransportState */
15
+ /** @typedef {{ readonly headers?: Readonly<Record<string, string>>, readonly operation: string, readonly payload: unknown, readonly responseType?: "blob" | "json" }} HomeNodeRequest */
16
+ /** @typedef {{ readonly cursor?: string, readonly domain: string }} DeltaRequest */
17
+ /** @typedef {{ readonly aborted: boolean }} CancellationSignal */
18
+
19
+ export const CAPABILITY_INVOKE_OPERATION = "POST /api/v2/system/capabilities/invoke";
20
+
21
+ /** @typedef {{ readonly data: Readonly<Record<string, RepositoryJsonValue>>, readonly instance?: string, readonly resource: string, readonly type: string }} AppEventEnvelope */
22
+ /** @typedef {{ readonly resourceId?: string }} AppEventSubscribeOptions */
23
+ /** @typedef {() => void} AppEventSubscription */
24
+ /** @typedef {{ deltas: <T>(request: DeltaRequest) => AsyncIterable<T>, invoke: <T>(request: HomeNodeRequest) => Promise<T>, subscribeAppEvents?: (resource: string, listener: (event: AppEventEnvelope) => void, options?: AppEventSubscribeOptions) => AppEventSubscription, readonly state: TransportState }} HomeNodeClient */
25
+
26
+ /**
27
+ * @typedef {object} PeerTransportPort
28
+ * @property {(peer: string, payload: Uint8Array, signal?: CancellationSignal) => Promise<void>} send
29
+ * @property {(topic: string, payload: Uint8Array) => Promise<void>} publish
30
+ * @property {(topic: string, signal?: CancellationSignal) => AsyncIterable<Uint8Array>} subscribe
31
+ */
32
+
33
+ export {};
@@ -0,0 +1,65 @@
1
+ // @ts-check
2
+
3
+ /** @typedef {import("./repositories.js").StageCatalogEntry} StageCatalogEntry */
4
+ /** @typedef {import("./repositories.js").StageManifest} StageManifest */
5
+ /** @typedef {import("./repositories.js").StageRegistrySnapshot} StageRegistrySnapshot */
6
+ /** @typedef {import("./repositories/repository-mutation.js").RepositoryJsonValue} RepositoryJsonValue */
7
+
8
+ /** @typedef {{ readonly deviceDid: string, readonly homeNodeId: string }} OfflineIdentityScope */
9
+ /** @typedef {"caching-assets" | "needs-first-sync" | "offline-ready" | "update-available" | "failed" | "removing"} OfflineStageState */
10
+ /** @typedef {"asset-fetch-failed" | "asset-integrity-failed" | "projection-seed-failed" | "schema-incompatible" | "quota-exceeded" | "identity-mismatch" | "active-resource-corrupt"} OfflineReadinessFailureCode */
11
+ /** @typedef {{ readonly appName: string, readonly activeManifestSha256?: string, readonly candidateManifestSha256?: string, readonly failureCode?: OfflineReadinessFailureCode, readonly state: OfflineStageState, readonly updatedAt: string }} OfflineStageReadiness */
12
+ /** @typedef {{ readonly assetRevision: string, readonly appName: string, readonly entryUrl: string, readonly manifestSha256: string, readonly version: string }} StageAssetCandidate */
13
+ /** @typedef {{ prepareCandidate(entry: StageCatalogEntry, catalogRevision: string): Promise<StageAssetCandidate>, validateCandidate(candidate: StageAssetCandidate, manifest: StageManifest): Promise<void>, discardCandidate(candidate: StageAssetCandidate): Promise<void>, deleteUnreferenced(keepAssetRevisions: readonly string[]): Promise<void> }} VerifiedStageAssetPort */
14
+ /** @typedef {{ estimate(): Promise<{ readonly quota?: number, readonly usage?: number }> }} OfflineStorageEstimatePort */
15
+
16
+ /** @typedef {{ readonly appName: string, readonly candidateManifestSha256: string, readonly sourceRevision: string, readonly state: "caching-assets" | "update-available", readonly updatedAt: string }} OfflineStagePreparationRecord */
17
+ /** @typedef {{ readonly assetRevision: string, readonly entry: StageCatalogEntry, readonly projectionRevision?: string, readonly sourceRevision: string }} OfflineStageCommit */
18
+ /** @typedef {{ readonly active: OfflineStageCommit, readonly rollback?: OfflineStageCommit }} OfflineStageCommitRecord */
19
+ /** @typedef {{ readonly candidate: StageAssetCandidate, readonly entry: StageCatalogEntry, readonly sourceRevision: string }} OfflineStageBundleCommitInput */
20
+ /** @typedef {{ readonly candidate: StageAssetCandidate, readonly entry: StageCatalogEntry, readonly projectionRevision: string, readonly requiredProjectionKeys: readonly string[], readonly sourceRevision: string }} OfflineStagePromotionInput */
21
+ /** @typedef {{ readonly appName: string, readonly candidateManifestSha256?: string, readonly failureCode: OfflineReadinessFailureCode, readonly updatedAt: string }} OfflineStageFailureInput */
22
+ /** @typedef {{ readonly cursor?: string, readonly updatedAt: string, readonly value: RepositoryJsonValue }} OfflineProjectionRecord */
23
+ /** @typedef {{ readonly key: string, readonly record: OfflineProjectionRecord }} OfflineProjectionBatchRecord */
24
+ /** @typedef {{ readonly cursor?: string, readonly records: readonly OfflineProjectionBatchRecord[], readonly updatedAt: string }} OfflineProjectionBatchCommit */
25
+
26
+ /**
27
+ * Durable installed-stage entries, preparation journals, active/rollback
28
+ * references, and readiness state. Every operation requires an explicit
29
+ * identity scope.
30
+ *
31
+ * Catalog mutations are confined to bundle commit/promotion, rollback
32
+ * activation, and removal state-machine operations.
33
+ *
34
+ * @typedef {{
35
+ * readCatalog(scope: OfflineIdentityScope): Promise<StageRegistrySnapshot | undefined>,
36
+ * readCommit(scope: OfflineIdentityScope, appName: string): Promise<OfflineStageCommitRecord | undefined>,
37
+ * listReadiness(scope: OfflineIdentityScope): Promise<readonly OfflineStageReadiness[]>,
38
+ * recordPreparation(scope: OfflineIdentityScope, record: OfflineStagePreparationRecord): Promise<void>,
39
+ * commitNewBundle(scope: OfflineIdentityScope, input: OfflineStageBundleCommitInput): Promise<void>,
40
+ * confirmBootHealth(scope: OfflineIdentityScope, appName: string, manifestSha256: string): Promise<OfflineStageCommit | undefined>,
41
+ * activateRollback(scope: OfflineIdentityScope, appName: string, activeManifestSha256: string, rollbackManifestSha256: string): Promise<OfflineStageCommit | undefined>,
42
+ * promoteOfflineReady(scope: OfflineIdentityScope, input: OfflineStagePromotionInput): Promise<void>,
43
+ * markFailed(scope: OfflineIdentityScope, input: OfflineStageFailureInput): Promise<void>,
44
+ * removeStage(scope: OfflineIdentityScope, appName: string, sourceRevision: string): Promise<void>,
45
+ * finalizeRemoval(scope: OfflineIdentityScope, appName: string): Promise<void>,
46
+ * invalidateScope(scope: OfflineIdentityScope): Promise<void>,
47
+ * clearScope(scope: OfflineIdentityScope): Promise<void>
48
+ * }} OfflineStageRepository
49
+ */
50
+
51
+ /**
52
+ * Identity-scoped versioned projection records and revision heads. Every
53
+ * operation requires an explicit identity scope.
54
+ *
55
+ * @typedef {{
56
+ * read(scope: OfflineIdentityScope, namespace: string, projectionRevision: string, key: string): Promise<OfflineProjectionRecord | undefined>,
57
+ * write(scope: OfflineIdentityScope, namespace: string, projectionRevision: string, key: string, record: OfflineProjectionRecord): Promise<void>,
58
+ * writeBatch(scope: OfflineIdentityScope, namespace: string, projectionRevision: string, commit: OfflineProjectionBatchCommit): Promise<void>,
59
+ * hasKeys(scope: OfflineIdentityScope, namespace: string, projectionRevision: string, keys: readonly string[]): Promise<boolean>,
60
+ * deleteRevision(scope: OfflineIdentityScope, namespace: string, projectionRevision: string): Promise<void>,
61
+ * clearScope(scope: OfflineIdentityScope): Promise<void>
62
+ * }} OfflineProjectionRepository
63
+ */
64
+
65
+ export {};
@@ -0,0 +1,21 @@
1
+ // @ts-check
2
+ export {};
3
+ /**
4
+ * The access code a person types (or scans via the step-9 QR) to claim this node — the
5
+ * shared secret `OwnerClaimService.prepareClaim(accessCode)` sends as `access_code` on
6
+ * `POST /api/v2/node/onboarding-prepare`.
7
+ *
8
+ * Deliberately `localStorage`-shaped, exactly like `OnboardingCheckpointStoragePort`: step 7
9
+ * destroys the tab the owner was working in, and a code re-typed by hand before that
10
+ * navigation must still be there afterwards rather than forcing a second entry. Not
11
+ * `sessionStorage` for the same reason — the owner may land in a brand-new tab.
12
+ *
13
+ * Every method degrades silently. Reading or writing `localStorage` — and even *touching*
14
+ * the property — can throw in locked-down embedders, and losing this code must never abort
15
+ * onboarding; the person simply gets asked to type it again.
16
+ * @typedef {object} OnboardingAccessCodeStore
17
+ * @property {() => string | null} read Returns the stored code, or `null` for "never saved" AND for "the read itself failed"
18
+ * alike — the caller must not (and cannot) tell those two cases apart.
19
+ * @property {(code: string) => void} save Stores `code`. A failed write is swallowed; the flow continues without persistence.
20
+ * @property {() => void} clear Empties the slot. Never throws.
21
+ */
@@ -0,0 +1,26 @@
1
+ // @ts-check
2
+ export {};
3
+ /**
4
+ * The one string slot the onboarding flow needs to survive a full navigation.
5
+ *
6
+ * Step 7 deliberately destroys the tab the owner was working in (the node's AP closes and the
7
+ * browser must be pointed at the home-LAN address), and the kernel is an elected leader tab —
8
+ * not a SharedWorker — so its in-memory service state dies with that tab. Without a slot that
9
+ * outlives the navigation the flow restarts at step 3.
10
+ *
11
+ * Deliberately `localStorage`-shaped rather than OPFS-backed: OPFS is gated pre-claim
12
+ * (`requiresDurableStorage`), which is exactly the window this must work in. Deliberately
13
+ * NOT `sessionStorage` (`SessionFlagPort`): the owner may be handed a brand-new tab.
14
+ *
15
+ * Deliberately synchronous and string-typed: `OnboardingCheckpointStore` owns the schema, the
16
+ * allow-list and the staleness rule, so this port cannot be handed a structured object that
17
+ * happens to carry a Wi-Fi password.
18
+ *
19
+ * Every method degrades silently, exactly like `SessionFlagPort`: reading or writing
20
+ * `localStorage` — and even *touching* the property — can throw in locked-down embedders, and
21
+ * losing a resume point must never abort onboarding.
22
+ * @typedef {object} OnboardingCheckpointStoragePort
23
+ * @property {() => void} clear
24
+ * @property {() => string | undefined} read
25
+ * @property {(value: string) => void} write
26
+ */
@@ -0,0 +1,15 @@
1
+ // @ts-check
2
+ /**
3
+ * @returns {OnboardingPresencePort}
4
+ */
5
+ export function createNullOnboardingPresence() {
6
+ return { announce: () => Promise.resolve() };
7
+ }
8
+ /**
9
+ * Phases the node's onboarding FSM understands. Fire-and-forget hints (spec §3.2).
10
+ * @typedef {"portal" | "wifi_setup" | "register" | "install"} OnboardingPresencePhase
11
+ */
12
+ /**
13
+ * @typedef {object} OnboardingPresencePort
14
+ * @property {(phase: OnboardingPresencePhase) => Promise<void>} announce
15
+ */
@@ -0,0 +1,55 @@
1
+ // @ts-check
2
+ /** For compositions with no browser session client (tests, headless kernels): every claim is
3
+ * reported as unpaired, which is the safe direction — the shell runs its pairing flow.
4
+ * @returns {OwnerClaimSessionPort}
5
+ */
6
+ export function createNullOwnerClaimSession() {
7
+ return { adopt: async () => false };
8
+ }
9
+ /**
10
+ * The seam through which a successful first-owner claim becomes a REAL browser session
11
+ * (design §7.1).
12
+ *
13
+ * `POST /api/auth/complete-onboarding` answers with `token` / `refresh_token` /
14
+ * `token_expires_at` (`core/adapters-network/src/transport/auth/handlers.rs`), but
15
+ * `client/domain` cannot do anything with them: turning an owner JWT into a client-device
16
+ * session means talking to `/api/v2/client-devices/*` with browser-held key material, which is
17
+ * `client/adapters-web`'s job. Hence a port.
18
+ *
19
+ * `adopt` returns a BOOLEAN rather than throwing or returning void, and that is the whole
20
+ * design: a `false` means "the claim succeeded but this browser is not paired", and the flow
21
+ * then falls back to the shell's ordinary pairing screen. Nothing here may assert a trust
22
+ * relationship the browser does not hold — whether the node auto-approves the owner's first
23
+ * device is the node's decision, not this client's assumption.
24
+ *
25
+ * SECURITY: only the access token and its expiry cross this boundary. The refresh token is
26
+ * deliberately NOT part of `OwnerClaimSessionTokens` — no client code today can spend it, and a
27
+ * long-lived credential held with no consumer is pure downside.
28
+ * @typedef {object} OwnerClaimSessionTokens
29
+ * @property {string} accessToken
30
+ * @property {string} expiresAt ISO instant. An already-expired value must be refused rather than adopted.
31
+ */
32
+ /**
33
+ * Wire-shaped, snake_case: `genesis_device` crosses to the node verbatim as a field of
34
+ * `POST /api/auth/complete-onboarding`'s payload (`core/domain/src/services/
35
+ * onboarding_completion_service.rs`'s `GenesisDeviceDescriptor`), so this typedef mirrors the
36
+ * Rust struct field-for-field rather than the camelCase this package otherwise prefers — the
37
+ * same precedent as `OwnerClaimSessionTokens` staying JS-shaped for values that stay client-side
38
+ * while wire payloads built for direct forwarding stay wire-shaped.
39
+ * @typedef {object} OwnerClaimGenesisDevice
40
+ * @property {string} public_key_multibase
41
+ * @property {string} device_name
42
+ * @property {string} device_type
43
+ * @property {string} signature
44
+ */
45
+ /**
46
+ * @typedef {object} OwnerClaimSessionPort
47
+ * @property {(tokens: OwnerClaimSessionTokens) => Promise<boolean>} adopt
48
+ * @property {(challenge: string) => Promise<OwnerClaimGenesisDevice | undefined>} [describeGenesisDevice] OPTIONAL: builds the signed
49
+ * `genesis_device` proof-of-possession descriptor for the given onboarding-challenge string, so
50
+ * the node can mint the genesis DID device with this browser's real pairing identity instead of
51
+ * a synthetic fallback. `undefined` — from an implementation that omits the member entirely, or
52
+ * from any failure to build the descriptor — means the claim proceeds without one; `adopt` must
53
+ * never depend on this having been called. `createNullOwnerClaimSession()` deliberately leaves
54
+ * this unimplemented (adopt-only) since it is optional.
55
+ */
@@ -0,0 +1,22 @@
1
+ // @ts-check
2
+ export {};
3
+ /**
4
+ * Ephemeral secp256k1 identity used ONLY for the legacy BIP39-shaped first-owner
5
+ * onboarding contract (`POST /api/v2/node/onboarding-prepare` /
6
+ * `POST /api/auth/{onboarding-challenge,complete-onboarding}` —
7
+ * verified server-side by `core/auth/src/crypto.rs`'s `CryptoService`).
8
+ *
9
+ * Deliberately separate from `IdentityPort` (Ed25519, used for client-device
10
+ * pairing/session, `./identity.ts`): the host's owner-onboarding endpoints verify
11
+ * secp256k1 ECDSA signatures over a hex-encoded uncompressed public key — a different
12
+ * scheme from the device identity this browser establishes afterwards to pair as a
13
+ * client device. One browser session generates and holds exactly one such key per
14
+ * onboarding attempt (mirrors the legacy `system/ui` flow's "ONE device keypair for
15
+ * the whole onboarding attempt" invariant — the node's physical-confirmation gate binds
16
+ * approval to the exact public key sent to `onboarding-prepare`).
17
+ * @typedef {object} OwnerClaimCryptoPort
18
+ * @property {() => Promise<string>} publicKey
19
+ * @property {(challenge: string) => Promise<string>} sign
20
+ * @property {() => string} deviceInfo
21
+ * @property {() => void} reset
22
+ */
@@ -0,0 +1,20 @@
1
+ // @ts-check
2
+
3
+ /** @template {import("./durable-event.js").DurableEvent} [TEvent=import("./durable-event.js").DurableEvent] @typedef {import("./durable-event.js").RepositoryWriteResult<TEvent>} RepositoryWriteResult */
4
+
5
+ export const CONVERSATION_MESSAGE_STATES = /** @type {const} */ (["pending", "confirmed", "failed"]);
6
+ /** @typedef {typeof CONVERSATION_MESSAGE_STATES[number]} ConversationMessageState */
7
+ /** @typedef {{ readonly id: string, readonly lastMessagePreview?: string, readonly lastMessageAt?: string, readonly participantIds: readonly string[], readonly title: string, readonly unreadCount: number, readonly updatedAt: string }} Conversation */
8
+ /** @typedef {{ readonly body: string, readonly clientMessageId?: string, readonly conversationId: string, readonly createdAt: string, readonly failureReason?: string, readonly id: string, readonly senderId: string, readonly serverMessageId?: string, readonly state: ConversationMessageState }} ConversationMessage */
9
+ /** @typedef {{ readonly body: string, readonly createdAt: string, readonly id: string, readonly readAt?: string, readonly referenceId?: string, readonly title: string, readonly type: string }} Notification */
10
+ /** @typedef {{ readonly conversation: Conversation, readonly kind: "conversation.upsert" } | { readonly kind: "message.append", readonly message: ConversationMessage } | { readonly acknowledgedAt: string, readonly kind: "message.ack", readonly messageId: string, readonly serverMessageId?: string } | { readonly failedAt: string, readonly kind: "message.fail", readonly messageId: string, readonly reason: string } | { readonly kind: "notification.upsert", readonly notification: Notification } | { readonly kind: "notification.read", readonly notificationId: string, readonly readAt: string }} ConversationLogDelta */
11
+ /** @typedef {{ readonly before?: string, readonly limit?: number }} ConversationListParams */
12
+ /** @typedef {{ readonly before?: string, readonly limit?: number }} MessageListParams */
13
+ /** @typedef {{ readonly limit?: number, readonly unreadOnly?: boolean }} NotificationListParams */
14
+ /** @typedef {{ readonly occurredAt: string, readonly payload: { readonly action: "created" | "updated", readonly entityId: string, readonly entityType: "conversation" | "message" }, readonly type: "domain.entity-changed" }} ConversationRepositoryEvent */
15
+ /** @typedef {{ readonly occurredAt: string, readonly payload: { readonly action: "created" | "updated", readonly entityId: string, readonly entityType: "notification" }, readonly type: "domain.entity-changed" }} NotificationRepositoryEvent */
16
+ /** @typedef {ConversationRepositoryEvent | NotificationRepositoryEvent} RepositoryEvent */
17
+ /** @typedef {{ acknowledgeMessage(input: { readonly acknowledgedAt: string, readonly messageId: string, readonly serverMessageId?: string }): Promise<RepositoryWriteResult<ConversationRepositoryEvent>>, appendPendingMessage(input: { readonly message: ConversationMessage }): { commit(): Promise<RepositoryWriteResult<ConversationRepositoryEvent>> }, applyDelta(delta: ConversationLogDelta): { commit(): Promise<RepositoryWriteResult<RepositoryEvent>> }, failMessage(input: { readonly failedAt: string, readonly messageId: string, readonly reason: string }): Promise<RepositoryWriteResult<ConversationRepositoryEvent>>, listConversations(params?: ConversationListParams): Promise<readonly Conversation[]>, listMessages(conversationId: string, params?: MessageListParams): Promise<readonly ConversationMessage[]> }} ConversationRepository */
18
+ /** @typedef {{ applyDelta(delta: ConversationLogDelta): { commit(): Promise<RepositoryWriteResult<RepositoryEvent>> }, listNotifications(params?: NotificationListParams): Promise<readonly Notification[]>, markRead(input: { readonly notificationId: string, readonly readAt: string }): { commit(): Promise<RepositoryWriteResult<NotificationRepositoryEvent>> } }} NotificationRepository */
19
+
20
+ export {};
@@ -0,0 +1,8 @@
1
+ // @ts-check
2
+
3
+ /** @typedef {{ readonly occurredAt: string, readonly payload: unknown, readonly type: string }} DurableEvent */
4
+ /** @template {DurableEvent} [TEvent=DurableEvent] @typedef {{ readonly deliveredAt: string | undefined, readonly deliveryAttempts: number, readonly event: TEvent, readonly sequence: number }} DurableRepositoryEvent */
5
+ /** @template {DurableEvent} [TEvent=DurableEvent] @typedef {{ listUndelivered(): Promise<readonly DurableRepositoryEvent<TEvent>[]>, markDelivered(sequence: number, deliveredAt: string): Promise<void>, markDeliveryAttempt(sequence: number): Promise<void> }} DurableRepositoryEventStore */
6
+ /** @template {DurableEvent} [TEvent=DurableEvent] @typedef {{ readonly eventSequences: readonly number[], readonly events: readonly TEvent[] }} RepositoryWriteResult */
7
+
8
+ export {};