@toolproof-npm/schema 0.1.43 → 0.1.44

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.
@@ -1151,5 +1151,5 @@
1151
1151
  "pattern": "^WORKSTEP-.+$"
1152
1152
  }
1153
1153
  },
1154
- "$comment": "This schema defines all genesis schemas used throughout the Toolproof ecosystem. The genesis schemas themselves are defined in $defs.<ResourceType>.extractionSchema. The build process (via extractSchemas.js) extracts these schemas and writes them to a separate file (src/schemas/Genesis.json). The reason for this indirection is to have all these schema envelopes validate positively against the schema at $defs/ResourceType, effectively making them ResourceTypes, which are first-class citizens in the Toolproof ecosystem."
1154
+ "$comment": "This schema defines all genesis schemas used throughout the Toolproof ecosystem. The genesis schemas themselves are defined in $defs.<ResourceType>.extractionSchema. The build process (via extractSchemas.js) extracts these schemas and writes them to a separate file (genesis/generated/schemas/Genesis.json). The reason for this indirection is to have all these schema envelopes validate positively against the schema at $defs/ResourceType, effectively making them ResourceTypes, which are first-class citizens in the Toolproof ecosystem."
1155
1155
  }
@@ -4,7 +4,7 @@
4
4
  "description": "dummy-description",
5
5
  "resourceFormatId": "FORMAT-ApplicationJson",
6
6
  "extractionSchema": {
7
- "$comment": "This schema defines all genesis schemas used throughout the Toolproof ecosystem. The genesis schemas themselves are defined in $defs.<ResourceType>.extractionSchema. The build process (via extractSchemas.js) extracts these schemas and writes them to a separate file (src/schemas/Genesis.json). The reason for this indirection is to have all these schema envelopes validate positively against the schema at $defs/ResourceType, effectively making them ResourceTypes, which are first-class citizens in the Toolproof ecosystem.",
7
+ "$comment": "This schema defines all genesis schemas used throughout the Toolproof ecosystem. The genesis schemas themselves are defined in $defs.<ResourceType>.extractionSchema. The build process (via extractSchemas.js) extracts these schemas and writes them to a separate file (genesis/generated/schemas/Genesis.json). The reason for this indirection is to have all these schema envelopes validate positively against the schema at $defs/ResourceType, effectively making them ResourceTypes, which are first-class citizens in the Toolproof ecosystem.",
8
8
  "$id": "https://schemas.toolproof.com/v0/Genesis.json",
9
9
  "$schema": "https://json-schema.org/draft/2020-12/schema",
10
10
  "$defs": {
package/dist/index.d.ts CHANGED
@@ -4,6 +4,6 @@ export { default as JobSchema } from './genesis/generated/schemas/Job.js';
4
4
  export type { Resource_ResourceFormat as Resource_ResourceFormatJson } from './_lib/types/Resource_ResourceFormat.js';
5
5
  export type { Resource_ResourceType as Resource_ResourceTypeJson } from './_lib/types/Resource_ResourceType.js';
6
6
  export type { Resource_Job as Resource_JobJson } from './_lib/types/Resource_Job.js';
7
- export type { Documented as DocumentedJson, ResourceFormatId as ResourceFormatIdJson, ResourceFormat as ResourceFormatJson, ExtractionSchema as ExtractionSchemaJson, ExtractionSchemaValue as ExtractionSchemaValueJson, IdentityProp as IdentityPropJson, MeritProp as MeritPropJson, ResourceTypeId as ResourceTypeIdJson, ResourceType as ResourceTypeJson, ResourceRoleId as ResourceRoleIdJson, ResourceRoleValue as ResourceRoleValueJson, ExecutionId as ExecutionIdJson, Execution as ExecutionJson, ConditionalWrapper as ConditionalWrapperJson, RoleMap as RoleMapJson, RolesOuter as RolesOuterJson, RoleBindingMap as RoleBindingMapJson, RoleBindingsOuter as RoleBindingsOuterJson, ResourceId as ResourceIdJson, WorkStepId as WorkStepIdJson, BranchStepId as BranchStepIdJson, WhileStepId as WhileStepIdJson, ForStepId as ForStepIdJson, WorkStep as WorkStepJson, BranchStep as BranchStepJson, WhileStep as WhileStepJson, ForStep as ForStepJson, Step as StepJson, CreationContextValue as CreationContextValueJson, ResourcePotentialInput as ResourcePotentialInputJson, ResourcePotentialOutput as ResourcePotentialOutputJson, ResourceMetaBase as ResourceMetaJson, // ATTENTION: type not generated for ResourceMeta
7
+ export type { Documented as DocumentedJson, ResourceFormatId as ResourceFormatIdJson, ResourceFormat as ResourceFormatJson, ExtractionSchema as ExtractionSchemaJson, ExtractionSchemaValue as ExtractionSchemaValueJson, IdentityProp as IdentityPropJson, MeritProp as MeritPropJson, ResourceTypeId as ResourceTypeIdJson, ResourceType as ResourceTypeJson, ResourceRoleId as ResourceRoleIdJson, ResourceRoleValue as ResourceRoleValueJson, ExecutionId as ExecutionIdJson, Execution as ExecutionJson, ConditionalWrapper as ConditionalWrapperJson, RoleMap as RoleMapJson, RolesOuter as RolesOuterJson, RoleBindingMap as RoleBindingMapJson, RoleBindingsOuter as RoleBindingsOuterJson, ResourceId as ResourceIdJson, WorkStepId as WorkStepIdJson, BranchStepId as BranchStepIdJson, WhileStepId as WhileStepIdJson, ForStepId as ForStepIdJson, WorkStep as WorkStepJson, BranchStep as BranchStepJson, WhileStep as WhileStepJson, ForStep as ForStepJson, Step as StepJson, CreationContextValue as CreationContextValueJson, ResourceMissing as ResourceMissingJson, ResourcePotentialInput as ResourcePotentialInputJson, ResourcePotentialOutput as ResourcePotentialOutputJson, ResourceMetaBase as ResourceMetaJson, // ATTENTION: type not generated for ResourceMeta
8
8
  Resource as ResourceJson, StrategyState as StrategyStateJson, StrategyStateValue as StrategyStateValueJson, StatelessStrategyId as StatelessStrategyIdJson, StatelessStrategy as StatelessStrategyJson, StatefulStrategyId as StatefulStrategyIdJson, StatefulStrategy as StatefulStrategyJson, JobId as JobIdJson, Job as JobJson, JsonValue as JsonValueJson, } from './_lib/types/types.js';
9
9
  export { unsafeBrand, asResourceTypeId, asResourceRoleId, asExecutionId, asResourceId, asWorkStepId, asBranchStepId, asForStepId, asResourceFormatId, asWhileStepId, asStatelessStrategyId, asStatefulStrategyId, asResourceTypeIds, asResourceRoleIds, asExecutionIds, asResourceIds, asWorkStepIds, asBranchStepIds, asForStepIds, asResourceFormatIds, asWhileStepIds, asStatelessStrategyIds, asStatefulStrategyIds, } from './scripts/brandFactories.js';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,199 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { getConfig } from "./_lib/config.js";
5
+ function stripSurroundingQuotes(v) {
6
+ if ((v.startsWith("'") && v.endsWith("'")) || (v.startsWith('"') && v.endsWith('"'))) {
7
+ return v.slice(1, -1);
8
+ }
9
+ return v;
10
+ }
11
+ function parseArgs() {
12
+ const config = getConfig();
13
+ const argv = process.argv.slice(2);
14
+ let storyInPath = "";
15
+ let storyOutPath = "";
16
+ let schemasPath = "";
17
+ let resourceTypesPath = "";
18
+ for (let i = 0; i < argv.length; i++) {
19
+ const a = argv[i];
20
+ if ((a === "--in" || a === "--storyIn") && i + 1 < argv.length)
21
+ storyInPath = stripSurroundingQuotes(argv[++i]);
22
+ else if ((a === "--out" || a === "--storyOut") && i + 1 < argv.length)
23
+ storyOutPath = stripSurroundingQuotes(argv[++i]);
24
+ else if ((a === "--schemas" || a === "--schemasPath") && i + 1 < argv.length)
25
+ schemasPath = stripSurroundingQuotes(argv[++i]);
26
+ else if ((a === "--resourceTypes" || a === "--resourceTypesPath") && i + 1 < argv.length)
27
+ resourceTypesPath = stripSurroundingQuotes(argv[++i]);
28
+ }
29
+ // Defaults
30
+ if (!storyInPath)
31
+ storyInPath = path.join(config.getRoot(), "src/genesis/GenesisStory.json");
32
+ if (!storyOutPath)
33
+ storyOutPath = path.join(config.getRoot(), "src/genesis/generated/GenesisStory.json");
34
+ // Extracted schemas (raw JSON Schemas)
35
+ if (!schemasPath)
36
+ schemasPath = config.getOutputPath(config.getSourceFile());
37
+ // ResourceType envelopes (ToolProof resources)
38
+ if (!resourceTypesPath)
39
+ resourceTypesPath = config.getSourcePath();
40
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
41
+ // Resolve relative paths against config root; then fallback relative to script dir
42
+ const resolveWithFallback = (p) => {
43
+ const wasRelative = !path.isAbsolute(p);
44
+ let candidate = p;
45
+ if (wasRelative)
46
+ candidate = path.join(config.getRoot(), p);
47
+ if (fs.existsSync(candidate))
48
+ return candidate;
49
+ if (wasRelative) {
50
+ const alt = path.resolve(scriptDir, p);
51
+ if (fs.existsSync(alt))
52
+ return alt;
53
+ }
54
+ return candidate;
55
+ };
56
+ storyInPath = resolveWithFallback(storyInPath);
57
+ storyOutPath = !path.isAbsolute(storyOutPath) ? path.join(config.getRoot(), storyOutPath) : storyOutPath;
58
+ schemasPath = resolveWithFallback(schemasPath);
59
+ resourceTypesPath = resolveWithFallback(resourceTypesPath);
60
+ return { storyInPath, storyOutPath, schemasPath, resourceTypesPath };
61
+ }
62
+ function deepClone(v) {
63
+ return JSON.parse(JSON.stringify(v));
64
+ }
65
+ function isRefWrapper(node) {
66
+ return (!!node &&
67
+ typeof node === "object" &&
68
+ !Array.isArray(node) &&
69
+ node.$ref &&
70
+ typeof node.$ref === "string" &&
71
+ Object.keys(node).length === 1);
72
+ }
73
+ function refToDefName(ref) {
74
+ let r = stripSurroundingQuotes(ref.trim());
75
+ // Support URL#anchor or URL#/$defs/Name
76
+ const hashIndex = r.indexOf("#");
77
+ if (hashIndex >= 0)
78
+ r = r.slice(hashIndex + 1);
79
+ // r could now be "Name" or "/$defs/Name" or "$defs/Name"
80
+ if (r.startsWith("/"))
81
+ r = r.slice(1);
82
+ if (r.startsWith("$defs/"))
83
+ r = r.slice("$defs/".length);
84
+ // If it's still a JSON pointer-like path, support only '$defs/<Name>'
85
+ if (r.includes("/")) {
86
+ const parts = r.split("/").filter(Boolean);
87
+ if (parts.length === 2 && parts[0] === "$defs")
88
+ return parts[1];
89
+ throw new Error(`Unsupported $ref format in GenesisStory: '${ref}'`);
90
+ }
91
+ return r;
92
+ }
93
+ function applyRefAliases(defName) {
94
+ // Backwards-compat / naming aliases (story names -> canonical def names)
95
+ const aliasMap = {
96
+ ExtractedSchemaValue: "ExtractionSchemaValue",
97
+ ExtractedSchema: "ExtractionSchema",
98
+ };
99
+ return defName in aliasMap ? aliasMap[defName] : defName;
100
+ }
101
+ function resolveRawSchemaDef(extractedSchemas, defName) {
102
+ defName = applyRefAliases(defName);
103
+ const defs = extractedSchemas?.$defs;
104
+ if (!defs || typeof defs !== "object") {
105
+ throw new Error("Extracted schemas file must contain a top-level $defs object");
106
+ }
107
+ if (defName in defs)
108
+ return defs[defName];
109
+ for (const v of Object.values(defs)) {
110
+ if (v && typeof v === "object" && v.$anchor === defName)
111
+ return v;
112
+ }
113
+ const available = Object.keys(defs).slice(0, 25);
114
+ throw new Error(`Could not resolve raw schema for ref '${defName}' from extracted schemas.$defs. ` +
115
+ `Example available keys: ${available.join(", ")}${Object.keys(defs).length > 25 ? ", ..." : ""}`);
116
+ }
117
+ function resolveResourceTypeEnvelope(resourceTypesDoc, defName) {
118
+ defName = applyRefAliases(defName);
119
+ const defs = resourceTypesDoc?.extractionSchema?.$defs;
120
+ if (!defs || typeof defs !== "object") {
121
+ throw new Error("ResourceTypes file must contain extractionSchema.$defs");
122
+ }
123
+ if (defName in defs)
124
+ return defs[defName];
125
+ // Fallback: match by envelope name or inner extractionSchema $anchor
126
+ for (const v of Object.values(defs)) {
127
+ if (!v || typeof v !== "object")
128
+ continue;
129
+ if (v.name === defName)
130
+ return v;
131
+ const innerAnchor = v.extractionSchema?.$anchor;
132
+ if (innerAnchor === defName)
133
+ return v;
134
+ }
135
+ const available = Object.keys(defs).slice(0, 25);
136
+ throw new Error(`Could not resolve ResourceType envelope for ref '${defName}' from resourceTypes.extractionSchema.$defs. ` +
137
+ `Example available keys: ${available.join(", ")}${Object.keys(defs).length > 25 ? ", ..." : ""}`);
138
+ }
139
+ function inlineRefs(node, resolve) {
140
+ if (isRefWrapper(node)) {
141
+ const defName = refToDefName(node.$ref);
142
+ const resolved = resolve(defName);
143
+ return deepClone(resolved);
144
+ }
145
+ if (Array.isArray(node)) {
146
+ return node.map((v) => inlineRefs(v, resolve));
147
+ }
148
+ if (node && typeof node === "object") {
149
+ const out = {};
150
+ for (const [k, v] of Object.entries(node)) {
151
+ out[k] = inlineRefs(v, resolve);
152
+ }
153
+ return out;
154
+ }
155
+ return node;
156
+ }
157
+ function main() {
158
+ const { storyInPath, storyOutPath, schemasPath, resourceTypesPath } = parseArgs();
159
+ if (!fs.existsSync(storyInPath))
160
+ throw new Error(`Story file not found: ${storyInPath}`);
161
+ if (!fs.existsSync(schemasPath))
162
+ throw new Error(`Schemas file not found: ${schemasPath}`);
163
+ if (!fs.existsSync(resourceTypesPath))
164
+ throw new Error(`ResourceTypes file not found: ${resourceTypesPath}`);
165
+ const storyRaw = fs.readFileSync(storyInPath, "utf8");
166
+ const story = JSON.parse(storyRaw);
167
+ const schemasRaw = fs.readFileSync(schemasPath, "utf8");
168
+ const extractedSchemas = JSON.parse(schemasRaw);
169
+ const resourceTypesRaw = fs.readFileSync(resourceTypesPath, "utf8");
170
+ const resourceTypesDoc = JSON.parse(resourceTypesRaw);
171
+ const resolved = resolveStoryDocument(story, extractedSchemas, resourceTypesDoc);
172
+ fs.mkdirSync(path.dirname(storyOutPath), { recursive: true });
173
+ fs.writeFileSync(storyOutPath, JSON.stringify(resolved, null, 4) + "\n", "utf8");
174
+ console.log(`Wrote resolved GenesisStory to ${storyOutPath}`);
175
+ }
176
+ function resolveStoryDocument(storyDoc, extractedSchemas, resourceTypesDoc) {
177
+ // Special handling: switch ref source after a marker in the GenesisStory array.
178
+ if (storyDoc &&
179
+ typeof storyDoc === "object" &&
180
+ !Array.isArray(storyDoc) &&
181
+ Array.isArray(storyDoc.GenesisStory)) {
182
+ const items = storyDoc.GenesisStory;
183
+ let inResourceTypeSpace = false;
184
+ const resolvedItems = items.map((item) => {
185
+ if (typeof item === "string" && item.trim() === "!!!!!") {
186
+ inResourceTypeSpace = true;
187
+ return item;
188
+ }
189
+ const resolver = inResourceTypeSpace
190
+ ? (name) => resolveResourceTypeEnvelope(resourceTypesDoc, name)
191
+ : (name) => resolveRawSchemaDef(extractedSchemas, name);
192
+ return inlineRefs(item, resolver);
193
+ });
194
+ return { ...storyDoc, GenesisStory: resolvedItems };
195
+ }
196
+ // Fallback: if it's not a GenesisStory doc, just resolve using raw schemas.
197
+ return inlineRefs(storyDoc, (name) => resolveRawSchemaDef(extractedSchemas, name));
198
+ }
199
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolproof-npm/schema",
3
- "version": "0.1.43",
3
+ "version": "0.1.44",
4
4
  "description": "JSON schemas and TypeScript types for ToolProof",
5
5
  "keywords": [
6
6
  "toolproof",
@@ -47,6 +47,7 @@
47
47
  "rewriteAnchors": "node ./dist/scripts/rewriteAnchors.js",
48
48
  "extractSchemas": "node ./dist/scripts/extractSchemas.js",
49
49
  "extractSubschema": "node ./dist/scripts/extractSubschemaWithDefs.js",
50
+ "resolveRefsInGenesisStory": "pnpm run build:scripts && node ./dist/scripts/resolveRefsInGenesisStory.js",
50
51
  "generateTypes": "node ./dist/scripts/generateTypes.js",
51
52
  "generateResourceTypeType": "node ./dist/scripts/generateResourceTypeType.js",
52
53
  "generateResourceEnvelopes": "node ./dist/scripts/generateResourceEnvelopes.js",