@econ-v1/ports 6.7.0 → 6.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/dist/app-data.d.ts +39 -0
- package/dist/app-data.d.ts.map +1 -0
- package/dist/client-ui-preference.d.ts +23 -0
- package/dist/client-ui-preference.d.ts.map +1 -0
- package/dist/clipboard.d.ts +41 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clock.d.ts +8 -8
- package/dist/clock.d.ts.map +1 -1
- package/dist/credential-handoff.d.ts +57 -0
- package/dist/credential-handoff.d.ts.map +1 -0
- package/dist/crypto.d.ts +4 -4
- package/dist/crypto.d.ts.map +1 -1
- package/dist/id.d.ts +2 -2
- package/dist/id.d.ts.map +1 -1
- package/dist/identity.d.ts +31 -25
- package/dist/identity.d.ts.map +1 -1
- package/dist/index.d.ts +226 -30
- package/dist/index.d.ts.map +1 -1
- package/dist/install-environment.d.ts +22 -32
- package/dist/install-environment.d.ts.map +1 -1
- package/dist/lcd-tree-source.d.ts +54 -0
- package/dist/lcd-tree-source.d.ts.map +1 -0
- package/dist/legacy-credential.d.ts +30 -0
- package/dist/legacy-credential.d.ts.map +1 -0
- package/dist/media-preferences.d.ts +2 -44
- package/dist/media-preferences.d.ts.map +1 -1
- package/dist/network/certificate-trust.d.ts +31 -0
- package/dist/network/certificate-trust.d.ts.map +1 -0
- package/dist/network/client-node-origin.d.ts +3 -15
- package/dist/network/client-node-origin.d.ts.map +1 -1
- package/dist/network/home-node-candidate-store.d.ts +4 -32
- package/dist/network/home-node-candidate-store.d.ts.map +1 -1
- package/dist/network/home-node-candidate.d.ts +9 -7
- package/dist/network/home-node-candidate.d.ts.map +1 -1
- package/dist/network/l402-payment.d.ts +11 -28
- package/dist/network/l402-payment.d.ts.map +1 -1
- package/dist/network/node-discovery.d.ts +5 -16
- package/dist/network/node-discovery.d.ts.map +1 -1
- package/dist/network/webrtc-signaling.d.ts +36 -52
- package/dist/network/webrtc-signaling.d.ts.map +1 -1
- package/dist/network.d.ts +65 -40
- package/dist/network.d.ts.map +1 -1
- package/dist/offline-readiness.d.ts +122 -0
- package/dist/offline-readiness.d.ts.map +1 -0
- package/dist/onboarding-access-code.d.ts +30 -0
- package/dist/onboarding-access-code.d.ts.map +1 -0
- package/dist/onboarding-checkpoint.d.ts +26 -0
- package/dist/onboarding-checkpoint.d.ts.map +1 -0
- package/dist/onboarding-presence.d.ts +12 -0
- package/dist/onboarding-presence.d.ts.map +1 -0
- package/dist/owner-claim-session.d.ts +60 -0
- package/dist/owner-claim-session.d.ts.map +1 -0
- package/dist/owner-claim.d.ts +22 -0
- package/dist/owner-claim.d.ts.map +1 -0
- package/dist/repositories/conversation-repository.d.ts +21 -20
- package/dist/repositories/conversation-repository.d.ts.map +1 -1
- package/dist/repositories/durable-event.d.ts +8 -8
- package/dist/repositories/durable-event.d.ts.map +1 -1
- package/dist/repositories/node-app-memory-repository.d.ts +168 -0
- package/dist/repositories/node-app-memory-repository.d.ts.map +1 -0
- package/dist/repositories/outbox.d.ts +33 -20
- package/dist/repositories/outbox.d.ts.map +1 -1
- package/dist/repositories/repository-mutation.d.ts +3 -1
- package/dist/repositories/repository-mutation.d.ts.map +1 -1
- package/dist/repositories/revisioned-repository.d.ts +14 -12
- package/dist/repositories/revisioned-repository.d.ts.map +1 -1
- package/dist/repositories/settings.d.ts +10 -10
- package/dist/repositories/settings.d.ts.map +1 -1
- package/dist/repositories/sync-ledger.d.ts +11 -11
- package/dist/repositories/sync-ledger.d.ts.map +1 -1
- package/dist/repositories/wallet-mirror-repository.d.ts +22 -13
- package/dist/repositories/wallet-mirror-repository.d.ts.map +1 -1
- package/dist/repositories.d.ts +52 -17
- package/dist/repositories.d.ts.map +1 -1
- package/dist/scheduler.d.ts +4 -4
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/session-flag.d.ts +48 -0
- package/dist/session-flag.d.ts.map +1 -0
- package/dist/shell-cache-reset.d.ts +4 -0
- package/dist/shell-cache-reset.d.ts.map +1 -0
- package/dist/shell-version.d.ts +8 -0
- package/dist/shell-version.d.ts.map +1 -0
- package/dist/storage.d.ts +76 -34
- package/dist/storage.d.ts.map +1 -1
- package/dist/theme.d.ts +2 -24
- package/dist/theme.d.ts.map +1 -1
- package/package.json +14 -10
- package/src/app-data.js +42 -0
- package/src/client-ui-preference.js +23 -0
- package/src/clipboard.js +41 -0
- package/src/clock.js +13 -0
- package/src/credential-handoff.js +68 -0
- package/src/crypto.js +9 -0
- package/src/id.js +5 -0
- package/src/identity.js +33 -0
- package/src/index.js +250 -0
- package/src/install-environment.js +44 -0
- package/src/lcd-tree-source.js +56 -0
- package/src/legacy-credential.js +30 -0
- package/src/media-preferences.js +40 -0
- package/src/network/certificate-trust.js +25 -0
- package/src/network/client-node-origin.js +7 -0
- package/src/network/home-node-candidate-store.js +6 -0
- package/src/network/home-node-candidate.js +9 -0
- package/src/network/l402-payment.js +20 -0
- package/src/network/node-discovery.js +13 -0
- package/src/network/webrtc-signaling.js +40 -0
- package/src/network.js +33 -0
- package/src/offline-readiness.js +65 -0
- package/src/onboarding-access-code.js +21 -0
- package/src/onboarding-checkpoint.js +26 -0
- package/src/onboarding-presence.js +15 -0
- package/src/owner-claim-session.js +55 -0
- package/src/owner-claim.js +22 -0
- package/src/repositories/conversation-repository.js +20 -0
- package/src/repositories/durable-event.js +8 -0
- package/src/repositories/node-app-memory-repository.js +221 -0
- package/src/repositories/outbox.js +27 -0
- package/src/repositories/repository-mutation.js +8 -0
- package/src/repositories/revisioned-repository.js +17 -0
- package/src/repositories/settings.js +10 -0
- package/src/repositories/sync-ledger.js +11 -0
- package/src/repositories/wallet-mirror-repository.js +73 -0
- package/src/repositories.js +22 -0
- package/src/scheduler.js +6 -0
- package/src/session-flag.js +48 -0
- package/src/shell-cache-reset.js +3 -0
- package/src/shell-version.js +4 -0
- package/src/storage.js +60 -0
- package/src/theme.js +21 -0
- package/dist/clock.js +0 -2
- package/dist/clock.js.map +0 -1
- package/dist/crypto.js +0 -2
- package/dist/crypto.js.map +0 -1
- package/dist/id.js +0 -2
- package/dist/id.js.map +0 -1
- package/dist/identity.js +0 -2
- package/dist/identity.js.map +0 -1
- package/dist/index.js +0 -9
- package/dist/index.js.map +0 -1
- package/dist/install-environment.js +0 -26
- package/dist/install-environment.js.map +0 -1
- package/dist/media-preferences.js +0 -2
- package/dist/media-preferences.js.map +0 -1
- package/dist/network/client-node-origin.js +0 -18
- package/dist/network/client-node-origin.js.map +0 -1
- package/dist/network/home-node-candidate-store.js +0 -2
- package/dist/network/home-node-candidate-store.js.map +0 -1
- package/dist/network/home-node-candidate.js +0 -2
- package/dist/network/home-node-candidate.js.map +0 -1
- package/dist/network/l402-payment.js +0 -2
- package/dist/network/l402-payment.js.map +0 -1
- package/dist/network/node-discovery.js +0 -13
- package/dist/network/node-discovery.js.map +0 -1
- package/dist/network/webrtc-signaling.js +0 -30
- package/dist/network/webrtc-signaling.js.map +0 -1
- package/dist/network.js +0 -2
- package/dist/network.js.map +0 -1
- package/dist/repositories/conversation-repository.js +0 -2
- package/dist/repositories/conversation-repository.js.map +0 -1
- package/dist/repositories/durable-event.js +0 -2
- package/dist/repositories/durable-event.js.map +0 -1
- package/dist/repositories/outbox.js +0 -9
- package/dist/repositories/outbox.js.map +0 -1
- package/dist/repositories/repository-mutation.js +0 -2
- package/dist/repositories/repository-mutation.js.map +0 -1
- package/dist/repositories/revisioned-repository.js +0 -2
- package/dist/repositories/revisioned-repository.js.map +0 -1
- package/dist/repositories/settings.js +0 -2
- package/dist/repositories/settings.js.map +0 -1
- package/dist/repositories/sync-ledger.js +0 -2
- package/dist/repositories/sync-ledger.js.map +0 -1
- package/dist/repositories/wallet-mirror-repository.js +0 -53
- package/dist/repositories/wallet-mirror-repository.js.map +0 -1
- package/dist/repositories.js +0 -2
- package/dist/repositories.js.map +0 -1
- package/dist/scheduler.js +0 -2
- package/dist/scheduler.js.map +0 -1
- package/dist/storage.js +0 -14
- package/dist/storage.js.map +0 -1
- package/dist/theme.js +0 -2
- 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,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 {};
|