@happyvertical/smrt-web 0.38.6 → 0.38.8
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 +85 -0
- package/dist/index.d.ts +207 -6
- package/dist/index.js +531 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -282,6 +282,91 @@ replayed signal STILL fires, which is exactly what makes a reconnect miss no
|
|
|
282
282
|
invalidation. `lastSeq` is a resume cursor for the poll fallback, not a dedup
|
|
283
283
|
filter.
|
|
284
284
|
|
|
285
|
+
## Version awareness & persistence (#1764)
|
|
286
|
+
|
|
287
|
+
The read-side twin of the outbox plus a first-class "an update is available"
|
|
288
|
+
signal. Two locked architecture decisions (maintainer): the manifest-hash source
|
|
289
|
+
is a **build-time inject** (core's web-module generator emits a `manifestHash`
|
|
290
|
+
constant — NOT a runtime endpoint), and the ETag salt is **folded into this PR**
|
|
291
|
+
(the same hash threads into `computeTableVersionEtag`, closing #1765's documented
|
|
292
|
+
shape-change staleness gap). See `packages/core/AGENTS.md` for the core halves.
|
|
293
|
+
|
|
294
|
+
### Persistence capability — `persistCollection(config)` (opt-in)
|
|
295
|
+
|
|
296
|
+
The read-cache rehydrate the outbox left to #1764. Add it to a collection's
|
|
297
|
+
`capabilities`; a collection **without** it is byte-for-byte non-persistent — the
|
|
298
|
+
seam's no-op guarantee, and the "opt-in per model" AC. **Sensitive models simply
|
|
299
|
+
omit the capability and never touch disk.**
|
|
300
|
+
|
|
301
|
+
- `warmStart(ctx)` reads the persisted snapshot for
|
|
302
|
+
`(durableStoreNamespace(namespace), collection)` and returns its rows to seed
|
|
303
|
+
the cache — the stale render paints instantly; the engine then revalidates in
|
|
304
|
+
the background via its normal SWR (the preload path AWAITS the async warmStart,
|
|
305
|
+
so the first `list()` is suppressed). **Manifest-hash change drops caches
|
|
306
|
+
automatically:** the namespace INCLUDES `manifestHash`, so a contract-changing
|
|
307
|
+
deploy lands on a DIFFERENT IndexedDB database → the old snapshot is never found
|
|
308
|
+
→ an empty warmStart → a fresh fetch with NO stale-schema hydration. No explicit
|
|
309
|
+
invalidation.
|
|
310
|
+
- Write-back: `onAttach` subscribes to the collection's changes (via the seam's
|
|
311
|
+
engine-free `ctx.subscribe`/`ctx.snapshot` — added for this slice, plain-DTO
|
|
312
|
+
payloads only, no engine type) and persists the current rows DEBOUNCED (250ms
|
|
313
|
+
trailing default; the scheduler is timer-based even at 0ms so a pending write is
|
|
314
|
+
cancelable). `teardown` sets `detached` and CLEARS the pending timer FIRST so
|
|
315
|
+
the "rows removed" change that the engine's own `cleanup()` fires just before
|
|
316
|
+
teardown can't persist an empty snapshot over the good one, then drains any
|
|
317
|
+
in-flight write. The per-change write-back already persisted the latest rows
|
|
318
|
+
during the collection's life, so the durable snapshot survives for the next
|
|
319
|
+
load.
|
|
320
|
+
- Storage: `persistence/snapshot-store.ts` — raw IndexedDB, ONE blob per
|
|
321
|
+
`(namespace, collection)`, mirroring the outbox's hand-rolled queue (ZERO
|
|
322
|
+
`@tanstack/*`; the boundary check is the proof). N collections under one
|
|
323
|
+
namespace share ONE ref-counted store; the last detach closes the db.
|
|
324
|
+
- `registerDurableResource(namespace, { kind: 'persisted-collection', clear })` at
|
|
325
|
+
first attach, unregistered on last detach BEFORE the db closes (so a later
|
|
326
|
+
`wipeDurableStore` is a no-op, not a double-clear — the #1762 discipline).
|
|
327
|
+
- **Namespace segregation:** the namespace folds api/tenant/identity/manifest, so
|
|
328
|
+
switching users on one device lands on a different database — one user can never
|
|
329
|
+
read another's persisted rows.
|
|
330
|
+
- **Logout wipe clears the outbox too:** the outbox (#1762) registers its queue
|
|
331
|
+
under the SAME namespace, so ONE `wipeDurableStore(namespace)` clears BOTH the
|
|
332
|
+
persisted collections AND the outbox queue.
|
|
333
|
+
- **IndexedDB unavailable** (probe `open()` throws — private mode, sandboxed
|
|
334
|
+
iframe): `console.warn` ONCE + behave as non-persistent (warmStart returns
|
|
335
|
+
nothing, write-back is a no-op). Never throws — the outbox's `probeIndexedDb`
|
|
336
|
+
posture.
|
|
337
|
+
|
|
338
|
+
### `updateAvailable` primitive — `createUpdateState(config)` (framework-free)
|
|
339
|
+
|
|
340
|
+
A tiny pub/sub (`update-state.ts`) with TWO INDEPENDENT signals; `updateAvailable
|
|
341
|
+
= bundle || contract`:
|
|
342
|
+
|
|
343
|
+
- **bundle** — the client BUNDLE changed on the server. SvelteKit detects it
|
|
344
|
+
natively (`updated` store); the consumer/smrt-svelte binding pushes it via
|
|
345
|
+
`notifyBundleUpdated()`. This module owns no polling.
|
|
346
|
+
- **contract** — the API CONTRACT (manifest hash) changed, which only SMRT knows.
|
|
347
|
+
Under BUILD-TIME INJECT: on init the primitive compares the RUNNING build's
|
|
348
|
+
`manifestHash` (passed in by the consumer, imported from
|
|
349
|
+
`@happyvertical/smrt-virt-web`) against a persisted "last-seen manifestHash" in
|
|
350
|
+
durable storage; if they differ → fire `contract` + store the new value. First
|
|
351
|
+
run (no baseline) records without firing.
|
|
352
|
+
|
|
353
|
+
The last-seen hash lives in `update-state/meta-store.ts` (a tiny IDB key/value
|
|
354
|
+
store) under the durable namespace, registered as a durable resource so
|
|
355
|
+
`wipeDurableStore` clears it too (the "wipe clears the last-seen-hash record"
|
|
356
|
+
AC). Degrades gracefully if IndexedDB is absent (bundle-only) or no running hash
|
|
357
|
+
is supplied.
|
|
358
|
+
|
|
359
|
+
> **Trade-off / documented follow-up:** contract detection under build-time
|
|
360
|
+
> inject fires at most once **per load** (compare-on-init). LIVE-while-open
|
|
361
|
+
> contract detection — learning mid-session that the server redeployed (an SSE
|
|
362
|
+
> deploy signal / runtime version endpoint) — is an EXPLICIT follow-up, not this
|
|
363
|
+
> slice; it would push a second `contract` signal into the same primitive, which
|
|
364
|
+
> is shaped for it.
|
|
365
|
+
|
|
366
|
+
The reactive Svelte binding (`useUpdateAvailable`) ships in
|
|
367
|
+
`@happyvertical/smrt-svelte/web` — it wires SvelteKit's `updated` store into the
|
|
368
|
+
bundle signal and surfaces both reactively for a toast/reload UX.
|
|
369
|
+
|
|
285
370
|
## The engine-absorption boundary (ratified conditions, #1761)
|
|
286
371
|
|
|
287
372
|
1. **No engine types in the public API.** `@tanstack/*` types must never appear
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialize `list` query params into the query string the generated REST list
|
|
3
|
+
* route parses (`handleList` in `core/src/generators/rest.ts`): `limit`,
|
|
4
|
+
* `offset`, `orderBy` as scalars, and `where` entries as `field=value`
|
|
5
|
+
* (equality) or `field[op]=value` for a `{ op, value }` condition (`in` joins an
|
|
6
|
+
* array with commas). Called with no params (the collection runtime's argument-
|
|
7
|
+
* free `list()`) it returns `''`, so the bare-URL behavior is unchanged.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildListQuery(params?: Record<string, unknown>): string;
|
|
10
|
+
|
|
1
11
|
/**
|
|
2
12
|
* Build CRUD fetchers from a generated collection definition — the same URL
|
|
3
13
|
* scheme and payload handling as the generated REST client
|
|
@@ -120,6 +130,17 @@ export declare function createSmrtWebClient(): SmrtWebClient;
|
|
|
120
130
|
*/
|
|
121
131
|
export declare function createSmrtWebEventSubscriber(config: SmrtWebEventSubscriberConfig): SmrtWebEventSubscriber;
|
|
122
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Create the framework-free `updateAvailable` primitive. Kicks off async
|
|
135
|
+
* contract detection immediately (compare running vs. persisted manifest hash);
|
|
136
|
+
* {@link UpdateState.notifyBundleUpdated} feeds the bundle signal. A change to
|
|
137
|
+
* EITHER signal notifies subscribers.
|
|
138
|
+
*/
|
|
139
|
+
export declare function createUpdateState(config: UpdateStateConfig): UpdateState;
|
|
140
|
+
|
|
141
|
+
/** Default trailing-debounce window (ms) for the write-back. */
|
|
142
|
+
export declare const DEFAULT_PERSIST_DEBOUNCE_MS = 250;
|
|
143
|
+
|
|
123
144
|
/**
|
|
124
145
|
* A durable artifact registered under a namespace — the outbox queue (#1762) or
|
|
125
146
|
* a persisted collection store (#1764). Each owns its storage engine and
|
|
@@ -412,6 +433,51 @@ export declare interface OutboxSnapshotItem {
|
|
|
412
433
|
*/
|
|
413
434
|
export declare type OutboxSyncState = 'pending' | 'uploading' | 'synced' | 'failed';
|
|
414
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Build a durable persistence capability for a collection. Add the returned
|
|
438
|
+
* capability to the collection's `capabilities` array; a collection without it
|
|
439
|
+
* is unaffected (the seam's no-op guarantee — the "opt-in per model" AC).
|
|
440
|
+
*
|
|
441
|
+
* The same `namespace` across multiple collections shares ONE snapshot database
|
|
442
|
+
* (one open, one durable resource, one wipe), and each collection is keyed by
|
|
443
|
+
* its own `collection` name within it.
|
|
444
|
+
*/
|
|
445
|
+
export declare function persistCollection<TData extends object = object>(config: PersistCollectionConfig<TData>): SmrtWebCapability<TData>;
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Configuration for {@link persistCollection}. Generic in the collection's row
|
|
449
|
+
* type `TData` so the capability matches the collection it plugs into.
|
|
450
|
+
*/
|
|
451
|
+
export declare interface PersistCollectionConfig<TData extends object = object> {
|
|
452
|
+
/**
|
|
453
|
+
* The REST collection name this snapshot is keyed by — MUST be the SAME
|
|
454
|
+
* `definition.name` passed to {@link createSmrtCollection} (it is the store
|
|
455
|
+
* key under the namespace). Provided explicitly (not read off the ctx) so the
|
|
456
|
+
* config is self-describing and testable.
|
|
457
|
+
*/
|
|
458
|
+
collection: string;
|
|
459
|
+
/**
|
|
460
|
+
* The durable-store identity this snapshot is namespaced under — folds api
|
|
461
|
+
* base / tenant / identity / manifest hash, so a logout, tenant switch, or a
|
|
462
|
+
* contract-changing deploy lands on a different IndexedDB database (never
|
|
463
|
+
* cross-identity reuse, and a shape change drops old snapshots). Shared with
|
|
464
|
+
* the outbox (#1762) via the same {@link durableStoreNamespace}, so one
|
|
465
|
+
* {@link wipeDurableStore} clears both.
|
|
466
|
+
*
|
|
467
|
+
* The canonical `manifestHash` source is the build-time inject — the
|
|
468
|
+
* `manifestHash` constant the generated `@happyvertical/smrt-virt-web` module
|
|
469
|
+
* exports (#1764). Thread it here at the call site.
|
|
470
|
+
*/
|
|
471
|
+
namespace: DurableStoreKey;
|
|
472
|
+
/**
|
|
473
|
+
* Trailing-debounce window (ms) for the write-back snapshot. A burst of
|
|
474
|
+
* mutations within the window collapses to ONE IndexedDB write of the final
|
|
475
|
+
* row set. Default {@link DEFAULT_PERSIST_DEBOUNCE_MS}. Set 0 to write on
|
|
476
|
+
* every change (tests).
|
|
477
|
+
*/
|
|
478
|
+
debounceMs?: number;
|
|
479
|
+
}
|
|
480
|
+
|
|
415
481
|
/**
|
|
416
482
|
* Register a durable artifact under a namespace so {@link wipeDurableStore} can
|
|
417
483
|
* later clear it. Returns an unregister function that removes just this
|
|
@@ -420,6 +486,29 @@ export declare type OutboxSyncState = 'pending' | 'uploading' | 'synced' | 'fail
|
|
|
420
486
|
*/
|
|
421
487
|
export declare function registerDurableResource(namespace: string, resource: DurableResource): () => void;
|
|
422
488
|
|
|
489
|
+
/**
|
|
490
|
+
* Register every collection's generated tool descriptors with WebMCP.
|
|
491
|
+
*
|
|
492
|
+
* @returns a disposer that deregisters all tools this call registered. On a
|
|
493
|
+
* browser without WebMCP the call is a no-op and the disposer is inert.
|
|
494
|
+
*/
|
|
495
|
+
export declare function registerWebMcpTools(definitions: SmrtWebCollectionDefinition[], options?: RegisterWebMcpToolsOptions): () => void;
|
|
496
|
+
|
|
497
|
+
export declare interface RegisterWebMcpToolsOptions {
|
|
498
|
+
/** REST base path for the fetchers (default `/api/v1`). */
|
|
499
|
+
basePath?: string;
|
|
500
|
+
/** Injectable fetch (tests / SSR-safe wrappers). */
|
|
501
|
+
fetchFn?: typeof fetch;
|
|
502
|
+
/**
|
|
503
|
+
* Override how a definition's CRUD fetchers are built. Defaults to
|
|
504
|
+
* {@link createDefinitionFetchers}; the primary seam for testing `execute`
|
|
505
|
+
* without a live server.
|
|
506
|
+
*/
|
|
507
|
+
resolveFetchers?: (definition: SmrtWebCollectionDefinition) => SmrtCrudFetchers;
|
|
508
|
+
/** Predicate to include/exclude individual tools (e.g. reads-only surfaces). */
|
|
509
|
+
filter?: (definition: SmrtWebCollectionDefinition, descriptor: NonNullable<SmrtWebCollectionDefinition['toolDescriptors']>[number]) => boolean;
|
|
510
|
+
}
|
|
511
|
+
|
|
423
512
|
/**
|
|
424
513
|
* Run the `wrapMutation` hook across `capabilities` in array order for one
|
|
425
514
|
* mutation, short-circuiting on the FIRST capability that returns `{ handled:
|
|
@@ -535,6 +624,34 @@ export declare interface SmrtWebCapabilityContext<TData extends object = object>
|
|
|
535
624
|
* message) without reaching into the engine.
|
|
536
625
|
*/
|
|
537
626
|
invalidate(): void;
|
|
627
|
+
/**
|
|
628
|
+
* A snapshot of the collection's current rows as plain public DTOs — the same
|
|
629
|
+
* projection as {@link SmrtWebCollection.toArray}, so the engine's virtual
|
|
630
|
+
* props never cross the boundary. The persistence slice (#1764) reads this in
|
|
631
|
+
* its write-back to serialize the current cache to disk. Populated by the real
|
|
632
|
+
* factory; a hand-built test context may omit it, so callers guard for its
|
|
633
|
+
* presence.
|
|
634
|
+
*
|
|
635
|
+
* SAFE ONLY FROM `onAttach` ONWARD. The factory's implementation closes over
|
|
636
|
+
* the engine collection, which is constructed AFTER `contributeCacheKey` and
|
|
637
|
+
* `warmStart` run — calling this from those earlier hooks would throw
|
|
638
|
+
* (temporal-dead-zone). Read rows from `onAttach`, `onSettled`, or an external
|
|
639
|
+
* trigger, never during construction.
|
|
640
|
+
*/
|
|
641
|
+
snapshot?(): ReadonlyArray<SmrtWebRow<TData>>;
|
|
642
|
+
/**
|
|
643
|
+
* Subscribe to the collection's change notifications — the same stream as
|
|
644
|
+
* {@link SmrtWebCollection.subscribeChanges} (plain-DTO payloads). The
|
|
645
|
+
* persistence slice (#1764) wires its debounced write-back here in `onAttach`
|
|
646
|
+
* and detaches in `teardown`. Engine-free by construction: the payload is
|
|
647
|
+
* `unknown` (the capability only needs the change SIGNAL, then re-reads via
|
|
648
|
+
* {@link snapshot}), so no engine type crosses the seam. Populated by the real
|
|
649
|
+
* factory; a hand-built test context may omit it.
|
|
650
|
+
*
|
|
651
|
+
* SAFE ONLY FROM `onAttach` ONWARD (same reason as {@link snapshot}): it closes
|
|
652
|
+
* over the engine collection built after the pre-construction hooks.
|
|
653
|
+
*/
|
|
654
|
+
subscribe?(callback: (changes: unknown) => void): SmrtWebSubscription;
|
|
538
655
|
}
|
|
539
656
|
|
|
540
657
|
/**
|
|
@@ -578,12 +695,6 @@ export declare interface SmrtWebCollection<TData extends object> {
|
|
|
578
695
|
insert(row: SmrtWebRow<TData>): SmrtWebTransaction;
|
|
579
696
|
}
|
|
580
697
|
|
|
581
|
-
/**
|
|
582
|
-
* One generated collection definition: everything needed to construct a client
|
|
583
|
-
* collection over the generated REST surface. The `_row` property is a phantom
|
|
584
|
-
* type carrier threaded through codegen — it never exists at runtime, it only
|
|
585
|
-
* lets factories infer the row type from a definition.
|
|
586
|
-
*/
|
|
587
698
|
export declare interface SmrtWebCollectionDefinition<TData extends object = object> {
|
|
588
699
|
/** REST collection name (e.g. `products`). */
|
|
589
700
|
name: string;
|
|
@@ -595,6 +706,12 @@ export declare interface SmrtWebCollectionDefinition<TData extends object = obje
|
|
|
595
706
|
idField: string;
|
|
596
707
|
/** CRUD + custom actions exposed by the api decorator config. */
|
|
597
708
|
actions: string[];
|
|
709
|
+
/**
|
|
710
|
+
* WebMCP/MCP tool descriptors for the exposed actions (#1812). Optional so
|
|
711
|
+
* hand-built definitions (older codegen, tests) still satisfy the type; a
|
|
712
|
+
* missing value means "no WebMCP tools to register".
|
|
713
|
+
*/
|
|
714
|
+
toolDescriptors?: WebToolDescriptor[];
|
|
598
715
|
/** Persisted field metadata keyed by field name. */
|
|
599
716
|
fields: Record<string, SmrtWebFieldDefinition>;
|
|
600
717
|
/**
|
|
@@ -821,6 +938,90 @@ export declare function unwrapItemResult(result: unknown, context: string): Reco
|
|
|
821
938
|
*/
|
|
822
939
|
export declare function unwrapListResult(result: unknown, collectionName: string): Array<Record<string, unknown>>;
|
|
823
940
|
|
|
941
|
+
/** The two independent update signals plus their derived union. */
|
|
942
|
+
export declare interface UpdateAvailableState {
|
|
943
|
+
/** The client bundle changed on the server (SvelteKit `updated`). */
|
|
944
|
+
readonly bundle: boolean;
|
|
945
|
+
/** The API contract (manifest hash) changed across this load. */
|
|
946
|
+
readonly contract: boolean;
|
|
947
|
+
/** `bundle || contract` — either means an update is available. */
|
|
948
|
+
readonly updateAvailable: boolean;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/** The framework-free update-available primitive. */
|
|
952
|
+
export declare interface UpdateState {
|
|
953
|
+
/** The current signal state (a fresh immutable snapshot). */
|
|
954
|
+
get(): UpdateAvailableState;
|
|
955
|
+
/**
|
|
956
|
+
* Subscribe to state changes; the callback fires immediately with the current
|
|
957
|
+
* state and again on every transition. Returns an unsubscribe function.
|
|
958
|
+
*/
|
|
959
|
+
subscribe(callback: (state: UpdateAvailableState) => void): () => void;
|
|
960
|
+
/**
|
|
961
|
+
* Push the BUNDLE signal — call from the consumer/smrt-svelte binding when
|
|
962
|
+
* SvelteKit's `updated` store flips true. Idempotent: once set it stays set
|
|
963
|
+
* (a bundle update does not un-happen).
|
|
964
|
+
*/
|
|
965
|
+
notifyBundleUpdated(): void;
|
|
966
|
+
/**
|
|
967
|
+
* Resolves once the async contract detection has settled (compared the running
|
|
968
|
+
* hash against the persisted last-seen value and fired the signal if needed).
|
|
969
|
+
* Primarily for tests/SSR; UI code just subscribes.
|
|
970
|
+
*/
|
|
971
|
+
readonly ready: Promise<void>;
|
|
972
|
+
/** Detach the durable resource (does NOT wipe — that's wipeDurableStore). */
|
|
973
|
+
dispose(): void;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/** Configuration for {@link createUpdateState}. */
|
|
977
|
+
export declare interface UpdateStateConfig {
|
|
978
|
+
/**
|
|
979
|
+
* The RUNNING build's web-collection shape digest — the `manifestHash`
|
|
980
|
+
* constant the generated `@happyvertical/smrt-virt-web` module exports
|
|
981
|
+
* (#1764). Compared against the persisted last-seen value to detect a contract
|
|
982
|
+
* change across loads. Omit to disable contract detection (bundle-only).
|
|
983
|
+
*/
|
|
984
|
+
manifestHash?: string;
|
|
985
|
+
/**
|
|
986
|
+
* The durable-store identity the last-seen manifest hash is namespaced under —
|
|
987
|
+
* folds api base / tenant / identity (and, yes, the manifest hash itself, but
|
|
988
|
+
* see below). Shared with persistence/outbox so one {@link wipeDurableStore}
|
|
989
|
+
* clears the last-seen record too.
|
|
990
|
+
*
|
|
991
|
+
* NOTE the namespace's OWN `manifestHash` segment should be a STABLE value
|
|
992
|
+
* (e.g. the app's api-version or a constant), NOT the running build hash —
|
|
993
|
+
* otherwise a contract change would move the namespace and the compare would
|
|
994
|
+
* never find the prior value (always "first run"). Contract change is detected
|
|
995
|
+
* by the STORED VALUE under a stable namespace, not by the namespace moving.
|
|
996
|
+
*/
|
|
997
|
+
namespace: DurableStoreKey;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* One generated collection definition: everything needed to construct a client
|
|
1002
|
+
* collection over the generated REST surface. The `_row` property is a phantom
|
|
1003
|
+
* type carrier threaded through codegen — it never exists at runtime, it only
|
|
1004
|
+
* lets factories infer the row type from a definition.
|
|
1005
|
+
*/
|
|
1006
|
+
/**
|
|
1007
|
+
* One WebMCP / MCP tool descriptor for a collection action (#1812). Emitted by
|
|
1008
|
+
* the core web-collections codegen as PLAIN DATA (this package has no smrt
|
|
1009
|
+
* dependency), shaped to match Chrome's `document.modelContext.registerTool`
|
|
1010
|
+
* input — see https://developer.chrome.com/docs/ai/webmcp. Consumed by
|
|
1011
|
+
* {@link registerWebMcpTools} in `./webmcp`.
|
|
1012
|
+
*/
|
|
1013
|
+
export declare interface WebToolDescriptor {
|
|
1014
|
+
/** The action this tool performs (`list` | `get` | … | a custom method name). */
|
|
1015
|
+
action: string;
|
|
1016
|
+
/** Tool id, `${className.toLowerCase()}_${action}` (e.g. `product_list`). */
|
|
1017
|
+
name: string;
|
|
1018
|
+
description: string;
|
|
1019
|
+
/** JSON Schema for the tool's arguments. */
|
|
1020
|
+
inputSchema: Record<string, unknown>;
|
|
1021
|
+
/** True for non-mutating reads → WebMCP `annotations.readOnlyHint`. */
|
|
1022
|
+
readOnly: boolean;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
824
1025
|
/**
|
|
825
1026
|
* Clear every durable artifact registered under `namespace`, then drop the
|
|
826
1027
|
* namespace. This is the single teardown point a logout / tenant-switch calls:
|