@happyvertical/smrt-web 0.43.2 → 0.43.4

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/AGENTS.md CHANGED
@@ -38,9 +38,10 @@ module you are editing. This file keeps what holds in every module.
38
38
  | `index.ts` hooks + `durable-store.ts` | the six capability hook points, hook error isolation, the no-op guarantee, and the shared durable-store namespacing/wipe registry | [agents/capability-seam.md](agents/capability-seam.md) |
39
39
  | `offline/` | durable offline writes — config, sync-apply-only replay, idempotency, the shared namespace-keyed engine, and Web Locks leader election | [agents/offline-outbox.md](agents/offline-outbox.md) |
40
40
  | `sse-client.ts` | the client half of live cache invalidation — the app-wide subscriber, the wire contract it consumes, and SSE-vs-polling behaviour | [agents/live-invalidation.md](agents/live-invalidation.md) |
41
- | `webmcp.ts` | framework-agnostic WebMCP registrar; registers generated collection tools and routes mutations through shared smrt-web cache state | — |
41
+ | `webmcp.ts` | framework-agnostic WebMCP registrar; validates an optionally bounded/namespaced, effect-filtered prospective set atomically, keeps legacy list-backed tools on collection state, and executes canonical tool-only definitions directly through REST fetchers | — |
42
42
  | `persistence/` + `update-state.ts` | the read-cache rehydrate capability and the framework-free `updateAvailable` primitive (bundle + contract signals) | [agents/version-persistence.md](agents/version-persistence.md) |
43
43
  | `data-query.ts` | dependency-free browser mirror and defensive response normalizer for the canonical bounded data-query envelope (#2444) | — |
44
+ | `remote-query.ts` | query-shaped remote pages over a `SmrtWebCollection`, with keyed stale cache, execution modes, cancellation/latest-query-wins, and optional query-scoped live subscriptions (#2445) | — |
44
45
 
45
46
  ## The engine-absorption boundary (ratified conditions, #1761)
46
47
 
@@ -60,6 +61,34 @@ module you are editing. This file keeps what holds in every module.
60
61
 
61
62
  ## Conventions
62
63
 
64
+ - **WebMCP definition mirror** — `WebMcpToolDefinition` textually mirrors the
65
+ generated `@happyvertical/smrt-virt-web` / physical `@smrt/web` declaration.
66
+ It is transport-complete and does not imply that a list-materialized client
67
+ collection exists. Keep the mirror dependency-free. Register canonical tools
68
+ or legacy collection definitions for overlapping collections; when composing
69
+ the two forms, keep their names and collection/action identities disjoint.
70
+ Duplicates fail atomically before registration.
71
+ With no exposure policy, only `read` effects are selected; broader effects
72
+ require explicit opt-in, and undeclared custom actions are destructive.
73
+ Direct mutations invalidate their
74
+ own and relationship-derived collection names through the public
75
+ `invalidateSmrtWebCollections()` seam when the host supplies its shared
76
+ `SmrtWebClient`. Legacy `filter` callbacks receive complete collection
77
+ metadata; canonical definitions use `filterTool`. Supplying either filter for
78
+ definitions of the other kind fails closed rather than ignoring the predicate
79
+ or fabricating incomplete metadata for a policy decision. Filters and fetcher
80
+ resolvers receive isolated value snapshots, so integrations must key external
81
+ state by stable values such as collection/action rather than definition object
82
+ identity.
83
+ Canonical writes validate that shared client handle before registration, and
84
+ string or structured `{ error }` REST envelopes fail before cache
85
+ invalidation. The private `__smrt_options` GET sentinel is reserved only for
86
+ no-path single-options-bag actions; positional actions preserve a legitimate
87
+ parameter with that name.
88
+ This capability policy is not authorization:
89
+ the authenticated REST surface remains the auth, tenant, field-write, and
90
+ sensitive-data boundary.
91
+
63
92
  - **No inter-smrt dependencies** — depends only on TanStack packages
64
93
  (dependency-DAG guardrails). Definitions and fetchers arrive as arguments.
65
94
  - **Data-query mirror** — `data-query.ts` mirrors the portable
package/README.md CHANGED
@@ -9,6 +9,12 @@ Use it for browser-side collection state, shared request deduplication, offline
9
9
  writes, persisted read caches, and live invalidation. Svelte bindings live in
10
10
  [`@happyvertical/smrt-svelte/web`](../smrt-svelte/README.md).
11
11
 
12
+ Query-backed surfaces can use `createSmrtWebQuery(collection, transport)` to
13
+ fetch one canonical bounded page. Visible runs update state; `background`,
14
+ `prefetch`, and `silent` runs stay out of visible state. The controller keeps
15
+ stale rows available while refreshing, cancels superseded visible runs, and can
16
+ attach a query-scoped live subscription.
17
+
12
18
  ## Installation
13
19
 
14
20
  ```bash
@@ -119,6 +125,7 @@ core.
119
125
  | Group | Main exports |
120
126
  | --- | --- |
121
127
  | Collections | `createSmrtCollection`, `createSmrtWebClient`, `newLocalId` |
128
+ | Remote queries | `createSmrtWebQuery`, `SmrtWebQueryTransport` |
122
129
  | HTTP | `createDefinitionFetchers`, `unwrapListResult`, `unwrapItemResult` |
123
130
  | Offline | `offlineOutbox`, `getOutboxHandle` |
124
131
  | Persistence | `persistCollection`, `wipeDurableStore` |
@@ -126,6 +133,83 @@ core.
126
133
  | Version awareness | `createUpdateState` |
127
134
  | WebMCP | `registerWebMcpTools` |
128
135
 
136
+ ## WebMCP capability exposure
137
+
138
+ `registerWebMcpTools()` is secure by default: omitting an exposure policy
139
+ registers only `read` tools. CRUD effects are fixed (`list`/`get` are `read`,
140
+ `create`/`update` are `write`, and `delete` is `destructive`). A custom action
141
+ without declared metadata is treated as destructive, non-idempotent, and open
142
+ world. Declare safer custom-action semantics in the route metadata only when
143
+ they are true:
144
+
145
+ ```ts
146
+ @smrt({
147
+ api: {
148
+ routes: {
149
+ preview: {
150
+ method: 'GET',
151
+ effect: 'read',
152
+ idempotent: true,
153
+ openWorld: false,
154
+ },
155
+ },
156
+ },
157
+ })
158
+ class Report extends SmrtObject {}
159
+ ```
160
+
161
+ Opt into broader capabilities explicitly. `namespace` prevents cross-surface
162
+ name collisions, an explicit `maxTools` bounds the selected set, and duplicate
163
+ names or stable collection/action identities reject the entire call before the
164
+ first browser registration. No implicit budget is applied to whole-manifest
165
+ read registration:
166
+
167
+ ```ts
168
+ registerWebMcpTools(definitions, {
169
+ effects: ['read', 'write', 'destructive'],
170
+ namespace: 'admin',
171
+ maxTools: 32,
172
+ filter: (collection, tool) => collection.fields.tenantId !== undefined,
173
+ filterTool: (tool) => tool.collection === 'reports',
174
+ });
175
+ ```
176
+
177
+ The returned disposer also exposes a `ready` promise. Await it when the host
178
+ must report browser-side registration rejection; any rejected tool aborts all
179
+ sibling registrations from that call before `ready` rejects.
180
+
181
+ `filter` receives legacy collection metadata; `filterTool` receives canonical
182
+ per-tool definitions. Configuring only one filter while registering definitions
183
+ for the other filter kind fails closed; canonical tools do not carry complete
184
+ collection field metadata, and legacy descriptors do not satisfy the canonical
185
+ filter contract. Policy callbacks and fetcher resolvers receive isolated value
186
+ snapshots; key host-side maps by stable values such as collection/action or tool
187
+ name, not definition object identity.
188
+
189
+ Do not concatenate complete legacy and canonical definition sets for the same
190
+ collections. Their duplicate tool names or collection/action identities reject
191
+ the registration atomically. Prefer the canonical set for complete generated
192
+ coverage, or compose only disjoint legacy and canonical subsets.
193
+
194
+ WebMCP policy controls which capabilities a page advertises; it is not an
195
+ authorization boundary. Execution still uses the page's authenticated REST
196
+ transport, whose auth, tenancy, writable-field, and sensitive-field guards must
197
+ remain enabled. All application-derived tool results are annotated as untrusted
198
+ content, including mutation responses.
199
+
200
+ Policy only narrows the actions already exposed by the generated API metadata.
201
+ Legacy descriptors outside their collection's `actions` set reject the whole
202
+ registration, and intrinsic CRUD effects cannot be relabeled by caller data.
203
+
204
+ Migration note: registrations that previously relied on every descriptor being
205
+ exposed must now pass `effects: ['read', 'write', 'destructive']`. Prefer a
206
+ narrower allowlist for each browser surface. Integrations that previously keyed
207
+ filter or resolver state by definition object identity must migrate to stable
208
+ name or collection/action keys. If both legacy and canonical definition arrays
209
+ are available, select one complete source or remove overlaps before combining
210
+ them. Set `maxTools` explicitly on surfaces that need a hard capability budget;
211
+ overflow rejects the complete registration rather than truncating it.
212
+
129
213
  ## Development
130
214
 
131
215
  ```bash