@peers-app/peers-sdk 0.26.6 → 0.27.1
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 +3 -1
- package/bin/peers-package.js +13 -0
- package/define/package.json +4 -0
- package/dist/contracts/__tests__/action-dispatch.test.js +56 -76
- package/dist/contracts/__tests__/also-implements.test.d.ts +1 -0
- package/dist/contracts/__tests__/also-implements.test.js +110 -0
- package/dist/contracts/__tests__/builder.test.js +45 -0
- package/dist/contracts/__tests__/contract-resolvers.test.js +134 -4
- package/dist/contracts/__tests__/frozen-contracts.test.d.ts +1 -0
- package/dist/contracts/__tests__/frozen-contracts.test.js +191 -0
- package/dist/contracts/__tests__/generic-tool-dispatch.test.js +13 -56
- package/dist/contracts/__tests__/host-contract-client.test.d.ts +1 -0
- package/dist/contracts/__tests__/host-contract-client.test.js +793 -0
- package/dist/contracts/__tests__/host-contract-transport.test.js +15 -0
- package/dist/contracts/__tests__/registry.test.js +68 -0
- package/dist/contracts/__tests__/target-dispatch.d.ts +11 -0
- package/dist/contracts/__tests__/target-dispatch.js +24 -0
- package/dist/contracts/__tests__/tool-catalog-service.test.js +37 -1
- package/dist/contracts/__tests__/tool-catalog.test.js +12 -3
- package/dist/contracts/__tests__/validate.test.js +177 -7
- package/dist/contracts/__tests__/workflow-context.test.js +0 -11
- package/dist/contracts/builder.d.ts +16 -2
- package/dist/contracts/builder.js +44 -3
- package/dist/contracts/contract-definition-schema.d.ts +251 -0
- package/dist/contracts/contract-definition-schema.js +48 -0
- package/dist/contracts/contract-provider-router.d.ts +2 -0
- package/dist/contracts/contract-provider-router.js +5 -2
- package/dist/contracts/contract-proxy.d.ts +52 -18
- package/dist/contracts/contract-proxy.js +144 -203
- package/dist/contracts/contract-resolvers.d.ts +6 -4
- package/dist/contracts/contract-resolvers.js +32 -9
- package/dist/contracts/frozen-contracts.d.ts +59 -0
- package/dist/contracts/frozen-contracts.js +86 -0
- package/dist/contracts/host-contract-client.d.ts +134 -0
- package/dist/contracts/host-contract-client.js +396 -0
- package/dist/contracts/host-contract-transport.d.ts +2 -0
- package/dist/contracts/host-contract-transport.js +8 -0
- package/dist/contracts/index.d.ts +6 -4
- package/dist/contracts/index.js +11 -5
- package/dist/contracts/registry.d.ts +31 -1
- package/dist/contracts/registry.js +100 -6
- package/dist/contracts/system/contract-catalog-contract.d.ts +61 -6
- package/dist/contracts/system/contract-catalog-contract.js +17 -5
- package/dist/contracts/tool-catalog-service.js +26 -6
- package/dist/contracts/tool-catalog.d.ts +1 -1
- package/dist/contracts/tool-catalog.js +4 -1
- package/dist/contracts/types.d.ts +32 -2
- package/dist/contracts/validate.d.ts +16 -1
- package/dist/contracts/validate.js +139 -25
- package/dist/data/change-tracking.d.ts +17 -5
- package/dist/data/change-tracking.js +22 -6
- package/dist/data/change-tracking.test.d.ts +1 -0
- package/dist/data/change-tracking.test.js +62 -0
- package/dist/data/create-group.js +1 -1
- package/dist/data/group-members.d.ts +14 -0
- package/dist/data/group-members.js +36 -0
- package/dist/data/group-members.test.js +49 -0
- package/dist/data/orm/sql.data-source.d.ts +9 -0
- package/dist/data/orm/sql.data-source.js +47 -2
- package/dist/data/orm/sql.data-source.test.js +142 -0
- package/dist/data/orm/table-container.js +9 -9
- package/dist/data/orm/table-container.test.js +44 -0
- package/dist/data/orm/types.d.ts +41 -1
- package/dist/data/orm/types.js +159 -28
- package/dist/data/orm/types.test.js +129 -0
- package/dist/data/peer-types.d.ts +8 -62
- package/dist/data/persistent-vars.d.ts +4 -1
- package/dist/data/persistent-vars.js +7 -2
- package/dist/data/tools.d.ts +29 -221
- package/dist/define/also-implements.test.d.ts +1 -0
- package/dist/define/also-implements.test.js +123 -0
- package/dist/define/context.d.ts +77 -0
- package/dist/define/context.js +2 -0
- package/dist/define/contract-ref.d.ts +177 -0
- package/dist/define/contract-ref.js +67 -0
- package/dist/define/declare-contract.d.ts +167 -0
- package/dist/define/declare-contract.js +220 -0
- package/dist/define/declare-contract.test.d.ts +1 -0
- package/dist/define/declare-contract.test.js +573 -0
- package/dist/define/define-package.d.ts +203 -0
- package/dist/define/define-package.js +289 -0
- package/dist/define/define.test.d.ts +1 -0
- package/dist/define/define.test.js +1010 -0
- package/dist/define/empty-fields.d.ts +15 -0
- package/dist/define/empty-fields.js +134 -0
- package/dist/define/empty-fields.test.d.ts +1 -0
- package/dist/define/empty-fields.test.js +55 -0
- package/dist/define/events-observables.test.d.ts +1 -0
- package/dist/define/events-observables.test.js +1013 -0
- package/dist/define/events.d.ts +81 -0
- package/dist/define/events.js +81 -0
- package/dist/define/generated-field.d.ts +20 -0
- package/dist/define/generated-field.js +25 -0
- package/dist/define/index.d.ts +28 -0
- package/dist/define/index.js +62 -0
- package/dist/define/lazy-members.d.ts +8 -0
- package/dist/define/lazy-members.js +38 -0
- package/dist/define/manifest.d.ts +63 -0
- package/dist/define/manifest.js +653 -0
- package/dist/define/observables.d.ts +146 -0
- package/dist/define/observables.js +187 -0
- package/dist/define/serve-members.d.ts +21 -0
- package/dist/define/serve-members.js +152 -0
- package/dist/define/serve-members.test.d.ts +1 -0
- package/dist/define/serve-members.test.js +630 -0
- package/dist/define/serve.d.ts +22 -0
- package/dist/define/serve.js +58 -0
- package/dist/define/system-contracts.d.ts +41 -0
- package/dist/define/system-contracts.js +21 -0
- package/dist/define/table-handle.d.ts +13 -0
- package/dist/define/table-handle.js +36 -0
- package/dist/define/table.d.ts +128 -0
- package/dist/define/table.js +122 -0
- package/dist/define/tool.d.ts +129 -0
- package/dist/define/tool.js +91 -0
- package/dist/define/vars.d.ts +82 -0
- package/dist/define/vars.js +256 -0
- package/dist/define/vars.test.d.ts +1 -0
- package/dist/define/vars.test.js +39 -0
- package/dist/define/wire-value.d.ts +12 -0
- package/dist/define/wire-value.js +76 -0
- package/dist/define/worker-state.d.ts +15 -0
- package/dist/define/worker-state.js +45 -0
- package/dist/device/tx-encoding.test.js +10 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -1
- package/dist/invites/group-approval.js +4 -2
- package/dist/invites/group-approval.test.js +2 -0
- package/dist/observable.d.ts +42 -2
- package/dist/observable.js +25 -7
- package/dist/package-build/build-package.d.ts +60 -0
- package/dist/package-build/build-package.js +312 -0
- package/dist/package-build/build-package.test.d.ts +1 -0
- package/dist/package-build/build-package.test.js +204 -0
- package/dist/package-build/cli.d.ts +7 -0
- package/dist/package-build/cli.js +218 -0
- package/dist/package-build/contract-add.d.ts +35 -0
- package/dist/package-build/contract-add.js +97 -0
- package/dist/package-build/contract-add.test.d.ts +1 -0
- package/dist/package-build/contract-add.test.js +467 -0
- package/dist/package-build/contract-source.d.ts +59 -0
- package/dist/package-build/contract-source.js +471 -0
- package/dist/package-build/freeze-contracts.d.ts +56 -0
- package/dist/package-build/freeze-contracts.js +198 -0
- package/dist/package-build/freeze-contracts.test.d.ts +1 -0
- package/dist/package-build/freeze-contracts.test.js +355 -0
- package/dist/package-installer/package-creator.d.ts +4 -0
- package/dist/package-installer/package-creator.js +57 -16
- package/dist/package-installer/package-creator.test.js +86 -0
- package/dist/package-loader/contract-members.d.ts +53 -0
- package/dist/package-loader/contract-members.js +31 -0
- package/dist/package-loader/contract-package-loader.js +5 -1
- package/dist/package-loader/contract-package-loader.test.js +11 -0
- package/dist/package-loader/host-authority-loading.test.js +9 -5
- package/dist/package-loader/index.d.ts +1 -0
- package/dist/package-loader/index.js +1 -0
- package/dist/package-loader/isolated-artifact.d.ts +80 -17
- package/dist/package-loader/isolated-artifact.js +257 -51
- package/dist/package-loader/isolated-artifact.test.js +294 -21
- package/dist/package-loader/isolated-runtime.d.ts +22 -7
- package/dist/package-loader/isolated-table-gateway.js +30 -11
- package/dist/package-loader/isolated-table-gateway.test.js +243 -0
- package/dist/package-loader/package-loader.d.ts +8 -1
- package/dist/package-loader/package-loader.js +39 -20
- package/dist/package-loader/package-loader.test.js +47 -0
- package/dist/peer-id.d.ts +25 -0
- package/dist/peer-id.js +99 -0
- package/dist/serial-json.compat.test.d.ts +1 -0
- package/dist/serial-json.compat.test.js +369 -0
- package/dist/serial-json.d.ts +41 -5
- package/dist/serial-json.js +216 -157
- package/dist/serial-json.oracle.d.ts +21 -0
- package/dist/serial-json.oracle.js +222 -0
- package/dist/serial-json.test.js +315 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/mock-host-contract.d.ts +123 -0
- package/dist/testing/mock-host-contract.js +434 -0
- package/dist/testing/mock-host-contract.test.d.ts +1 -0
- package/dist/testing/mock-host-contract.test.js +782 -0
- package/dist/types/field-type.d.ts +35 -18
- package/dist/types/field-type.js +4 -1
- package/dist/types/zod-types.d.ts +9 -0
- package/dist/types/zod-types.js +8 -2
- package/dist/utils.d.ts +22 -25
- package/dist/utils.js +45 -92
- package/dist/utils.test.js +36 -0
- package/package.json +9 -2
- package/testing/package.json +4 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.startPackage = startPackage;
|
|
4
|
+
exports.servePackage = servePackage;
|
|
5
|
+
const events_1 = require("./events");
|
|
6
|
+
const observables_1 = require("./observables");
|
|
7
|
+
const serve_members_1 = require("./serve-members");
|
|
8
|
+
const tool_1 = require("./tool");
|
|
9
|
+
const vars_1 = require("./vars");
|
|
10
|
+
const worker_state_1 = require("./worker-state");
|
|
11
|
+
/**
|
|
12
|
+
* Start a package's own state with the worker's context: load its variables, tell its events
|
|
13
|
+
* which contracts they're on, run its `pkg.onStart` hooks, and publish its in-memory
|
|
14
|
+
* observables. `servePackage` runs this when the worker starts; call it yourself only in tests,
|
|
15
|
+
* with a fake `ctx`.
|
|
16
|
+
*
|
|
17
|
+
* @param definition - The package's default export from `src/package.ts`.
|
|
18
|
+
* @param ctx - The worker's context, bound to no call.
|
|
19
|
+
*/
|
|
20
|
+
async function startPackage(definition, ctx) {
|
|
21
|
+
(0, worker_state_1.setPackageContext)(ctx);
|
|
22
|
+
(0, events_1.bindPackageEvents)(definition.provides);
|
|
23
|
+
// An in-memory source may derive from a variable. Load stored values before taking its
|
|
24
|
+
// initial snapshot, so the first read cannot race a publication of the variable's default.
|
|
25
|
+
await (0, vars_1.bindPackageVars)(ctx);
|
|
26
|
+
// After the variables load, before the observables' first publish: a value a hook sets is
|
|
27
|
+
// the first one readers see.
|
|
28
|
+
for (const hook of definition.startHooks ?? []) {
|
|
29
|
+
await hook(ctx);
|
|
30
|
+
}
|
|
31
|
+
await (0, observables_1.servePackageObservables)(definition.provides, ctx);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Register every provided tool's handler with the Runtime, serve each provided contract's
|
|
35
|
+
* tables, observables, and events from the worker (guest API 3: every consumer call and
|
|
36
|
+
* outgoing message goes through the package), and start the package's variables, events, and
|
|
37
|
+
* observables when the worker starts, before any call. `peers-package build` generates the
|
|
38
|
+
* call; packages don't call this themselves.
|
|
39
|
+
*
|
|
40
|
+
* @param definition - The package's default export from `src/package.ts`.
|
|
41
|
+
*/
|
|
42
|
+
function servePackage(definition) {
|
|
43
|
+
const worker = globalThis;
|
|
44
|
+
const register = worker.registerContractTool;
|
|
45
|
+
const registerHandler = worker.registerHandler;
|
|
46
|
+
if (typeof register !== "function" ||
|
|
47
|
+
typeof registerHandler !== "function" ||
|
|
48
|
+
typeof worker.onWorkerStart !== "function") {
|
|
49
|
+
throw new Error("servePackage runs inside an isolated package worker");
|
|
50
|
+
}
|
|
51
|
+
worker.onWorkerStart((ctx) => startPackage(definition, ctx));
|
|
52
|
+
for (const contract of definition.provides) {
|
|
53
|
+
for (const tool of contract.tools) {
|
|
54
|
+
register(contract.contractId, contract.version, tool.definition.name, (args, ctx) => (0, tool_1.runPackageTool)(tool, args, ctx));
|
|
55
|
+
}
|
|
56
|
+
(0, serve_members_1.serveContractMembers)(contract, registerHandler);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System HTTP v1: requests to the hosts in the package's `network.allowedHosts`. Declare it
|
|
3
|
+
* with `pkg.consumes(SystemHttp)`.
|
|
4
|
+
*/
|
|
5
|
+
export declare const SystemHttp: import("./contract-ref").IContractRef<{
|
|
6
|
+
request(input: import("../contracts/system/http-contract").IHttpRequestInput): Promise<import("../contracts/system/http-contract").IHttpRequestResult>;
|
|
7
|
+
cancel(input: import("../contracts/system/http-contract").IHttpCancelInput): Promise<{
|
|
8
|
+
cancelled: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
describePolicy(input: {
|
|
11
|
+
packageId: string;
|
|
12
|
+
}): Promise<import("../contracts/system/http-contract").IHttpPolicyInfo>;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* System Scheduler v1: run one of the package's own tools later, on this device. Declare it
|
|
16
|
+
* with `pkg.consumes(SystemScheduler)`, optionally.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SystemScheduler: import("./contract-ref").IContractRef<{
|
|
19
|
+
schedule(input: import("../contracts/system/scheduler-contract").IScheduleInput): Promise<{
|
|
20
|
+
scheduleId: string;
|
|
21
|
+
}>;
|
|
22
|
+
cancel(input: {
|
|
23
|
+
scheduleId: string;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
cancelled: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
list(): Promise<{
|
|
28
|
+
schedules: import("../contracts/system/scheduler-contract").IScheduleInfo[];
|
|
29
|
+
}>;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* System Contract Catalog v1: every installed provider of a contract, not just the active one,
|
|
33
|
+
* for a hub that calls them all (Voice Hub and its skills). Declare it with
|
|
34
|
+
* `pkg.consumes(SystemContractCatalog)`.
|
|
35
|
+
*/
|
|
36
|
+
export declare const SystemContractCatalog: import("./contract-ref").IContractRef<{
|
|
37
|
+
listContractProviders(input: import("../contracts/system/contract-catalog-contract").IListContractProvidersInput): Promise<{
|
|
38
|
+
providers: import("../contracts/system/contract-catalog-contract").IContractProviderInfo[];
|
|
39
|
+
}>;
|
|
40
|
+
getContractDefinition(input: import("../contracts/system/contract-catalog-contract").IGetContractDefinitionInput): Promise<import("../contracts/system/contract-catalog-contract").IContractDefinitionWithPolicy>;
|
|
41
|
+
}>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SystemContractCatalog = exports.SystemScheduler = exports.SystemHttp = void 0;
|
|
4
|
+
const system_ids_1 = require("../system-ids");
|
|
5
|
+
const contract_ref_1 = require("./contract-ref");
|
|
6
|
+
/**
|
|
7
|
+
* System HTTP v1: requests to the hosts in the package's `network.allowedHosts`. Declare it
|
|
8
|
+
* with `pkg.consumes(SystemHttp)`.
|
|
9
|
+
*/
|
|
10
|
+
exports.SystemHttp = (0, contract_ref_1.contractRef)(system_ids_1.httpContractId, 1);
|
|
11
|
+
/**
|
|
12
|
+
* System Scheduler v1: run one of the package's own tools later, on this device. Declare it
|
|
13
|
+
* with `pkg.consumes(SystemScheduler)`, optionally.
|
|
14
|
+
*/
|
|
15
|
+
exports.SystemScheduler = (0, contract_ref_1.contractRef)(system_ids_1.schedulerContractId, 1);
|
|
16
|
+
/**
|
|
17
|
+
* System Contract Catalog v1: every installed provider of a contract, not just the active one,
|
|
18
|
+
* for a hub that calls them all (Voice Hub and its skills). Declare it with
|
|
19
|
+
* `pkg.consumes(SystemContractCatalog)`.
|
|
20
|
+
*/
|
|
21
|
+
exports.SystemContractCatalog = (0, contract_ref_1.contractRef)(system_ids_1.contractCatalogContractId, 1);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IsolatedContractTableOperation } from "../package-loader/isolated-runtime";
|
|
2
|
+
import type { IPackageTableHandle } from "./table";
|
|
3
|
+
/** Sends one table operation with its arguments and resolves with the host's reply. */
|
|
4
|
+
export type PackageTableCall = (operation: IsolatedContractTableOperation, args: unknown[]) => Promise<unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* A consumer's table methods over one way of calling the host: a consumed contract's
|
|
7
|
+
* (`News(ctx).tables.Feeds`) or a UI client's (`hostContract`). Replies are normalized the same
|
|
8
|
+
* way for both: a missing row is `undefined`, a list is an array. Saved records are copied for
|
|
9
|
+
* the bridge (undefined fields omitted); the providing package's table parses a `save`.
|
|
10
|
+
*
|
|
11
|
+
* @param call - Sends an operation to the table.
|
|
12
|
+
*/
|
|
13
|
+
export declare function packageTableMethods<T extends Record<string, any>, TInput = T>(call: PackageTableCall): IPackageTableHandle<T, TInput>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.packageTableMethods = packageTableMethods;
|
|
4
|
+
const wire_value_1 = require("./wire-value");
|
|
5
|
+
/**
|
|
6
|
+
* A consumer's table methods over one way of calling the host: a consumed contract's
|
|
7
|
+
* (`News(ctx).tables.Feeds`) or a UI client's (`hostContract`). Replies are normalized the same
|
|
8
|
+
* way for both: a missing row is `undefined`, a list is an array. Saved records are copied for
|
|
9
|
+
* the bridge (undefined fields omitted); the providing package's table parses a `save`.
|
|
10
|
+
*
|
|
11
|
+
* @param call - Sends an operation to the table.
|
|
12
|
+
*/
|
|
13
|
+
function packageTableMethods(call) {
|
|
14
|
+
return {
|
|
15
|
+
async get(recordId) {
|
|
16
|
+
return ((await call("get", [recordId])) ?? undefined);
|
|
17
|
+
},
|
|
18
|
+
async list(filter = {}, options = {}) {
|
|
19
|
+
const rows = await call("list", [filter, options]);
|
|
20
|
+
return (Array.isArray(rows) ? rows : []);
|
|
21
|
+
},
|
|
22
|
+
async count(filter = {}) {
|
|
23
|
+
return Number(await call("count", [filter]));
|
|
24
|
+
},
|
|
25
|
+
async findOne(filter = {}) {
|
|
26
|
+
return ((await call("findOne", [filter])) ?? undefined);
|
|
27
|
+
},
|
|
28
|
+
async save(record) {
|
|
29
|
+
// A field left undefined is omitted (stored unset); the bridge carries no undefined.
|
|
30
|
+
return (await call("save", [(0, wire_value_1.snapshotPackageValue)(record)]));
|
|
31
|
+
},
|
|
32
|
+
async delete(recordId) {
|
|
33
|
+
await call("delete", [recordId]);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { DataFilter, IDataQueryParams } from "../data/orm/data-query";
|
|
3
|
+
import type { IDataChangedEvent } from "../data/orm/subscribable.data-source";
|
|
4
|
+
import type { IPackageContext } from "./context";
|
|
5
|
+
/** What {@link defineTable} takes. */
|
|
6
|
+
export interface IPackageTableDefinition<S extends z.AnyZodObject = z.AnyZodObject> {
|
|
7
|
+
/** Table name, unique in the package. Other packages reach it only through a contract. */
|
|
8
|
+
name: string;
|
|
9
|
+
/** What the rows are. */
|
|
10
|
+
description: string;
|
|
11
|
+
/** The field that holds each row's id. */
|
|
12
|
+
primaryKeyName: Extract<keyof z.infer<S>, string>;
|
|
13
|
+
/** The row schema. The manifest carries its fields as Peers IR. */
|
|
14
|
+
schema: S;
|
|
15
|
+
/** Raise when the fields change, so every device adopts the new definition. */
|
|
16
|
+
versionNumber?: number;
|
|
17
|
+
/** Keep rows on this device only. They never sync. */
|
|
18
|
+
localOnly?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The table's class, to override its methods: `Feeds(ctx)` returns one, and so does every
|
|
21
|
+
* consumer call through a contract, so an override is always the logic in use. Defaults to
|
|
22
|
+
* {@link PackageTable}.
|
|
23
|
+
*/
|
|
24
|
+
tableClass?: PackageTableClass<S>;
|
|
25
|
+
}
|
|
26
|
+
/** A class extending {@link PackageTable} for a table's schema. */
|
|
27
|
+
export type PackageTableClass<S extends z.AnyZodObject = z.AnyZodObject> = new (ctx: IPackageContext, definition: Readonly<IPackageTableDefinition<S>>) => PackageTable<S>;
|
|
28
|
+
/** One table's methods, bound to one handler call. */
|
|
29
|
+
export interface IPackageTableHandle<T extends Record<string, any>, TInput = T> {
|
|
30
|
+
/** One row by id, or `undefined`. */
|
|
31
|
+
get(recordId: string): Promise<T | undefined>;
|
|
32
|
+
/** Rows matching `filter` (all rows when omitted), with paging and sorting options. */
|
|
33
|
+
list(filter?: DataFilter<T>, options?: IDataQueryParams<T>): Promise<T[]>;
|
|
34
|
+
/** How many rows match `filter`. */
|
|
35
|
+
count(filter?: DataFilter<T>): Promise<number>;
|
|
36
|
+
/** The first row matching `filter`, or `undefined`. */
|
|
37
|
+
findOne(filter?: DataFilter<T>): Promise<T | undefined>;
|
|
38
|
+
/**
|
|
39
|
+
* Insert or replace a row. It reaches the providing package's table, which parses it with the
|
|
40
|
+
* schema first (so defaults and transforms apply), through any override its class makes.
|
|
41
|
+
*/
|
|
42
|
+
save(record: TInput): Promise<T>;
|
|
43
|
+
/** Delete a row by id. */
|
|
44
|
+
delete(recordId: string): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One of the package's tables, for one call: what `Feeds(ctx)` returns, and what answers a
|
|
48
|
+
* consumer's call through a contract (from a UI or another package). It runs in the provider's
|
|
49
|
+
* worker, never in the host.
|
|
50
|
+
*
|
|
51
|
+
* Subclass it to change a method, as with any table class, and pass the class to
|
|
52
|
+
* {@link defineTable}. The package's own handlers and its consumers then go through the same
|
|
53
|
+
* methods. `super` reaches Peers: `save` parses the row with the schema (so defaults and
|
|
54
|
+
* transforms apply), then stores it. Rows read from storage leave out empty optional fields,
|
|
55
|
+
* including nested fields written by an older build.
|
|
56
|
+
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* class FeedsTable extends PackageTable<typeof feedSchema> {
|
|
59
|
+
* async save(feed: z.input<typeof feedSchema>) {
|
|
60
|
+
* return super.save({ ...feed, title: feed.title.trim() });
|
|
61
|
+
* }
|
|
62
|
+
* }
|
|
63
|
+
* export const Feeds = defineTable({ name: "Feeds", ..., schema: feedSchema, tableClass: FeedsTable });
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* Consumers can call `get`, `list`, `count`, `findOne`, `save`, and `delete`; other methods on a
|
|
67
|
+
* subclass stay private to the package. `this.ctx` is the call's context, carrying its caller.
|
|
68
|
+
*/
|
|
69
|
+
export declare class PackageTable<S extends z.AnyZodObject = z.AnyZodObject> implements IPackageTableHandle<z.infer<S>, z.input<S>> {
|
|
70
|
+
readonly ctx: IPackageContext;
|
|
71
|
+
readonly definition: Readonly<IPackageTableDefinition<S>>;
|
|
72
|
+
/**
|
|
73
|
+
* @param ctx - The call this instance serves.
|
|
74
|
+
* @param definition - The table's declaration.
|
|
75
|
+
*/
|
|
76
|
+
constructor(ctx: IPackageContext, definition: Readonly<IPackageTableDefinition<S>>);
|
|
77
|
+
/** Send one operation to storage and clean rows before provider code receives them. */
|
|
78
|
+
private store;
|
|
79
|
+
/** One row by id, or `undefined`. */
|
|
80
|
+
get(recordId: string): Promise<z.infer<S> | undefined>;
|
|
81
|
+
/** Rows matching `filter` (all rows when omitted), with paging and sorting options. */
|
|
82
|
+
list(filter?: DataFilter<z.infer<S>>, options?: IDataQueryParams<z.infer<S>>): Promise<z.infer<S>[]>;
|
|
83
|
+
/** How many rows match `filter`. */
|
|
84
|
+
count(filter?: DataFilter<z.infer<S>>): Promise<number>;
|
|
85
|
+
/** The first row matching `filter`, or `undefined`. */
|
|
86
|
+
findOne(filter?: DataFilter<z.infer<S>>): Promise<z.infer<S> | undefined>;
|
|
87
|
+
/**
|
|
88
|
+
* Insert or replace a row, parsed with the schema first so defaults and transforms apply. An
|
|
89
|
+
* optional field that is `null` or `undefined` is left out, so it's stored unset; a field with
|
|
90
|
+
* a default gets the default. Resolves to the row as stored.
|
|
91
|
+
*/
|
|
92
|
+
save(record: z.input<S>): Promise<z.infer<S>>;
|
|
93
|
+
/** Delete a row by id. */
|
|
94
|
+
delete(recordId: string): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* One change on its way to one subscriber of the table's `dataChanged`, with `this.ctx`
|
|
97
|
+
* carrying that subscriber. Return it to send it, a changed copy to send that instead, or
|
|
98
|
+
* `undefined` to drop it. Sends it unchanged by default.
|
|
99
|
+
*
|
|
100
|
+
* @param event - The change: the row and whether it was inserted, updated, or deleted.
|
|
101
|
+
*/
|
|
102
|
+
dataChanged(event: IDataChangedEvent<z.infer<S>>): IDataChangedEvent<z.infer<S>> | undefined | Promise<IDataChangedEvent<z.infer<S>> | undefined>;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A table declared with {@link defineTable}, named `N`. Call it with a handler's `ctx` to use it:
|
|
106
|
+
* `Headlines(ctx).list({ feedId })`. `H` is the table's class instance.
|
|
107
|
+
*/
|
|
108
|
+
export interface IPackageTable<S extends z.AnyZodObject = z.AnyZodObject, N extends string = string, H extends PackageTable<S> = PackageTable<S>> {
|
|
109
|
+
(ctx: IPackageContext): H;
|
|
110
|
+
/** Marks a table declaration. */
|
|
111
|
+
readonly kind: "table";
|
|
112
|
+
/** What was passed to {@link defineTable}. */
|
|
113
|
+
readonly definition: Readonly<IPackageTableDefinition<S>> & {
|
|
114
|
+
readonly name: N;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Declare one of the package's tables from a zod schema.
|
|
119
|
+
*
|
|
120
|
+
* List it in a contract's `tables` (`defineContract`) to expose it, or on `pkg.tables` to keep
|
|
121
|
+
* it private to the package. Pass `tableClass` to override its methods ({@link PackageTable}).
|
|
122
|
+
*
|
|
123
|
+
* @param definition - Name, description, primary key, row schema, and optional class.
|
|
124
|
+
*/
|
|
125
|
+
export declare function defineTable<S extends z.AnyZodObject, N extends string, C extends PackageTableClass<S> = typeof PackageTable<S>>(definition: IPackageTableDefinition<S> & {
|
|
126
|
+
name: N;
|
|
127
|
+
tableClass?: C;
|
|
128
|
+
}): IPackageTable<S, N, InstanceType<C>>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PackageTable = void 0;
|
|
4
|
+
exports.defineTable = defineTable;
|
|
5
|
+
const empty_fields_1 = require("./empty-fields");
|
|
6
|
+
const wire_value_1 = require("./wire-value");
|
|
7
|
+
/**
|
|
8
|
+
* One of the package's tables, for one call: what `Feeds(ctx)` returns, and what answers a
|
|
9
|
+
* consumer's call through a contract (from a UI or another package). It runs in the provider's
|
|
10
|
+
* worker, never in the host.
|
|
11
|
+
*
|
|
12
|
+
* Subclass it to change a method, as with any table class, and pass the class to
|
|
13
|
+
* {@link defineTable}. The package's own handlers and its consumers then go through the same
|
|
14
|
+
* methods. `super` reaches Peers: `save` parses the row with the schema (so defaults and
|
|
15
|
+
* transforms apply), then stores it. Rows read from storage leave out empty optional fields,
|
|
16
|
+
* including nested fields written by an older build.
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* class FeedsTable extends PackageTable<typeof feedSchema> {
|
|
20
|
+
* async save(feed: z.input<typeof feedSchema>) {
|
|
21
|
+
* return super.save({ ...feed, title: feed.title.trim() });
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* export const Feeds = defineTable({ name: "Feeds", ..., schema: feedSchema, tableClass: FeedsTable });
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Consumers can call `get`, `list`, `count`, `findOne`, `save`, and `delete`; other methods on a
|
|
28
|
+
* subclass stay private to the package. `this.ctx` is the call's context, carrying its caller.
|
|
29
|
+
*/
|
|
30
|
+
class PackageTable {
|
|
31
|
+
ctx;
|
|
32
|
+
definition;
|
|
33
|
+
/**
|
|
34
|
+
* @param ctx - The call this instance serves.
|
|
35
|
+
* @param definition - The table's declaration.
|
|
36
|
+
*/
|
|
37
|
+
constructor(ctx, definition) {
|
|
38
|
+
this.ctx = ctx;
|
|
39
|
+
this.definition = definition;
|
|
40
|
+
}
|
|
41
|
+
/** Send one operation to storage and clean rows before provider code receives them. */
|
|
42
|
+
async store(operation, args) {
|
|
43
|
+
const result = await this.ctx.callOwnedTable(this.definition.name, operation, args);
|
|
44
|
+
const { schema } = this.definition;
|
|
45
|
+
if (operation === "list" && Array.isArray(result)) {
|
|
46
|
+
return result.map((row) => (0, empty_fields_1.withoutEmptySchemaFields)(schema, row));
|
|
47
|
+
}
|
|
48
|
+
if (operation === "get" || operation === "findOne" || operation === "save") {
|
|
49
|
+
return (0, empty_fields_1.withoutEmptySchemaFields)(schema, result);
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
/** One row by id, or `undefined`. */
|
|
54
|
+
async get(recordId) {
|
|
55
|
+
return ((await this.store("get", [recordId])) ?? undefined);
|
|
56
|
+
}
|
|
57
|
+
/** Rows matching `filter` (all rows when omitted), with paging and sorting options. */
|
|
58
|
+
async list(filter = {}, options = {}) {
|
|
59
|
+
const rows = await this.store("list", [filter, options]);
|
|
60
|
+
return (Array.isArray(rows) ? rows : []);
|
|
61
|
+
}
|
|
62
|
+
/** How many rows match `filter`. */
|
|
63
|
+
async count(filter = {}) {
|
|
64
|
+
return Number(await this.store("count", [filter]));
|
|
65
|
+
}
|
|
66
|
+
/** The first row matching `filter`, or `undefined`. */
|
|
67
|
+
async findOne(filter = {}) {
|
|
68
|
+
return ((await this.store("findOne", [filter])) ?? undefined);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Insert or replace a row, parsed with the schema first so defaults and transforms apply. An
|
|
72
|
+
* optional field that is `null` or `undefined` is left out, so it's stored unset; a field with
|
|
73
|
+
* a default gets the default. Resolves to the row as stored.
|
|
74
|
+
*/
|
|
75
|
+
async save(record) {
|
|
76
|
+
const { schema } = this.definition;
|
|
77
|
+
const row = (0, wire_value_1.snapshotPackageValue)(schema.parse((0, empty_fields_1.withoutEmptySchemaFields)(schema, record)));
|
|
78
|
+
return (await this.store("save", [row]));
|
|
79
|
+
}
|
|
80
|
+
/** Delete a row by id. */
|
|
81
|
+
async delete(recordId) {
|
|
82
|
+
await this.store("delete", [recordId]);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* One change on its way to one subscriber of the table's `dataChanged`, with `this.ctx`
|
|
86
|
+
* carrying that subscriber. Return it to send it, a changed copy to send that instead, or
|
|
87
|
+
* `undefined` to drop it. Sends it unchanged by default.
|
|
88
|
+
*
|
|
89
|
+
* @param event - The change: the row and whether it was inserted, updated, or deleted.
|
|
90
|
+
*/
|
|
91
|
+
dataChanged(event) {
|
|
92
|
+
return event;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.PackageTable = PackageTable;
|
|
96
|
+
/**
|
|
97
|
+
* Declare one of the package's tables from a zod schema.
|
|
98
|
+
*
|
|
99
|
+
* List it in a contract's `tables` (`defineContract`) to expose it, or on `pkg.tables` to keep
|
|
100
|
+
* it private to the package. Pass `tableClass` to override its methods ({@link PackageTable}).
|
|
101
|
+
*
|
|
102
|
+
* @param definition - Name, description, primary key, row schema, and optional class.
|
|
103
|
+
*/
|
|
104
|
+
function defineTable(definition) {
|
|
105
|
+
if (typeof definition.name !== "string" || definition.name.trim() === "") {
|
|
106
|
+
throw new Error("defineTable requires a name");
|
|
107
|
+
}
|
|
108
|
+
if (!definition.schema || typeof definition.schema.shape !== "object") {
|
|
109
|
+
throw new Error(`Table ${definition.name} needs a zod object schema`);
|
|
110
|
+
}
|
|
111
|
+
if (!(definition.primaryKeyName in definition.schema.shape)) {
|
|
112
|
+
throw new Error(`Table ${definition.name} primary key "${definition.primaryKeyName}" is not in its schema`);
|
|
113
|
+
}
|
|
114
|
+
const TableClass = definition.tableClass ?? PackageTable;
|
|
115
|
+
if (typeof TableClass !== "function" ||
|
|
116
|
+
!(TableClass.prototype instanceof PackageTable || TableClass === PackageTable)) {
|
|
117
|
+
throw new Error(`Table ${definition.name} tableClass must extend PackageTable`);
|
|
118
|
+
}
|
|
119
|
+
const frozen = Object.freeze({ ...definition });
|
|
120
|
+
const table = (ctx) => new TableClass(ctx, frozen);
|
|
121
|
+
return Object.freeze(Object.assign(table, { kind: "table", definition: frozen }));
|
|
122
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { IField } from "../types/field-type";
|
|
3
|
+
import type { IPackageContext } from "./context";
|
|
4
|
+
/** Settings every tool declaration shares. */
|
|
5
|
+
export interface IPackageToolOptions {
|
|
6
|
+
/** Tool name, unique on its contract. */
|
|
7
|
+
name: string;
|
|
8
|
+
/** What the tool does, for people and agents choosing it. */
|
|
9
|
+
usageDescription: string;
|
|
10
|
+
/** Access level (0–100) a caller needs. Use the {@link AccessLevel} constants. */
|
|
11
|
+
accessLevel: number;
|
|
12
|
+
/** Keep the caller's level for this tool's outgoing calls (`preserve`), or cap it (`cap`). */
|
|
13
|
+
authorityPropagation?: "cap" | "preserve";
|
|
14
|
+
/** A package-internal target, left out of the public contract definition. */
|
|
15
|
+
private?: boolean;
|
|
16
|
+
/** Level a caller on another device needs. Omit to keep the tool local to this device. */
|
|
17
|
+
remoteAccessLevel?: number;
|
|
18
|
+
/** Per-call deadline in milliseconds (at most 120 000). */
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
}
|
|
21
|
+
/** A tool declared with zod schemas. Its input is parsed with `inputSchema` before `handler` runs. */
|
|
22
|
+
export interface IPackageToolSchemaDefinition<I extends z.AnyZodObject = z.AnyZodObject, O extends z.AnyZodObject = z.AnyZodObject> extends IPackageToolOptions {
|
|
23
|
+
/** Input schema. The manifest carries its fields as Peers IR. */
|
|
24
|
+
inputSchema: I;
|
|
25
|
+
/** Output schema. It describes the result; the result is not parsed with it. */
|
|
26
|
+
outputSchema: O;
|
|
27
|
+
/** Runs the call. `ctx` carries the host calls for this call's caller. */
|
|
28
|
+
handler: (args: z.output<I>, ctx: IPackageContext) => z.output<O> | Promise<z.output<O>>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A tool declared directly in Peers IR, for a contract defined elsewhere (Voice Skill, for
|
|
32
|
+
* example). Its input reaches `handler` as sent.
|
|
33
|
+
*/
|
|
34
|
+
export interface IPackageToolFieldsDefinition extends IPackageToolOptions {
|
|
35
|
+
/** Input fields. */
|
|
36
|
+
inputFields: IField[];
|
|
37
|
+
/** Output fields. */
|
|
38
|
+
outputFields: IField[];
|
|
39
|
+
/** Runs the call. `ctx` carries the host calls for this call's caller. */
|
|
40
|
+
handler: (args: unknown, ctx: IPackageContext) => unknown;
|
|
41
|
+
}
|
|
42
|
+
/** Either form of tool declaration. */
|
|
43
|
+
export type PackageToolDefinition = IPackageToolSchemaDefinition | IPackageToolFieldsDefinition;
|
|
44
|
+
/** A tool's shape from zod schemas, without its handler: what {@link declareTool} takes. */
|
|
45
|
+
export type IPackageToolSchemaShape<I extends z.AnyZodObject = z.AnyZodObject, O extends z.AnyZodObject = z.AnyZodObject> = Omit<IPackageToolSchemaDefinition<I, O>, "handler">;
|
|
46
|
+
/** A tool's shape in Peers IR, without its handler: what {@link declareTool} takes. */
|
|
47
|
+
export type IPackageToolFieldsShape = Omit<IPackageToolFieldsDefinition, "handler">;
|
|
48
|
+
/** Either form of tool shape. */
|
|
49
|
+
export type PackageToolShape = IPackageToolSchemaShape | IPackageToolFieldsShape;
|
|
50
|
+
/**
|
|
51
|
+
* A tool's shape, from {@link declareTool}: what a contract declaration lists, with no handler.
|
|
52
|
+
* `I` and `O` are what a caller sends and receives, and `N` is its name; {@link contractRef}
|
|
53
|
+
* uses them to type calls. `P` is the parsed input a handler (or a mock host's fake) receives
|
|
54
|
+
* after defaults and transforms.
|
|
55
|
+
*/
|
|
56
|
+
export interface IPackageToolDeclaration<I = unknown, O = unknown, N extends string = string, P = unknown> {
|
|
57
|
+
/** Marks a tool. */
|
|
58
|
+
readonly kind: "tool";
|
|
59
|
+
/** What was passed to {@link declareTool} (or {@link defineTool}). */
|
|
60
|
+
readonly definition: Readonly<PackageToolShape> & {
|
|
61
|
+
readonly name: N;
|
|
62
|
+
};
|
|
63
|
+
/** Type-only: what a caller sends and receives. Always undefined at runtime. */
|
|
64
|
+
readonly types?: {
|
|
65
|
+
input: I;
|
|
66
|
+
output: O;
|
|
67
|
+
parsedInput: P;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** A tool declared with {@link defineTool}: its shape and its handler. */
|
|
71
|
+
export interface IPackageTool<I = unknown, O = unknown, N extends string = string, P = unknown> extends IPackageToolDeclaration<I, O, N, P> {
|
|
72
|
+
/** What was passed to {@link defineTool}. */
|
|
73
|
+
readonly definition: Readonly<PackageToolDefinition> & {
|
|
74
|
+
readonly name: N;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Declare a tool from zod schemas: its shape and its handler together. The handler gets the
|
|
79
|
+
* input as `inputSchema` parses it.
|
|
80
|
+
*
|
|
81
|
+
* @param definition - Name, description, access level, schemas, and handler.
|
|
82
|
+
*/
|
|
83
|
+
export declare function defineTool<I extends z.AnyZodObject, O extends z.AnyZodObject, N extends string>(definition: IPackageToolSchemaDefinition<I, O> & {
|
|
84
|
+
name: N;
|
|
85
|
+
}): IPackageTool<z.input<I>, z.output<O>, NoInfer<N>, z.output<I>>;
|
|
86
|
+
/**
|
|
87
|
+
* Declare a tool from Peers IR fields, for a contract defined elsewhere. The handler gets the
|
|
88
|
+
* input as sent.
|
|
89
|
+
*
|
|
90
|
+
* @param definition - Name, description, access level, IR fields, and handler.
|
|
91
|
+
*/
|
|
92
|
+
export declare function defineTool<N extends string>(definition: IPackageToolFieldsDefinition & {
|
|
93
|
+
name: N;
|
|
94
|
+
}): IPackageTool<unknown, unknown, NoInfer<N>>;
|
|
95
|
+
/**
|
|
96
|
+
* Declare a tool's shape from zod schemas, with no handler, for a contract declared with
|
|
97
|
+
* `declareContract`: each package that implements the contract supplies the handler.
|
|
98
|
+
*
|
|
99
|
+
* @param definition - Name, description, access level, and schemas.
|
|
100
|
+
*/
|
|
101
|
+
export declare function declareTool<I extends z.AnyZodObject, O extends z.AnyZodObject, N extends string>(definition: IPackageToolSchemaShape<I, O> & {
|
|
102
|
+
name: N;
|
|
103
|
+
}): IPackageToolDeclaration<z.input<I>, z.output<O>, NoInfer<N>, z.output<I>>;
|
|
104
|
+
/**
|
|
105
|
+
* Declare a tool's shape in Peers IR fields, with no handler. An implementation's handler gets
|
|
106
|
+
* the input as sent.
|
|
107
|
+
*
|
|
108
|
+
* @param definition - Name, description, access level, and IR fields.
|
|
109
|
+
*/
|
|
110
|
+
export declare function declareTool<N extends string>(definition: IPackageToolFieldsShape & {
|
|
111
|
+
name: N;
|
|
112
|
+
}): IPackageToolDeclaration<unknown, unknown, NoInfer<N>>;
|
|
113
|
+
/**
|
|
114
|
+
* True when `tool` has a handler: from {@link defineTool}, not {@link declareTool}.
|
|
115
|
+
*
|
|
116
|
+
* @param tool - A tool from either.
|
|
117
|
+
*/
|
|
118
|
+
export declare function isImplementedTool(tool: IPackageToolDeclaration): tool is IPackageTool;
|
|
119
|
+
/**
|
|
120
|
+
* Run a tool the way the Runtime does: parse the input with its schema, call the handler, and
|
|
121
|
+
* snapshot what it returns as the bridge carries it. An optional field that is `null` or
|
|
122
|
+
* `undefined` is left out, in the input before parsing and in the result. Handy in tests with a
|
|
123
|
+
* fake `ctx`.
|
|
124
|
+
*
|
|
125
|
+
* @param tool - A tool from {@link defineTool}.
|
|
126
|
+
* @param args - The call's arguments, as a caller sends them.
|
|
127
|
+
* @param ctx - The host calls for this call.
|
|
128
|
+
*/
|
|
129
|
+
export declare function runPackageTool<I, O>(tool: IPackageTool<I, O, string, any>, args: unknown, ctx: IPackageContext): Promise<O>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineTool = defineTool;
|
|
4
|
+
exports.declareTool = declareTool;
|
|
5
|
+
exports.isImplementedTool = isImplementedTool;
|
|
6
|
+
exports.runPackageTool = runPackageTool;
|
|
7
|
+
const empty_fields_1 = require("./empty-fields");
|
|
8
|
+
const wire_value_1 = require("./wire-value");
|
|
9
|
+
/**
|
|
10
|
+
* Declare a tool: its shape and its handler together.
|
|
11
|
+
*
|
|
12
|
+
* @param definition - Name, description, access level, schemas (or IR fields), and handler.
|
|
13
|
+
*/
|
|
14
|
+
function defineTool(definition) {
|
|
15
|
+
checkToolShape(definition, "defineTool");
|
|
16
|
+
if (typeof definition.handler !== "function") {
|
|
17
|
+
throw new Error(`Tool ${definition.name} needs a handler`);
|
|
18
|
+
}
|
|
19
|
+
return Object.freeze({ kind: "tool", definition: Object.freeze({ ...definition }) });
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Declare a tool's shape, with no handler.
|
|
23
|
+
*
|
|
24
|
+
* @param definition - Name, description, access level, and schemas (or IR fields).
|
|
25
|
+
*/
|
|
26
|
+
function declareTool(definition) {
|
|
27
|
+
checkToolShape(definition, "declareTool");
|
|
28
|
+
if ("handler" in definition) {
|
|
29
|
+
throw new Error(`Tool ${definition.name}: declareTool takes no handler; implementContract supplies it`);
|
|
30
|
+
}
|
|
31
|
+
return Object.freeze({ kind: "tool", definition: Object.freeze({ ...definition }) });
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* True when `tool` has a handler: from {@link defineTool}, not {@link declareTool}.
|
|
35
|
+
*
|
|
36
|
+
* @param tool - A tool from either.
|
|
37
|
+
*/
|
|
38
|
+
function isImplementedTool(tool) {
|
|
39
|
+
return typeof tool.definition.handler === "function";
|
|
40
|
+
}
|
|
41
|
+
/** Check what every tool, declared or defined, needs: a name, a level, and one form of shape. */
|
|
42
|
+
function checkToolShape(definition, what) {
|
|
43
|
+
if (typeof definition?.name !== "string" || definition.name.trim() === "") {
|
|
44
|
+
throw new Error(`${what} requires a name`);
|
|
45
|
+
}
|
|
46
|
+
if (typeof definition.accessLevel !== "number" ||
|
|
47
|
+
!Number.isInteger(definition.accessLevel) ||
|
|
48
|
+
definition.accessLevel < 0 ||
|
|
49
|
+
definition.accessLevel > 100) {
|
|
50
|
+
throw new Error(`Tool ${definition.name} needs an accessLevel from 0 through 100`);
|
|
51
|
+
}
|
|
52
|
+
const withSchemas = "inputSchema" in definition || "outputSchema" in definition;
|
|
53
|
+
const withFields = "inputFields" in definition || "outputFields" in definition;
|
|
54
|
+
if (withSchemas === withFields) {
|
|
55
|
+
throw new Error(`Tool ${definition.name} needs either inputSchema and outputSchema, or inputFields and outputFields`);
|
|
56
|
+
}
|
|
57
|
+
if (withSchemas) {
|
|
58
|
+
const { inputSchema, outputSchema } = definition;
|
|
59
|
+
if (typeof inputSchema?.shape !== "object" || typeof outputSchema?.shape !== "object") {
|
|
60
|
+
throw new Error(`Tool ${definition.name} schemas must be zod objects`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const { inputFields, outputFields } = definition;
|
|
65
|
+
if (!Array.isArray(inputFields) || !Array.isArray(outputFields)) {
|
|
66
|
+
throw new Error(`Tool ${definition.name} fields must be arrays`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Run a tool the way the Runtime does: parse the input with its schema, call the handler, and
|
|
72
|
+
* snapshot what it returns as the bridge carries it. An optional field that is `null` or
|
|
73
|
+
* `undefined` is left out, in the input before parsing and in the result. Handy in tests with a
|
|
74
|
+
* fake `ctx`.
|
|
75
|
+
*
|
|
76
|
+
* @param tool - A tool from {@link defineTool}.
|
|
77
|
+
* @param args - The call's arguments, as a caller sends them.
|
|
78
|
+
* @param ctx - The host calls for this call.
|
|
79
|
+
*/
|
|
80
|
+
async function runPackageTool(tool, args, ctx) {
|
|
81
|
+
const definition = tool.definition;
|
|
82
|
+
if (!("inputSchema" in definition)) {
|
|
83
|
+
const result = await definition.handler(args, ctx);
|
|
84
|
+
return (result === undefined ? result : (0, wire_value_1.snapshotPackageValue)(result));
|
|
85
|
+
}
|
|
86
|
+
const input = (0, empty_fields_1.withoutEmptySchemaFields)(definition.inputSchema, args ?? {});
|
|
87
|
+
const result = await definition.handler(definition.inputSchema.parse(input), ctx);
|
|
88
|
+
return (result === undefined
|
|
89
|
+
? result
|
|
90
|
+
: (0, wire_value_1.snapshotPackageValue)((0, empty_fields_1.withoutEmptySchemaFields)(definition.outputSchema, result)));
|
|
91
|
+
}
|