@happyvertical/smrt-web 0.51.31 → 0.51.33

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,157 @@
1
+ /**
2
+ * @happyvertical/smrt-web — the durable persistence capability (#1764).
3
+ *
4
+ * The read-side twin of the offline outbox (#1762): an opted-in collection
5
+ * WARM-STARTS from a durable browser snapshot on load — rendering stale rows
6
+ * instantly — then the engine revalidates in the background via its normal SWR.
7
+ * A reload therefore paints last-known data with zero network round-trip before
8
+ * the fresh fetch lands. This is the read-cache rehydrate the outbox explicitly
9
+ * left to #1764 (see offline.ts "Known gap").
10
+ *
11
+ * This is a {@link SmrtWebCapability} plug-in, so it lives in its OWN module and
12
+ * an app opts a collection in by passing `persistCollection(config)` in that
13
+ * collection's `capabilities` array. A collection WITHOUT it is byte-for-byte
14
+ * the collection of today (the seam's no-op guarantee) — nothing persists
15
+ * implicitly, which is exactly the "persistence is opt-in per model" acceptance
16
+ * criterion. Sensitive models simply omit the capability and never touch disk.
17
+ *
18
+ * ## How it plugs into the seam
19
+ *
20
+ * - `warmStart(ctx)` — reads the persisted snapshot for
21
+ * `(durableStoreNamespace(namespace), collection)` and returns its rows to
22
+ * seed the cache (the factory's rehydrate path; `preload()` awaits the async
23
+ * return so it suppresses the first `list()`). If the namespace's
24
+ * `manifestHash` changed, the key differs → NO snapshot is found → an empty
25
+ * return → a fresh fetch with NO stale-schema hydration. So "a manifest-hash
26
+ * change drops persisted caches" is AUTOMATIC via the namespace including the
27
+ * hash — no explicit invalidation.
28
+ * - `onAttach(ctx)` — subscribes to the collection's changes (via the seam's
29
+ * engine-free `ctx.subscribe`) and persists the current row snapshot
30
+ * (`ctx.snapshot`) DEBOUNCED (trailing) to avoid thrashing IndexedDB on a
31
+ * burst of writes. Also registers the shared store as a durable resource so a
32
+ * logout `wipeDurableStore` clears it.
33
+ * - `teardown(ctx)` — unsubscribes, unregisters the durable resource BEFORE
34
+ * closing IndexedDB (the #1762 discipline: a later `wipeDurableStore` is then a
35
+ * no-op, never a double-clear), and the LAST detach closes the shared db. The
36
+ * durable SNAPSHOT survives for the next load.
37
+ *
38
+ * ## Namespace segregation + logout wipe (shared with the outbox)
39
+ *
40
+ * The durable namespace folds api base / tenant / identity / manifest hash, so
41
+ * switching users on one device lands on a DIFFERENT IndexedDB database — one
42
+ * user can never read another's persisted rows. The SAME namespace is shared
43
+ * with the outbox (#1762), so registering this store under it means a single
44
+ * {@link wipeDurableStore} on logout clears BOTH the persisted collections AND
45
+ * the outbox queue in one call (the "logout wipe clears persisted data AND
46
+ * outbox state" AC).
47
+ *
48
+ * ## Degrade, don't crash
49
+ *
50
+ * If IndexedDB is unavailable (a probe `open()` throws — private mode, sandboxed
51
+ * iframe), the capability `console.warn`s ONCE and behaves as non-persistent:
52
+ * `warmStart` returns nothing (normal fetch), write-back is a no-op. It never
53
+ * throws — matching the outbox's `probeIndexedDb()` posture.
54
+ *
55
+ * Engine-free public surface: no `@tanstack/*` type appears — inside the
56
+ * engine-absorption boundary (#1761), verified by
57
+ * scripts/check-smrt-web-engine-boundary.mjs.
58
+ */
59
+ import type { SmrtWebCapability } from './capability.js';
60
+ import type { SmrtWebDataSurfaceIdentity } from './data-surface-actions.js';
61
+ import { type DurableStoreKey } from './durable-store.js';
62
+ /** Default trailing-debounce window (ms) for the write-back. */
63
+ export declare const DEFAULT_PERSIST_DEBOUNCE_MS = 250;
64
+ /**
65
+ * Configuration for {@link persistCollection}. Generic in the collection's row
66
+ * type `TData` so the capability matches the collection it plugs into.
67
+ */
68
+ export interface PersistCollectionConfig<TData extends object = object> {
69
+ /**
70
+ * The REST collection name this snapshot is keyed by — MUST be the SAME
71
+ * `definition.name` passed to {@link createSmrtCollection} (it is the store
72
+ * key under the namespace). Provided explicitly (not read off the ctx) so the
73
+ * config is self-describing and testable.
74
+ */
75
+ collection: string;
76
+ /**
77
+ * The durable-store identity this snapshot is namespaced under — folds api
78
+ * base / tenant / identity / manifest hash, so a logout, tenant switch, or a
79
+ * contract-changing deploy lands on a different IndexedDB database (never
80
+ * cross-identity reuse, and a shape change drops old snapshots). Shared with
81
+ * the outbox (#1762) via the same {@link durableStoreNamespace}, so one
82
+ * {@link wipeDurableStore} clears both.
83
+ *
84
+ * The canonical `manifestHash` source is the build-time inject — the
85
+ * `manifestHash` constant the generated `@happyvertical/smrt-virt-web` module
86
+ * exports (#1764). Thread it here at the call site.
87
+ */
88
+ namespace: DurableStoreKey;
89
+ /**
90
+ * Trailing-debounce window (ms) for the write-back snapshot. A burst of
91
+ * mutations within the window collapses to ONE IndexedDB write of the final
92
+ * row set. Default {@link DEFAULT_PERSIST_DEBOUNCE_MS}. Set 0 to write on
93
+ * every change (tests).
94
+ */
95
+ debounceMs?: number;
96
+ }
97
+ /**
98
+ * Build a durable persistence capability for a collection. Add the returned
99
+ * capability to the collection's `capabilities` array; a collection without it
100
+ * is unaffected (the seam's no-op guarantee — the "opt-in per model" AC).
101
+ *
102
+ * The same `namespace` across multiple collections shares ONE snapshot database
103
+ * (one open, one durable resource, one wipe), and each collection is keyed by
104
+ * its own `collection` name within it.
105
+ */
106
+ export declare function persistCollection<TData extends object = object>(config: PersistCollectionConfig<TData>): SmrtWebCapability<TData>;
107
+ /** Configuration for {@link persistDataSurface}. */
108
+ export interface PersistDataSurfaceConfig {
109
+ /**
110
+ * The durable-store identity — the SAME key the app's collections and
111
+ * outbox use. It folds the principal (`identityId`) and tenant, so a surface
112
+ * whose rows the server scopes per principal lands on a per-principal
113
+ * database: another user on the device never reads them, and one
114
+ * `wipeDurableStore(namespace)` on logout clears them with everything else.
115
+ */
116
+ namespace: DurableStoreKey;
117
+ /** The mounted surface's identity (`descriptor.identity`) — the store key. */
118
+ identity: SmrtWebDataSurfaceIdentity;
119
+ /** Trailing-debounce window for `save` (ms). Default {@link DEFAULT_PERSIST_DEBOUNCE_MS}. */
120
+ debounceMs?: number;
121
+ }
122
+ /** A durable row snapshot for one mounted data surface (#3021). */
123
+ export interface PersistedDataSurface<TRow extends object = object> {
124
+ /**
125
+ * The rows last saved for this surface under this namespace, or `undefined`
126
+ * when none exist (first load, a different principal/tenant/manifest, a
127
+ * wipe, or no IndexedDB). Render them as stale, then revalidate. After a
128
+ * `wipeDurableStore` of its namespace a handle is inert (`load` finds
129
+ * nothing, `save` is ignored); create a new one for the next scope.
130
+ */
131
+ load(): Promise<TRow[] | undefined>;
132
+ /**
133
+ * Replace the persisted rows (debounced, trailing; last call wins). Pass the
134
+ * surface's CURRENT rows whenever the page refreshes them. Rows must be
135
+ * structured-clone-safe plain objects.
136
+ */
137
+ save(rows: readonly TRow[]): void;
138
+ /** Write any pending `save` now. */
139
+ flush(): Promise<void>;
140
+ /** Flush a pending `save`, then release the shared store. */
141
+ dispose(): Promise<void>;
142
+ }
143
+ /**
144
+ * Persist and rehydrate a mounted data surface's rows (#3021) — the
145
+ * `persistCollection` twin for a surface (e.g. one registered with
146
+ * `mountListDataSurface`) whose rows come from a server-scoped data-surface
147
+ * query rather than a generated `list` route. The page owns its rows, so it
148
+ * owns the calls: `load()` before the first fetch to paint stale rows, then
149
+ * `save(rows)` whenever it refreshes them.
150
+ *
151
+ * Shares the namespace's snapshot database, durable-resource registration,
152
+ * and IndexedDB-unavailable degrade with {@link persistCollection}: a
153
+ * manifest-hash, tenant, or identity change lands on a different database, and
154
+ * `wipeDurableStore(namespace)` clears the surface's rows with the rest.
155
+ */
156
+ export declare function persistDataSurface<TRow extends object = object>(config: PersistDataSurfaceConfig): PersistedDataSurface<TRow>;
157
+ //# sourceMappingURL=persistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EACL,KAAK,eAAe,EAGrB,MAAM,oBAAoB,CAAC;AAO5B,gEAAgE;AAChE,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,uBAAuB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACpE;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,eAAe,CAAC;IAC3B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAqID;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7D,MAAM,EAAE,uBAAuB,CAAC,KAAK,CAAC,GACrC,iBAAiB,CAAC,KAAK,CAAC,CAqJ1B;AAED,oDAAoD;AACpD,MAAM,WAAW,wBAAwB;IACvC;;;;;;OAMG;IACH,SAAS,EAAE,eAAe,CAAC;IAC3B,8EAA8E;IAC9E,QAAQ,EAAE,0BAA0B,CAAC;IACrC,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,mEAAmE;AACnE,MAAM,WAAW,oBAAoB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IAChE;;;;;;OAMG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,SAAS,CAAC,CAAC;IACpC;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,SAAS,IAAI,EAAE,GAAG,IAAI,CAAC;IAClC,oCAAoC;IACpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,6DAA6D;IAC7D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAsBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAC7D,MAAM,EAAE,wBAAwB,GAC/B,oBAAoB,CAAC,IAAI,CAAC,CAiF5B"}
@@ -0,0 +1,61 @@
1
+ import { type SmrtWebDataQueryRequest, type SmrtWebDataQueryResult, type SmrtWebDataQueryTransport } from './data-query.js';
2
+ import type { SmrtWebCollection } from './index.js';
3
+ /** How a query run is allowed to affect the visible query state. */
4
+ export type SmrtWebQueryMode = 'visible' | 'background' | 'prefetch' | 'silent';
5
+ export interface SmrtWebQueryRunOptions {
6
+ mode?: SmrtWebQueryMode;
7
+ signal?: AbortSignal;
8
+ /** Cancel this run after the given number of milliseconds. */
9
+ deadlineMs?: number;
10
+ /** Bypass the keyed cache and execute a fresh request (used by refresh). */
11
+ force?: boolean;
12
+ }
13
+ export interface SmrtWebQueryPage {
14
+ kind: 'offset' | 'cursor';
15
+ limit: number;
16
+ offset?: number;
17
+ nextCursor?: string;
18
+ hasMore: boolean;
19
+ }
20
+ export interface SmrtWebQueryState<TData extends object = object> {
21
+ readonly rows: ReadonlyArray<TData>;
22
+ readonly page: SmrtWebQueryPage | undefined;
23
+ readonly total: SmrtWebDataQueryResult['total'] | undefined;
24
+ readonly loading: boolean;
25
+ readonly refreshing: boolean;
26
+ readonly stale: boolean;
27
+ readonly error: unknown;
28
+ readonly lastUpdated: number | undefined;
29
+ readonly result: SmrtWebDataQueryResult | undefined;
30
+ }
31
+ export interface SmrtWebQueryLiveSubscription {
32
+ unsubscribe(): void;
33
+ reconnect(): void;
34
+ }
35
+ export interface SmrtWebQueryTransport extends SmrtWebDataQueryTransport {
36
+ /** Subscribe to changes for this exact query, not the entire collection. */
37
+ subscribe?(request: SmrtWebDataQueryRequest, onResult: (result: unknown) => void, options?: {
38
+ signal?: AbortSignal;
39
+ }): SmrtWebQueryLiveSubscription | {
40
+ unsubscribe(): void;
41
+ };
42
+ }
43
+ export interface SmrtWebQuery<TData extends object = object> {
44
+ readonly state: SmrtWebQueryState<TData>;
45
+ readonly request: SmrtWebDataQueryRequest | undefined;
46
+ execute(request: SmrtWebDataQueryRequest, options?: SmrtWebQueryRunOptions): Promise<SmrtWebDataQueryResult>;
47
+ refresh(options?: Omit<SmrtWebQueryRunOptions, 'mode' | 'force'>): Promise<SmrtWebDataQueryResult | undefined>;
48
+ retry(): Promise<SmrtWebDataQueryResult | undefined>;
49
+ subscribe(listener: (state: SmrtWebQueryState<TData>) => void): () => void;
50
+ subscribeLive(): SmrtWebQueryLiveSubscription | undefined;
51
+ invalidate(): void;
52
+ dispose(): void;
53
+ }
54
+ /**
55
+ * Create a query controller over one SMRT collection. Query rows stay in a
56
+ * separate keyed cache; the collection's full list is never loaded.
57
+ */
58
+ export declare function createSmrtWebQuery<TData extends object>(_collection: SmrtWebCollection<TData>, transport: SmrtWebQueryTransport, options?: {
59
+ staleTimeMs?: number;
60
+ }): SmrtWebQuery<TData>;
61
+ //# sourceMappingURL=remote-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-query.d.ts","sourceRoot":"","sources":["../src/remote-query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * @happyvertical/smrt-web — live-updates subscriber (#1763, CLIENT half).
3
+ *
4
+ * The browser half of live cache invalidation: ONE app-wide subscriber that
5
+ * turns the server's coarse change signals into collection refetches, so a
6
+ * dashboard reflects another session's writes without a manual refresh or
7
+ * aggressive polling. It speaks the two channels the #1763 server half
8
+ * generated:
9
+ *
10
+ * - the push channel — the generated `_events` Server-Sent-Events route
11
+ * (`packages/core/src/generators/events-route.ts`): NAMED events
12
+ * `event: change` / `event: resync` whose `data` is `{table, operation,
13
+ * rowId, tenantId}`, with the cursor `seq` carried ONLY in the SSE `id:`
14
+ * field (mirrored by the browser to `MessageEvent.lastEventId`), plus
15
+ * `event: manifest` for live contract detection. Heartbeats are
16
+ * `: heartbeat` comment lines EventSource ignores natively.
17
+ * - the pull channel — the generated `_changes` route
18
+ * (`packages/core/src/generators/changes-route.ts`): `GET {changesUrl}
19
+ * ?since=&tables=&limit=` →
20
+ * `{changes, cursor, resyncRequired?, resyncCursor?}`, the full fallback
21
+ * where SSE is unavailable.
22
+ *
23
+ * ## No row payload, no tenant logic, no authorization here
24
+ *
25
+ * The wire carries only a signal (table + row id + operation), never a row.
26
+ * A signal makes the subscriber INVALIDATE — the refetch re-reads through the
27
+ * authorized collection routes, so authorization and tenant-scoping stay
28
+ * ENTIRELY on the read path, exactly as the server enforces (the `_events`
29
+ * stream is auth-guarded and tenant-scoped at connection open; `_changes` is
30
+ * tenant-scoped per request). This module therefore does NO tenant filtering
31
+ * and needs no identity — it just maps `table → invalidate`.
32
+ *
33
+ * ## One app-wide instance (mirrors createSmrtWebClient)
34
+ *
35
+ * A consumer constructs ONE subscriber and passes it to every
36
+ * {@link liveInvalidation} capability — the same "one shared handle" convention
37
+ * as {@link createSmrtWebClient}. It is NOT auto-derived per collection: one
38
+ * EventSource / one poll loop feeds every registered collection.
39
+ *
40
+ * ## Idempotent invalidation → no client-side dedup
41
+ *
42
+ * Re-invalidating on a replayed change (a reconnect replays the tail from the
43
+ * cursor) is safe: invalidation only schedules a background refetch, and the
44
+ * factory's relationship-derived invalidation is itself idempotent
45
+ * (over-invalidation merely refetches). So the subscriber deliberately does NO
46
+ * seq dedup — a replayed signal STILL fires, which is exactly what guarantees a
47
+ * gap misses no invalidation. `lastSeq` is a resume cursor for the poll
48
+ * fallback, not a dedup filter.
49
+ *
50
+ * Engine boundary: no `@happyvertical/smrt-*` import and no `@tanstack/*`
51
+ * import — URLs and `fetch` arrive as config, and the refetch primitive arrives
52
+ * as the `invalidate` callback each capability wires from `ctx.invalidate()`.
53
+ * smrt-web has no logger dependency, so faults are surfaced via `console.warn`
54
+ * (matching `warnCapability` / core's `deliverLocally` style).
55
+ *
56
+ * The server may also emit a named `manifest` event at connection open (#1859):
57
+ * `{ manifestHash }`. When this differs from the running build hash configured
58
+ * here, the subscriber latches `updateState.contract`.
59
+ */
60
+ import type { SmrtWebCapability } from './capability.js';
61
+ import type { UpdateState } from './update-state.js';
62
+ /**
63
+ * The minimal `EventSource` surface the subscriber uses — declared here so a
64
+ * test injects a fake without a real DOM, and so this module compiles without
65
+ * DOM `lib` beyond the ambient global. A real `EventSource` satisfies it
66
+ * structurally.
67
+ */
68
+ export interface SmrtWebEventSource {
69
+ /** Native reconnect fires this after (re)connect. */
70
+ onopen: ((this: unknown, ev: unknown) => unknown) | null;
71
+ /**
72
+ * Fires on a stream error. A transient drop leaves `readyState` at OPEN
73
+ * (0→…) and the browser auto-reconnects with `Last-Event-ID`; a fatal error
74
+ * (server 401 / route disabled) leaves it CLOSED.
75
+ */
76
+ onerror: ((this: unknown, ev: unknown) => unknown) | null;
77
+ /**
78
+ * Fires only for UNNAMED (`message`) events. The `_events` frames are NAMED
79
+ * (`change` / `resync`), so this never fires for them — the subscriber uses
80
+ * {@link addEventListener} instead. Present only to satisfy the structural
81
+ * type of a real EventSource.
82
+ */
83
+ onmessage: ((this: unknown, ev: unknown) => unknown) | null;
84
+ /** Register a listener for a NAMED event (`change`, `resync`). */
85
+ addEventListener(type: string, listener: (ev: {
86
+ data: string;
87
+ lastEventId: string;
88
+ }) => void): void;
89
+ /** Close the stream (stops reconnection). */
90
+ close(): void;
91
+ /** `CONNECTING` (0), `OPEN` (1), or `CLOSED` (2). */
92
+ readonly readyState: number;
93
+ }
94
+ /** Factory that constructs an {@link SmrtWebEventSource} for a URL. */
95
+ export type SmrtWebEventSourceFactory = (url: string, init: {
96
+ withCredentials: boolean;
97
+ }) => SmrtWebEventSource | null | undefined;
98
+ /** Configuration for {@link createSmrtWebEventSubscriber}. */
99
+ export interface SmrtWebEventSubscriberConfig {
100
+ /** Absolute or same-origin URL of the generated `_events` SSE route. */
101
+ eventsUrl: string;
102
+ /** Absolute or same-origin URL of the generated `_changes` route (fallback). */
103
+ changesUrl: string;
104
+ /** `fetch` implementation for the polling fallback. Defaults to global fetch. */
105
+ fetchFn?: typeof fetch;
106
+ /**
107
+ * Factory for the EventSource. Defaults to `new globalThis.EventSource(url,
108
+ * { withCredentials })`, guarded by a `typeof` check — when EventSource is
109
+ * absent (or the factory yields nothing) the subscriber starts on the polling
110
+ * fallback instead.
111
+ */
112
+ eventSourceFactory?: SmrtWebEventSourceFactory;
113
+ /** Poll interval for the `_changes` fallback (ms). Default 5000. */
114
+ pollIntervalMs?: number;
115
+ /** `withCredentials` for the EventSource (cookie auth). Default true. */
116
+ withCredentials?: boolean;
117
+ /**
118
+ * The RUNNING build's web-collection shape digest. When paired with
119
+ * `updateState`, a differing server hash from the `_events` manifest frame
120
+ * latches the contract update signal (#1859).
121
+ */
122
+ manifestHash?: string;
123
+ /**
124
+ * Optional update-state primitive from `createUpdateState()`. Only its
125
+ * contract signal is used here; bundle polling remains the caller's job.
126
+ */
127
+ updateState?: Pick<UpdateState, 'notifyContractUpdated'>;
128
+ }
129
+ /** Which transport a subscriber is currently using. */
130
+ export type SmrtWebSubscriberTransport = 'sse' | 'polling' | 'idle';
131
+ /**
132
+ * The ONE app-wide live-updates subscriber. Construct once (see
133
+ * {@link createSmrtWebEventSubscriber}) and pass it to every
134
+ * {@link liveInvalidation} capability.
135
+ */
136
+ export interface SmrtWebEventSubscriber {
137
+ /**
138
+ * The live transport: `'sse'` while the EventSource is the source of truth,
139
+ * `'polling'` when running (or downgraded to) the `_changes` fallback,
140
+ * `'idle'` before any transport starts.
141
+ */
142
+ readonly transport: SmrtWebSubscriberTransport;
143
+ /**
144
+ * Register an invalidator for a physical table. Returns an unregister
145
+ * function. Multiple invalidators may share a table (two live collections);
146
+ * a `change` for that table fires them all.
147
+ */
148
+ registerTable(table: string, invalidate: () => void): () => void;
149
+ /** Invalidate every registered table (a `resync` / `resyncRequired`). */
150
+ invalidateAll(): void;
151
+ /** Tear down the live transport and drop all registrations. */
152
+ close(): void;
153
+ }
154
+ /**
155
+ * Create the app-wide live-updates subscriber. Feature-detects ONCE at
156
+ * construction: an available EventSource connects the SSE stream; otherwise it
157
+ * starts the `_changes` poll loop. A fatal SSE error later downgrades to
158
+ * polling for the rest of the subscriber's life (no flap-back).
159
+ */
160
+ export declare function createSmrtWebEventSubscriber(config: SmrtWebEventSubscriberConfig): SmrtWebEventSubscriber;
161
+ /** Configuration for the {@link liveInvalidation} capability. */
162
+ export interface LiveInvalidationConfig {
163
+ /** The one app-wide subscriber from {@link createSmrtWebEventSubscriber}. */
164
+ subscriber: Pick<SmrtWebEventSubscriber, 'registerTable' | 'invalidateAll' | 'transport'>;
165
+ /**
166
+ * The PHYSICAL table name this collection reads. EXPLICIT because a
167
+ * `SmrtWebCollectionDefinition` has no physical-table field and STI children
168
+ * share one base table — the subscriber keys signals by physical table, so a
169
+ * guess would mis-route invalidations.
170
+ */
171
+ tableName: string;
172
+ }
173
+ /**
174
+ * A thin per-collection capability that subscribes THIS collection to live
175
+ * signals for its `tableName`. On attach it registers `ctx.invalidate` (the
176
+ * factory's relationship-derived refetch primitive) with the shared subscriber;
177
+ * on teardown it unregisters. All transport, reconnection, and fan-out live in
178
+ * the {@link createSmrtWebEventSubscriber}; this capability is just the wire
179
+ * between one collection and that one subscriber.
180
+ */
181
+ export declare function liveInvalidation<TData extends object = object>(config: LiveInvalidationConfig): SmrtWebCapability<TData>;
182
+ //# sourceMappingURL=sse-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse-client.d.ts","sourceRoot":"","sources":["../src/sse-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @happyvertical/smrt-web — the durable version-meta store (#1764).
3
+ *
4
+ * A tiny raw IndexedDB key/value store for version-awareness bookkeeping —
5
+ * currently the single "last-seen manifestHash" per durable-store namespace,
6
+ * which contract-change detection ({@link ../update-state}) compares the running
7
+ * build against across loads. One database per namespace
8
+ * ({@link durableStoreNamespace}), one object store (`meta`) keyed by a string
9
+ * key, each record `{ key, value }`.
10
+ *
11
+ * Separate from the collection snapshot store (persistence/snapshot-store.ts):
12
+ * that one holds per-collection row blobs; this holds scalar version state, and
13
+ * update-awareness must work even for an app that persists no collection. Both
14
+ * register under the SAME namespace so one {@link wipeDurableStore} clears
15
+ * everything on logout.
16
+ *
17
+ * Raw IndexedDB, ZERO new runtime dependency — inside the engine-absorption
18
+ * boundary (#1761), mirroring offline/durable-queue.ts.
19
+ */
20
+ /** IndexedDB object-store name inside every version-meta database. */
21
+ export declare const META_STORE = "meta";
22
+ /** IndexedDB schema version. Bump only on a store/index migration. */
23
+ export declare const META_DB_VERSION = 1;
24
+ /**
25
+ * Suffix appended to the durable-store namespace to form this store's OWN
26
+ * IndexedDB database name — DISTINCT from both the merged outbox's bare-namespace
27
+ * DB and the snapshot store's `::snapshots` DB. Required so persistence + outbox
28
+ * + version-meta under one shared namespace never collide on a single-store v1
29
+ * database (a second opener would find the existing v1 DB, skip
30
+ * `onupgradeneeded`, and throw `NotFoundError` on `objectStore('meta')`).
31
+ * `wipeDurableStore` still clears all three via each registered `clear()`.
32
+ */
33
+ export declare const META_DB_SUFFIX = "::meta";
34
+ /** The reserved key under which the last-seen manifest hash is stored. */
35
+ export declare const LAST_SEEN_MANIFEST_HASH_KEY = "lastSeenManifestHash";
36
+ /**
37
+ * The durable version-meta store over one IndexedDB database. Constructed via
38
+ * {@link openVersionMetaStore}; used through the async methods below.
39
+ */
40
+ export declare class VersionMetaStore {
41
+ private readonly db;
42
+ /** The IndexedDB database name (== the durable-store namespace). */
43
+ readonly dbName: string;
44
+ constructor(db: IDBDatabase, dbName: string);
45
+ /** Read the value for `key`, or `undefined` if unset / malformed. */
46
+ get(key: string): Promise<string | undefined>;
47
+ /** Write (replacing) the value for `key`; resolves once durably committed. */
48
+ set(key: string, value: string): Promise<void>;
49
+ /**
50
+ * Drop EVERY meta record — the durable-store `clear()` for
51
+ * {@link wipeDurableStore}, so a logout also clears the last-seen manifest
52
+ * hash (the AC "wipe clears the last-seen-hash record").
53
+ */
54
+ clear(): Promise<void>;
55
+ /** Close the underlying database handle. */
56
+ close(): void;
57
+ }
58
+ /**
59
+ * Open (creating/upgrading as needed) the version-meta database for `namespace`
60
+ * and wrap it in a {@link VersionMetaStore}. `namespace` MUST be a
61
+ * {@link durableStoreNamespace} string. The actual IndexedDB database name is
62
+ * `${namespace}${META_DB_SUFFIX}` — a DISTINCT database from the outbox's and
63
+ * the snapshot store's, so the three never collide (see {@link META_DB_SUFFIX}).
64
+ */
65
+ export declare function openVersionMetaStore(namespace: string): Promise<VersionMetaStore>;
66
+ //# sourceMappingURL=meta-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta-store.d.ts","sourceRoot":"","sources":["../../src/update-state/meta-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,sEAAsE;AACtE,eAAO,MAAM,UAAU,SAAS,CAAC;AACjC,sEAAsE;AACtE,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,WAAW,CAAC;AACvC,0EAA0E;AAC1E,eAAO,MAAM,2BAA2B,yBAAyB,CAAC;AA4BlE;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAc;IACjC,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM;IAK3C,qEAAqE;IAC/D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAWnD,8EAA8E;IACxE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpD;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,4CAA4C;IAC5C,KAAK,IAAI,IAAI;CAGd;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,CAAC,CA4B3B"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @happyvertical/smrt-web — the framework-free `updateAvailable` primitive (#1764).
3
+ *
4
+ * A tiny pub/sub carrying an app's "an update is available" state, driven by TWO
5
+ * INDEPENDENT signals so the presentation (a toast / reload prompt, shipped as a
6
+ * Svelte binding in smrt-svelte) can react to either:
7
+ *
8
+ * - **bundle** — the client BUNDLE changed on the server (a new deploy of the
9
+ * front-end assets). SvelteKit already detects this via its native `updated`
10
+ * store (version.json polling); the consumer/smrt-svelte binding pushes it in
11
+ * here through {@link UpdateState.notifyBundleUpdated}. This module owns no
12
+ * polling — it just holds the state and fans out.
13
+ * - **contract** — the API CONTRACT changed: the manifest hash, which only SMRT
14
+ * knows. Detected here under the BUILD-TIME-INJECT decision (#1764): on init
15
+ * we compare the RUNNING build's `manifestHash` (passed in by the consumer,
16
+ * imported from `@happyvertical/smrt-virt-web`) against a persisted
17
+ * "last-seen manifestHash" in durable storage. If they differ, the contract
18
+ * changed across this load → the `contract` signal fires and the new value is
19
+ * stored. So a returning user whose app was rebuilt against a new schema is
20
+ * told to reload.
21
+ *
22
+ * `updateAvailable` is simply `bundle || contract` — either signal means "an
23
+ * update is available".
24
+ *
25
+ * ## Scope: load-time contract detection only (documented follow-up)
26
+ *
27
+ * Contract detection here fires at MOST once per load (the compare-on-init).
28
+ * LIVE-while-open contract detection — learning mid-session that the server
29
+ * redeployed a new contract, via the `_events` SSE manifest frame — pushes the
30
+ * same sticky `contract` signal through {@link UpdateState.notifyContractUpdated}.
31
+ *
32
+ * ## Durable last-seen storage + logout wipe
33
+ *
34
+ * The last-seen hash lives in the version-meta store
35
+ * (update-state/meta-store.ts) under the durable-store namespace, registered as
36
+ * a durable resource so a logout {@link wipeDurableStore} clears it too (the AC
37
+ * "wipe clears the last-seen-hash record"). If IndexedDB is unavailable the
38
+ * primitive still works for the bundle signal; contract detection degrades to a
39
+ * no-op (no durable baseline to compare against) rather than throwing.
40
+ *
41
+ * Engine-free (no `@tanstack/*` import) and framework-free (no Svelte/DOM) — the
42
+ * reactive binding is a separate smrt-svelte adapter (#1764 PART E).
43
+ */
44
+ import { type DurableStoreKey } from './durable-store.js';
45
+ /** The two independent update signals plus their derived union. */
46
+ export interface UpdateAvailableState {
47
+ /** The client bundle changed on the server (SvelteKit `updated`). */
48
+ readonly bundle: boolean;
49
+ /** The API contract (manifest hash) changed across this load. */
50
+ readonly contract: boolean;
51
+ /** `bundle || contract` — either means an update is available. */
52
+ readonly updateAvailable: boolean;
53
+ }
54
+ /** Configuration for {@link createUpdateState}. */
55
+ export interface UpdateStateConfig {
56
+ /**
57
+ * The RUNNING build's web-collection shape digest — the `manifestHash`
58
+ * constant the generated `@happyvertical/smrt-virt-web` module exports
59
+ * (#1764). Compared against the persisted last-seen value to detect a contract
60
+ * change across loads. Omit to disable contract detection (bundle-only).
61
+ */
62
+ manifestHash?: string;
63
+ /**
64
+ * The durable-store identity the last-seen manifest hash is namespaced under —
65
+ * folds api base / tenant / identity (and, yes, the manifest hash itself, but
66
+ * see below). Shared with persistence/outbox so one {@link wipeDurableStore}
67
+ * clears the last-seen record too.
68
+ *
69
+ * NOTE the namespace's OWN `manifestHash` segment should be a STABLE value
70
+ * (e.g. the app's api-version or a constant), NOT the running build hash —
71
+ * otherwise a contract change would move the namespace and the compare would
72
+ * never find the prior value (always "first run"). Contract change is detected
73
+ * by the STORED VALUE under a stable namespace, not by the namespace moving.
74
+ */
75
+ namespace: DurableStoreKey;
76
+ }
77
+ /** The framework-free update-available primitive. */
78
+ export interface UpdateState {
79
+ /** The current signal state (a fresh immutable snapshot). */
80
+ get(): UpdateAvailableState;
81
+ /**
82
+ * Subscribe to state changes; the callback fires immediately with the current
83
+ * state and again on every transition. Returns an unsubscribe function.
84
+ */
85
+ subscribe(callback: (state: UpdateAvailableState) => void): () => void;
86
+ /**
87
+ * Push the BUNDLE signal — call from the consumer/smrt-svelte binding when
88
+ * SvelteKit's `updated` store flips true. Idempotent: once set it stays set
89
+ * (a bundle update does not un-happen).
90
+ */
91
+ notifyBundleUpdated(): void;
92
+ /**
93
+ * Push the API CONTRACT signal — call when a live transport observes a server
94
+ * `manifestHash` that differs from this running build (#1859). Idempotent:
95
+ * once set it stays set (a contract mismatch does not un-happen).
96
+ */
97
+ notifyContractUpdated(): void;
98
+ /**
99
+ * Resolves once the async contract detection has settled (compared the running
100
+ * hash against the persisted last-seen value and fired the signal if needed).
101
+ * Primarily for tests/SSR; UI code just subscribes.
102
+ */
103
+ readonly ready: Promise<void>;
104
+ /** Detach the durable resource (does NOT wipe — that's wipeDurableStore). */
105
+ dispose(): void;
106
+ }
107
+ /**
108
+ * Create the framework-free `updateAvailable` primitive. Kicks off async
109
+ * contract detection immediately (compare running vs. persisted manifest hash);
110
+ * {@link UpdateState.notifyBundleUpdated} feeds the bundle signal, and
111
+ * {@link UpdateState.notifyContractUpdated} feeds the live contract signal. A
112
+ * change to EITHER signal notifies subscribers.
113
+ */
114
+ export declare function createUpdateState(config: UpdateStateConfig): UpdateState;
115
+ //# sourceMappingURL=update-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-state.d.ts","sourceRoot":"","sources":["../src/update-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EACL,KAAK,eAAe,EAGrB,MAAM,oBAAoB,CAAC;AAO5B,mEAAmE;AACnE,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED,mDAAmD;AACnD,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,eAAe,CAAC;CAC5B;AAED,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,6DAA6D;IAC7D,GAAG,IAAI,oBAAoB,CAAC;IAC5B;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvE;;;;OAIG;IACH,mBAAmB,IAAI,IAAI,CAAC;IAC5B;;;;OAIG;IACH,qBAAqB,IAAI,IAAI,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,6EAA6E;IAC7E,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAoHxE"}