@happyvertical/smrt-web 0.51.32 → 0.51.34

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 (47) hide show
  1. package/dist/__typechecks__/webmcp-tool-definition.d.ts +2 -0
  2. package/dist/__typechecks__/webmcp-tool-definition.d.ts.map +1 -0
  3. package/dist/ai.d.ts +330 -358
  4. package/dist/ai.d.ts.map +1 -0
  5. package/dist/capability-classification.d.ts +89 -0
  6. package/dist/capability-classification.d.ts.map +1 -0
  7. package/dist/capability.d.ts +191 -0
  8. package/dist/capability.d.ts.map +1 -0
  9. package/dist/data-query.d.ts +121 -0
  10. package/dist/data-query.d.ts.map +1 -0
  11. package/dist/data-surface-actions.d.ts +118 -0
  12. package/dist/data-surface-actions.d.ts.map +1 -0
  13. package/dist/durable-store.d.ts +88 -0
  14. package/dist/durable-store.d.ts.map +1 -0
  15. package/dist/index.d.ts +468 -2245
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/intents.d.ts +273 -300
  18. package/dist/intents.d.ts.map +1 -0
  19. package/dist/internal.d.ts +15 -0
  20. package/dist/internal.d.ts.map +1 -0
  21. package/dist/offline/durable-queue.d.ts +171 -0
  22. package/dist/offline/durable-queue.d.ts.map +1 -0
  23. package/dist/offline/engine.d.ts +359 -0
  24. package/dist/offline/engine.d.ts.map +1 -0
  25. package/dist/offline/leader.d.ts +57 -0
  26. package/dist/offline/leader.d.ts.map +1 -0
  27. package/dist/offline/types.d.ts +232 -0
  28. package/dist/offline/types.d.ts.map +1 -0
  29. package/dist/offline.d.ts +258 -0
  30. package/dist/offline.d.ts.map +1 -0
  31. package/dist/persistence/snapshot-store.d.ts +121 -0
  32. package/dist/persistence/snapshot-store.d.ts.map +1 -0
  33. package/dist/persistence.d.ts +157 -0
  34. package/dist/persistence.d.ts.map +1 -0
  35. package/dist/remote-query.d.ts +61 -0
  36. package/dist/remote-query.d.ts.map +1 -0
  37. package/dist/sse-client.d.ts +182 -0
  38. package/dist/sse-client.d.ts.map +1 -0
  39. package/dist/update-state/meta-store.d.ts +66 -0
  40. package/dist/update-state/meta-store.d.ts.map +1 -0
  41. package/dist/update-state.d.ts +115 -0
  42. package/dist/update-state.d.ts.map +1 -0
  43. package/dist/webmcp-tool-names.d.ts +106 -112
  44. package/dist/webmcp-tool-names.d.ts.map +1 -0
  45. package/dist/webmcp.d.ts +193 -653
  46. package/dist/webmcp.d.ts.map +1 -0
  47. package/package.json +4 -4
@@ -0,0 +1,232 @@
1
+ /**
2
+ * @happyvertical/smrt-web — offline outbox shared types (#1762).
3
+ *
4
+ * The SMRT-owned vocabulary the outbox modules (`durable-queue`, `leader`,
5
+ * `engine`) and the public surface (`offline.ts`) share. Kept in its own leaf
6
+ * module — imported by everything, importing nothing from the package — so there
7
+ * is no cycle back through `index.ts` and the whole outbox stays inside the
8
+ * engine-absorption boundary (#1761): NOT ONE `@tanstack/*` type appears here.
9
+ *
10
+ * The sync-apply op/status/reason names mirror
11
+ * `packages/core/src/sync/apply.ts` (the replay target) verbatim, so a queue row
12
+ * maps to a batch item and a batch result maps to a state transition with no
13
+ * translation layer. smrt-web has no inter-smrt dependency (dependency-DAG
14
+ * guardrails), so these are re-declared here rather than imported from core.
15
+ */
16
+ /**
17
+ * Mutation kinds the sync-apply batch endpoint accepts. Mirrors core's
18
+ * `SyncApplyOp`. The outbox maps the capability seam's insert/update/delete
19
+ * envelope kinds onto these (`insert → create`).
20
+ */
21
+ export type SyncApplyOp = 'create' | 'update' | 'delete';
22
+ /**
23
+ * Per-item outcome status from a sync-apply batch result. Mirrors core's
24
+ * `SyncApplyStatus`. The engine maps each onto a durable transition per the
25
+ * contract's "Web outbox (#1762)" consumer notes.
26
+ */
27
+ export type SyncApplyStatus = 'applied' | 'conflict' | 'rejected';
28
+ /**
29
+ * Machine-readable reasons a sync-apply item was `conflict` or `rejected`.
30
+ * Mirrors core's `SyncApplyReason` union. Drives the engine's result mapping:
31
+ * `auth_required`/`forbidden` pause the loop; `write_failed` retries; the rest
32
+ * are terminal.
33
+ */
34
+ export type SyncApplyReason = 'invalid_item' | 'invalid_id' | 'invalid_payload' | 'unknown_object' | 'op_not_allowed' | 'auth_required' | 'forbidden' | 'not_found' | 'id_conflict' | 'write_failed' | 'stale_write' | 'create_conflict';
35
+ /**
36
+ * One item in a `POST {basePath}/sync/apply` request body. Mirrors core's
37
+ * `SyncApplyItem`. Built from an {@link OutboxRow} 1:1.
38
+ */
39
+ export interface SyncApplyItem {
40
+ itemId: string;
41
+ object: string;
42
+ op: SyncApplyOp;
43
+ id: string;
44
+ payload?: Record<string, unknown>;
45
+ baseUpdatedAt?: string;
46
+ }
47
+ /**
48
+ * One entry in a sync-apply batch response's `results[]`. Mirrors core's
49
+ * `SyncApplyItemResult`. `results[i]` corresponds to `items[i]` positionally;
50
+ * `itemId` is echoed for convenience.
51
+ */
52
+ export interface SyncApplyItemResult {
53
+ itemId: string | null;
54
+ id: string | null;
55
+ status: SyncApplyStatus;
56
+ reason?: SyncApplyReason;
57
+ updatedAt?: string;
58
+ }
59
+ /** The HTTP-200 response body of `POST {basePath}/sync/apply`. */
60
+ export interface SyncApplyBatchResponse {
61
+ results: SyncApplyItemResult[];
62
+ }
63
+ /**
64
+ * Maximum items the endpoint accepts in one batch (core's
65
+ * `MAX_SYNC_APPLY_BATCH_SIZE`). The engine chunks a larger backlog into
66
+ * successive POSTs, oldest-first, so FIFO order is preserved across chunks.
67
+ */
68
+ export declare const MAX_SYNC_APPLY_BATCH_SIZE = 1000;
69
+ /**
70
+ * The URL segments of the batch apply endpoint relative to the API base path:
71
+ * `POST {basePath}/sync/apply` (core's `SYNC_APPLY_ROUTE_SEGMENTS`).
72
+ */
73
+ export declare const SYNC_APPLY_ROUTE_SEGMENTS: readonly ["sync", "apply"];
74
+ /**
75
+ * The app-observable sync state of a queued mutation — the SAME four-state
76
+ * machine the KMP mobile foundation exposes (ADR 0001), so web and mobile
77
+ * outbox indicators render identically:
78
+ *
79
+ * - `pending` — enqueued, awaiting (re)send.
80
+ * - `uploading` — currently in a sync-apply POST.
81
+ * - `synced` — the mutation's effect is confirmed on the server (a terminal
82
+ * success — INCLUDING a surfaced conflict, which is a RESOLVED outcome: the
83
+ * server state won and the item left the queue).
84
+ * - `failed` — a terminal rejection the client cannot resolve by retrying
85
+ * (`invalid_*`, `not_found`, `id_conflict`, `op_not_allowed`, …) — surfaced
86
+ * for app-level handling.
87
+ */
88
+ export type OutboxSyncState = 'pending' | 'uploading' | 'synced' | 'failed';
89
+ /**
90
+ * A sync-state transition delivered to {@link OfflineOutboxConfig.onSyncStateChange}.
91
+ * A PUSH callback (not a subscribable store): smrt-svelte wraps it into a
92
+ * reactive binding later (#1762 follow-on). Carries enough for an app to render
93
+ * a per-row indicator and a retry affordance.
94
+ */
95
+ export interface SyncStateEvent {
96
+ /** The queue row's `itemId` (its idempotency handle) this state is for. */
97
+ itemId: string;
98
+ /** The client-generated row UUID the mutation targets. */
99
+ rowId: string;
100
+ /** The collection route segment (e.g. `products`). */
101
+ object: string;
102
+ /** The new observable state. */
103
+ state: OutboxSyncState;
104
+ /** How many replay attempts have run (0 until the first send). */
105
+ attempts: number;
106
+ /** The last replay error message, when `state` is `pending` after a retry. */
107
+ error?: string;
108
+ }
109
+ /**
110
+ * A conflict surfaced to {@link OfflineOutboxConfig.onConflict} when a replayed
111
+ * item comes back `conflict`. Per the contract this is a RESOLVED outcome — the
112
+ * server state won, the item is removed, and its terminal observable state is
113
+ * `synced` — so an app treats it as "your write was superseded; here is the
114
+ * server's `updatedAt` to rebase from", NOT as a failure to retry.
115
+ */
116
+ export interface OutboxConflict {
117
+ /** The queue row's `itemId`. */
118
+ itemId: string;
119
+ /** The collection route segment. */
120
+ object: string;
121
+ /** The client-generated row UUID that conflicted. */
122
+ rowId: string;
123
+ /**
124
+ * Why the item conflicted: `stale_write` (an update/delete whose
125
+ * `baseUpdatedAt` was older than the server row) or `create_conflict` (a
126
+ * create landing on an existing, diverged row).
127
+ */
128
+ reason: 'stale_write' | 'create_conflict';
129
+ /**
130
+ * The server row's `updated_at` after processing — the value to persist as
131
+ * the new `baseUpdatedAt` before re-editing. Present when the endpoint
132
+ * returned it.
133
+ */
134
+ serverUpdatedAt?: string;
135
+ }
136
+ /**
137
+ * One queued write handed to a consumer-declared {@link OutboxCommandTransport}
138
+ * at replay time (#3021). The transport replays it through a server operation
139
+ * the consumer owns — a data-surface action, a named remote command, a
140
+ * hand-written permission-gated service — instead of the generated
141
+ * `sync/apply`, which calls `collection.create()` directly.
142
+ */
143
+ export interface OutboxCommand {
144
+ /**
145
+ * The durable, client-minted idempotency key (the queue row's `itemId`). It
146
+ * is IDENTICAL on every attempt of this write — including a blind resend
147
+ * after a lost response — so the server operation MUST dedupe on it; that is
148
+ * what makes the outbox's ambiguous-failure retries safe on this path.
149
+ */
150
+ idempotencyKey: string;
151
+ /** The transport name the write was queued under. */
152
+ name: string;
153
+ /** The mutation kind in sync-apply terms. */
154
+ op: SyncApplyOp;
155
+ /** The client-generated row id the write targets. */
156
+ rowId: string;
157
+ /** Row / changed fields (create, update); absent for delete. */
158
+ payload?: Record<string, unknown>;
159
+ /** The server `updated_at` the client last saw, when the caller supplied one. */
160
+ baseUpdatedAt?: string;
161
+ /** 1-based number of this attempt (`1` on the first send). */
162
+ attempt: number;
163
+ /** The value this write's transport pinned on an earlier attempt, if any. */
164
+ pinned?: unknown;
165
+ /**
166
+ * Durably pin a structured-clone-safe value to this write before sending,
167
+ * so a later attempt (including after a reload) sees it as `pinned`. A
168
+ * transport whose server fingerprints the request per idempotency key pins
169
+ * the request it built, so a resend after a lost response is identical.
170
+ */
171
+ pin(value: unknown): Promise<void>;
172
+ }
173
+ /**
174
+ * The server's outcome for one {@link OutboxCommand}, in the SAME vocabulary
175
+ * the sync-apply replay maps — so the four-state machine and backoff are
176
+ * shared, not re-derived:
177
+ *
178
+ * - `applied` → removed, `synced`.
179
+ * - `conflict` → removed, `onConflict` fired, `synced` (a RESOLVED outcome).
180
+ * - `rejected` with `write_failed` → kept, backoff, `pending` (retryable).
181
+ * - `rejected` with `auth_required` / `forbidden` → the whole loop pauses until
182
+ * re-auth (a later enqueue or `retry()`), kept `pending`.
183
+ * - `rejected` with any other reason → removed, `failed` (terminal).
184
+ *
185
+ * A transport that THROWS (network error, lost response, 5xx) is the ambiguous
186
+ * path: the write stays `pending` with backoff and is blindly resent later
187
+ * under the same {@link OutboxCommand.idempotencyKey}.
188
+ */
189
+ export type OutboxCommandResult = {
190
+ status: 'applied';
191
+ updatedAt?: string;
192
+ } | {
193
+ status: 'conflict';
194
+ reason?: 'stale_write' | 'create_conflict';
195
+ updatedAt?: string;
196
+ } | {
197
+ status: 'rejected';
198
+ reason: SyncApplyReason | (string & {});
199
+ };
200
+ /** Replays one queued write through a consumer-declared server operation. */
201
+ export type OutboxCommandTransport = (command: OutboxCommand) => Promise<OutboxCommandResult>;
202
+ /** Exponential-backoff tuning for retryable replay failures. */
203
+ export interface OutboxBackoff {
204
+ /** Delay before the first retry, ms (default 1000). */
205
+ initialDelayMs?: number;
206
+ /** Multiplier applied per attempt (default 2). */
207
+ multiplier?: number;
208
+ /** Ceiling on the computed delay, ms (default 60000). */
209
+ maxDelayMs?: number;
210
+ }
211
+ /** Resolved backoff config (defaults filled in). */
212
+ export interface ResolvedBackoff {
213
+ initialDelayMs: number;
214
+ multiplier: number;
215
+ maxDelayMs: number;
216
+ }
217
+ /** Backoff defaults, per the blueprint (initial=1s, ×2, cap 60s). */
218
+ export declare const DEFAULT_BACKOFF: ResolvedBackoff;
219
+ /**
220
+ * Compute the backoff delay before the NEXT attempt given how many attempts
221
+ * have already been made. Exponential with a ceiling, then multiplied by a
222
+ * jitter factor in `[0.5, 1.0)` to spread a thundering herd of tabs/rows
223
+ * reconnecting at once: `min(maxDelay, initial * mult ** attempts) * jitter`.
224
+ *
225
+ * `attempts` is the count BEFORE this retry (so the first retry, after attempts
226
+ * became 1, uses `initial * mult ** 1`? no — see below). We pass the attempt
227
+ * count AFTER incrementing, and index the exponent off `attempts - 1` so the
228
+ * first retry waits ~`initialDelayMs`. `random` is injectable for deterministic
229
+ * tests.
230
+ */
231
+ export declare function computeBackoffDelay(attempts: number, backoff: ResolvedBackoff, random?: () => number): number;
232
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/offline/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,258 @@
1
+ /**
2
+ * @happyvertical/smrt-web — the durable offline outbox capability (#1762).
3
+ *
4
+ * The web twin of the KMP mobile write queue: mutations against opted-in
5
+ * collections are captured in a durable browser-side outbox (IndexedDB) that
6
+ * survives reloads and crashes, then replayed FIFO against the idempotent
7
+ * sync-apply batch contract when connectivity returns, with exponential-backoff
8
+ * retries. Because items carry client-generated UUIDs and the endpoint is
9
+ * idempotent (`_insertOnly` create + no-op re-apply), blind retries after an
10
+ * ambiguous failure (request sent, response lost) can never duplicate rows — see
11
+ * the "Idempotency" section of docs/content/architecture/sync-apply-contract.md.
12
+ *
13
+ * This is a {@link SmrtWebCapability} plug-in, so it lives in its OWN module and
14
+ * an app opts a collection in by passing `offlineOutbox(config)` in that
15
+ * collection's `capabilities` array. A collection WITHOUT it is byte-for-byte
16
+ * the collection of today (the seam's no-op guarantee), which is exactly the
17
+ * "offline is opt-in per model" acceptance criterion.
18
+ *
19
+ * ## How it plugs into the seam
20
+ *
21
+ * - `wrapMutation(envelope, ctx)` — BEFORE the fetcher, it enqueues the write
22
+ * durably and returns `{ handled: true, result: envelope.data }`. The factory
23
+ * then SUPPRESSES the post-mutation refetch + `invalidateRelated()` (its
24
+ * `{ refetch: false }` path), so the optimistic row the app just inserted
25
+ * STANDS instead of being dropped by a refetch of a server list that has never
26
+ * seen the offline write. If the durable queue is unavailable, it returns
27
+ * `{ handled: false }` so the normal fetcher path runs instead of silently
28
+ * acknowledging a non-durable write. A handled write never touches
29
+ * `ctx.fetchers.create` — it replays ONLY through `sync/apply`, which is
30
+ * load-bearing: the normal REST create strips the client id (#1540) and would
31
+ * mint a NEW server id, orphaning the optimistic row; sync/apply's
32
+ * strict-insert path preserves the client UUID (#1540, #1540's
33
+ * mass-assignment guard on the sync path).
34
+ * - `onAttach(ctx)` — attaches this collection to the shared, namespace-keyed
35
+ * {@link OutboxEngine} (ref-counted so N collections share ONE engine / IDB
36
+ * db / leader lock / FIFO queue).
37
+ * - `teardown(ctx)` — detaches; the last detach disposes the engine (the durable
38
+ * ROWS survive for the next load).
39
+ *
40
+ * ## Observable state (push callbacks, not a store)
41
+ *
42
+ * Per-mutation sync state — `pending → uploading → synced` (and `→ failed` /
43
+ * conflict) — is delivered through {@link OfflineOutboxConfig.onSyncStateChange}
44
+ * and {@link OfflineOutboxConfig.onConflict}, PUSH callbacks matching the KMP
45
+ * foundation's `pending/uploading/synced/failed` state machine. This is
46
+ * deliberately not a subscribable store: smrt-svelte wraps it into a reactive
47
+ * binding in a later slice. Programmatic access to the durable queue is via
48
+ * {@link getOutboxHandle} (a bridge like `getEngineCollection`).
49
+ *
50
+ * ## Known gap (this slice's scope)
51
+ *
52
+ * The outbox does NOT rehydrate the READ cache after a reload — a reloaded tab's
53
+ * `collection.toArray()` will NOT show captured-offline rows until a fetch runs;
54
+ * that read-side rehydrate is #1764's `warmStart`. So the outbox's durability is
55
+ * proven via {@link OutboxHandle.snapshot} / the raw IndexedDB store, NOT via
56
+ * `collection.toArray()`. The WRITE side (capture → durable → exactly-once
57
+ * replay) is complete here.
58
+ *
59
+ * Engine-free public surface: no `@tanstack/*` type appears — inside the
60
+ * engine-absorption boundary (#1761), verified by
61
+ * scripts/check-smrt-web-engine-boundary.mjs.
62
+ */
63
+ import type { SmrtWebCapability } from './capability.js';
64
+ import { type DurableStoreKey } from './durable-store.js';
65
+ import { type OutboxBackoff, type OutboxCommandTransport, type OutboxConflict, type SyncApplyOp, type SyncStateEvent } from './offline/types.js';
66
+ export type { OutboxBackoff, OutboxCommand, OutboxCommandResult, OutboxCommandTransport, OutboxConflict, OutboxSyncState, SyncStateEvent, } from './offline/types.js';
67
+ /**
68
+ * Configuration for {@link offlineOutbox}. Generic in the collection's row type
69
+ * `TData` so the capability matches the collection it plugs into.
70
+ */
71
+ export interface OfflineOutboxConfig<TData extends object = object> {
72
+ /**
73
+ * The generated collection definition this outbox serves — its `name` is the
74
+ * sync-apply `object` route segment, so it MUST be the SAME definition passed
75
+ * to {@link createSmrtCollection}. (Named `object` to mirror the capability
76
+ * context; carries the collection's REST route segment.)
77
+ */
78
+ object: {
79
+ name: string;
80
+ _row?: TData;
81
+ };
82
+ /**
83
+ * The durable-store identity this outbox's queue is namespaced under — folds
84
+ * api base / tenant / identity / manifest hash, so a logout or tenant switch
85
+ * lands on a different IndexedDB database (never cross-identity reuse). Shared
86
+ * with #1764's persistence slice via the same {@link durableStoreNamespace}.
87
+ * `manifestHash` is opaque caller-supplied config for #1762; its canonical
88
+ * source is #1764's call (a schema-shape digest) — comment your call site.
89
+ */
90
+ namespace: DurableStoreKey;
91
+ /**
92
+ * API base path the sync-apply endpoint lives under (`POST
93
+ * {syncApplyBasePath}/sync/apply`). Defaults to `/api/v1`, matching the REST
94
+ * generator's default. Set to the SvelteKit route base (`/api`) when replaying
95
+ * against the generated SvelteKit `sync/apply/+server.ts`. Ignored when
96
+ * {@link transport} is set.
97
+ */
98
+ syncApplyBasePath?: string;
99
+ /**
100
+ * Replay this collection's queued writes through a consumer-declared server
101
+ * operation instead of the generated `sync/apply` (#3021) — for a model whose
102
+ * generated write verbs are closed because a hand-written, permission-gated
103
+ * service owns its write rules. Rows are queued under `object.name` and replay
104
+ * ONLY through this transport (never `sync/apply`), each carrying its durable
105
+ * `idempotencyKey`, with the same four-state machine, per-route FIFO, and
106
+ * backoff.
107
+ * See {@link offlineCommandQueue} for writes with no collection at all.
108
+ */
109
+ transport?: OutboxCommandTransport;
110
+ /** Fetch implementation override (tests, SSR). Defaults to global fetch. */
111
+ fetchFn?: typeof fetch;
112
+ /** Exponential-backoff tuning for retryable replay failures. */
113
+ backoff?: OutboxBackoff;
114
+ /**
115
+ * Called on every observable sync-state transition of a captured mutation
116
+ * (`pending → uploading → synced`, `→ failed`, and the `pending` re-arm after
117
+ * a retryable failure). A push callback; smrt-svelte turns it into a reactive
118
+ * binding later.
119
+ */
120
+ onSyncStateChange?: (event: SyncStateEvent) => void;
121
+ /**
122
+ * Called when a replayed mutation comes back `conflict` — a RESOLVED outcome
123
+ * (server state won; the item leaves the queue with terminal state `synced`).
124
+ * Persist `serverUpdatedAt` as the new `baseUpdatedAt`, refetch the row, and
125
+ * surface the conflict to the user as appropriate.
126
+ */
127
+ onConflict?: (conflict: OutboxConflict) => void;
128
+ /** Test-only: inject a deterministic RNG for backoff jitter. */
129
+ random?: () => number;
130
+ }
131
+ /**
132
+ * A programmatic handle to one namespace's outbox — the bridge pattern (like
133
+ * `getEngineCollection`) for trusted callers (an app's outbox panel, a
134
+ * smrt-svelte binding, tests). Fetched via {@link getOutboxHandle}.
135
+ */
136
+ export interface OutboxHandle {
137
+ /**
138
+ * A read-only snapshot of the durable queue (every state). The way to prove
139
+ * durability after a reload, since this slice does not rehydrate the read
140
+ * cache (that's #1764).
141
+ */
142
+ snapshot(): Promise<OutboxSnapshotItem[]>;
143
+ /**
144
+ * Force a retry of a specific queued item now (clears its backoff gate, wakes
145
+ * the loop, and clears an auth pause). A no-op for an item no longer queued.
146
+ */
147
+ retry(itemId: string): Promise<void>;
148
+ }
149
+ /** One item in an {@link OutboxHandle.snapshot} result. */
150
+ export interface OutboxSnapshotItem {
151
+ /** The transport name, when the item replays through one (#3021). */
152
+ transport?: string;
153
+ /** The queue row's idempotency handle. */
154
+ itemId: string;
155
+ /** The collection route segment. */
156
+ object: string;
157
+ /** The mutation kind, in sync-apply terms. */
158
+ op: 'create' | 'update' | 'delete';
159
+ /** The client-generated row UUID. */
160
+ rowId: string;
161
+ /** The on-disk lifecycle state. */
162
+ state: 'pending' | 'synced' | 'failed';
163
+ /** Replay attempts so far. */
164
+ attempts: number;
165
+ /** Epoch ms before which this row won't be retried (backoff). */
166
+ nextAttemptAt: number;
167
+ /** Last replay error, if any. */
168
+ lastError?: string;
169
+ }
170
+ /**
171
+ * Build a durable offline-outbox capability for a collection. Add the returned
172
+ * capability to the collection's `capabilities` array; a collection without it
173
+ * is unaffected (the seam's no-op guarantee — the "opt-in per model" AC).
174
+ *
175
+ * The same `namespace` across multiple collections shares ONE engine (one IDB
176
+ * db, one queue) — so cross-collection ordering and the multi-tab
177
+ * single-replayer guarantee hold across every sync-apply collection of a given
178
+ * identity (one route). A collection with a `transport` is its own route.
179
+ */
180
+ export declare function offlineOutbox<TData extends object = object>(config: OfflineOutboxConfig<TData>): SmrtWebCapability<TData>;
181
+ /** Configuration for {@link offlineCommandQueue}. */
182
+ export interface OfflineCommandQueueConfig {
183
+ /**
184
+ * The queue's name — the key queued writes are stored and routed under. It
185
+ * shares the namespace's durable queue and per-object event routing with
186
+ * every collection outbox, so it MUST NOT equal a sibling collection's
187
+ * `definition.name` (their events would cross). Durable: a write queued under a name replays only once a queue
188
+ * with that name (and a transport) is attached again, e.g. after a reload.
189
+ */
190
+ name: string;
191
+ /** The durable-store identity — the SAME key the app's other outboxes use. */
192
+ namespace: DurableStoreKey;
193
+ /** Replays each queued write through the consumer's own server operation. */
194
+ transport: OutboxCommandTransport;
195
+ /** Exponential-backoff tuning for retryable replay failures. */
196
+ backoff?: OutboxBackoff;
197
+ /** Push callback for every sync-state transition (see {@link offlineOutbox}). */
198
+ onSyncStateChange?: (event: SyncStateEvent) => void;
199
+ /** Push callback for a `conflict` outcome (a RESOLVED outcome). */
200
+ onConflict?: (conflict: OutboxConflict) => void;
201
+ /** Test-only: inject a deterministic RNG for backoff jitter. */
202
+ random?: () => number;
203
+ }
204
+ /** One write for {@link OfflineCommandQueue.enqueue}. */
205
+ export interface OfflineCommandInput {
206
+ /** Command arguments — structured-clone-safe; persisted verbatim. */
207
+ payload?: Record<string, unknown>;
208
+ /** The row the write targets, when there is one. Defaults to the item id. */
209
+ rowId?: string;
210
+ /** Mutation kind, passed through to the transport. Defaults to `create`. */
211
+ op?: SyncApplyOp;
212
+ /** The server `updated_at` last seen, passed through to the transport. */
213
+ baseUpdatedAt?: string;
214
+ }
215
+ /** A durable queue of consumer-declared server operations (#3021). */
216
+ export interface OfflineCommandQueue {
217
+ /**
218
+ * Durably queue one write and kick replay. Resolves to its idempotency key
219
+ * once the write is committed to IndexedDB, or `undefined` when the durable
220
+ * queue is unavailable (no IndexedDB) or the queue was disposed — the write
221
+ * was NOT captured, so the caller must perform it online itself or surface
222
+ * the failure; it is never acknowledged non-durably.
223
+ */
224
+ enqueue(input: OfflineCommandInput): Promise<string | undefined>;
225
+ /** The namespace's durable queue (every route), like {@link OutboxHandle}. */
226
+ snapshot(): Promise<OutboxSnapshotItem[]>;
227
+ /** Force a retry of a queued item now (clears backoff and an auth pause). */
228
+ retry(itemId: string): Promise<void>;
229
+ /** Detach. Queued writes stay on disk and replay on the next attach. */
230
+ dispose(): Promise<void>;
231
+ }
232
+ /**
233
+ * A durable offline queue for writes that go through a consumer-declared
234
+ * server operation — a data-surface action, a named remote command, a
235
+ * permission-gated service — rather than a generated collection (#3021).
236
+ *
237
+ * Shares the namespace's outbox engine with every {@link offlineOutbox}: one
238
+ * IndexedDB queue, a cross-tab leader per route, FIFO within the route, the
239
+ * `pending → uploading → synced | failed` state machine, backoff, auth pause,
240
+ * and the `wipeDurableStore` registration. Each replay carries the write's
241
+ * durable `idempotencyKey`, which the server operation must dedupe on — that
242
+ * is what makes a blind resend after a lost response safe.
243
+ *
244
+ * Attaches immediately; call `dispose()` when the owning view unmounts.
245
+ */
246
+ export declare function offlineCommandQueue(config: OfflineCommandQueueConfig): OfflineCommandQueue;
247
+ /**
248
+ * Retrieve the programmatic {@link OutboxHandle} for a durable-store namespace,
249
+ * or `undefined` if no opted-in collection is currently attached under it. The
250
+ * bridge for trusted callers (outbox UI, smrt-svelte binding, tests) to read the
251
+ * durable queue and force retries. Pass the SAME
252
+ * `durableStoreNamespace(config.namespace)` string the capability used.
253
+ *
254
+ * Mirrors the `getEngineCollection` bridge convention: an escape hatch that
255
+ * returns a narrow SMRT-owned surface rather than the engine itself.
256
+ */
257
+ export declare function getOutboxHandle(namespace: string): OutboxHandle | undefined;
258
+ //# sourceMappingURL=offline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline.d.ts","sourceRoot":"","sources":["../src/offline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @happyvertical/smrt-web — the durable collection snapshot store (#1764).
3
+ *
4
+ * The read-side twin of the offline outbox's write queue: a raw IndexedDB store
5
+ * that persists ONE blob per (namespace, collection) — the collection's last
6
+ * known rows — so a reload can warm-start (render stale instantly) before the
7
+ * engine revalidates in the background. One database per durable-store namespace
8
+ * ({@link durableStoreNamespace}), one object store (`snapshots`) keyed by the
9
+ * collection name, each record `{ collection, rows }`.
10
+ *
11
+ * ## Why raw IndexedDB, mirroring offline/durable-queue.ts
12
+ *
13
+ * Same decisive finding as #1762's outbox: the TanStack persistence layer's
14
+ * public API is engine-typed, so importing it would emit a `@tanstack/`
15
+ * specifier into a `.d.ts` and fail `scripts/check-smrt-web-engine-boundary.mjs`.
16
+ * So persistence is hand-rolled over `indexedDB` with ZERO new runtime
17
+ * dependency and stays inside the engine-absorption boundary (#1761) — this
18
+ * module speaks only plain rows, never an engine type.
19
+ *
20
+ * Rows are stored as structured-clone-safe plain objects (the public DTOs the
21
+ * factory already projects); IndexedDB clones on write, so callers may keep
22
+ * mutating the source array after a save.
23
+ *
24
+ * The namespace already folds api base / tenant / identity / manifest hash (see
25
+ * {@link durableStoreNamespace}), so a logout, tenant switch, or contract-
26
+ * changing deploy lands on a DIFFERENT database — a stale-schema snapshot is
27
+ * simply never found (the read returns nothing and the engine fetches fresh),
28
+ * which is exactly the "manifest-hash change drops persisted caches" guarantee
29
+ * without any explicit invalidation.
30
+ */
31
+ /** IndexedDB object-store name inside every snapshot database. */
32
+ export declare const SNAPSHOT_STORE = "snapshots";
33
+ /** IndexedDB schema version. Bump only on a store/index migration. */
34
+ export declare const SNAPSHOT_DB_VERSION = 1;
35
+ /**
36
+ * Suffix appended to the durable-store namespace to form this store's OWN
37
+ * IndexedDB database name. Distinct DBs per store are REQUIRED for correctness:
38
+ * the merged outbox (#1762) already opens the BARE namespace as a v1 database
39
+ * with only its `outbox` object store. If persistence opened the same bare
40
+ * namespace at v1, whichever store opened SECOND would find the existing v1 DB,
41
+ * `onupgradeneeded` would NOT fire, and `objectStore('snapshots')` would throw
42
+ * `NotFoundError` — silently disabling persistence (or the outbox). A distinct
43
+ * DB name sidesteps the collision entirely; `wipeDurableStore` still clears both
44
+ * because it fans out via each registered resource's `clear()`, never by DB name.
45
+ */
46
+ export declare const SNAPSHOT_DB_SUFFIX = "::snapshots";
47
+ /** One persisted collection snapshot record. */
48
+ export interface SnapshotRecord {
49
+ /** The REST collection name — the store key. */
50
+ collection: string;
51
+ /** The rows last seen for this collection (plain public DTOs). */
52
+ rows: Array<Record<string, unknown>>;
53
+ }
54
+ /**
55
+ * Feature-detect a usable IndexedDB. Some environments expose `indexedDB` but
56
+ * throw on `open()` (private-mode Firefox historically, sandboxed iframes), so a
57
+ * real probe opens a throwaway database. Returns `false` rather than throwing so
58
+ * the persistence capability can degrade to non-persistent instead of crashing
59
+ * the host — matching the outbox's `probeIndexedDb()` posture.
60
+ */
61
+ export declare function probeIndexedDb(): Promise<boolean>;
62
+ /**
63
+ * The durable snapshot store over one IndexedDB database. Constructed via
64
+ * {@link openSnapshotStore} (async open + schema upgrade), then used through the
65
+ * async methods below (each resolves once its txn commits).
66
+ *
67
+ * One store backs EVERY opted-in collection under a namespace, each keyed by its
68
+ * collection name — so N persisted collections of one identity share one
69
+ * database (like the outbox's shared queue), and the durable-store `clear()`
70
+ * wipes them all at once.
71
+ */
72
+ export declare class SnapshotStore {
73
+ private readonly db;
74
+ /** The IndexedDB database name (== the durable-store namespace). */
75
+ readonly dbName: string;
76
+ constructor(db: IDBDatabase, dbName: string);
77
+ /**
78
+ * Read the persisted rows for `collection`, or `undefined` if none were ever
79
+ * saved (or the record is malformed). `undefined` is the warm-start "nothing
80
+ * on disk" signal — the engine then fetches fresh.
81
+ */
82
+ load(collection: string): Promise<Array<Record<string, unknown>> | undefined>;
83
+ /**
84
+ * Write (replacing) the snapshot for `collection`. Resolves once the write is
85
+ * durably committed. A single blob per collection — the whole current row set,
86
+ * not a delta — so a restore is one read with no reconciliation.
87
+ */
88
+ save(collection: string, rows: Array<Record<string, unknown>>): Promise<void>;
89
+ /**
90
+ * Drop the snapshot for a single `collection` (its capability's own teardown
91
+ * does NOT clear — the persisted rows must survive for the next load; this is
92
+ * only for an explicit targeted purge). Kept for completeness / tests.
93
+ */
94
+ remove(collection: string): Promise<void>;
95
+ /**
96
+ * Drop EVERY snapshot — the durable-store `clear()` for `wipeDurableStore`.
97
+ * Empties the store but keeps the database so a subsequent save still works.
98
+ */
99
+ clear(): Promise<void>;
100
+ /** Close the underlying database handle (called on the last detach). */
101
+ close(): void;
102
+ }
103
+ /**
104
+ * Open (creating/upgrading as needed) the snapshot database for `namespace` and
105
+ * wrap it in a {@link SnapshotStore}. `namespace` MUST be a
106
+ * {@link durableStoreNamespace} string — already `encodeURIComponent`-safe and
107
+ * carrying the api/tenant/identity/manifest isolation boundary, so two
108
+ * identities never share one snapshot database.
109
+ *
110
+ * The actual IndexedDB database name is `${namespace}${SNAPSHOT_DB_SUFFIX}` — a
111
+ * DISTINCT database from the merged outbox's bare-namespace one, so persistence +
112
+ * outbox under a shared namespace never collide on a single-store v1 DB (see
113
+ * {@link SNAPSHOT_DB_SUFFIX}).
114
+ *
115
+ * The `onupgradeneeded` handler creates the `snapshots` store keyed by
116
+ * `collection`. Idempotent: re-opening an existing database at the same version
117
+ * skips the upgrade — the reload path (a new tab re-opens the same durable
118
+ * backing and sees prior snapshots).
119
+ */
120
+ export declare function openSnapshotStore(namespace: string): Promise<SnapshotStore>;
121
+ //# sourceMappingURL=snapshot-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-store.d.ts","sourceRoot":"","sources":["../../src/persistence/snapshot-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,kEAAkE;AAClE,eAAO,MAAM,cAAc,cAAc,CAAC;AAC1C,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,gBAAgB,CAAC;AAEhD,gDAAgD;AAChD,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACtC;AA8BD;;;;;;GAMG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAsBvD;AAED;;;;;;;;;GASG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAc;IACjC,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM;IAK3C;;;;OAIG;IACG,IAAI,CACR,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAYtD;;;;OAIG;IACG,IAAI,CACR,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACnC,OAAO,CAAC,IAAI,CAAC;IAQhB;;;;OAIG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/C;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,wEAAwE;IACxE,KAAK,IAAI,IAAI;CAGd;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA8B3E"}