sysprom 1.12.0 → 1.13.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/dist/src/cli/commands/add.js +2 -2
- package/dist/src/cli/define-command.js +4 -2
- package/dist/src/cli/program.js +31 -2
- package/dist/src/mcp/server.js +5 -27
- package/dist/src/md-to-json.js +5 -5
- package/dist/src/operations/add-relationship.js +3 -2
- package/dist/src/operations/update-node.js +2 -2
- package/dist/src/operations/validate.js +3 -2
- package/dist/src/schema.d.ts +138 -0
- package/dist/src/schema.js +26 -12
- package/package.json +1 -1
|
@@ -46,7 +46,7 @@ export const addCommand = {
|
|
|
46
46
|
const { doc } = loaded;
|
|
47
47
|
const type = args.nodeType;
|
|
48
48
|
if (!NodeType.is(type)) {
|
|
49
|
-
console.error(`Unknown node type: ${type}`);
|
|
49
|
+
console.error(`Unknown node type: "${type}". Valid types: ${NodeType.options.join(", ")}`);
|
|
50
50
|
process.exit(1);
|
|
51
51
|
}
|
|
52
52
|
const id = opts.id ?? nextIdOp({ doc, type });
|
|
@@ -57,7 +57,7 @@ export const addCommand = {
|
|
|
57
57
|
}
|
|
58
58
|
if (opts.status) {
|
|
59
59
|
if (!NodeStatus.is(opts.status)) {
|
|
60
|
-
console.error(`Unknown status: ${opts.status}`);
|
|
60
|
+
console.error(`Unknown status: "${opts.status}". Valid statuses: ${NodeStatus.options.join(", ")}`);
|
|
61
61
|
process.exit(1);
|
|
62
62
|
}
|
|
63
63
|
node.status = opts.status;
|
|
@@ -101,12 +101,14 @@ export function buildCommander(def, parent) {
|
|
|
101
101
|
continue;
|
|
102
102
|
const desc = fieldDescription(field);
|
|
103
103
|
const choices = fieldChoices(field);
|
|
104
|
+
const optional = fieldIsOptional(field);
|
|
104
105
|
const flagName = camelToKebab(key);
|
|
105
106
|
if (choices) {
|
|
106
|
-
|
|
107
|
+
const arg = new Argument(optional ? `[${flagName}]` : `<${flagName}>`, desc).choices(choices);
|
|
108
|
+
cmd.addArgument(arg);
|
|
107
109
|
}
|
|
108
110
|
else {
|
|
109
|
-
cmd.argument(`<${flagName}>`, desc);
|
|
111
|
+
cmd.argument(optional ? `[${flagName}]` : `<${flagName}>`, desc);
|
|
110
112
|
}
|
|
111
113
|
}
|
|
112
114
|
}
|
package/dist/src/cli/program.js
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
1
4
|
import { Command } from "commander";
|
|
2
|
-
import packageJson from "../../package.json" with { type: "json" };
|
|
3
5
|
import { buildCommander } from "./define-command.js";
|
|
6
|
+
let cachedVersion;
|
|
7
|
+
function getVersion() {
|
|
8
|
+
if (cachedVersion === undefined) {
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
// Works from both src/cli/ (../../) and dist/src/cli/ (../../../)
|
|
11
|
+
const candidates = [
|
|
12
|
+
resolve(__dirname, "../../package.json"),
|
|
13
|
+
resolve(__dirname, "../../../package.json"),
|
|
14
|
+
];
|
|
15
|
+
const pkgPath = candidates.find((p) => existsSync(p) && !p.includes("/dist/"));
|
|
16
|
+
if (!pkgPath)
|
|
17
|
+
throw new Error("Could not find sysprom package.json");
|
|
18
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
19
|
+
if (typeof pkg !== "object" ||
|
|
20
|
+
pkg === null ||
|
|
21
|
+
!("version" in pkg) ||
|
|
22
|
+
typeof pkg.version !== "string") {
|
|
23
|
+
throw new Error("Invalid package.json: missing version field");
|
|
24
|
+
}
|
|
25
|
+
cachedVersion = pkg.version;
|
|
26
|
+
}
|
|
27
|
+
return cachedVersion;
|
|
28
|
+
}
|
|
4
29
|
import { validateCommand } from "./commands/validate.js";
|
|
5
30
|
import { statsCommand } from "./commands/stats.js";
|
|
6
31
|
import { json2mdCommand } from "./commands/json2md.js";
|
|
@@ -22,7 +47,11 @@ export const program = new Command();
|
|
|
22
47
|
program
|
|
23
48
|
.name("sysprom")
|
|
24
49
|
.description("System Provenance Model CLI — record where every part of a system came from")
|
|
25
|
-
.
|
|
50
|
+
.option("-V, --version", "output the version number")
|
|
51
|
+
.on("option:version", () => {
|
|
52
|
+
console.log(getVersion());
|
|
53
|
+
process.exit(0);
|
|
54
|
+
})
|
|
26
55
|
.showHelpAfterError(true);
|
|
27
56
|
export const commands = [
|
|
28
57
|
validateCommand,
|
package/dist/src/mcp/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { loadDocument } from "../io.js";
|
|
6
|
-
import { RelationshipType } from "../schema.js";
|
|
6
|
+
import { NodeType, RelationshipType } from "../schema.js";
|
|
7
7
|
import { validateOp, statsOp, queryNodesOp, queryNodeOp, queryRelationshipsOp, traceFromNodeOp, addNodeOp, removeNodeOp, updateNodeOp, addRelationshipOp, removeRelationshipOp, nextIdOp, } from "../operations/index.js";
|
|
8
8
|
// Create MCP server instance
|
|
9
9
|
const server = new McpServer({
|
|
@@ -146,31 +146,9 @@ server.registerTool("add-node", {
|
|
|
146
146
|
}),
|
|
147
147
|
}, ({ path, type, id, name, description }) => {
|
|
148
148
|
const { doc } = loadDocument(path);
|
|
149
|
-
const nodeType =
|
|
150
|
-
.enum([
|
|
151
|
-
"intent",
|
|
152
|
-
"concept",
|
|
153
|
-
"capability",
|
|
154
|
-
"element",
|
|
155
|
-
"realisation",
|
|
156
|
-
"invariant",
|
|
157
|
-
"principle",
|
|
158
|
-
"policy",
|
|
159
|
-
"protocol",
|
|
160
|
-
"stage",
|
|
161
|
-
"role",
|
|
162
|
-
"gate",
|
|
163
|
-
"mode",
|
|
164
|
-
"artefact",
|
|
165
|
-
"artefact_flow",
|
|
166
|
-
"decision",
|
|
167
|
-
"change",
|
|
168
|
-
"view",
|
|
169
|
-
"version",
|
|
170
|
-
])
|
|
171
|
-
.safeParse(type);
|
|
149
|
+
const nodeType = NodeType.safeParse(type);
|
|
172
150
|
if (!nodeType.success) {
|
|
173
|
-
throw new Error(`Invalid node type: ${type}`);
|
|
151
|
+
throw new Error(`Invalid node type: "${type}". Valid types: ${NodeType.options.join(", ")}`);
|
|
174
152
|
}
|
|
175
153
|
const nodeId = id ?? nextIdOp({ doc, type: nodeType.data });
|
|
176
154
|
const updated = addNodeOp({
|
|
@@ -280,7 +258,7 @@ server.registerTool("add-relationship", {
|
|
|
280
258
|
const { doc } = loadDocument(path);
|
|
281
259
|
const relType = RelationshipType.safeParse(type);
|
|
282
260
|
if (!relType.success) {
|
|
283
|
-
throw new Error(`Invalid relationship type: ${type}`);
|
|
261
|
+
throw new Error(`Invalid relationship type: "${type}". Valid types: ${RelationshipType.options.join(", ")}`);
|
|
284
262
|
}
|
|
285
263
|
const updated = addRelationshipOp({
|
|
286
264
|
doc,
|
|
@@ -315,7 +293,7 @@ server.registerTool("remove-relationship", {
|
|
|
315
293
|
const { doc } = loadDocument(path);
|
|
316
294
|
const relType = RelationshipType.safeParse(type);
|
|
317
295
|
if (!relType.success) {
|
|
318
|
-
throw new Error(`Invalid relationship type: ${type}`);
|
|
296
|
+
throw new Error(`Invalid relationship type: "${type}". Valid types: ${RelationshipType.options.join(", ")}`);
|
|
319
297
|
}
|
|
320
298
|
const result = removeRelationshipOp({
|
|
321
299
|
doc,
|
package/dist/src/md-to-json.js
CHANGED
|
@@ -7,25 +7,25 @@ const operationType = z.enum(["add", "update", "remove", "link"]);
|
|
|
7
7
|
function parseNodeType(s) {
|
|
8
8
|
const result = NodeType.safeParse(s);
|
|
9
9
|
if (!result.success)
|
|
10
|
-
throw new Error(`Unknown node type: ${s}`);
|
|
10
|
+
throw new Error(`Unknown node type: "${s}". Valid types: ${NodeType.options.join(", ")}`);
|
|
11
11
|
return result.data;
|
|
12
12
|
}
|
|
13
13
|
function parseRelType(s) {
|
|
14
14
|
const result = RelationshipType.safeParse(s);
|
|
15
15
|
if (!result.success)
|
|
16
|
-
throw new Error(`Unknown relationship type: ${s}`);
|
|
16
|
+
throw new Error(`Unknown relationship type: "${s}". Valid types: ${RelationshipType.options.join(", ")}`);
|
|
17
17
|
return result.data;
|
|
18
18
|
}
|
|
19
19
|
function parseNodeStatus(s) {
|
|
20
20
|
const result = NodeStatus.safeParse(s);
|
|
21
21
|
if (!result.success)
|
|
22
|
-
throw new Error(`Unknown node status: ${s}`);
|
|
22
|
+
throw new Error(`Unknown node status: "${s}". Valid statuses: ${NodeStatus.options.join(", ")}`);
|
|
23
23
|
return result.data;
|
|
24
24
|
}
|
|
25
25
|
function parseExtRefRole(s) {
|
|
26
26
|
const result = ExternalReferenceRole.safeParse(s);
|
|
27
27
|
if (!result.success)
|
|
28
|
-
throw new Error(`Unknown external reference role: ${s}`);
|
|
28
|
+
throw new Error(`Unknown external reference role: "${s}". Valid roles: ${ExternalReferenceRole.options.join(", ")}`);
|
|
29
29
|
return result.data;
|
|
30
30
|
}
|
|
31
31
|
// ---------------------------------------------------------------------------
|
|
@@ -286,7 +286,7 @@ function parseNodeFromSection(section) {
|
|
|
286
286
|
const rawType = parts[0];
|
|
287
287
|
const parsed = operationType.safeParse(rawType);
|
|
288
288
|
if (!parsed.success) {
|
|
289
|
-
throw new Error(`Unknown operation type: ${rawType}`);
|
|
289
|
+
throw new Error(`Unknown operation type: "${rawType}". Valid types: ${operationType.options.join(", ")}`);
|
|
290
290
|
}
|
|
291
291
|
const type = parsed.data;
|
|
292
292
|
const rest = parts.slice(1);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { defineOperation } from "./define-operation.js";
|
|
3
3
|
import { SysProMDocument, Relationship } from "../schema.js";
|
|
4
|
-
import { isValidEndpointPair } from "../endpoint-types.js";
|
|
4
|
+
import { isValidEndpointPair, RELATIONSHIP_ENDPOINT_TYPES, } from "../endpoint-types.js";
|
|
5
5
|
/**
|
|
6
6
|
* Add a relationship to a SysProM document. Returns a new document with the relationship appended.
|
|
7
7
|
* @throws {Error} If either endpoint node does not exist, endpoint types are invalid, or the relationship is a duplicate.
|
|
@@ -26,7 +26,8 @@ export const addRelationshipOp = defineOperation({
|
|
|
26
26
|
}
|
|
27
27
|
// Validate endpoint types for this relationship
|
|
28
28
|
if (!isValidEndpointPair(rel.type, fromNode.type, toNode.type)) {
|
|
29
|
-
|
|
29
|
+
const endpoints = RELATIONSHIP_ENDPOINT_TYPES[rel.type];
|
|
30
|
+
throw new Error(`Invalid endpoint types for ${rel.type}: ${fromNode.type} → ${toNode.type}. Valid: [${endpoints.from.join(", ")}] → [${endpoints.to.join(", ")}]`);
|
|
30
31
|
}
|
|
31
32
|
// Check for duplicate relationship
|
|
32
33
|
const isDuplicate = (doc.relationships ?? []).some((r) => r.from === rel.from && r.to === rel.to && r.type === rel.type);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { defineOperation } from "./define-operation.js";
|
|
3
|
-
import { SysProMDocument,
|
|
3
|
+
import { SysProMDocument, NodeBase } from "../schema.js";
|
|
4
4
|
/**
|
|
5
5
|
* Update specified fields on a node, merging the provided fields into the existing node.
|
|
6
6
|
* @throws {Error} If the node ID is not found.
|
|
@@ -11,7 +11,7 @@ export const updateNodeOp = defineOperation({
|
|
|
11
11
|
input: z.object({
|
|
12
12
|
doc: SysProMDocument,
|
|
13
13
|
id: z.string(),
|
|
14
|
-
fields:
|
|
14
|
+
fields: NodeBase.partial(),
|
|
15
15
|
}),
|
|
16
16
|
output: SysProMDocument,
|
|
17
17
|
fn({ doc, id, fields }) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { defineOperation } from "./define-operation.js";
|
|
3
3
|
import { SysProMDocument } from "../schema.js";
|
|
4
|
-
import { isValidEndpointPair } from "../endpoint-types.js";
|
|
4
|
+
import { isValidEndpointPair, RELATIONSHIP_ENDPOINT_TYPES, } from "../endpoint-types.js";
|
|
5
5
|
/** Zod schema for the result of validating a SysProM document. */
|
|
6
6
|
export const ValidationResult = z.object({
|
|
7
7
|
valid: z.boolean(),
|
|
@@ -67,7 +67,8 @@ export const validateOp = defineOperation({
|
|
|
67
67
|
if (fromNode &&
|
|
68
68
|
toNode &&
|
|
69
69
|
!isValidEndpointPair(r.type, fromNode.type, toNode.type)) {
|
|
70
|
-
|
|
70
|
+
const endpoints = RELATIONSHIP_ENDPOINT_TYPES[r.type];
|
|
71
|
+
issues.push(`Invalid endpoint types for ${r.type}: ${fromNode.type} → ${toNode.type} (${r.from} → ${r.to}). Valid: [${endpoints.from.join(", ")}] → [${endpoints.to.join(", ")}]`);
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
// Operational relationships to retired nodes
|
package/dist/src/schema.d.ts
CHANGED
|
@@ -412,6 +412,144 @@ declare const SysProMDocumentSchema: z.ZodObject<{
|
|
|
412
412
|
};
|
|
413
413
|
}>>;
|
|
414
414
|
}, z.core.$strip>;
|
|
415
|
+
/** Base node object schema without ID-prefix refinement. Supports .partial(). */
|
|
416
|
+
export declare const NodeBase: z.ZodObject<{
|
|
417
|
+
id: z.ZodString;
|
|
418
|
+
type: z.ZodEnum<{
|
|
419
|
+
intent: "intent";
|
|
420
|
+
concept: "concept";
|
|
421
|
+
capability: "capability";
|
|
422
|
+
element: "element";
|
|
423
|
+
realisation: "realisation";
|
|
424
|
+
invariant: "invariant";
|
|
425
|
+
principle: "principle";
|
|
426
|
+
policy: "policy";
|
|
427
|
+
protocol: "protocol";
|
|
428
|
+
stage: "stage";
|
|
429
|
+
role: "role";
|
|
430
|
+
gate: "gate";
|
|
431
|
+
mode: "mode";
|
|
432
|
+
artefact: "artefact";
|
|
433
|
+
artefact_flow: "artefact_flow";
|
|
434
|
+
decision: "decision";
|
|
435
|
+
change: "change";
|
|
436
|
+
view: "view";
|
|
437
|
+
milestone: "milestone";
|
|
438
|
+
version: "version";
|
|
439
|
+
}> & {
|
|
440
|
+
is(value: unknown): value is "intent" | "concept" | "capability" | "element" | "realisation" | "invariant" | "principle" | "policy" | "protocol" | "stage" | "role" | "gate" | "mode" | "artefact" | "artefact_flow" | "decision" | "change" | "view" | "milestone" | "version";
|
|
441
|
+
};
|
|
442
|
+
name: z.ZodString;
|
|
443
|
+
description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
444
|
+
is(value: unknown): value is string | string[];
|
|
445
|
+
}>;
|
|
446
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
447
|
+
deprecated: "deprecated";
|
|
448
|
+
proposed: "proposed";
|
|
449
|
+
accepted: "accepted";
|
|
450
|
+
active: "active";
|
|
451
|
+
implemented: "implemented";
|
|
452
|
+
adopted: "adopted";
|
|
453
|
+
defined: "defined";
|
|
454
|
+
introduced: "introduced";
|
|
455
|
+
in_progress: "in_progress";
|
|
456
|
+
complete: "complete";
|
|
457
|
+
consolidated: "consolidated";
|
|
458
|
+
experimental: "experimental";
|
|
459
|
+
retired: "retired";
|
|
460
|
+
superseded: "superseded";
|
|
461
|
+
abandoned: "abandoned";
|
|
462
|
+
deferred: "deferred";
|
|
463
|
+
}> & {
|
|
464
|
+
is(value: unknown): value is "deprecated" | "proposed" | "accepted" | "active" | "implemented" | "adopted" | "defined" | "introduced" | "in_progress" | "complete" | "consolidated" | "experimental" | "retired" | "superseded" | "abandoned" | "deferred";
|
|
465
|
+
}>;
|
|
466
|
+
lifecycle: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>>;
|
|
467
|
+
context: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
468
|
+
is(value: unknown): value is string | string[];
|
|
469
|
+
}>;
|
|
470
|
+
options: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
471
|
+
id: z.ZodString;
|
|
472
|
+
description: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
473
|
+
is(value: unknown): value is string | string[];
|
|
474
|
+
};
|
|
475
|
+
}, z.core.$loose> & {
|
|
476
|
+
is(value: unknown): value is {
|
|
477
|
+
[x: string]: unknown;
|
|
478
|
+
id: string;
|
|
479
|
+
description: string | string[];
|
|
480
|
+
};
|
|
481
|
+
}>>;
|
|
482
|
+
selected: z.ZodOptional<z.ZodString>;
|
|
483
|
+
rationale: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
484
|
+
is(value: unknown): value is string | string[];
|
|
485
|
+
}>;
|
|
486
|
+
scope: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
487
|
+
operations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
488
|
+
type: z.ZodEnum<{
|
|
489
|
+
link: "link";
|
|
490
|
+
add: "add";
|
|
491
|
+
update: "update";
|
|
492
|
+
remove: "remove";
|
|
493
|
+
}>;
|
|
494
|
+
target: z.ZodOptional<z.ZodString>;
|
|
495
|
+
description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
496
|
+
is(value: unknown): value is string | string[];
|
|
497
|
+
}>;
|
|
498
|
+
}, z.core.$loose> & {
|
|
499
|
+
is(value: unknown): value is {
|
|
500
|
+
[x: string]: unknown;
|
|
501
|
+
type: "link" | "add" | "update" | "remove";
|
|
502
|
+
target?: string | undefined;
|
|
503
|
+
description?: string | string[] | undefined;
|
|
504
|
+
};
|
|
505
|
+
}>>;
|
|
506
|
+
plan: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
507
|
+
description: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
508
|
+
is(value: unknown): value is string | string[];
|
|
509
|
+
};
|
|
510
|
+
done: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
511
|
+
}, z.core.$loose> & {
|
|
512
|
+
is(value: unknown): value is {
|
|
513
|
+
[x: string]: unknown;
|
|
514
|
+
description: string | string[];
|
|
515
|
+
done?: boolean | undefined;
|
|
516
|
+
};
|
|
517
|
+
}>>;
|
|
518
|
+
propagation: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
|
|
519
|
+
includes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
520
|
+
input: z.ZodOptional<z.ZodString>;
|
|
521
|
+
output: z.ZodOptional<z.ZodString>;
|
|
522
|
+
external_references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
523
|
+
role: z.ZodEnum<{
|
|
524
|
+
output: "output";
|
|
525
|
+
input: "input";
|
|
526
|
+
context: "context";
|
|
527
|
+
evidence: "evidence";
|
|
528
|
+
source: "source";
|
|
529
|
+
standard: "standard";
|
|
530
|
+
prior_art: "prior_art";
|
|
531
|
+
}> & {
|
|
532
|
+
is(value: unknown): value is "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
|
|
533
|
+
};
|
|
534
|
+
identifier: z.ZodString;
|
|
535
|
+
description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
536
|
+
is(value: unknown): value is string | string[];
|
|
537
|
+
}>;
|
|
538
|
+
node_id: z.ZodOptional<z.ZodString>;
|
|
539
|
+
internalised: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
540
|
+
is(value: unknown): value is string | string[];
|
|
541
|
+
}>;
|
|
542
|
+
}, z.core.$strip> & {
|
|
543
|
+
is(value: unknown): value is {
|
|
544
|
+
role: "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
|
|
545
|
+
identifier: string;
|
|
546
|
+
description?: string | string[] | undefined;
|
|
547
|
+
node_id?: string | undefined;
|
|
548
|
+
internalised?: string | string[] | undefined;
|
|
549
|
+
};
|
|
550
|
+
}>>;
|
|
551
|
+
readonly subsystem: z.ZodOptional<typeof SysProMDocumentSchema>;
|
|
552
|
+
}, z.core.$loose>;
|
|
415
553
|
declare const NodeSchema: z.ZodObject<{
|
|
416
554
|
id: z.ZodString;
|
|
417
555
|
type: z.ZodEnum<{
|
package/dist/src/schema.js
CHANGED
|
@@ -250,7 +250,8 @@ const SysProMDocumentSchema = z
|
|
|
250
250
|
title: "SysProM: System Provenance Model",
|
|
251
251
|
description: "JSON Schema for SysProM — a recursive, decision-driven model for recording system provenance.",
|
|
252
252
|
});
|
|
253
|
-
|
|
253
|
+
/** Base node object schema without ID-prefix refinement. Supports .partial(). */
|
|
254
|
+
export const NodeBase = z
|
|
254
255
|
.looseObject({
|
|
255
256
|
id: z.string().describe("Unique identifier for this node."),
|
|
256
257
|
type: NodeType,
|
|
@@ -308,6 +309,19 @@ const NodeSchema = z
|
|
|
308
309
|
},
|
|
309
310
|
})
|
|
310
311
|
.describe("A uniquely identifiable entity within the system.");
|
|
312
|
+
const NodeSchema = NodeBase.superRefine((node, ctx) => {
|
|
313
|
+
const prefix = NODE_ID_PREFIX[node.type];
|
|
314
|
+
if (!prefix)
|
|
315
|
+
return; // Unknown type — skip validation
|
|
316
|
+
const pattern = new RegExp(`^${prefix}\\d+(-[A-Z][A-Z0-9_]*)*$`);
|
|
317
|
+
if (!pattern.test(node.id)) {
|
|
318
|
+
ctx.addIssue({
|
|
319
|
+
code: z.ZodIssueCode.custom,
|
|
320
|
+
path: ["id"],
|
|
321
|
+
message: `Node ID "${node.id}" does not match required pattern for type "${node.type}": expected ${prefix}<number> with optional -SUFFIX segments (e.g. ${prefix}1, ${prefix}1-MY-LABEL)`,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
});
|
|
311
325
|
// Attach .is() type guards after both schemas are declared
|
|
312
326
|
/**
|
|
313
327
|
* Zod schema for a complete SysProM document — the root container holding
|
|
@@ -344,13 +358,13 @@ export const NODE_FILE_MAP = {
|
|
|
344
358
|
};
|
|
345
359
|
/** Conventional ID prefix for each node type. */
|
|
346
360
|
export const NODE_ID_PREFIX = {
|
|
347
|
-
intent: "
|
|
348
|
-
concept: "
|
|
349
|
-
capability: "
|
|
350
|
-
element: "
|
|
351
|
-
realisation: "
|
|
361
|
+
intent: "INT",
|
|
362
|
+
concept: "CON",
|
|
363
|
+
capability: "CAP",
|
|
364
|
+
element: "ELEM",
|
|
365
|
+
realisation: "REAL",
|
|
352
366
|
invariant: "INV",
|
|
353
|
-
principle: "
|
|
367
|
+
principle: "PRIN",
|
|
354
368
|
policy: "POL",
|
|
355
369
|
protocol: "PROT",
|
|
356
370
|
stage: "STG",
|
|
@@ -358,11 +372,11 @@ export const NODE_ID_PREFIX = {
|
|
|
358
372
|
gate: "GATE",
|
|
359
373
|
mode: "MODE",
|
|
360
374
|
artefact: "ART",
|
|
361
|
-
artefact_flow: "
|
|
362
|
-
decision: "
|
|
363
|
-
change: "
|
|
364
|
-
view: "
|
|
365
|
-
milestone: "
|
|
375
|
+
artefact_flow: "FLOW",
|
|
376
|
+
decision: "DEC",
|
|
377
|
+
change: "CHG",
|
|
378
|
+
view: "VIEW",
|
|
379
|
+
milestone: "MILE",
|
|
366
380
|
version: "VER",
|
|
367
381
|
};
|
|
368
382
|
// ---------------------------------------------------------------------------
|