@happyvertical/smrt-web 0.40.13 → 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.
- package/README.md +139 -0
- 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.
|
|
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",
|