@happyvertical/smrt-web 0.38.7 → 0.38.9
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/dist/index.d.ts +64 -6
- package/dist/index.js +107 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
@@ -476,6 +486,29 @@ export declare interface PersistCollectionConfig<TData extends object = object>
|
|
|
476
486
|
*/
|
|
477
487
|
export declare function registerDurableResource(namespace: string, resource: DurableResource): () => void;
|
|
478
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
|
+
|
|
479
512
|
/**
|
|
480
513
|
* Run the `wrapMutation` hook across `capabilities` in array order for one
|
|
481
514
|
* mutation, short-circuiting on the FIRST capability that returns `{ handled:
|
|
@@ -662,12 +695,6 @@ export declare interface SmrtWebCollection<TData extends object> {
|
|
|
662
695
|
insert(row: SmrtWebRow<TData>): SmrtWebTransaction;
|
|
663
696
|
}
|
|
664
697
|
|
|
665
|
-
/**
|
|
666
|
-
* One generated collection definition: everything needed to construct a client
|
|
667
|
-
* collection over the generated REST surface. The `_row` property is a phantom
|
|
668
|
-
* type carrier threaded through codegen — it never exists at runtime, it only
|
|
669
|
-
* lets factories infer the row type from a definition.
|
|
670
|
-
*/
|
|
671
698
|
export declare interface SmrtWebCollectionDefinition<TData extends object = object> {
|
|
672
699
|
/** REST collection name (e.g. `products`). */
|
|
673
700
|
name: string;
|
|
@@ -679,6 +706,12 @@ export declare interface SmrtWebCollectionDefinition<TData extends object = obje
|
|
|
679
706
|
idField: string;
|
|
680
707
|
/** CRUD + custom actions exposed by the api decorator config. */
|
|
681
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[];
|
|
682
715
|
/** Persisted field metadata keyed by field name. */
|
|
683
716
|
fields: Record<string, SmrtWebFieldDefinition>;
|
|
684
717
|
/**
|
|
@@ -964,6 +997,31 @@ export declare interface UpdateStateConfig {
|
|
|
964
997
|
namespace: DurableStoreKey;
|
|
965
998
|
}
|
|
966
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
|
+
|
|
967
1025
|
/**
|
|
968
1026
|
* Clear every durable artifact registered under `namespace`, then drop the
|
|
969
1027
|
* namespace. This is the single teardown point a logout / tenant-switch calls:
|
package/dist/index.js
CHANGED
|
@@ -1433,6 +1433,84 @@ function createUpdateState(config) {
|
|
|
1433
1433
|
};
|
|
1434
1434
|
}
|
|
1435
1435
|
//#endregion
|
|
1436
|
+
//#region src/webmcp.ts
|
|
1437
|
+
function getModelContext() {
|
|
1438
|
+
const mc = globalThis.document?.modelContext;
|
|
1439
|
+
if (mc && typeof mc.registerTool === "function") return mc;
|
|
1440
|
+
}
|
|
1441
|
+
function registerWebMcpTools(definitions, options = {}) {
|
|
1442
|
+
const ctx = getModelContext();
|
|
1443
|
+
if (!ctx) return () => {};
|
|
1444
|
+
const basePath = options.basePath ?? "/api/v1";
|
|
1445
|
+
const controller = new AbortController();
|
|
1446
|
+
for (const definition of definitions) {
|
|
1447
|
+
const descriptors = definition.toolDescriptors;
|
|
1448
|
+
if (!descriptors || descriptors.length === 0) continue;
|
|
1449
|
+
const fetchers = options.resolveFetchers ? options.resolveFetchers(definition) : createDefinitionFetchers(definition, basePath, options.fetchFn);
|
|
1450
|
+
for (const descriptor of descriptors) {
|
|
1451
|
+
if (options.filter && !options.filter(definition, descriptor)) continue;
|
|
1452
|
+
ctx.registerTool({
|
|
1453
|
+
name: descriptor.name,
|
|
1454
|
+
description: descriptor.description,
|
|
1455
|
+
inputSchema: descriptor.inputSchema,
|
|
1456
|
+
annotations: { readOnlyHint: descriptor.readOnly },
|
|
1457
|
+
execute: (args) => dispatch(fetchers, definition, descriptor.action, args ?? {})
|
|
1458
|
+
}, { signal: controller.signal });
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
return () => controller.abort();
|
|
1462
|
+
}
|
|
1463
|
+
function requireId(args, action) {
|
|
1464
|
+
const id = args.id;
|
|
1465
|
+
if (typeof id !== "string" || id.length === 0) throw new Error(`WebMCP ${action} requires a string 'id' argument`);
|
|
1466
|
+
return id;
|
|
1467
|
+
}
|
|
1468
|
+
function requireIdentifier(args) {
|
|
1469
|
+
const value = args.id ?? args.slug;
|
|
1470
|
+
if (typeof value !== "string" || value.length === 0) throw new Error("WebMCP get requires a string 'id' or 'slug' argument");
|
|
1471
|
+
return value;
|
|
1472
|
+
}
|
|
1473
|
+
function listParams(args) {
|
|
1474
|
+
const params = {};
|
|
1475
|
+
if (args.limit !== void 0) params.limit = args.limit;
|
|
1476
|
+
if (args.offset !== void 0) params.offset = args.offset;
|
|
1477
|
+
if (args.orderBy !== void 0) params.orderBy = args.orderBy;
|
|
1478
|
+
if (args.where !== void 0) params.where = args.where;
|
|
1479
|
+
return params;
|
|
1480
|
+
}
|
|
1481
|
+
async function dispatch(fetchers, definition, action, args) {
|
|
1482
|
+
switch (action) {
|
|
1483
|
+
case "list": {
|
|
1484
|
+
const rows = unwrapListResult(await fetchers.list(listParams(args)), definition.name);
|
|
1485
|
+
return JSON.stringify(rows);
|
|
1486
|
+
}
|
|
1487
|
+
case "get": {
|
|
1488
|
+
if (!fetchers.get) throw new Error(`${definition.name} has no get action`);
|
|
1489
|
+
const row = unwrapItemResult(await fetchers.get(requireIdentifier(args)), `get(${definition.name})`);
|
|
1490
|
+
return JSON.stringify(row);
|
|
1491
|
+
}
|
|
1492
|
+
case "create": {
|
|
1493
|
+
const row = unwrapItemResult(await fetchers.create(args), `create(${definition.name})`);
|
|
1494
|
+
return JSON.stringify(row);
|
|
1495
|
+
}
|
|
1496
|
+
case "update": {
|
|
1497
|
+
if (!fetchers.update) throw new Error(`${definition.name} has no update action`);
|
|
1498
|
+
const { id: _id, ...body } = args;
|
|
1499
|
+
const row = unwrapItemResult(await fetchers.update(requireId(args, "update"), body), `update(${definition.name})`);
|
|
1500
|
+
return JSON.stringify(row);
|
|
1501
|
+
}
|
|
1502
|
+
case "delete":
|
|
1503
|
+
if (!fetchers.delete) throw new Error(`${definition.name} has no delete action`);
|
|
1504
|
+
await fetchers.delete(requireId(args, "delete"));
|
|
1505
|
+
return JSON.stringify({ success: true });
|
|
1506
|
+
default: return JSON.stringify({
|
|
1507
|
+
error: `WebMCP custom action '${action}' is not wired in the tracer`,
|
|
1508
|
+
action,
|
|
1509
|
+
collection: definition.name
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
//#endregion
|
|
1436
1514
|
//#region src/index.ts
|
|
1437
1515
|
var SmrtWebRequestError = class extends Error {
|
|
1438
1516
|
payload;
|
|
@@ -1460,6 +1538,33 @@ function unwrapItemResult(result, context) {
|
|
|
1460
1538
|
}
|
|
1461
1539
|
throw new SmrtWebRequestError(`[smrt-web] ${context} returned an unexpected payload shape`, result);
|
|
1462
1540
|
}
|
|
1541
|
+
var SMRT_TO_REST_OPERATOR = {
|
|
1542
|
+
">": "gt",
|
|
1543
|
+
">=": "gte",
|
|
1544
|
+
"<": "lt",
|
|
1545
|
+
"<=": "lte",
|
|
1546
|
+
"!=": "ne",
|
|
1547
|
+
in: "in",
|
|
1548
|
+
like: "like"
|
|
1549
|
+
};
|
|
1550
|
+
function buildListQuery(params) {
|
|
1551
|
+
if (!params) return "";
|
|
1552
|
+
const search = new URLSearchParams();
|
|
1553
|
+
const { limit, offset, orderBy, where } = params;
|
|
1554
|
+
if (limit !== void 0) search.set("limit", String(limit));
|
|
1555
|
+
if (offset !== void 0) search.set("offset", String(offset));
|
|
1556
|
+
if (orderBy !== void 0) search.set("orderBy", Array.isArray(orderBy) ? orderBy.join(", ") : String(orderBy));
|
|
1557
|
+
if (where && typeof where === "object") {
|
|
1558
|
+
for (const [field, condition] of Object.entries(where)) if (condition && typeof condition === "object" && !Array.isArray(condition) && "op" in condition && "value" in condition) {
|
|
1559
|
+
const { op, value } = condition;
|
|
1560
|
+
const restOp = SMRT_TO_REST_OPERATOR[op];
|
|
1561
|
+
const token = Array.isArray(value) ? value.join(",") : String(value);
|
|
1562
|
+
search.set(restOp ? `${field}[${restOp}]` : field, token);
|
|
1563
|
+
} else if (condition !== void 0 && condition !== null) search.set(field, String(condition));
|
|
1564
|
+
}
|
|
1565
|
+
const qs = search.toString();
|
|
1566
|
+
return qs ? `?${qs}` : "";
|
|
1567
|
+
}
|
|
1463
1568
|
function createDefinitionFetchers(definition, basePath = "/api/v1", fetchFn = (...args) => globalThis.fetch(...args)) {
|
|
1464
1569
|
const collectionUrl = `${basePath}${definition.endpoint}`;
|
|
1465
1570
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -1472,7 +1577,7 @@ function createDefinitionFetchers(definition, basePath = "/api/v1", fetchFn = (.
|
|
|
1472
1577
|
return payload;
|
|
1473
1578
|
};
|
|
1474
1579
|
return {
|
|
1475
|
-
list: async () => parse(await fetchFn(collectionUrl
|
|
1580
|
+
list: async (params) => parse(await fetchFn(`${collectionUrl}${buildListQuery(params)}`, { headers })),
|
|
1476
1581
|
get: async (id) => parse(await fetchFn(`${collectionUrl}/${id}`, { headers })),
|
|
1477
1582
|
create: async (data) => parse(await fetchFn(collectionUrl, {
|
|
1478
1583
|
method: "POST",
|
|
@@ -1779,6 +1884,6 @@ function createSmrtCollection(definition, options) {
|
|
|
1779
1884
|
return handle;
|
|
1780
1885
|
}
|
|
1781
1886
|
//#endregion
|
|
1782
|
-
export { DEFAULT_PERSIST_DEBOUNCE_MS, SmrtWebRequestError, createDefinitionFetchers, createSmrtCollection, createSmrtWebClient, createSmrtWebEventSubscriber, createUpdateState, durableStoreNamespace, getEngineCollection, getOutboxHandle, liveInvalidation, newLocalId, offlineOutbox, persistCollection, registerDurableResource, runWrapMutation, unwrapItemResult, unwrapListResult, wipeDurableStore };
|
|
1887
|
+
export { DEFAULT_PERSIST_DEBOUNCE_MS, SmrtWebRequestError, buildListQuery, createDefinitionFetchers, createSmrtCollection, createSmrtWebClient, createSmrtWebEventSubscriber, createUpdateState, durableStoreNamespace, getEngineCollection, getOutboxHandle, liveInvalidation, newLocalId, offlineOutbox, persistCollection, registerDurableResource, registerWebMcpTools, runWrapMutation, unwrapItemResult, unwrapListResult, wipeDurableStore };
|
|
1783
1888
|
|
|
1784
1889
|
//# sourceMappingURL=index.js.map
|