@happyvertical/smrt-web 0.40.14 → 0.40.15

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 (2) hide show
  1. package/README.md +139 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # @happyvertical/smrt-web
2
+
3
+ Framework-agnostic browser data runtime for generated s-m-r-t REST collections. It
4
+ turns manifest-generated collection definitions into cached, reactive,
5
+ optimistic client collections while keeping the underlying data engine behind a
6
+ s-m-r-t-owned public contract.
7
+
8
+ Use it for browser-side collection state, shared request deduplication, offline
9
+ writes, persisted read caches, and live invalidation. Svelte bindings live in
10
+ [`@happyvertical/smrt-svelte/web`](../smrt-svelte/README.md).
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pnpm add @happyvertical/smrt-web
16
+ ```
17
+
18
+ ## Quick start
19
+
20
+ s-m-r-t's Vite plugin emits collection definitions through the
21
+ `@happyvertical/smrt-virt-web` virtual module:
22
+
23
+ ```ts
24
+ import {
25
+ createSmrtCollection,
26
+ createSmrtWebClient,
27
+ } from '@happyvertical/smrt-web';
28
+ import { getCollectionDefinition } from '@happyvertical/smrt-virt-web';
29
+
30
+ const client = createSmrtWebClient();
31
+ const products = createSmrtCollection(
32
+ getCollectionDefinition('products'),
33
+ { basePath: '/api/v1', client },
34
+ );
35
+
36
+ await products.preload();
37
+ console.log(products.toArray);
38
+
39
+ const transaction = products.insert({
40
+ id: crypto.randomUUID(),
41
+ name: 'New product',
42
+ });
43
+ await transaction.isPersisted.promise;
44
+ ```
45
+
46
+ Pass the same `client` to related collections for shared cache identity,
47
+ in-flight request deduplication, and relationship-derived invalidation.
48
+
49
+ ## Data behavior
50
+
51
+ - Reads are stale-while-revalidate; `staleTimeMs` defaults to 30 seconds.
52
+ - Concurrent identical reads coalesce into one network request.
53
+ - Inserts are optimistic and roll back if persistence fails.
54
+ - `initialData` seeds SSR-hydrated rows without a duplicate first-render fetch.
55
+ - Public rows are plain DTOs; data-engine types and virtual fields do not cross
56
+ the package boundary.
57
+
58
+ ## Optional capabilities
59
+
60
+ Capabilities are opt-in per collection and run through a stable lifecycle seam.
61
+ A collection without them preserves the basic runtime behavior.
62
+
63
+ ### Durable offline writes
64
+
65
+ ```ts
66
+ import { offlineOutbox } from '@happyvertical/smrt-web';
67
+
68
+ const products = createSmrtCollection(definition, {
69
+ capabilities: [
70
+ offlineOutbox({
71
+ object: definition,
72
+ namespace: {
73
+ apiBase: '/api',
74
+ tenantId,
75
+ identityId: userId,
76
+ manifestHash,
77
+ },
78
+ syncApplyBasePath: '/api',
79
+ }),
80
+ ],
81
+ });
82
+ ```
83
+
84
+ The IndexedDB outbox replays FIFO through the generated sync-apply contract.
85
+ Client UUIDs and idempotent strict inserts reconcile the optimistic row instead
86
+ of creating a second server identity. Web Locks elect one replay leader across
87
+ tabs when supported.
88
+
89
+ ### Persisted read cache
90
+
91
+ `persistCollection()` warm-starts from an IndexedDB snapshot, then revalidates
92
+ in the background. Its namespace includes API, tenant, identity, and manifest
93
+ hash, so user switches and contract changes cannot hydrate another scope's rows.
94
+ Sensitive collections should omit this capability.
95
+
96
+ ### Live invalidation
97
+
98
+ Create one app-wide `createSmrtWebEventSubscriber()` and register
99
+ `liveInvalidation({ subscriber, tableName })` on each collection. It consumes
100
+ named `_events` SSE frames and permanently downgrades to `_changes` polling when
101
+ SSE is unavailable or fatally closed. Authorization and tenancy stay on the
102
+ generated server read path; signals contain no row payload.
103
+
104
+ ### Update awareness
105
+
106
+ `createUpdateState()` combines bundle-update and manifest-contract signals.
107
+ The Svelte adapter `useUpdateAvailable` connects it to SvelteKit's update store.
108
+
109
+ ## Engine boundary
110
+
111
+ The current implementation uses TanStack DB internally, but no `@tanstack/*`
112
+ type may appear in the public API or generated declarations. Applications must
113
+ depend on `SmrtWebCollection` and `SmrtWebClient`, which keeps the engine
114
+ replaceable and prevents Svelte-only exports from entering the framework-neutral
115
+ core.
116
+
117
+ ## Public API groups
118
+
119
+ | Group | Main exports |
120
+ | --- | --- |
121
+ | Collections | `createSmrtCollection`, `createSmrtWebClient`, `newLocalId` |
122
+ | HTTP | `createDefinitionFetchers`, `unwrapListResult`, `unwrapItemResult` |
123
+ | Offline | `offlineOutbox`, `getOutboxHandle` |
124
+ | Persistence | `persistCollection`, `wipeDurableStore` |
125
+ | Live updates | `createSmrtWebEventSubscriber`, `liveInvalidation` |
126
+ | Version awareness | `createUpdateState` |
127
+ | WebMCP | `registerWebMcpTools` |
128
+
129
+ ## Development
130
+
131
+ ```bash
132
+ pnpm --filter @happyvertical/smrt-web test
133
+ pnpm --filter @happyvertical/smrt-web typecheck
134
+ pnpm --filter @happyvertical/smrt-web build
135
+ ```
136
+
137
+ The build includes a generated-declaration scan that rejects leaked engine
138
+ types. See [`AGENTS.md`](./AGENTS.md) for cache, outbox, SSE, persistence, and
139
+ engine-boundary invariants.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-web",
3
- "version": "0.40.14",
3
+ "version": "0.40.15",
4
4
  "description": "SMRT browser client data runtime: typed collection factory wrapping the client-data engine over generated REST clients",
5
5
  "author": "HappyVertical",
6
6
  "type": "module",