@toolproof-npm/schema 0.1.51 → 0.1.52

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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { default as GenesisSchema } from './genesis/generated/schemas/Genesis.js';
2
- export { default as Genesis } from './genesis/resourceTypes/Genesis.js';
2
+ export { default as GenesisResourceType } from './genesis/generated/resource-type-envelopes/Genesis.js';
3
3
  export { default as JobSchema } from './genesis/generated/schemas/Job.js';
4
4
  export type { Resource_ResourceFormat as Resource_ResourceFormatJson } from './genesis/generated/types/Resource_ResourceFormat.js';
5
5
  export type { Resource_ResourceType as Resource_ResourceTypeJson } from './genesis/generated/types/Resource_ResourceType.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Re-export JSON schemas via .ts shims to avoid .json re-exports in declarations
2
2
  export { default as GenesisSchema } from './genesis/generated/schemas/Genesis.js';
3
- export { default as Genesis } from './genesis/resourceTypes/Genesis.js';
3
+ export { default as GenesisResourceType } from './genesis/generated/resource-type-envelopes/Genesis.js';
4
4
  export { default as JobSchema } from './genesis/generated/schemas/Job.js';
5
5
  // Re-export brand factories so consumers can construct branded identities at runtime.
6
6
  export { unsafeBrand, asResourceTypeIdentity, asResourceRoleIdentity, asExecutionIdentity, asResourceIdentity, asWorkStepIdentity, asBranchStepIdentity, asForStepIdentity, asResourceFormatIdentity, asWhileStepIdentity, asStatelessStrategyIdentity, asStatefulStrategyIdentity, asResourceTypeIdentities, asResourceRoleIdentities, asExecutionIdentities, asResourceIdentities, asWorkStepIdentities, asBranchStepIdentities, asForStepIdentities, asResourceFormatIdentities, asWhileStepIdentities, asStatelessStrategyIdentities, asStatefulStrategyIdentities, } from './scripts/brandFactories.js';
@@ -10,6 +10,7 @@ export declare class SchemaConfig {
10
10
  private readonly root;
11
11
  private readonly sourceDir;
12
12
  private readonly sourceFile;
13
+ private readonly normalizedDir;
13
14
  private readonly outputDir;
14
15
  private readonly typesSrcDir;
15
16
  private readonly typesDistDir;
@@ -20,6 +21,9 @@ export declare class SchemaConfig {
20
21
  getSourceDir(): string;
21
22
  getSourceFile(): string;
22
23
  getSourcePath(): string;
24
+ getNormalizedDir(): string;
25
+ getNormalizedSourceFile(): string;
26
+ getNormalizedSourcePath(): string;
23
27
  getOutputDir(): string;
24
28
  getOutputPath(filename: string): string;
25
29
  getTypesSrcDir(): string;
@@ -18,8 +18,11 @@ export class SchemaConfig {
18
18
  constructor() {
19
19
  // Environment variables with sensible defaults
20
20
  this.root = getEnv('TP_SCHEMA_ROOT', process.cwd());
21
- this.sourceDir = getEnv('TP_SCHEMA_SOURCE_DIR', 'src/genesis/resourceTypes');
21
+ this.sourceDir = getEnv('TP_SCHEMA_SOURCE_DIR', 'src/genesis');
22
22
  this.sourceFile = getEnv('TP_SCHEMA_SOURCE_FILE', 'Genesis.json');
23
+ // Intermediate, generated artifact produced by rewriteAnchors.
24
+ // This should NOT live next to the source-of-truth schemas.
25
+ this.normalizedDir = getEnv('TP_SCHEMA_NORMALIZED_DIR', 'src/genesis/generated/resourceTypes');
23
26
  this.outputDir = getEnv('TP_SCHEMA_OUTPUT_DIR', 'src/genesis/generated/schemas');
24
27
  this.typesSrcDir = getEnv('TP_SCHEMA_TYPES_SRC_DIR', 'src/genesis/generated/types');
25
28
  this.typesDistDir = getEnv('TP_SCHEMA_TYPES_DIST_DIR', 'dist/genesis/generated/types');
@@ -41,6 +44,20 @@ export class SchemaConfig {
41
44
  getSourcePath() {
42
45
  return path.join(this.getSourceDir(), this.sourceFile);
43
46
  }
47
+ getNormalizedDir() {
48
+ return path.isAbsolute(this.normalizedDir)
49
+ ? this.normalizedDir
50
+ : path.join(this.root, this.normalizedDir);
51
+ }
52
+ getNormalizedSourceFile() {
53
+ // We keep the same basename (Genesis.json) in the generated folder.
54
+ // The source-of-truth Genesis.json lives under `TP_SCHEMA_SOURCE_DIR`.
55
+ // The generated/normalized Genesis.json lives under `TP_SCHEMA_NORMALIZED_DIR`.
56
+ return this.sourceFile;
57
+ }
58
+ getNormalizedSourcePath() {
59
+ return path.join(this.getNormalizedDir(), this.getNormalizedSourceFile());
60
+ }
44
61
  getOutputDir() {
45
62
  return path.isAbsolute(this.outputDir)
46
63
  ? this.outputDir
@@ -25,8 +25,8 @@ function parseArgs() {
25
25
  }
26
26
  // Use config defaults if not provided via CLI
27
27
  if (!inPath) {
28
- // Use normalized version with anchor refs rewritten to pointers
29
- inPath = config.getSourcePath().replace('.json', '.normalized.json');
28
+ // Use generated/normalized version with anchor refs rewritten to pointers
29
+ inPath = config.getNormalizedSourcePath();
30
30
  }
31
31
  if (!outPath) {
32
32
  outPath = config.getOutputPath(config.getSourceFile());
@@ -18,7 +18,7 @@ import { getConfig } from './_lib/config.js';
18
18
  async function main() {
19
19
  const config = getConfig();
20
20
  // Use normalized version with anchor refs rewritten to pointers
21
- const genesisSourcePath = config.getSourcePath().replace('.json', '.normalized.json');
21
+ const genesisSourcePath = config.getNormalizedSourcePath();
22
22
  const outputPath = path.join(config.getRoot(), 'src', 'genesis', 'generated', 'resources', 'Genesis.json');
23
23
  if (!fs.existsSync(genesisSourcePath)) {
24
24
  console.error(`Genesis source file not found at ${genesisSourcePath}`);
@@ -18,6 +18,7 @@ async function main() {
18
18
  const config = getConfig();
19
19
  const schemasDir = config.getOutputDir();
20
20
  const resourcesDir = path.join(path.dirname(schemasDir), 'resources');
21
+ const generatedResourceTypesDir = config.getNormalizedDir();
21
22
  // Process schemas directory
22
23
  let totalCount = 0;
23
24
  if (fs.existsSync(schemasDir)) {
@@ -55,6 +56,26 @@ async function main() {
55
56
  else {
56
57
  console.warn(`Resources directory not found at ${resourcesDir}`);
57
58
  }
59
+ // Genesis (normalized) shim
60
+ // We treat the normalized Genesis.json under src/genesis/generated/resourceTypes as the
61
+ // public import target, and generate a shim right next to it.
62
+ try {
63
+ const genesisJsonPath = config.getNormalizedSourcePath();
64
+ if (fs.existsSync(genesisJsonPath)) {
65
+ fs.mkdirSync(generatedResourceTypesDir, { recursive: true });
66
+ const outPath = path.join(generatedResourceTypesDir, 'Genesis.ts');
67
+ const content = `import schema from './Genesis.json' with { type: 'json' };\nexport default schema;\n`;
68
+ fs.writeFileSync(outPath, content, 'utf-8');
69
+ console.log(`Generated Genesis.ts in ${generatedResourceTypesDir}`);
70
+ totalCount++;
71
+ }
72
+ else {
73
+ console.warn(`Genesis source JSON not found at ${genesisJsonPath}; skipping Genesis.ts shim`);
74
+ }
75
+ }
76
+ catch (e) {
77
+ console.warn('Failed to generate Genesis.ts shim:', e);
78
+ }
58
79
  console.log(`Generated ${totalCount} total TypeScript shims`);
59
80
  }
60
81
  main().catch((e) => {
@@ -220,8 +220,66 @@ async function main() {
220
220
  for (const [k, v] of Object.entries(node))
221
221
  normalizeArrays(v, k);
222
222
  }
223
+ // json-schema-to-typescript has a long-standing quirk:
224
+ // when a schema uses `allOf`, sibling object keywords like `properties`/`required`
225
+ // can be ignored in the emitted TS (it treats `allOf` as the whole schema).
226
+ //
227
+ // Example (Genesis $defs/Job):
228
+ // { type: 'object', allOf: [Documented, RolesWrapper], properties: { identity: ... }, required: [...] }
229
+ // can become:
230
+ // type Job = Documented & RolesWrapper
231
+ //
232
+ // To avoid cluttering the source JSON schemas, we normalize to a temp file:
233
+ // move those sibling object keywords into an extra `allOf` item.
234
+ function normalizeAllOfSiblingObjectKeywords(node) {
235
+ if (Array.isArray(node)) {
236
+ for (const item of node)
237
+ normalizeAllOfSiblingObjectKeywords(item);
238
+ return;
239
+ }
240
+ if (!node || typeof node !== 'object')
241
+ return;
242
+ const hasAllOf = Array.isArray(node.allOf) && node.allOf.length > 0;
243
+ const looksLikeObjectSchema = node.type === 'object' ||
244
+ node.properties !== undefined ||
245
+ node.required !== undefined ||
246
+ node.unevaluatedProperties !== undefined ||
247
+ node.additionalProperties !== undefined;
248
+ if (hasAllOf && looksLikeObjectSchema) {
249
+ const siblingKeys = [
250
+ 'properties',
251
+ 'required',
252
+ 'additionalProperties',
253
+ 'unevaluatedProperties',
254
+ 'propertyNames',
255
+ 'patternProperties',
256
+ 'dependentRequired',
257
+ 'dependentSchemas',
258
+ 'minProperties',
259
+ 'maxProperties'
260
+ ];
261
+ const hasSiblingObjectKeywords = siblingKeys.some((k) => k in node);
262
+ if (hasSiblingObjectKeywords) {
263
+ const overlay = {};
264
+ if (node.type === 'object')
265
+ overlay.type = 'object';
266
+ for (const k of siblingKeys) {
267
+ if (k in node) {
268
+ overlay[k] = node[k];
269
+ delete node[k];
270
+ }
271
+ }
272
+ // Prepend so it participates in the intersection early.
273
+ node.allOf = [overlay, ...node.allOf];
274
+ }
275
+ }
276
+ for (const v of Object.values(node))
277
+ normalizeAllOfSiblingObjectKeywords(v);
278
+ }
223
279
  // Normalize expected arrays to prevent traversal crashes
224
280
  normalizeArrays(parsedSchema);
281
+ // Normalize `allOf` + sibling object keywords so the TS generator doesn't drop them.
282
+ normalizeAllOfSiblingObjectKeywords(parsedSchema);
225
283
  const tmpPath = path.join(inputDir, `.normalized.${path.basename(fileName)}`);
226
284
  fs.writeFileSync(tmpPath, JSON.stringify(parsedSchema, null, 2), 'utf8');
227
285
  toCompilePath = tmpPath;
@@ -1,4 +1,5 @@
1
1
  import fs from 'fs';
2
+ import path from 'path';
2
3
  import { getConfig } from './_lib/config.js';
3
4
  /**
4
5
  * Rewrite anchor-style references to JSON Pointer references
@@ -65,8 +66,8 @@ function rewriteAnchorsToPointers(root) {
65
66
  async function main() {
66
67
  const config = getConfig();
67
68
  const genesisSourcePath = config.getSourcePath();
68
- // Create a temporary normalized version
69
- const normalizedPath = genesisSourcePath.replace('.json', '.normalized.json');
69
+ // Create a generated/normalized version (anchor refs rewritten to pointers)
70
+ const normalizedPath = config.getNormalizedSourcePath();
70
71
  if (!fs.existsSync(genesisSourcePath)) {
71
72
  console.error(`Genesis source file not found at ${genesisSourcePath}`);
72
73
  process.exit(1);
@@ -81,6 +82,7 @@ async function main() {
81
82
  // Rewrite anchors in the extractionSchema (where $defs is at the top level)
82
83
  rewriteAnchorsToPointers(genesis.extractionSchema);
83
84
  // Write normalized version
85
+ fs.mkdirSync(path.dirname(normalizedPath), { recursive: true });
84
86
  fs.writeFileSync(normalizedPath, JSON.stringify(genesis, null, 4), 'utf-8');
85
87
  console.log(`Wrote normalized Genesis with pointer refs to ${normalizedPath}`);
86
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolproof-npm/schema",
3
- "version": "0.1.51",
3
+ "version": "0.1.52",
4
4
  "description": "JSON schemas and TypeScript types for ToolProof",
5
5
  "keywords": [
6
6
  "toolproof",