From 708e9243dd6ce60c128d231efc15170c689278dc Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 10:45:26 +0100 Subject: [PATCH] T-1: a TypeScript part reads a definition's callers as the format's list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Text and a bare name are refused naming the verb, as the Go reader does. Builds: design 47 G §196, §336, §392 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- src/callers/index.ts | 22 ++++++++++++++++++++++ test/sdk.test.ts | 7 ++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/callers/index.ts b/src/callers/index.ts index 7e15e5eb..6ed8fa10 100644 --- a/src/callers/index.ts +++ b/src/callers/index.ts @@ -174,3 +174,25 @@ export function parseConfiguredVerbCallers(raw: unknown, verbs: readonly string[ if (typeof raw === "string") return parseVerbCallers(raw, verbs, defaults); return parseVerbCallers(JSON.stringify(raw), verbs, defaults); } + +/** The kinds of name a seat definition's `callers` entry may be besides `seat:`, `module:` and `holder:@same-node` (design 47 G §336, §392). */ +const callerWords = ["operator", "healers", "persons", "sessions", "invokers"]; +const callerForm = /^(seat:[a-z][a-z0-9-]*|module:[a-z][a-z0-9-]*|holder:[a-z][a-z0-9-]*@same-node)$/; + +/** + * The callers a seat definition names for a verb or an accepted kind (design 47 G §336, §392): a list of entries, each + * one of the forms the format knows. Text is refused naming the verb, and so is an entry that is no form (a bare name + * among them), as the Go reader does. + */ +export function readDefinitionCallers(verb: string, value: unknown): readonly string[] { + if (typeof value === "string") { + throw new Error(`${verb}: callers are text ("${value}"): a definition gives its callers as a list`); + } + if (!Array.isArray(value)) throw new Error(`${verb}: callers is not a list of entries`); + for (const e of value) { + if (typeof e !== "string" || !(callerWords.includes(e) || callerForm.test(e))) { + throw new Error(`${verb}: the callers entry ${JSON.stringify(e)} is none of ${callerWords.join(", ")}, seat:, module: and holder:@same-node`); + } + } + return value as string[]; +} diff --git a/test/sdk.test.ts b/test/sdk.test.ts index 65de1cef..cd71f496 100644 --- a/test/sdk.test.ts +++ b/test/sdk.test.ts @@ -663,7 +663,7 @@ test("the provider loop is named for what it is, and its old name stays an alias assert.equal(loop.FAILING_EVENT, "provisioner.failing"); }); -import { allows, refusal, parseCallers, parseVerbCallers, parseConfiguredVerbCallers, moduleOf, UNPROVEN } from "../dist/callers/index.js"; +import { allows, refusal, parseCallers, parseVerbCallers, parseConfiguredVerbCallers, readDefinitionCallers, moduleOf, UNPROVEN } from "../dist/callers/index.js"; import { Follower, statusTools, keepStatus, statusKey, viewWaitsFinding, type Answer, type Status } from "../dist/views/index.js"; test("callers and verb callers judge a proved caller by the Go side's rules (ADR 0459 §2, ADR 0627 §2)", () => { @@ -677,6 +677,11 @@ test("callers and verb callers judge a proved caller by the Go side's rules (ADR ]; for (const [what, list, caller, want] of rows) assert.equal(allows(list, caller), want, what); assert.deepEqual(parseCallers('{"callers":["a","person.b"]}'), ["a", "person.b"]); + // design 47 G §336, §392: a definition's callers are a list of the format's forms; text and a bare name are refused + assert.deepEqual(readDefinitionCallers("list", ["operator", "invokers", "seat:mesh-grants", "holder:node-backup@same-node"]), + ["operator", "invokers", "seat:mesh-grants", "holder:node-backup@same-node"]); + assert.throws(() => readDefinitionCallers("list", "a person"), /list: callers are text/); + assert.throws(() => readDefinitionCallers("list", ["mesh-grants"]), /list: the callers entry "mesh-grants" is none of/); assert.throws(() => parseCallers('{"callers":["a"],"more":1}'), /unknown field "more"/); const verbs = parseVerbCallers('{"pull":["*"],"merge-pull":["mesh-delivery-forge-bridge"],"comment":["operator"]}', ["pull", "merge-pull", "comment"], {});