@versionzero/schema 1.0.0 → 1.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/README.md CHANGED
@@ -227,7 +227,7 @@ You can think of it like having a toolkit for composing rich runtime-enforced in
227
227
  with reflection; embracing the dynamic nature of JavaScript, rather than pretending it has
228
228
  static types.
229
229
 
230
- _Read more about this [in the full documentation](https://docs.v0.net/schema/rationale)._
230
+ _Read more about this [in the full documentation](https://docs.v0.net/schema/guide/rationale)._
231
231
 
232
232
  ## License
233
233
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versionzero/schema",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A composable schema library that supports asynchronous processing",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
@@ -63,7 +63,7 @@
63
63
  "test:browser": "playwright test",
64
64
  "test:all": "npm test && npm run test:browser",
65
65
  "clean": "rm -rf types docs-output* processors-output* .mdrun .c8 .nyc_output",
66
- "release:dry": "npx -p semantic-release@24 -p @semantic-release/exec@7 semantic-release --dry-run --no-ci"
66
+ "release:dry": "npx -p semantic-release@24 -p @semantic-release/exec@7 -p @semantic-release/changelog@6 -p @semantic-release/git@10 semantic-release --dry-run --no-ci"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@playwright/test": "^1.60.0",
@@ -144,16 +144,16 @@ export class CompiledSchema
144
144
  }
145
145
 
146
146
  /**
147
- * Handlers are associated with asynchronous value processors.
147
+ * Handlers are associated with value processors.
148
148
  *
149
- * The "friendly" handler definitions from the source Schema are each compiled into asynchronous functions
150
- * that run as a pipeline.
149
+ * The "friendly" handler definitions from the source Schema are each compiled into an array
150
+ * of functions (sync or async) that run as a pipeline.
151
151
  *
152
- * All handlers have the same async signature, receiving:
152
+ * All value processors have the same signature, receiving:
153
153
  * 1. a value to be processed by the current schema
154
- * 2. a reference to the top-level aggregate target being built or processed by the entire schema hierarchy
155
- * 3. a location defining the current schema and the traversal path to where it was encountered
156
- * 5. any (unmanaged / developer defined) options passed to whatever invoked the handler processing
154
+ * 2. the top-level aggregate target value being built or processed by the entire schema hierarchy
155
+ * 3. a location cursor referencing the associated schema and the traversal path to where it was encountered
156
+ * 4. any (unmanaged / developer defined) options passed to whatever invoked the handler processing
157
157
  *
158
158
  * The compiled handlers may vary in their return types and exception handling behavior.
159
159
  *
@@ -617,7 +617,7 @@ export class CompiledSchema
617
617
 
618
618
  /**
619
619
  * Use the registered discriminator to return a matching union schema, or undefined if the union cannot be resolved.
620
- * Discriminator functions must return either one of the unionSchema members, a unionSchema key, or undefined.
620
+ * Discriminator functions must return either a unionSchema key or (compiled) member reference, or undefined.
621
621
  *
622
622
  * (This is an executor function that may return synchronous or asynchronous results.)
623
623
  *
@@ -668,7 +668,7 @@ export class CompiledSchema
668
668
  }
669
669
  /**
670
670
  * Use the registered discriminator to return a matching union schema, or undefined if the union cannot be resolved.
671
- * Discriminator functions must return either one of the unionSchema members, a unionSchema key, or undefined.
671
+ * Discriminator functions must return either a unionSchema key or (compiled) member reference, or undefined.
672
672
  *
673
673
  * (This an async wrapper around the internal `_discriminateUnion` executor function.)
674
674
  *
@@ -899,7 +899,7 @@ export class CompiledSchema
899
899
  * Finalize a transformed input value by running any necessary post-processing steps.
900
900
  *
901
901
  * Runs all finalizer value processors in a pipeline until one returns undefined or throws an error.
902
- * - The input to the pipeline is be assumed to be transformed.
902
+ * - The input to the pipeline is assumed to be transformed.
903
903
  * - Finalizers are generally only required for incremental schemas that need to check child values.
904
904
  * - A finalizer on the root schema (or that checks for the root path, if the root schema is shared)
905
905
  * can act as an "entire output" finalizer.
@@ -950,7 +950,7 @@ export class CompiledSchema
950
950
  * Finalize a transformed input value by running any necessary post-processing steps.
951
951
  *
952
952
  * Runs all finalizer value processors in a pipeline until one returns undefined or throws an error.
953
- * - The input to the pipeline is be assumed to be transformed.
953
+ * - The input to the pipeline is assumed to be transformed.
954
954
  * - Finalizers are generally only required for incremental schemas that need to check child values.
955
955
  * - A finalizer on the root schema (or that checks for the root path, if the root schema is shared)
956
956
  * can act as an "entire output" finalizer.
@@ -51,6 +51,8 @@ export function registerCoreLibrary(libraryFn) {
51
51
  /** @import { SchemaData } from './types.js' */
52
52
  /** @import { ValueProcessorDefinition, ValueProcessorSpec, ValueProcessorBuilder, ValueProcessorFunction, ValueProcessorArgs, ValueProcessorParameter, KeywordValueProcessorSpec } from './value-processor/value-processor.js' */
53
53
 
54
+ /** @typedef {{name:string, namespace?:string, schema:Schema}} RegisteredSchemaInfo */
55
+
54
56
  /**
55
57
  * The SchemaResolver uses its internal registries of named schemas and value processor keywords to
56
58
  * convert Schemas containing unresolved references into resolved Schemas that are fully self-contained.
@@ -58,7 +60,7 @@ export function registerCoreLibrary(libraryFn) {
58
60
  export class SchemaResolver
59
61
  {
60
62
  /**
61
- * @type {Map<string,Schema>}
63
+ * @type {Map<string,RegisteredSchemaInfo>}
62
64
  */
63
65
  #schemaMap = new Map();
64
66
  /**
@@ -85,7 +87,7 @@ export class SchemaResolver
85
87
  throw new ResolverError(`Registry can only store Schema instances`);
86
88
  }
87
89
  const registryName = toKebabCase(name);
88
- this.#schemaMap.set(registryName, schema);
90
+ this.#schemaMap.set(registryName, {name: registryName, schema});
89
91
  return this;
90
92
  }
91
93
 
@@ -96,11 +98,11 @@ export class SchemaResolver
96
98
  */
97
99
  getSchema(name) {
98
100
  const registryName = toKebabCase(name);
99
- const schema = this.#schemaMap.get(registryName);
100
- if (!schema) {
101
+ const schemaInfo = this.#schemaMap.get(registryName);
102
+ if (!schemaInfo) {
101
103
  throw new ResolverError(`Unable to resolve "${name}"`);
102
104
  }
103
- return schema;
105
+ return schemaInfo.schema;
104
106
  }
105
107
 
106
108
  /**
@@ -113,6 +115,14 @@ export class SchemaResolver
113
115
  return this.#schemaMap.has(registryName);
114
116
  }
115
117
 
118
+ /**
119
+ * return all registered schemas
120
+ * @returns {RegisteredSchemaInfo[]}
121
+ */
122
+ listRegisteredSchemas() {
123
+ return [...this.#schemaMap.values()];
124
+ }
125
+
116
126
  /**
117
127
  * Register a value processor definition
118
128
  * @param {ValueProcessorDefinition} definition
@@ -121,20 +131,24 @@ export class SchemaResolver
121
131
  registerValueProcessorDefinition(definition) {
122
132
  const {keyword, process, description, build} = definition;
123
133
 
124
- if (!keyword) {
134
+ if (!keyword || typeof keyword !== 'string') {
125
135
  throw new ResolverError('Missing keyword in processor definition');
126
136
  }
127
137
 
128
138
  if (process && build) {
129
- throw new ResolverError(`Processor definition for '${keyword}' cannot define both process and build functions`);
139
+ throw new ResolverError(`Processor definition for '$${keyword}' cannot define both process and build functions`);
130
140
  }
131
141
 
132
142
  if (!process && !build) {
133
- throw new ResolverError(`Processor definition for '${keyword}' must have define a process or build function`);
143
+ throw new ResolverError(`Processor definition for '$${keyword}' must have define a process or build function`);
134
144
  }
135
145
 
136
146
  if (description && typeof description !== 'string') {
137
- throw new ResolverError(`Processor definition description for '${keyword}' must be a string`);
147
+ throw new ResolverError(`Processor definition description for '$${keyword}' must be a string`);
148
+ }
149
+
150
+ if (this.#constantKeywordProcessors[`${keyword}`]) {
151
+ throw new ResolverError(`Processor keyword conflict with builtin literal '$${keyword}'`)
138
152
  }
139
153
 
140
154
  this.#processorMap.set(keyword, definition);
@@ -176,6 +190,24 @@ export class SchemaResolver
176
190
  });
177
191
  }
178
192
 
193
+ /**
194
+ * List all registered value processors (and reserved builtins)
195
+ *
196
+ * @returns {ValueProcessorDefinition[]}
197
+ */
198
+ listValueProcessorDefinitions() {
199
+
200
+ return [
201
+ ...Object.entries(this.#constantKeywordProcessors).map(([k,v]) => ({
202
+ keyword: k,
203
+ build: (() => v),
204
+ spec: `$${k}`,
205
+ reserved: true
206
+ })),
207
+ ...this.#processorMap.values()
208
+ ];
209
+ }
210
+
179
211
 
180
212
  /**
181
213
  * Load a library of schemas and/or value processors.
@@ -209,13 +241,11 @@ export class SchemaResolver
209
241
 
210
242
  }
211
243
 
212
-
213
-
214
- _constantKeywords = {
215
- $null: NULL_EXECUTOR,
216
- $undefined: UNDEFINED_EXECUTOR,
217
- $true: TRUE_EXECUTOR,
218
- $false: FALSE_EXECUTOR
244
+ #constantKeywordProcessors = {
245
+ 'null': new ComposedValueProcessor(NULL_EXECUTOR, '$null'),
246
+ 'undefined': new ComposedValueProcessor(UNDEFINED_EXECUTOR, '$undefined'),
247
+ 'true': new ComposedValueProcessor(TRUE_EXECUTOR, '$true'),
248
+ 'false': new ComposedValueProcessor(FALSE_EXECUTOR, '$false')
219
249
  }
220
250
 
221
251
  /**
@@ -230,8 +260,8 @@ export class SchemaResolver
230
260
  }
231
261
  const [keyword, rawArgs] = extractKeywordValueProcessorSpec(spec);
232
262
 
233
- if (this._constantKeywords[spec]) {
234
- return new ComposedValueProcessor(this._constantKeywords[spec], spec);
263
+ if (this.#constantKeywordProcessors[keyword]) {
264
+ return this.#constantKeywordProcessors[keyword];
235
265
  }
236
266
 
237
267
  if (keyword === 'literal') {
package/src/schema.js CHANGED
@@ -459,7 +459,7 @@ export class Schema
459
459
  }
460
460
 
461
461
  /**
462
- * The discriminator handler returns the key or schema of the union member that should be used
462
+ * The discriminator handler returns the key or schema of the union member that should be used.
463
463
  * This function appends a single value processor to the handler pipeline.
464
464
  *
465
465
  * @param {ValueProcessorSpec} spec
@@ -470,7 +470,7 @@ export class Schema
470
470
  }
471
471
 
472
472
  /**
473
- * The discriminator handler returns the key or schema of the union member that should be used
473
+ * The discriminator handler returns the key or schema of the union member that should be used.
474
474
  * This function applies multiple value processors to the handler pipeline.
475
475
  * (Note that it would be highly unusual to want more than one!)
476
476
  *
@@ -44,11 +44,13 @@ import { SchemaLocation } from '../schema-location.js';
44
44
  /**
45
45
  * @typedef {object} ValueProcessorDefinition
46
46
  * @property {string} keyword
47
+ * @property {string} [namespace]
47
48
  * @property {ValueProcessorFunction} [process]
48
49
  * @property {ValueProcessorParameter[]} [parameters]
49
50
  * @property {string} [description]
50
51
  * @property {ValueProcessorBuilder} [build]
51
52
  * @property {ValueProcessorDescriber} [describe]
53
+ * @property {boolean} [reserved]
52
54
  */
53
55
 
54
56
 
@@ -71,16 +71,16 @@ export class CompiledSchema {
71
71
  */
72
72
  get propertyEntries(): IteratorObject<[string, CompiledSchema]>;
73
73
  /**
74
- * Handlers are associated with asynchronous value processors.
74
+ * Handlers are associated with value processors.
75
75
  *
76
- * The "friendly" handler definitions from the source Schema are each compiled into asynchronous functions
77
- * that run as a pipeline.
76
+ * The "friendly" handler definitions from the source Schema are each compiled into an array
77
+ * of functions (sync or async) that run as a pipeline.
78
78
  *
79
- * All handlers have the same async signature, receiving:
79
+ * All value processors have the same signature, receiving:
80
80
  * 1. a value to be processed by the current schema
81
- * 2. a reference to the top-level aggregate target being built or processed by the entire schema hierarchy
82
- * 3. a location defining the current schema and the traversal path to where it was encountered
83
- * 5. any (unmanaged / developer defined) options passed to whatever invoked the handler processing
81
+ * 2. the top-level aggregate target value being built or processed by the entire schema hierarchy
82
+ * 3. a location cursor referencing the associated schema and the traversal path to where it was encountered
83
+ * 4. any (unmanaged / developer defined) options passed to whatever invoked the handler processing
84
84
  *
85
85
  * The compiled handlers may vary in their return types and exception handling behavior.
86
86
  *
@@ -386,7 +386,7 @@ export class CompiledSchema {
386
386
  _checkValue(value: any, ErrorClass: any): any;
387
387
  /**
388
388
  * Use the registered discriminator to return a matching union schema, or undefined if the union cannot be resolved.
389
- * Discriminator functions must return either one of the unionSchema members, a unionSchema key, or undefined.
389
+ * Discriminator functions must return either a unionSchema key or (compiled) member reference, or undefined.
390
390
  *
391
391
  * (This is an executor function that may return synchronous or asynchronous results.)
392
392
  *
@@ -400,7 +400,7 @@ export class CompiledSchema {
400
400
  _discriminateUnion(value: any, target?: any, location?: SchemaLocation, options?: object): CompiledSchema | undefined | Promise<CompiledSchema | undefined>;
401
401
  /**
402
402
  * Use the registered discriminator to return a matching union schema, or undefined if the union cannot be resolved.
403
- * Discriminator functions must return either one of the unionSchema members, a unionSchema key, or undefined.
403
+ * Discriminator functions must return either a unionSchema key or (compiled) member reference, or undefined.
404
404
  *
405
405
  * (This an async wrapper around the internal `_discriminateUnion` executor function.)
406
406
  *
@@ -526,7 +526,7 @@ export class CompiledSchema {
526
526
  * Finalize a transformed input value by running any necessary post-processing steps.
527
527
  *
528
528
  * Runs all finalizer value processors in a pipeline until one returns undefined or throws an error.
529
- * - The input to the pipeline is be assumed to be transformed.
529
+ * - The input to the pipeline is assumed to be transformed.
530
530
  * - Finalizers are generally only required for incremental schemas that need to check child values.
531
531
  * - A finalizer on the root schema (or that checks for the root path, if the root schema is shared)
532
532
  * can act as an "entire output" finalizer.
@@ -545,7 +545,7 @@ export class CompiledSchema {
545
545
  * Finalize a transformed input value by running any necessary post-processing steps.
546
546
  *
547
547
  * Runs all finalizer value processors in a pipeline until one returns undefined or throws an error.
548
- * - The input to the pipeline is be assumed to be transformed.
548
+ * - The input to the pipeline is assumed to be transformed.
549
549
  * - Finalizers are generally only required for incremental schemas that need to check child values.
550
550
  * - A finalizer on the root schema (or that checks for the root path, if the root schema is shared)
551
551
  * can act as an "entire output" finalizer.
@@ -9,6 +9,7 @@ export function registerCoreLibrary(libraryFn: (resolver: SchemaResolver, option
9
9
  /** @typedef {{name:string, [key:string]:any}} SchemaResolverLibraryOptions */
10
10
  /** @import { SchemaData } from './types.js' */
11
11
  /** @import { ValueProcessorDefinition, ValueProcessorSpec, ValueProcessorBuilder, ValueProcessorFunction, ValueProcessorArgs, ValueProcessorParameter, KeywordValueProcessorSpec } from './value-processor/value-processor.js' */
12
+ /** @typedef {{name:string, namespace?:string, schema:Schema}} RegisteredSchemaInfo */
12
13
  /**
13
14
  * The SchemaResolver uses its internal registries of named schemas and value processor keywords to
14
15
  * convert Schemas containing unresolved references into resolved Schemas that are fully self-contained.
@@ -33,6 +34,11 @@ export class SchemaResolver {
33
34
  * @returns {boolean}
34
35
  */
35
36
  hasSchema(name: string): boolean;
37
+ /**
38
+ * return all registered schemas
39
+ * @returns {RegisteredSchemaInfo[]}
40
+ */
41
+ listRegisteredSchemas(): RegisteredSchemaInfo[];
36
42
  /**
37
43
  * Register a value processor definition
38
44
  * @param {ValueProcessorDefinition} definition
@@ -54,6 +60,12 @@ export class SchemaResolver {
54
60
  * @returns {SchemaResolver}
55
61
  */
56
62
  registerValueProcessorBuilder(keyword: string, build: ValueProcessorBuilder): SchemaResolver;
63
+ /**
64
+ * List all registered value processors (and reserved builtins)
65
+ *
66
+ * @returns {ValueProcessorDefinition[]}
67
+ */
68
+ listValueProcessorDefinitions(): ValueProcessorDefinition[];
57
69
  /**
58
70
  * Load a library of schemas and/or value processors.
59
71
  *
@@ -68,12 +80,6 @@ export class SchemaResolver {
68
80
  * @returns {Promise<void>}
69
81
  */
70
82
  use(libraryFunction: (resolver: SchemaResolver, options: object) => Promise<void> | void, options?: object): Promise<void>;
71
- _constantKeywords: {
72
- $null: ConstantExecutor;
73
- $undefined: ConstantExecutor;
74
- $true: ConstantExecutor;
75
- $false: ConstantExecutor;
76
- };
77
83
  /**
78
84
  * @param {SchemaCompiler} compiler
79
85
  * @param {KeywordValueProcessorSpec} spec
@@ -131,11 +137,15 @@ export type SchemaResolverLibraryOptions = {
131
137
  name: string;
132
138
  [key: string]: any;
133
139
  };
140
+ export type RegisteredSchemaInfo = {
141
+ name: string;
142
+ namespace?: string;
143
+ schema: Schema;
144
+ };
134
145
  import { Schema } from './schema.js';
135
146
  import type { ValueProcessorDefinition } from './value-processor/value-processor.js';
136
147
  import type { ValueProcessorFunction } from './value-processor/value-processor.js';
137
148
  import type { ValueProcessorBuilder } from './value-processor/value-processor.js';
138
- import { ConstantExecutor } from './executor/executor.js';
139
149
  import { SchemaCompiler } from './schema-compiler.js';
140
150
  import type { KeywordValueProcessorSpec } from './value-processor/value-processor.js';
141
151
  import { ValueProcessor } from './value-processor/value-processor.js';
package/types/schema.d.ts CHANGED
@@ -234,7 +234,7 @@ export class Schema {
234
234
  */
235
235
  addMetadata(metadata: object, policy?: symbol): Schema;
236
236
  /**
237
- * The discriminator handler returns the key or schema of the union member that should be used
237
+ * The discriminator handler returns the key or schema of the union member that should be used.
238
238
  * This function appends a single value processor to the handler pipeline.
239
239
  *
240
240
  * @param {ValueProcessorSpec} spec
@@ -242,7 +242,7 @@ export class Schema {
242
242
  */
243
243
  unionDiscriminator(spec: ValueProcessorSpec): Schema;
244
244
  /**
245
- * The discriminator handler returns the key or schema of the union member that should be used
245
+ * The discriminator handler returns the key or schema of the union member that should be used.
246
246
  * This function applies multiple value processors to the handler pipeline.
247
247
  * (Note that it would be highly unusual to want more than one!)
248
248
  *
@@ -31,11 +31,13 @@
31
31
  /**
32
32
  * @typedef {object} ValueProcessorDefinition
33
33
  * @property {string} keyword
34
+ * @property {string} [namespace]
34
35
  * @property {ValueProcessorFunction} [process]
35
36
  * @property {ValueProcessorParameter[]} [parameters]
36
37
  * @property {string} [description]
37
38
  * @property {ValueProcessorBuilder} [build]
38
39
  * @property {ValueProcessorDescriber} [describe]
40
+ * @property {boolean} [reserved]
39
41
  */
40
42
  /**
41
43
  * @augments {Executor<any>}
@@ -82,11 +84,13 @@ export type ValueProcessorDescriber = (args: {
82
84
  } | ValueProcessor[] | undefined) => string | undefined;
83
85
  export type ValueProcessorDefinition = {
84
86
  keyword: string;
87
+ namespace?: string | undefined;
85
88
  process?: ValueProcessorFunction | undefined;
86
89
  parameters?: ValueProcessorParameter[] | undefined;
87
90
  description?: string | undefined;
88
91
  build?: ValueProcessorBuilder | undefined;
89
92
  describe?: ValueProcessorDescriber | undefined;
93
+ reserved?: boolean | undefined;
90
94
  };
91
95
  import { Executor } from '../executor/executor.js';
92
96
  import { SchemaLocation } from '../schema-location.js';