@toolbench/sdk 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ahmed Elmalt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # @toolbench/sdk
2
+
3
+ The contract a [Toolbench](https://github.com/eknowledger/toolbench) tool implements. No dependencies,
4
+ no DOM.
5
+
6
+ ```sh
7
+ pnpm add @toolbench/sdk
8
+ ```
9
+
10
+ A tool is one function and one JSON file:
11
+
12
+ ```ts
13
+ import type { Tool } from "@toolbench/sdk";
14
+
15
+ export default {
16
+ run({ text }) {
17
+ return { kind: "fields", fields: [{ label: "reversed", value: [...text].reverse().join("") }] };
18
+ },
19
+ } satisfies Tool<{ text: string }>;
20
+ ```
21
+
22
+ This package holds four things:
23
+
24
+ | Export | What it does |
25
+ |---|---|
26
+ | Types (`Tool`, `Ctx`, `Output`, `InputSpec`, `Manifest`) | The whole agreement between a tool and a host |
27
+ | `validateManifest` | Turns unknown JSON into a `Manifest`, or throws naming the field and saying what to do |
28
+ | `upgradeManifest`, `upgradeOutput`, `canLoad` | Version migration, so a tool written against an older contract keeps working |
29
+ | `runCases`, `assertCases`, `compare` | The fixture runner. Known-answer cases run in Node with no browser and no build step |
30
+
31
+ Nothing here touches the DOM, because `run` has to work in three places: Node during tests, a Web
32
+ Worker where no DOM exists, and a site build computing a precomputed result.
33
+
34
+ To render and run a tool in a page, add [`@toolbench/runtime`](https://www.npmjs.com/package/@toolbench/runtime).
35
+
36
+ ## Documentation
37
+
38
+ - [Writing a tool](https://github.com/eknowledger/toolbench/blob/main/docs/authoring-a-tool.md)
39
+ - [Architecture](https://github.com/eknowledger/toolbench/blob/main/docs/architecture.md)
40
+ - [Versioning and compatibility](https://github.com/eknowledger/toolbench/blob/main/docs/versioning.md)
41
+
42
+ MIT
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @toolbench/sdk — the contract a tool implements.
3
+ *
4
+ * A tool is one function plus one manifest. Nothing here touches the DOM, a framework, or a host
5
+ * site, which is what lets a tool be tested in plain Node and reused by any runtime.
6
+ *
7
+ * import type { Tool } from "@toolbench/sdk";
8
+ *
9
+ * export default {
10
+ * run({ value }, ctx) {
11
+ * return { kind: "fields", fields: [{ label: "doubled", value: String(Number(value) * 2) }] };
12
+ * },
13
+ * } satisfies Tool<{ value: string }>;
14
+ */
15
+ export { CAPABILITIES, INPUT_TYPES, OUTPUT_KINDS, type Capability, type Case, type Cell, type Chart, type Column, type Ctx, type Field, type InputSpec, type InputType, type InputValues, type LoadedTool, type Manifest, type Output, type OutputKind, type Series, type Tone, type Tool, type ToolModule, } from "./types.ts";
16
+ export { ManifestError, describeSdkVersion, validateManifest } from "./validate.ts";
17
+ export { MIGRATIONS, VersionError, canLoad, upgradeManifest, upgradeOutput, type Migration } from "./migrate.ts";
18
+ export { assertCases, compare, runCases, testCtx, type CaseResult, type RunCasesOptions } from "./testing.ts";
19
+ export { SDK_CHANGELOG, SDK_VERSION, SUPPORTED_SDK_VERSIONS } from "./version.ts";
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACN,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,IAAI,EACT,KAAK,KAAK,EACV,KAAK,MAAM,EACX,KAAK,GAAG,EACR,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,MAAM,EACX,KAAK,IAAI,EACT,KAAK,IAAI,EACT,KAAK,UAAU,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACjH,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAC9G,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @toolbench/sdk — the contract a tool implements.
3
+ *
4
+ * A tool is one function plus one manifest. Nothing here touches the DOM, a framework, or a host
5
+ * site, which is what lets a tool be tested in plain Node and reused by any runtime.
6
+ *
7
+ * import type { Tool } from "@toolbench/sdk";
8
+ *
9
+ * export default {
10
+ * run({ value }, ctx) {
11
+ * return { kind: "fields", fields: [{ label: "doubled", value: String(Number(value) * 2) }] };
12
+ * },
13
+ * } satisfies Tool<{ value: string }>;
14
+ */
15
+ export { CAPABILITIES, INPUT_TYPES, OUTPUT_KINDS, } from "./types.js";
16
+ export { ManifestError, describeSdkVersion, validateManifest } from "./validate.js";
17
+ export { MIGRATIONS, VersionError, canLoad, upgradeManifest, upgradeOutput } from "./migrate.js";
18
+ export { assertCases, compare, runCases, testCtx } from "./testing.js";
19
+ export { SDK_CHANGELOG, SDK_VERSION, SUPPORTED_SDK_VERSIONS } from "./version.js";
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACN,YAAY,EACZ,WAAW,EACX,YAAY,GAmBZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAkB,MAAM,cAAc,CAAC;AACjH,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAyC,MAAM,cAAc,CAAC;AAC9G,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Backward compatibility, as a mechanism rather than a promise.
3
+ *
4
+ * A tool is pinned to the contract version it was written against. The runtime keeps moving. So
5
+ * every version boundary gets a **migration**: a pair of pure functions that carry an old manifest
6
+ * and an old output forward one step. Loading a tool runs the chain from its declared version up to
7
+ * the current one, and after that the rest of the system only ever sees current shapes.
8
+ *
9
+ * ```
10
+ * tool declares sdk: 1 runtime speaks 3
11
+ * │
12
+ * └──▶ migrate 1→2 ──▶ migrate 2→3 ──▶ current
13
+ * ```
14
+ *
15
+ * Why a chain and not one big adapter: each step is small enough to read, and each step is testable
16
+ * on its own. Adding version 4 means writing one 3→4 step, not revisiting the previous three.
17
+ *
18
+ * The rules that keep this honest, and that `docs/versioning.md` states as policy:
19
+ *
20
+ * - **Additive only.** A new version may add output kinds, input types and optional fields. It may
21
+ * not remove or repurpose anything, because a migration cannot invent information an old tool
22
+ * never had.
23
+ * - **A migration never fails.** If a step would need to guess, the change was not additive and
24
+ * does not belong in a version bump.
25
+ * - **Old fixtures run forever.** Every tool's cases are checked against the current runtime in CI,
26
+ * which is what turns this file from a good intention into a tested claim.
27
+ */
28
+ import type { Manifest, Output } from "./types.ts";
29
+ export interface Migration {
30
+ /** Migrates a manifest from `from` to `from + 1`. */
31
+ readonly from: number;
32
+ manifest(raw: Record<string, unknown>): Record<string, unknown>;
33
+ /** Migrates an output produced by a tool written against `from`. */
34
+ output(out: Output): Output;
35
+ }
36
+ /**
37
+ * The chain, ordered by `from`.
38
+ *
39
+ * Empty at contract version 1 — there is nothing before it. The machinery exists now, with tests,
40
+ * because a compatibility mechanism written at the moment it is first needed is a compatibility
41
+ * mechanism written under pressure.
42
+ *
43
+ * A future entry looks like this (from the real plan for version 2, which adds a `bytes` output for
44
+ * hex dumps):
45
+ *
46
+ * ```ts
47
+ * {
48
+ * from: 1,
49
+ * // A v1 manifest cannot mention `bytes`, so nothing to change.
50
+ * manifest: (m) => m,
51
+ * // A v1 tool cannot return `bytes` either. Identity — and that is the shape of a healthy
52
+ * // additive change: the migration is trivial precisely because nothing was taken away.
53
+ * output: (o) => o,
54
+ * }
55
+ * ```
56
+ */
57
+ export declare const MIGRATIONS: readonly Migration[];
58
+ export declare class VersionError extends Error {
59
+ constructor(message: string);
60
+ }
61
+ /**
62
+ * Read a raw manifest of any supported version and return one shaped for the current contract.
63
+ *
64
+ * `migrations` is injectable so the chain itself can be tested without shipping a fake version.
65
+ */
66
+ export declare function upgradeManifest(raw: unknown, migrations?: readonly Migration[], currentVersion?: number): Record<string, unknown>;
67
+ /** Carry an output produced by a tool written against `declaredVersion` up to the current shape. */
68
+ export declare function upgradeOutput(out: Output, declaredVersion: number, migrations?: readonly Migration[], currentVersion?: number): Output;
69
+ /** True when this runtime can load a tool declaring `version`. */
70
+ export declare function canLoad(version: number, currentVersion?: number): boolean;
71
+ export type { Manifest };
72
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAGnD,MAAM,WAAW,SAAS;IACzB,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChE,oEAAoE;IACpE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EAAO,CAAC;AAEnD,qBAAa,YAAa,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI3B;AAyBD;;;;GAIG;AACH,wBAAgB,eAAe,CAC9B,GAAG,EAAE,OAAO,EACZ,UAAU,GAAE,SAAS,SAAS,EAAe,EAC7C,cAAc,GAAE,MAAoB,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAwBzB;AAED,oGAAoG;AACpG,wBAAgB,aAAa,CAC5B,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,MAAM,EACvB,UAAU,GAAE,SAAS,SAAS,EAAe,EAC7C,cAAc,GAAE,MAAoB,GAClC,MAAM,CAOR;AAED,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,GAAE,MAAoB,GAAG,OAAO,CAEtF;AAED,YAAY,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,89 @@
1
+ import { SDK_VERSION } from "./version.js";
2
+ /**
3
+ * The chain, ordered by `from`.
4
+ *
5
+ * Empty at contract version 1 — there is nothing before it. The machinery exists now, with tests,
6
+ * because a compatibility mechanism written at the moment it is first needed is a compatibility
7
+ * mechanism written under pressure.
8
+ *
9
+ * A future entry looks like this (from the real plan for version 2, which adds a `bytes` output for
10
+ * hex dumps):
11
+ *
12
+ * ```ts
13
+ * {
14
+ * from: 1,
15
+ * // A v1 manifest cannot mention `bytes`, so nothing to change.
16
+ * manifest: (m) => m,
17
+ * // A v1 tool cannot return `bytes` either. Identity — and that is the shape of a healthy
18
+ * // additive change: the migration is trivial precisely because nothing was taken away.
19
+ * output: (o) => o,
20
+ * }
21
+ * ```
22
+ */
23
+ export const MIGRATIONS = [];
24
+ export class VersionError extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = "VersionError";
28
+ }
29
+ }
30
+ /**
31
+ * The steps needed to carry `version` up to `currentVersion`, in order.
32
+ *
33
+ * ⚠️ `currentVersion` is a parameter, not the module constant. It was the constant in the first
34
+ * draft, which meant the injected version was ignored and the chain silently did nothing — the
35
+ * migration tests caught it, which is the argument for testing a compatibility mechanism before the
36
+ * day you need it.
37
+ */
38
+ function chainFrom(version, migrations, currentVersion) {
39
+ const steps = [];
40
+ for (let v = version; v < currentVersion; v++) {
41
+ const step = migrations.find((m) => m.from === v);
42
+ if (!step) {
43
+ throw new VersionError(`No migration from contract version ${v} to ${v + 1}. This is a bug in @toolbench/sdk: ` +
44
+ `the current version is ${currentVersion}, so every version below it needs a migration step.`);
45
+ }
46
+ steps.push(step);
47
+ }
48
+ return steps;
49
+ }
50
+ /**
51
+ * Read a raw manifest of any supported version and return one shaped for the current contract.
52
+ *
53
+ * `migrations` is injectable so the chain itself can be tested without shipping a fake version.
54
+ */
55
+ export function upgradeManifest(raw, migrations = MIGRATIONS, currentVersion = SDK_VERSION) {
56
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
57
+ throw new VersionError("A manifest must be an object.");
58
+ }
59
+ const manifest = raw;
60
+ const declared = manifest.sdk;
61
+ if (typeof declared !== "number" || !Number.isInteger(declared) || declared < 1) {
62
+ throw new VersionError(`A manifest must declare an integer contract version, e.g. { "sdk": ${currentVersion} }. Got ${JSON.stringify(declared)}.`);
63
+ }
64
+ if (declared > currentVersion) {
65
+ throw new VersionError(`This tool needs contract version ${declared}; this runtime speaks ${currentVersion}. ` +
66
+ "Upgrade @toolbench/runtime, or lower the tool's sdk if it does not use the newer features.");
67
+ }
68
+ let out = manifest;
69
+ for (const step of chainFrom(declared, migrations, currentVersion)) {
70
+ out = step.manifest(out);
71
+ out = { ...out, sdk: step.from + 1 };
72
+ }
73
+ return { ...out, sdk: currentVersion };
74
+ }
75
+ /** Carry an output produced by a tool written against `declaredVersion` up to the current shape. */
76
+ export function upgradeOutput(out, declaredVersion, migrations = MIGRATIONS, currentVersion = SDK_VERSION) {
77
+ if (declaredVersion > currentVersion) {
78
+ throw new VersionError(`Cannot downgrade an output from version ${declaredVersion} to ${currentVersion}.`);
79
+ }
80
+ let value = out;
81
+ for (const step of chainFrom(declaredVersion, migrations, currentVersion))
82
+ value = step.output(value);
83
+ return value;
84
+ }
85
+ /** True when this runtime can load a tool declaring `version`. */
86
+ export function canLoad(version, currentVersion = SDK_VERSION) {
87
+ return Number.isInteger(version) && version >= 1 && version <= currentVersion;
88
+ }
89
+ //# sourceMappingURL=migrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The fixture runner: a tool's `cases.json` is its test suite, and this runs it.
3
+ *
4
+ * It lives in the SDK rather than in the runtime for one reason — **a tool's correctness has nothing
5
+ * to do with a browser.** Cases run in plain Node, which means a tool author gets a red test in
6
+ * milliseconds, CI needs no browser, and the same fixtures can be re-run by a host site's build to
7
+ * prove that the tool it pinned still does what it claimed.
8
+ *
9
+ * Matching is exact by default. That is deliberate: subset matching quietly passes a field emitted
10
+ * twice, fields reordered, a field dropped, or garbage returned beside a correct error message. A
11
+ * case may opt out with `match: "subset"`, and then it owes two things — a `why`, and a `fieldCount`
12
+ * so a disappearing field is still a failure.
13
+ */
14
+ import type { Case, Ctx, Output, Tool } from "./types.ts";
15
+ export interface CaseResult {
16
+ name: string;
17
+ ok: boolean;
18
+ /** Present when `ok` is false: a human-readable account of the difference. */
19
+ detail?: string;
20
+ /** Wall-clock duration, so a "main thread" tool can be held to a bound. */
21
+ ms: number;
22
+ }
23
+ export interface RunCasesOptions {
24
+ /** Fails any case that takes longer than this. Use it to hold main-thread tools to a bound. */
25
+ maxMs?: number;
26
+ /** Aborts a case that hangs, so a bad loop fails the suite instead of wedging it. */
27
+ timeoutMs?: number;
28
+ }
29
+ /** A `Ctx` for a headless run: nothing to draw progress on, and an abort that a timeout can pull. */
30
+ export declare function testCtx(signal?: AbortSignal): Ctx;
31
+ export declare function runCases(tool: Tool, cases: readonly Case[], options?: RunCasesOptions): Promise<CaseResult[]>;
32
+ /** Throws with every failure listed. The shape most test runners want. */
33
+ export declare function assertCases(tool: Tool, cases: readonly Case[], options?: RunCasesOptions): Promise<void>;
34
+ /** Returns undefined when the output matches, or a description of the first difference. */
35
+ export declare function compare(actual: Output, testCase: Case): string | undefined;
36
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,GAAG,EAAS,MAAM,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,eAAe;IAC/B,+FAA+F;IAC/F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qGAAqG;AACrG,wBAAgB,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,GAAG,CAKjD;AAED,wBAAsB,QAAQ,CAC7B,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,OAAO,GAAE,eAAoB,GAC3B,OAAO,CAAC,UAAU,EAAE,CAAC,CAqCvB;AAED,0EAA0E;AAC1E,wBAAsB,WAAW,CAChC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,OAAO,GAAE,eAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC,CASf;AAMD,2FAA2F;AAC3F,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAY1E"}
@@ -0,0 +1,211 @@
1
+ /** A `Ctx` for a headless run: nothing to draw progress on, and an abort that a timeout can pull. */
2
+ export function testCtx(signal) {
3
+ return {
4
+ signal: signal ?? new AbortController().signal,
5
+ progress: () => { },
6
+ };
7
+ }
8
+ export async function runCases(tool, cases, options = {}) {
9
+ const results = [];
10
+ for (const testCase of cases) {
11
+ const controller = new AbortController();
12
+ const timeout = options.timeoutMs
13
+ ? setTimeout(() => controller.abort(new Error(`case exceeded ${options.timeoutMs}ms`)), options.timeoutMs)
14
+ : undefined;
15
+ const started = performance.now();
16
+ try {
17
+ const actual = await tool.run(testCase.input, testCtx(controller.signal));
18
+ const ms = performance.now() - started;
19
+ const detail = compare(actual, testCase);
20
+ const tooSlow = options.maxMs !== undefined && ms > options.maxMs;
21
+ results.push({
22
+ name: testCase.name,
23
+ ok: detail === undefined && !tooSlow,
24
+ ms,
25
+ ...(detail !== undefined
26
+ ? { detail }
27
+ : tooSlow
28
+ ? { detail: `took ${ms.toFixed(1)}ms, over the ${options.maxMs}ms bound for this tool` }
29
+ : {}),
30
+ });
31
+ }
32
+ catch (error) {
33
+ results.push({
34
+ name: testCase.name,
35
+ ok: false,
36
+ ms: performance.now() - started,
37
+ detail: `threw ${error?.name ?? "Error"}: ${error?.message ?? String(error)}\n` +
38
+ "A tool should THROW only when the tool itself is broken. If this input is simply invalid, " +
39
+ 'return { kind: "error", message, input } instead.',
40
+ });
41
+ }
42
+ finally {
43
+ if (timeout)
44
+ clearTimeout(timeout);
45
+ }
46
+ }
47
+ return results;
48
+ }
49
+ /** Throws with every failure listed. The shape most test runners want. */
50
+ export async function assertCases(tool, cases, options = {}) {
51
+ const results = await runCases(tool, cases, options);
52
+ const failed = results.filter((r) => !r.ok);
53
+ if (failed.length > 0) {
54
+ throw new Error(`${failed.length} of ${results.length} cases failed:\n\n` +
55
+ failed.map((f) => ` ✗ ${f.name}\n ${f.detail?.split("\n").join("\n ")}`).join("\n\n"));
56
+ }
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+ // comparison
60
+ // ---------------------------------------------------------------------------
61
+ /** Returns undefined when the output matches, or a description of the first difference. */
62
+ export function compare(actual, testCase) {
63
+ const mode = testCase.match ?? "exact";
64
+ if (mode === "subset") {
65
+ if (!testCase.why) {
66
+ return 'match: "subset" requires a `why` — an opt-out of exact matching needs a stated reason.';
67
+ }
68
+ if (testCase.expect.kind === "fields" && testCase.fieldCount === undefined) {
69
+ return 'match: "subset" on a fields output requires `fieldCount`, or a dropped field would pass.';
70
+ }
71
+ return subset(actual, testCase);
72
+ }
73
+ return exact(actual, testCase.expect, "output");
74
+ }
75
+ function exact(actual, expected, path) {
76
+ if (expected === actual)
77
+ return undefined;
78
+ if (typeof expected !== typeof actual) {
79
+ return `${path}: expected ${typeof expected} ${show(expected)}, got ${typeof actual} ${show(actual)}`;
80
+ }
81
+ if (Array.isArray(expected) || Array.isArray(actual)) {
82
+ if (!Array.isArray(expected) || !Array.isArray(actual)) {
83
+ return `${path}: expected ${show(expected)}, got ${show(actual)}`;
84
+ }
85
+ if (expected.length !== actual.length) {
86
+ return `${path}: expected ${expected.length} item(s), got ${actual.length}\n expected: ${show(expected)}\n actual: ${show(actual)}`;
87
+ }
88
+ for (let i = 0; i < expected.length; i++) {
89
+ const diff = exact(actual[i], expected[i], `${path}[${i}]`);
90
+ if (diff)
91
+ return diff;
92
+ }
93
+ return undefined;
94
+ }
95
+ if (expected !== null && actual !== null && typeof expected === "object") {
96
+ const e = expected;
97
+ const a = actual;
98
+ const keys = [...new Set([...Object.keys(e), ...Object.keys(a)])].sort();
99
+ for (const key of keys) {
100
+ if (!(key in e))
101
+ return `${path}.${key}: unexpected field ${show(a[key])}`;
102
+ if (!(key in a))
103
+ return `${path}.${key}: missing, expected ${show(e[key])}`;
104
+ const diff = exact(a[key], e[key], `${path}.${key}`);
105
+ if (diff)
106
+ return diff;
107
+ }
108
+ return undefined;
109
+ }
110
+ return `${path}: expected ${show(expected)}, got ${show(actual)}`;
111
+ }
112
+ function subset(actual, testCase) {
113
+ const expected = testCase.expect;
114
+ /*
115
+ * ⚠️ A subset case looks INSIDE a group.
116
+ *
117
+ * Composite output is the normal case, not the exception — a decode that returns both a field list
118
+ * and a table is a `group` — so a case asserting "somewhere in there, these fields have these
119
+ * values" has to be able to reach the part it means. Without this, subset matching is unusable for
120
+ * exactly the tools most likely to need it.
121
+ *
122
+ * The rule is "some part matches", chosen because it is predictable: with two `fields` parts, a
123
+ * case that matches either one passes, and a case that matches neither reports what it tried.
124
+ */
125
+ if (actual.kind === "group" && expected.kind !== "group") {
126
+ const candidates = flatten(actual).filter((part) => part.kind === expected.kind);
127
+ if (candidates.length === 0) {
128
+ return `output: no "${expected.kind}" part inside the group (it has ${flatten(actual).map((p) => p.kind).join(", ")})`;
129
+ }
130
+ const diffs = candidates.map((candidate) => subset(candidate, testCase)).filter((d) => d !== undefined);
131
+ if (diffs.length < candidates.length)
132
+ return undefined; // at least one part matched
133
+ return candidates.length === 1
134
+ ? diffs[0]
135
+ : `output: none of the ${candidates.length} "${expected.kind}" parts matched. Closest: ${diffs[0]}`;
136
+ }
137
+ if (actual.kind !== expected.kind) {
138
+ return `output.kind: expected "${expected.kind}", got "${actual.kind}"`;
139
+ }
140
+ if (expected.kind === "fields" && actual.kind === "fields") {
141
+ if (actual.fields.length !== testCase.fieldCount) {
142
+ return `output.fields: expected exactly ${testCase.fieldCount} field(s) (from fieldCount), got ${actual.fields.length}`;
143
+ }
144
+ /*
145
+ * ⚠️ Fields are identified by GROUP + LABEL, not by label alone.
146
+ *
147
+ * A tool that reports the same quantity two ways — "time in system" under "Formula" and again
148
+ * under "Simulation" — is the normal case, and that is exactly what `group` is for. Keying on
149
+ * the label alone made those two collide and reported a duplicate that was not one.
150
+ */
151
+ const key = (f) => `${f.group ?? ""}\u0000${f.label}`;
152
+ const byKey = new Map();
153
+ for (const f of actual.fields)
154
+ byKey.set(key(f), [...(byKey.get(key(f)) ?? []), f]);
155
+ for (const want of expected.fields) {
156
+ const where = want.group ? `"${want.label}" in group "${want.group}"` : `"${want.label}"`;
157
+ const got = byKey.get(key(want));
158
+ if (!got) {
159
+ const labels = actual.fields.map((f) => (f.group ? `${f.group}/${f.label}` : f.label)).join(", ");
160
+ return `output.fields: no field ${where}. Present: ${labels}`;
161
+ }
162
+ if (got.length > 1)
163
+ return `output.fields: ${where} appears ${got.length} times`;
164
+ const diff = exact(got[0], { ...got[0], ...want }, `output.fields[${where}]`);
165
+ if (diff)
166
+ return diff;
167
+ }
168
+ return undefined;
169
+ }
170
+ /*
171
+ * A chart in subset mode asserts its FRAME, not its data: axis labels, units and annotations.
172
+ *
173
+ * Pinning hundreds of computed points in a fixture is both unreadable and the wrong test — the
174
+ * numbers belong in a unit test that says why they are right. What a fixture usefully checks is
175
+ * that the chart the tool describes is the chart the renderer will get.
176
+ */
177
+ if (expected.kind === "series" && actual.kind === "series") {
178
+ const { x: _ax, series: _as, ...actualFrame } = actual.chart;
179
+ const { x: _ex, series: _es, ...expectedFrame } = expected.chart;
180
+ const diff = exact(actualFrame, { ...actualFrame, ...expectedFrame }, "output.chart");
181
+ if (diff)
182
+ return diff;
183
+ if (actual.chart.series.length === 0)
184
+ return "output.chart.series: the chart has no series at all";
185
+ if (actual.chart.x.length === 0)
186
+ return "output.chart.x: the chart has no x values";
187
+ return undefined;
188
+ }
189
+ if (expected.kind === "error" && actual.kind === "error") {
190
+ if (!actual.message.includes(expected.message)) {
191
+ return `output.message: expected to contain "${expected.message}", got "${actual.message}"`;
192
+ }
193
+ if (expected.input !== undefined && actual.input !== expected.input) {
194
+ return `output.input: expected "${expected.input}", got ${show(actual.input)}`;
195
+ }
196
+ return undefined;
197
+ }
198
+ // For every other kind, subset has no defined meaning — fall back to exact rather than pretend.
199
+ return exact(actual, expected, "output");
200
+ }
201
+ /** Every part of a possibly-nested group, in order, excluding the groups themselves. */
202
+ function flatten(output) {
203
+ return output.kind === "group" ? output.parts.flatMap(flatten) : [output];
204
+ }
205
+ function show(value) {
206
+ const json = JSON.stringify(value);
207
+ if (json === undefined)
208
+ return String(value);
209
+ return json.length > 160 ? `${json.slice(0, 157)}…` : json;
210
+ }
211
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,269 @@
1
+ /**
2
+ * The contract. A tool is a function; everything else here describes what goes in and what comes
3
+ * out, so a runtime can build an interface for a tool it has never seen.
4
+ *
5
+ * Two rules govern every change to this file:
6
+ *
7
+ * 1. **Additive only.** New output kinds and input types get added. Existing ones never change
8
+ * meaning, never lose a field, and never gain a required one.
9
+ * 2. **No DOM, no framework, no host.** A tool must be runnable in plain Node, because that is how
10
+ * its fixtures are checked. Anything that needs a browser belongs in the runtime, not here.
11
+ */
12
+ /** A label and a value. The plainest thing a tool can say. */
13
+ export interface Field {
14
+ label: string;
15
+ value: string;
16
+ /** A qualifier shown beside the value: a unit, a caveat, "(dynamic)". */
17
+ note?: string;
18
+ /** Fields sharing a group are rendered together under its name. */
19
+ group?: string;
20
+ /** Draws attention without inventing a severity scale. */
21
+ tone?: Tone;
22
+ }
23
+ export type Tone = "normal" | "warn" | "bad" | "good";
24
+ export interface Column {
25
+ label: string;
26
+ align?: "start" | "end";
27
+ /** Monospace, for anything the reader will compare digit by digit. */
28
+ mono?: boolean;
29
+ }
30
+ export type Cell = string | number | {
31
+ text: string;
32
+ mono?: boolean;
33
+ tone?: Tone;
34
+ };
35
+ /** One line, bar or area on a chart. */
36
+ export interface Series {
37
+ label: string;
38
+ /** One value per x. A `null` is a gap, not a zero — the distinction matters on a chart. */
39
+ points: (number | null)[];
40
+ unit?: string;
41
+ shape?: "line" | "area" | "bar";
42
+ axis?: "left" | "right";
43
+ }
44
+ export interface Chart {
45
+ xLabel: string;
46
+ yLabel: string;
47
+ x: number[];
48
+ series: Series[];
49
+ xUnit?: string;
50
+ yUnit?: string;
51
+ /** A vertical marker with a name: "the knee", "capacity", "p99". */
52
+ annotations?: {
53
+ x: number;
54
+ label: string;
55
+ }[];
56
+ /** Second axis label, required if any series sets `axis: "right"`. */
57
+ yLabelRight?: string;
58
+ }
59
+ /**
60
+ * What a tool returns. A closed union: a runtime can render every member, and a tool cannot invent
61
+ * a shape nobody can draw.
62
+ *
63
+ * ⚠️ `group` is what lets one tool answer in more than one shape — a decode that produces both a
64
+ * field list and a table is the common case, not the exception.
65
+ */
66
+ export type Output = {
67
+ kind: "fields";
68
+ fields: Field[];
69
+ } | {
70
+ kind: "text";
71
+ text: string;
72
+ mono?: boolean;
73
+ } | {
74
+ kind: "code";
75
+ lang: string;
76
+ source: string;
77
+ } | {
78
+ kind: "table";
79
+ columns: Column[];
80
+ rows: Cell[][];
81
+ caption?: string;
82
+ } | {
83
+ kind: "series";
84
+ chart: Chart;
85
+ } | {
86
+ kind: "group";
87
+ parts: Output[];
88
+ }
89
+ /**
90
+ * The INPUT was wrong, and the tool worked correctly by saying so. `input` names the control at
91
+ * fault so a generated form can mark it invalid and point a screen reader at the message —
92
+ * without it, an error can only ever float unattached.
93
+ *
94
+ * A tool that is itself broken should THROW instead. The two render differently on purpose.
95
+ */
96
+ | {
97
+ kind: "error";
98
+ message: string;
99
+ input?: string;
100
+ at?: number;
101
+ len?: number;
102
+ };
103
+ export type OutputKind = Output["kind"];
104
+ /** Every kind this contract version can express. The runtime checks its renderers against this. */
105
+ export declare const OUTPUT_KINDS: readonly OutputKind[];
106
+ interface InputBase {
107
+ id: string;
108
+ label: string;
109
+ /** The hint a form shows and a screen reader reads. Not a placeholder — those vanish on focus. */
110
+ description?: string;
111
+ /** Shown after the control: "ms", "req/s", "bytes". A number without one is a riddle. */
112
+ unit?: string;
113
+ /** The one input a compact card shows. At most one per tool. */
114
+ primary?: boolean;
115
+ /**
116
+ * Force text direction. Bytes, code and identifiers are left-to-right even inside right-to-left
117
+ * prose, and a runtime cannot know which is which — the tool says.
118
+ */
119
+ dir?: "ltr" | "auto";
120
+ }
121
+ export type InputSpec = (InputBase & {
122
+ type: "text";
123
+ default: string;
124
+ maxLength?: number;
125
+ }) | (InputBase & {
126
+ type: "textarea";
127
+ default: string;
128
+ rows?: number;
129
+ maxLength?: number;
130
+ }) | (InputBase & {
131
+ type: "number";
132
+ default: number;
133
+ min: number;
134
+ max: number;
135
+ step?: number;
136
+ }) | (InputBase & {
137
+ type: "select";
138
+ default: string;
139
+ options: {
140
+ value: string;
141
+ label: string;
142
+ }[];
143
+ }) | (InputBase & {
144
+ type: "toggle";
145
+ default: boolean;
146
+ });
147
+ export type InputType = InputSpec["type"];
148
+ export declare const INPUT_TYPES: readonly InputType[];
149
+ /** The shape a tool's `run` receives: one entry per declared input, keyed by id. */
150
+ export type InputValues = Record<string, string | number | boolean>;
151
+ /**
152
+ * `pure` is the only capability in contract version 1: a function of its inputs, no I/O, no clock,
153
+ * no randomness that is not seeded. That is not a limitation to apologise for — it is what makes a
154
+ * tool testable by fixture, runnable at build time, and safe to put on a landing page.
155
+ *
156
+ * Reading files and calling networks arrive in later versions, each with its own capability, so a
157
+ * runtime can always tell from the manifest what a tool will try to do before it runs.
158
+ */
159
+ export type Capability = "pure";
160
+ export declare const CAPABILITIES: readonly Capability[];
161
+ export interface Manifest {
162
+ /** The contract version this tool was written against. */
163
+ sdk: number;
164
+ /** Stable, lowercase, hyphenated. Also the directory name and the URL segment. */
165
+ id: string;
166
+ name: string;
167
+ /** One sentence. Shown on cards and used as a page description. */
168
+ blurb: string;
169
+ /** The tool's own version, independent of `sdk`. */
170
+ version: string;
171
+ capabilities: Capability[];
172
+ inputs: InputSpec[];
173
+ /** Every kind `run` can return, so a host can refuse a tool it cannot draw. */
174
+ kinds: OutputKind[];
175
+ runtime: {
176
+ /** Entry file, relative to the tool's directory. */
177
+ entry: string;
178
+ /**
179
+ * `main` is the default and costs nothing. `worker` is for a tool whose running time depends
180
+ * on its input — it is the only mode where a timeout can actually stop anything.
181
+ */
182
+ thread?: "main" | "worker";
183
+ };
184
+ status?: "live" | "deprecated" | "retired";
185
+ /** How the tool appears in a compact slot: runnable, a summary that links out, or not at all. */
186
+ card?: "live" | "info" | "none";
187
+ /** How many fields a card shows before it stops. Errors are never truncated. */
188
+ cardFields?: number;
189
+ /**
190
+ * Re-run as the reader types, instead of waiting for the Run button.
191
+ *
192
+ * Off by default, and that default is deliberate. A tool that runs on every keystroke does work
193
+ * nobody asked for, makes the Run button look broken (the answer is already there), and takes the
194
+ * decision away from the reader. Opt in only for a tool that is genuinely instant and reads better
195
+ * live — a unit converter, say. Anything with a worker or a heavy loop should leave it alone.
196
+ */
197
+ autoRun?: boolean;
198
+ /** Worker mode only. A tool that declares this on the main thread is rejected: nothing there can be stopped. */
199
+ timeoutMs?: number;
200
+ /** Markdown file, relative to the tool's directory, rendered as the tool's help. */
201
+ help?: string;
202
+ tags?: string[];
203
+ /**
204
+ * Anything the tool wants to point at: a spec it implements, a post explaining it, a reference
205
+ * decoder. Deliberately generic — a host site's own content relations are the host's business.
206
+ */
207
+ links?: {
208
+ label: string;
209
+ href: string;
210
+ }[];
211
+ }
212
+ /** Passed to `run`. Built by whatever is executing the tool, never sent across a boundary. */
213
+ export interface Ctx {
214
+ /**
215
+ * Aborted when the reader cancels, when the input changes mid-run, or when a worker-mode timeout
216
+ * fires. A loop whose length depends on input MUST check it — on the main thread it is the only
217
+ * way to stop.
218
+ */
219
+ signal: AbortSignal;
220
+ /**
221
+ * Progress, and optionally a partial result. A runtime renders `partial` at most once per frame,
222
+ * which is what lets a simulation show itself converging instead of hiding behind a bar.
223
+ */
224
+ progress(fraction: number, partial?: Output): void;
225
+ }
226
+ /**
227
+ * What a tool is.
228
+ *
229
+ * ⚠️ **Declare your input type as a `type`, not an `interface`.** TypeScript gives an implicit index
230
+ * signature to a type alias of an object literal and not to an interface, so an interface will not
231
+ * satisfy `InputValues` and the error message ("index signature is missing") does not hint at the
232
+ * fix. This is the first thing a new tool author trips over.
233
+ *
234
+ * ```ts
235
+ * type Input = { values: string; method: string }; // ✓
236
+ * interface Input { values: string } // ✗ won't satisfy InputValues
237
+ *
238
+ * export default { run(input: Input, ctx) { … } } satisfies Tool<Input>;
239
+ * ```
240
+ */
241
+ export interface Tool<I extends InputValues = InputValues> {
242
+ run(input: I, ctx: Ctx): Output | Promise<Output>;
243
+ }
244
+ /** What a tool module default-exports. */
245
+ export type ToolModule<I extends InputValues = InputValues> = {
246
+ default: Tool<I>;
247
+ };
248
+ /** A manifest plus the module that implements it, which is what a runtime actually consumes. */
249
+ export interface LoadedTool<I extends InputValues = InputValues> {
250
+ manifest: Manifest;
251
+ tool: Tool<I>;
252
+ }
253
+ export interface Case {
254
+ name: string;
255
+ input: InputValues;
256
+ expect: Output;
257
+ /**
258
+ * Exact deep equality by default, because that is what catches a duplicated field, a reordered
259
+ * one, or garbage emitted beside a correct error.
260
+ *
261
+ * `subset` is an opt-in escape for a tool whose output is genuinely open-ended, and it demands
262
+ * two things in return: a `why`, and a `fieldCount`, so a dropped field is still a failure.
263
+ */
264
+ match?: "exact" | "subset";
265
+ why?: string;
266
+ fieldCount?: number;
267
+ }
268
+ export {};
269
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,8DAA8D;AAC9D,MAAM,WAAW,KAAK;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,IAAI,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,MAAM;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IACxB,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAEnF,wCAAwC;AACxC,MAAM,WAAW,MAAM;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,MAAM,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,KAAK;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,CAAC,EAAE,MAAM,EAAE,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,WAAW,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,MAAM,GACf;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,KAAK,EAAE,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE;AACpC;;;;;;GAMG;GACD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAExC,mGAAmG;AACnG,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAQ7C,CAAC;AAMF,UAAU,SAAS;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,SAAS,GAClB,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACnE,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACtF,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAC1F,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAAC,GAC9F,CAAC,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAEtD,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;AAE1C,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAAuD,CAAC;AAEpG,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAMpE;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC;AAEhC,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAAa,CAAC;AAM5D,MAAM,WAAW,QAAQ;IACxB,0DAA0D;IAC1D,GAAG,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,+EAA+E;IAC/E,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,OAAO,EAAE;QACR,oDAAoD;QACpD,KAAK,EAAE,MAAM,CAAC;QACd;;;WAGG;QACH,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;KAC3B,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IAC3C,iGAAiG;IACjG,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAChC,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gHAAgH;IAChH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1C;AAED,8FAA8F;AAC9F,MAAM,WAAW,GAAG;IACnB;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnD;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,IAAI,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IACxD,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAClD;AAED,0CAA0C;AAC1C,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAAI;IAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC;AAEnF,gGAAgG;AAChG,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IAC9D,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;CACd;AAMD,MAAM,WAAW,IAAI;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB"}
package/dist/types.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The contract. A tool is a function; everything else here describes what goes in and what comes
3
+ * out, so a runtime can build an interface for a tool it has never seen.
4
+ *
5
+ * Two rules govern every change to this file:
6
+ *
7
+ * 1. **Additive only.** New output kinds and input types get added. Existing ones never change
8
+ * meaning, never lose a field, and never gain a required one.
9
+ * 2. **No DOM, no framework, no host.** A tool must be runnable in plain Node, because that is how
10
+ * its fixtures are checked. Anything that needs a browser belongs in the runtime, not here.
11
+ */
12
+ /** Every kind this contract version can express. The runtime checks its renderers against this. */
13
+ export const OUTPUT_KINDS = [
14
+ "fields",
15
+ "text",
16
+ "code",
17
+ "table",
18
+ "series",
19
+ "group",
20
+ "error",
21
+ ];
22
+ export const INPUT_TYPES = ["text", "textarea", "number", "select", "toggle"];
23
+ export const CAPABILITIES = ["pure"];
24
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA6EH,mGAAmG;AACnG,MAAM,CAAC,MAAM,YAAY,GAA0B;IAClD,QAAQ;IACR,MAAM;IACN,MAAM;IACN,OAAO;IACP,QAAQ;IACR,OAAO;IACP,OAAO;CACP,CAAC;AA+BF,MAAM,CAAC,MAAM,WAAW,GAAyB,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAmBpG,MAAM,CAAC,MAAM,YAAY,GAA0B,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Manifest validation, hand-written on purpose.
3
+ *
4
+ * A schema library would be shorter, but it would also be a runtime dependency of the one package
5
+ * every tool author installs — and the error messages would be about JSON paths rather than about
6
+ * tools. These messages name the field, say what was wrong, and say what to do instead, because the
7
+ * person reading them is usually writing their first tool.
8
+ *
9
+ * ⚠️ The invariants at the bottom are the interesting part. They encode rules the rest of the system
10
+ * relies on, so a bad combination fails here rather than three layers later:
11
+ *
12
+ * - a compact card may only run a `pure` tool;
13
+ * - `timeoutMs` is meaningless on the main thread, so declaring it there is an error rather than a
14
+ * silently ignored field;
15
+ * - a tool may not declare an output kind this contract version cannot express.
16
+ */
17
+ import { type Manifest } from "./types.ts";
18
+ export declare class ManifestError extends Error {
19
+ readonly field: string;
20
+ constructor(field: string, message: string);
21
+ }
22
+ /**
23
+ * Validate a manifest that has already been brought up to the current contract version.
24
+ *
25
+ * Call `upgradeManifest` first unless you know the manifest is current — this function deliberately
26
+ * rejects an `sdk` it does not recognise rather than guessing.
27
+ */
28
+ export declare function validateManifest(raw: unknown): Manifest;
29
+ /** Human-readable summary of what a contract version offers. Used in error messages and docs. */
30
+ export declare function describeSdkVersion(version: number): string;
31
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAKN,KAAK,QAAQ,EAGb,MAAM,YAAY,CAAC;AAGpB,qBAAa,aAAc,SAAQ,KAAK;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBACX,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAwFD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,CAgHvD;AAED,iGAAiG;AACjG,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE1D"}
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Manifest validation, hand-written on purpose.
3
+ *
4
+ * A schema library would be shorter, but it would also be a runtime dependency of the one package
5
+ * every tool author installs — and the error messages would be about JSON paths rather than about
6
+ * tools. These messages name the field, say what was wrong, and say what to do instead, because the
7
+ * person reading them is usually writing their first tool.
8
+ *
9
+ * ⚠️ The invariants at the bottom are the interesting part. They encode rules the rest of the system
10
+ * relies on, so a bad combination fails here rather than three layers later:
11
+ *
12
+ * - a compact card may only run a `pure` tool;
13
+ * - `timeoutMs` is meaningless on the main thread, so declaring it there is an error rather than a
14
+ * silently ignored field;
15
+ * - a tool may not declare an output kind this contract version cannot express.
16
+ */
17
+ import { CAPABILITIES, INPUT_TYPES, OUTPUT_KINDS, } from "./types.js";
18
+ import { SDK_CHANGELOG, SDK_VERSION, SUPPORTED_SDK_VERSIONS } from "./version.js";
19
+ export class ManifestError extends Error {
20
+ field;
21
+ constructor(field, message) {
22
+ super(`${field}: ${message}`);
23
+ this.name = "ManifestError";
24
+ this.field = field;
25
+ }
26
+ }
27
+ const ID = /^[a-z0-9][a-z0-9-]*$/;
28
+ function fail(field, message) {
29
+ throw new ManifestError(field, message);
30
+ }
31
+ function str(o, field, path) {
32
+ const v = o[field];
33
+ if (typeof v !== "string" || v.length === 0)
34
+ fail(`${path}${field}`, "must be a non-empty string");
35
+ return v;
36
+ }
37
+ function optStr(o, field, path) {
38
+ if (o[field] === undefined)
39
+ return undefined;
40
+ return str(o, field, path);
41
+ }
42
+ function obj(v, path) {
43
+ if (typeof v !== "object" || v === null || Array.isArray(v))
44
+ fail(path, "must be an object");
45
+ return v;
46
+ }
47
+ function arr(v, path) {
48
+ if (!Array.isArray(v))
49
+ fail(path, "must be an array");
50
+ return v;
51
+ }
52
+ function oneOf(value, allowed, path) {
53
+ if (typeof value !== "string" || !allowed.includes(value)) {
54
+ fail(path, `must be one of ${allowed.map((a) => `"${a}"`).join(", ")}, got ${JSON.stringify(value)}`);
55
+ }
56
+ return value;
57
+ }
58
+ function validateInput(raw, path) {
59
+ const o = obj(raw, path);
60
+ const id = str(o, "id", `${path}.`);
61
+ if (!ID.test(id))
62
+ fail(`${path}.id`, `"${id}" must be lowercase letters, digits and hyphens`);
63
+ str(o, "label", `${path}.`);
64
+ optStr(o, "description", `${path}.`);
65
+ optStr(o, "unit", `${path}.`);
66
+ if (o.primary !== undefined && typeof o.primary !== "boolean")
67
+ fail(`${path}.primary`, "must be a boolean");
68
+ if (o.dir !== undefined)
69
+ oneOf(o.dir, ["ltr", "auto"], `${path}.dir`);
70
+ const type = oneOf(o.type, INPUT_TYPES, `${path}.type`);
71
+ switch (type) {
72
+ case "text":
73
+ case "textarea": {
74
+ if (typeof o.default !== "string")
75
+ fail(`${path}.default`, `a ${type} input needs a string default`);
76
+ break;
77
+ }
78
+ case "number": {
79
+ if (typeof o.default !== "number" || !Number.isFinite(o.default)) {
80
+ fail(`${path}.default`, "a number input needs a finite number default");
81
+ }
82
+ if (typeof o.min !== "number" || typeof o.max !== "number") {
83
+ fail(`${path}.min/max`, "a number input must declare min and max — they are the only guard against a runaway input");
84
+ }
85
+ if (o.min >= o.max)
86
+ fail(`${path}.min`, `min (${o.min}) must be below max (${o.max})`);
87
+ if (o.default < o.min || o.default > o.max) {
88
+ fail(`${path}.default`, `default ${o.default} is outside min..max (${o.min}..${o.max})`);
89
+ }
90
+ break;
91
+ }
92
+ case "select": {
93
+ const options = arr(o.options, `${path}.options`);
94
+ if (options.length < 2)
95
+ fail(`${path}.options`, "a select needs at least two options; with one, use a fixed value");
96
+ const values = options.map((opt, i) => {
97
+ const oo = obj(opt, `${path}.options[${i}]`);
98
+ str(oo, "label", `${path}.options[${i}].`);
99
+ return str(oo, "value", `${path}.options[${i}].`);
100
+ });
101
+ if (new Set(values).size !== values.length)
102
+ fail(`${path}.options`, "option values must be unique");
103
+ if (typeof o.default !== "string" || !values.includes(o.default)) {
104
+ fail(`${path}.default`, `must be one of the option values: ${values.join(", ")}`);
105
+ }
106
+ break;
107
+ }
108
+ case "toggle": {
109
+ if (typeof o.default !== "boolean")
110
+ fail(`${path}.default`, "a toggle input needs a boolean default");
111
+ break;
112
+ }
113
+ }
114
+ return raw;
115
+ }
116
+ /**
117
+ * Validate a manifest that has already been brought up to the current contract version.
118
+ *
119
+ * Call `upgradeManifest` first unless you know the manifest is current — this function deliberately
120
+ * rejects an `sdk` it does not recognise rather than guessing.
121
+ */
122
+ export function validateManifest(raw) {
123
+ const o = obj(raw, "manifest");
124
+ // --- the contract version, first, because it decides how to read everything else -------------
125
+ if (typeof o.sdk !== "number" || !Number.isInteger(o.sdk)) {
126
+ fail("sdk", `must be an integer contract version; this SDK speaks ${SUPPORTED_SDK_VERSIONS.join(", ")}`);
127
+ }
128
+ if (o.sdk > SDK_VERSION) {
129
+ fail("sdk", `this tool needs contract version ${o.sdk}, but this runtime only speaks up to ${SDK_VERSION}. ` +
130
+ "Upgrade @toolbench/runtime, or lower the tool's sdk if it does not use the newer features.");
131
+ }
132
+ if (!SUPPORTED_SDK_VERSIONS.includes(o.sdk)) {
133
+ fail("sdk", `contract version ${o.sdk} is not one this SDK can read (${SUPPORTED_SDK_VERSIONS.join(", ")})`);
134
+ }
135
+ // --- identity ---------------------------------------------------------------------------------
136
+ const id = str(o, "id", "");
137
+ if (!ID.test(id))
138
+ fail("id", `"${id}" must be lowercase letters, digits and hyphens — it is also a directory name and a URL segment`);
139
+ str(o, "name", "");
140
+ const blurb = str(o, "blurb", "");
141
+ if (blurb.length > 200)
142
+ fail("blurb", `is ${blurb.length} characters; keep it under 200 so it works as a card line and a page description`);
143
+ str(o, "version", "");
144
+ if (o.status !== undefined)
145
+ oneOf(o.status, ["live", "deprecated", "retired"], "status");
146
+ if (o.help !== undefined)
147
+ optStr(o, "help", "");
148
+ if (o.tags !== undefined)
149
+ arr(o.tags, "tags").forEach((t, i) => { if (typeof t !== "string")
150
+ fail(`tags[${i}]`, "must be a string"); });
151
+ if (o.links !== undefined) {
152
+ arr(o.links, "links").forEach((l, i) => {
153
+ const lo = obj(l, `links[${i}]`);
154
+ str(lo, "label", `links[${i}].`);
155
+ str(lo, "href", `links[${i}].`);
156
+ });
157
+ }
158
+ // --- capabilities -----------------------------------------------------------------------------
159
+ const caps = arr(o.capabilities, "capabilities");
160
+ if (caps.length === 0)
161
+ fail("capabilities", `must declare at least one of ${CAPABILITIES.join(", ")}`);
162
+ const capabilities = caps.map((c, i) => oneOf(c, CAPABILITIES, `capabilities[${i}]`));
163
+ // --- runtime ----------------------------------------------------------------------------------
164
+ const runtime = obj(o.runtime, "runtime");
165
+ str(runtime, "entry", "runtime.");
166
+ const thread = runtime.thread === undefined ? "main" : oneOf(runtime.thread, ["main", "worker"], "runtime.thread");
167
+ // --- inputs -----------------------------------------------------------------------------------
168
+ const inputs = arr(o.inputs, "inputs");
169
+ if (inputs.length === 0)
170
+ fail("inputs", "a tool needs at least one input; a tool with none is a constant");
171
+ const specs = inputs.map((input, i) => validateInput(input, `inputs[${i}]`));
172
+ const ids = specs.map((s) => s.id);
173
+ if (new Set(ids).size !== ids.length)
174
+ fail("inputs", `input ids must be unique, got ${ids.join(", ")}`);
175
+ const primaries = specs.filter((s) => s.primary === true);
176
+ if (primaries.length > 1) {
177
+ fail("inputs", `only one input may be primary (a compact card shows exactly one), got ${primaries.map((p) => p.id).join(", ")}`);
178
+ }
179
+ // --- output kinds -----------------------------------------------------------------------------
180
+ const kinds = arr(o.kinds, "kinds");
181
+ if (kinds.length === 0)
182
+ fail("kinds", "declare every output kind `run` can return, so a host can refuse a tool it cannot draw");
183
+ const outputKinds = kinds.map((k, i) => oneOf(k, OUTPUT_KINDS, `kinds[${i}]`));
184
+ if (!outputKinds.includes("error")) {
185
+ fail("kinds", 'must include "error" — every tool can be given bad input, and saying so is part of working correctly');
186
+ }
187
+ // --- card -------------------------------------------------------------------------------------
188
+ const card = o.card === undefined ? "info" : oneOf(o.card, ["live", "info", "none"], "card");
189
+ if (o.cardFields !== undefined && (typeof o.cardFields !== "number" || o.cardFields < 1)) {
190
+ fail("cardFields", "must be a positive number of fields");
191
+ }
192
+ if (o.autoRun !== undefined && typeof o.autoRun !== "boolean")
193
+ fail("autoRun", "must be a boolean");
194
+ // --- timeout ----------------------------------------------------------------------------------
195
+ if (o.timeoutMs !== undefined) {
196
+ if (typeof o.timeoutMs !== "number" || o.timeoutMs < 100 || o.timeoutMs > 30_000) {
197
+ fail("timeoutMs", "must be between 100 and 30000 ms");
198
+ }
199
+ }
200
+ /*
201
+ * --- the invariants ---------------------------------------------------------------------------
202
+ * These are the rules the rest of the system leans on. Failing here is the whole point: a bad
203
+ * combination becomes a manifest error the author sees immediately, rather than a subtle problem
204
+ * a reader finds later.
205
+ */
206
+ if (card === "live" && !(capabilities.length === 1 && capabilities[0] === "pure")) {
207
+ fail("card", 'only a "pure" tool may be a live card — a compact slot must not read files or call networks');
208
+ }
209
+ if (o.autoRun === true && thread === "worker") {
210
+ fail("autoRun", 'is for tools that are instant, and a worker-mode tool is by definition not — it declared thread: "worker" ' +
211
+ "because its running time depends on its input. Leave autoRun off and let the reader press Run.");
212
+ }
213
+ if (thread === "main" && o.timeoutMs !== undefined) {
214
+ fail("timeoutMs", "is only meaningful with runtime.thread = \"worker\". On the main thread there is nothing to terminate, " +
215
+ "so a timeout here would be a field that lies. Either move the tool to a worker or bound its input.");
216
+ }
217
+ return {
218
+ ...raw,
219
+ capabilities,
220
+ inputs: specs,
221
+ kinds: outputKinds,
222
+ runtime: { ...runtime, entry: runtime.entry, thread },
223
+ card,
224
+ status: o.status ?? "live",
225
+ };
226
+ }
227
+ /** Human-readable summary of what a contract version offers. Used in error messages and docs. */
228
+ export function describeSdkVersion(version) {
229
+ return SDK_CHANGELOG[version] ?? `unknown contract version ${version}`;
230
+ }
231
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The version of the CONTRACT, not of any package.
3
+ *
4
+ * Every tool declares `sdk: <n>` in its manifest, and the runtime supports every version it has
5
+ * ever shipped. Raising this number is a deliberate act with a checklist — see docs/versioning.md.
6
+ */
7
+ export declare const SDK_VERSION = 1;
8
+ /** Every contract version this SDK can read. Never shrinks. */
9
+ export declare const SUPPORTED_SDK_VERSIONS: readonly number[];
10
+ /**
11
+ * What each contract version added. Kept as data so the runtime can explain itself: when a tool
12
+ * asks for a version we do not have, the error can say what it would need rather than just failing.
13
+ */
14
+ export declare const SDK_CHANGELOG: Record<number, string>;
15
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B,+DAA+D;AAC/D,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAAQ,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAEhD,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The version of the CONTRACT, not of any package.
3
+ *
4
+ * Every tool declares `sdk: <n>` in its manifest, and the runtime supports every version it has
5
+ * ever shipped. Raising this number is a deliberate act with a checklist — see docs/versioning.md.
6
+ */
7
+ export const SDK_VERSION = 1;
8
+ /** Every contract version this SDK can read. Never shrinks. */
9
+ export const SUPPORTED_SDK_VERSIONS = [1];
10
+ /**
11
+ * What each contract version added. Kept as data so the runtime can explain itself: when a tool
12
+ * asks for a version we do not have, the error can say what it would need rather than just failing.
13
+ */
14
+ export const SDK_CHANGELOG = {
15
+ 1: "computation and visualisation: fields, text, code, table, series, group, error outputs; text, textarea, number, select, toggle inputs; pure capability; main and worker threads.",
16
+ };
17
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAE7B,+DAA+D;AAC/D,MAAM,CAAC,MAAM,sBAAsB,GAAsB,CAAC,CAAC,CAAC,CAAC;AAE7D;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAA2B;IACpD,CAAC,EAAE,kLAAkL;CACrL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@toolbench/sdk",
3
+ "version": "0.1.0",
4
+ "description": "The contract a Toolbench tool implements: types, manifest validation, version migration, and a fixture runner. No runtime dependencies, no DOM.",
5
+ "license": "MIT",
6
+ "author": "Ahmed Elmalt",
7
+ "homepage": "https://github.com/eknowledger/toolbench#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/eknowledger/toolbench.git",
11
+ "directory": "packages/sdk"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/eknowledger/toolbench/issues"
15
+ },
16
+ "keywords": [
17
+ "toolbench",
18
+ "interactive",
19
+ "widget",
20
+ "tool",
21
+ "manifest",
22
+ "validation",
23
+ "fixtures",
24
+ "typescript",
25
+ "zero-dependencies"
26
+ ],
27
+ "type": "module",
28
+ "sideEffects": false,
29
+ "engines": {
30
+ "node": ">=20"
31
+ },
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "default": "./dist/index.js"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist"
40
+ ],
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc -b"
46
+ }
47
+ }