@tsofist/schema-forge 2.0.0 → 2.2.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.
@@ -11,7 +11,7 @@ interface Options {
11
11
  definitions: string[];
12
12
  schemaId?: string;
13
13
  expose?: TypeExposeKind;
14
- openAPI?: boolean;
14
+ openapiCompatible?: boolean;
15
15
  sortObjectProperties?: boolean;
16
16
  allowUseFallbackDescription?: boolean;
17
17
  }
@@ -26,10 +26,13 @@ async function generateSchemaByDraftTypes(options) {
26
26
  expose: options.expose ?? types_1.SG_CONFIG_DEFAULTS.expose,
27
27
  path: `${options.sourcesDirectoryPattern}/*${types_1.TMP_FILES_SUFFIX}.ts`,
28
28
  tsconfig: options.tsconfig,
29
- discriminatorType: options.openAPI ? 'open-api' : ts_json_schema_generator_1.DEFAULT_CONFIG.discriminatorType,
29
+ discriminatorType: options.openapiCompatible
30
+ ? 'open-api'
31
+ : ts_json_schema_generator_1.DEFAULT_CONFIG.discriminatorType,
30
32
  ...types_1.SG_CONFIG_MANDATORY,
31
33
  };
32
34
  const generatorProgram = (0, ts_json_schema_generator_1.createProgram)(generatorConfig);
35
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
33
36
  const typeChecker = generatorProgram.getTypeChecker();
34
37
  const parser = (0, ts_json_schema_generator_1.createParser)(generatorProgram, options.sourcesTypesGeneratorConfig, (parser) => {
35
38
  parser.addNodeParser(new TupleTypeParser(parser, allowUseFallbackDescription));
@@ -41,6 +44,10 @@ async function generateSchemaByDraftTypes(options) {
41
44
  $schema: 'http://json-schema.org/draft-07/schema#',
42
45
  $id: options.schemaId,
43
46
  hash: '',
47
+ title: undefined,
48
+ description: undefined,
49
+ version: undefined,
50
+ $comment: undefined,
44
51
  definitions: {},
45
52
  };
46
53
  for (const definitionName of options.definitions) {
package/lib/generator.js CHANGED
@@ -10,10 +10,10 @@ const promises_2 = require("node:fs/promises");
10
10
  const as_array_1 = require("@tsofist/stem/lib/as-array");
11
11
  const error_1 = require("@tsofist/stem/lib/error");
12
12
  const noop_1 = require("@tsofist/stem/lib/noop");
13
- const keys_1 = require("@tsofist/stem/lib/object/keys");
14
13
  const random_1 = require("@tsofist/stem/lib/string/random");
15
14
  const schema_generator_1 = require("./generator/schema-generator");
16
15
  const types_generator_1 = require("./generator/types-generator");
16
+ const index_1 = require("./index");
17
17
  const KEEP_ARTEFACTS = false;
18
18
  async function forgeSchema(options) {
19
19
  const { schemaId, sourcesDirectoryPattern, outputSchemaFile } = options;
@@ -37,33 +37,30 @@ async function forgeSchema(options) {
37
37
  tsconfig,
38
38
  sourcesPattern,
39
39
  });
40
- const refs = definitions.map((item) => `${options.schemaId || ''}#/definitions/${item}`);
40
+ const refs = definitions.map((item) => (0, index_1.buildSchemaDefinitionRef)(item, options.schemaId));
41
41
  let schema;
42
42
  try {
43
43
  {
44
- schema = await (0, schema_generator_1.generateSchemaByDraftTypes)({
45
- schemaId,
46
- tsconfig,
47
- definitions,
48
- sourcesDirectoryPattern,
49
- outputSchemaFile,
50
- sourcesTypesGeneratorConfig,
51
- expose: options.expose,
52
- openAPI: options.openapiCompatible,
53
- sortObjectProperties: options.sortObjectProperties,
54
- allowUseFallbackDescription: options.allowUseFallbackDescription,
55
- });
56
- if (options.schemaMetadata) {
57
- for (const key of (0, keys_1.keysOf)(options.schemaMetadata)) {
58
- schema[key] = options.schemaMetadata[key];
59
- }
60
- }
44
+ schema = {
45
+ ...(await (0, schema_generator_1.generateSchemaByDraftTypes)({
46
+ schemaId,
47
+ tsconfig,
48
+ definitions,
49
+ sourcesDirectoryPattern,
50
+ outputSchemaFile,
51
+ sourcesTypesGeneratorConfig,
52
+ expose: options.expose,
53
+ openapiCompatible: options.openapiCompatible,
54
+ sortObjectProperties: options.sortObjectProperties,
55
+ allowUseFallbackDescription: options.allowUseFallbackDescription,
56
+ })),
57
+ ...(options.schemaMetadata || {}),
58
+ };
61
59
  {
62
- const algorithm = options.schemaMetadata?.hash == null
60
+ const algorithm = options.schemaMetadata?.hash == null ||
61
+ options.schemaMetadata?.hash === true
63
62
  ? 'md5'
64
- : options.schemaMetadata?.hash === true
65
- ? 'md5'
66
- : options.schemaMetadata.hash;
63
+ : options.schemaMetadata.hash;
67
64
  if (algorithm) {
68
65
  schema.hash = (0, node_crypto_1.createHash)(algorithm, {})
69
66
  .update(JSON.stringify(schema))
@@ -79,11 +76,11 @@ async function forgeSchema(options) {
79
76
  if (options.outputSchemaMetadataFile) {
80
77
  const map = {
81
78
  $id: options.schemaId || '',
82
- version: options.schemaMetadata?.version,
79
+ schemaHash: schema.hash,
83
80
  title: options.schemaMetadata?.title,
84
81
  description: options.schemaMetadata?.description,
82
+ version: options.schemaMetadata?.version,
85
83
  $comment: options.schemaMetadata?.$comment,
86
- schemaHash: schema.hash,
87
84
  refs: {},
88
85
  names: {},
89
86
  serviceRefs: {},
@@ -91,14 +88,15 @@ async function forgeSchema(options) {
91
88
  };
92
89
  const defs = new Set(Object.keys((schema.definitions || {})));
93
90
  for (const name of definitions) {
94
- const ref = `${options.schemaId || ''}#/definitions/${name}`;
91
+ const ref = (0, index_1.buildSchemaDefinitionRef)(name, schemaId);
95
92
  map.names[name] = ref;
96
93
  map.refs[ref] = name;
97
94
  defs.delete(name);
98
95
  }
99
96
  for (const name of defs) {
100
- map.serviceNames[name] = `${options.schemaId || ''}#/definitions/${name}`;
101
- map.serviceRefs[`${options.schemaId || ''}#/definitions/${name}`] = name;
97
+ const ref = (0, index_1.buildSchemaDefinitionRef)(name, options.schemaId);
98
+ map.serviceNames[name] = ref;
99
+ map.serviceRefs[ref] = name;
102
100
  }
103
101
  const content = JSON.stringify(map, null, 2);
104
102
  await (0, promises_1.writeFile)(options.outputSchemaMetadataFile, content, {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const promises_1 = require("node:fs/promises");
4
4
  const error_1 = require("@tsofist/stem/lib/error");
5
5
  const noop_1 = require("@tsofist/stem/lib/noop");
6
+ const pick_1 = require("@tsofist/stem/lib/object/pick");
6
7
  const generator_1 = require("./generator");
7
8
  const types_1 = require("./types");
8
9
  const validator_1 = require("./validator");
@@ -12,8 +13,14 @@ const KEEP_ARTEFACTS = false;
12
13
  describe('generator for a7', () => {
13
14
  const outputSchemaFile = './a7.generated.schema.tmp.json';
14
15
  const outputSchemaMetadataFile = './a7.generated.definitions.tmp.json';
16
+ const schemaMetadata = {
17
+ title: 'Generator TEST',
18
+ version: '1.0.0',
19
+ $comment: 'WARN: This is a test schema.',
20
+ };
15
21
  let forgeSchemaResult;
16
22
  let validator;
23
+ let loadedSchema;
17
24
  beforeAll(async () => {
18
25
  forgeSchemaResult = await (0, generator_1.forgeSchema)({
19
26
  schemaId: 'test',
@@ -25,10 +32,11 @@ describe('generator for a7', () => {
25
32
  outputSchemaMetadataFile,
26
33
  expose: 'all',
27
34
  explicitPublic: true,
35
+ schemaMetadata,
28
36
  });
29
37
  validator = (0, validator_1.createSchemaForgeValidator)({}, true);
30
- const schema = await (0, generator_1.loadJSONSchema)([outputSchemaFile]);
31
- validator.addSchema(schema);
38
+ loadedSchema = await (0, generator_1.loadJSONSchema)([outputSchemaFile]);
39
+ validator.addSchema(loadedSchema);
32
40
  });
33
41
  afterAll(async () => {
34
42
  if (!KEEP_ARTEFACTS) {
@@ -36,6 +44,11 @@ describe('generator for a7', () => {
36
44
  await (0, promises_1.unlink)(outputSchemaMetadataFile).catch(noop_1.noop);
37
45
  }
38
46
  });
47
+ it('generated schema should have correct metadata', () => {
48
+ expect(forgeSchemaResult).toBeTruthy();
49
+ const schema = forgeSchemaResult.schema;
50
+ expect((0, pick_1.pickProps)(schema, Object.keys(schemaMetadata))).toStrictEqual(schemaMetadata);
51
+ });
39
52
  it('generated schema should be valid', () => {
40
53
  expect(forgeSchemaResult).toBeTruthy();
41
54
  const schema = validator.getSchema('test#/definitions/SomeAPI_doSomeWithUser_Args');
@@ -50,6 +63,48 @@ describe('generator for a7', () => {
50
63
  expect(schema.maxItems).toStrictEqual(3);
51
64
  });
52
65
  });
66
+ describe('validator for a7', () => {
67
+ const outputSchemaFile = './a7.generated.schema.tmp.json';
68
+ const outputSchemaMetadataFile = './a7.generated.definitions.tmp.json';
69
+ let validator;
70
+ let loadedSchema;
71
+ beforeAll(async () => {
72
+ await (0, generator_1.forgeSchema)({
73
+ schemaId: 'test',
74
+ allowUseFallbackDescription: true,
75
+ tsconfigFrom: './tsconfig.build-test.json',
76
+ sourcesDirectoryPattern: 'test-sources/a7',
77
+ sourcesFilesPattern: ['service-api.ts', 'types.ts'],
78
+ outputSchemaFile,
79
+ outputSchemaMetadataFile,
80
+ expose: 'all',
81
+ explicitPublic: true,
82
+ });
83
+ validator = (0, validator_1.createSchemaForgeValidator)({}, true);
84
+ loadedSchema = await (0, generator_1.loadJSONSchema)([outputSchemaFile]);
85
+ validator.addSchema(loadedSchema);
86
+ });
87
+ afterAll(async () => {
88
+ if (!KEEP_ARTEFACTS) {
89
+ await (0, promises_1.unlink)(outputSchemaFile).catch(noop_1.noop);
90
+ await (0, promises_1.unlink)(outputSchemaMetadataFile).catch(noop_1.noop);
91
+ }
92
+ });
93
+ it('should be able to warm-up cache', async () => {
94
+ const initial = validator.compilationArtifactCount;
95
+ expect(initial).toStrictEqual(2);
96
+ validator.warmupCacheSync();
97
+ const warmed = validator.compilationArtifactCount;
98
+ expect(warmed).toStrictEqual(10);
99
+ validator.clear();
100
+ const cleared = validator.compilationArtifactCount;
101
+ expect(cleared).toStrictEqual(1);
102
+ validator.addSchema(loadedSchema);
103
+ expect(validator.compilationArtifactCount).toStrictEqual(initial);
104
+ await validator.warmupCache();
105
+ expect(validator.compilationArtifactCount).toStrictEqual(warmed);
106
+ });
107
+ });
53
108
  describe('generator for a6', () => {
54
109
  const outputSchemaFile = './a6.generated.schema.tmp.json';
55
110
  const outputSchemaMetadataFile = './a6.generated.definitions.tmp.json';
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import '@total-typescript/ts-reset';
2
- import { SchemaDefinitionInfo, SchemaForgeSignatureSuffix } from './types';
2
+ import { SchemaDefinitionInfo, SchemaForgeDefinitionRef, SchemaForgeSignatureSuffix } from './types';
3
3
  export declare function buildAPIInterfaceSchemaSignature(interfaceName: string): string;
4
4
  export declare function buildAPIInterfaceSchemaSignature(interfaceName: string, memberName: string): string;
5
5
  export declare function buildAPIInterfaceSchemaSignature(interfaceName: string, methodName: string, suffix: SchemaForgeSignatureSuffix): string;
6
+ export declare function buildSchemaDefinitionRef(definitionName: string, schemaId: string | undefined): SchemaForgeDefinitionRef;
6
7
  export declare function parseSchemaDefinitionInfo(name: string, schemaId: string): SchemaDefinitionInfo;
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildAPIInterfaceSchemaSignature = buildAPIInterfaceSchemaSignature;
4
+ exports.buildSchemaDefinitionRef = buildSchemaDefinitionRef;
4
5
  exports.parseSchemaDefinitionInfo = parseSchemaDefinitionInfo;
5
6
  require("@total-typescript/ts-reset");
6
7
  const substr_1 = require("@tsofist/stem/lib/string/substr");
@@ -20,6 +21,9 @@ function buildAPIInterfaceSchemaSignature(interfaceName, memberName, suffix) {
20
21
  }
21
22
  return result;
22
23
  }
24
+ function buildSchemaDefinitionRef(definitionName, schemaId) {
25
+ return `${schemaId || ''}#/definitions/${definitionName}`;
26
+ }
23
27
  function parseSchemaDefinitionInfo(name, schemaId) {
24
28
  const kind = name.endsWith(N_I)
25
29
  ? types_1.SchemaDefinitionKind.API
@@ -28,7 +32,7 @@ function parseSchemaDefinitionInfo(name, schemaId) {
28
32
  : name.endsWith(N_R)
29
33
  ? types_1.SchemaDefinitionKind.APIMethodResult
30
34
  : types_1.SchemaDefinitionKind.Type;
31
- const ref = `${schemaId}#/definitions/${name}`;
35
+ const ref = buildSchemaDefinitionRef(name, schemaId);
32
36
  switch (kind) {
33
37
  case types_1.SchemaDefinitionKind.API:
34
38
  return {
@@ -1,35 +1,47 @@
1
- import { Nullable } from '@tsofist/stem';
2
- import { AnySchema, ErrorsTextOptions, Options, Schema, SchemaObject } from 'ajv';
1
+ import { ArrayMay, Nullable } from '@tsofist/stem';
2
+ import { ErrorsTextOptions, Options, SchemaObject } from 'ajv';
3
3
  import { SchemaDefinitionInfo, SchemaForgeDefinitionRef, SchemaForgeValidationContextBase, SchemaForgeValidationFunction, SchemaForgeValidationReport, SchemaForgeValidationResult } from './types';
4
4
  export type SchemaForgeValidator = ReturnType<typeof createSchemaForgeValidator>;
5
+ export type SchemaForgeValidatorOptions = Parameters<typeof createSchemaForgeValidator>;
6
+ /**
7
+ * Create SchemaForge Registry: a json-schema validator with additional features
8
+ */
5
9
  export declare function createSchemaForgeValidator(engineOptions?: Options, useAdditionalFormats?: boolean): {
10
+ readonly compilationArtifactCount: number;
6
11
  readonly rev: number;
12
+ clear: () => void;
7
13
  clone: (options?: Omit<Options, "schemas">, onSchema?: (value: SchemaObject) => SchemaObject) => {
14
+ readonly compilationArtifactCount: number;
8
15
  readonly rev: number;
16
+ clear: () => void;
9
17
  clone: /*elided*/ any;
10
- removeSchema: (schemaIdentity?: AnySchema | SchemaForgeDefinitionRef | RegExp) => void;
18
+ removeSchema: (ref: ArrayMay<SchemaForgeDefinitionRef>) => void;
11
19
  hasValidator: (ref: SchemaForgeDefinitionRef) => boolean;
12
- getValidator: <TData = unknown>(ref: SchemaForgeDefinitionRef | AnySchema) => SchemaForgeValidationFunction<TData> | undefined;
13
- getSchema: (ref: SchemaForgeDefinitionRef) => AnySchema | undefined;
20
+ getValidator: <TData = unknown>(ref: SchemaForgeDefinitionRef | SchemaObject) => SchemaForgeValidationFunction<TData> | undefined;
21
+ getSchema: (ref: SchemaForgeDefinitionRef) => import("ajv").AnySchema | undefined;
14
22
  getRootSchema: (schemaId: string) => (SchemaObject & {
15
- definitions?: AnySchema;
23
+ definitions?: SchemaObject;
16
24
  }) | undefined;
17
- addSchema: (schema: Schema[]) => void;
25
+ addSchema: (schema: SchemaObject[]) => void;
18
26
  validateBySchema: (ref: SchemaForgeDefinitionRef, data: unknown, instancePath?: string) => SchemaForgeValidationResult;
19
27
  checkBySchema: <T = unknown, Ctx extends SchemaForgeValidationContextBase = SchemaForgeValidationContextBase>(ref: SchemaForgeDefinitionRef, data: unknown, context?: Ctx) => data is T;
20
28
  validationErrorsText: (errors: Nullable<SchemaForgeValidationReport>, options?: ErrorsTextOptions) => string;
21
29
  listDefinitions: (predicate?: (info: SchemaDefinitionInfo) => boolean) => SchemaDefinitionInfo[];
30
+ warmupCache: (schemasPerIteration?: number, delayMs?: number) => Promise<void>;
31
+ warmupCacheSync: () => void;
22
32
  };
23
- removeSchema: (schemaIdentity?: AnySchema | SchemaForgeDefinitionRef | RegExp) => void;
33
+ removeSchema: (ref: ArrayMay<SchemaForgeDefinitionRef>) => void;
24
34
  hasValidator: (ref: SchemaForgeDefinitionRef) => boolean;
25
- getValidator: <TData = unknown>(ref: SchemaForgeDefinitionRef | AnySchema) => SchemaForgeValidationFunction<TData> | undefined;
26
- getSchema: (ref: SchemaForgeDefinitionRef) => AnySchema | undefined;
35
+ getValidator: <TData = unknown>(ref: SchemaForgeDefinitionRef | SchemaObject) => SchemaForgeValidationFunction<TData> | undefined;
36
+ getSchema: (ref: SchemaForgeDefinitionRef) => import("ajv").AnySchema | undefined;
27
37
  getRootSchema: (schemaId: string) => (SchemaObject & {
28
- definitions?: AnySchema;
38
+ definitions?: SchemaObject;
29
39
  }) | undefined;
30
- addSchema: (schema: Schema[]) => void;
40
+ addSchema: (schema: SchemaObject[]) => void;
31
41
  validateBySchema: (ref: SchemaForgeDefinitionRef, data: unknown, instancePath?: string) => SchemaForgeValidationResult;
32
42
  checkBySchema: <T = unknown, Ctx extends SchemaForgeValidationContextBase = SchemaForgeValidationContextBase>(ref: SchemaForgeDefinitionRef, data: unknown, context?: Ctx) => data is T;
33
43
  validationErrorsText: (errors: Nullable<SchemaForgeValidationReport>, options?: ErrorsTextOptions) => string;
34
44
  listDefinitions: (predicate?: (info: SchemaDefinitionInfo) => boolean) => SchemaDefinitionInfo[];
45
+ warmupCache: (schemasPerIteration?: number, delayMs?: number) => Promise<void>;
46
+ warmupCacheSync: () => void;
35
47
  };
package/lib/validator.js CHANGED
@@ -1,14 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createSchemaForgeValidator = createSchemaForgeValidator;
4
+ const as_array_1 = require("@tsofist/stem/lib/as-array");
5
+ const chunk_1 = require("@tsofist/stem/lib/chunk");
4
6
  const error_1 = require("@tsofist/stem/lib/error");
5
7
  const entries_1 = require("@tsofist/stem/lib/object/entries");
6
8
  const values_1 = require("@tsofist/stem/lib/object/values");
9
+ const delay_1 = require("@tsofist/stem/lib/timers/delay");
7
10
  const ajv_1 = require("ajv");
8
11
  const ajv_formats_1 = require("ajv-formats");
9
12
  const types_1 = require("./types");
10
13
  const index_1 = require("./index");
11
14
  const DEF_OPTIONS = {
15
+ meta: true,
16
+ defaultMeta: 'http://json-schema.org/draft-07/schema',
12
17
  allErrors: true,
13
18
  strict: true,
14
19
  strictSchema: true,
@@ -18,14 +23,33 @@ const DEF_OPTIONS = {
18
23
  coerceTypes: false,
19
24
  removeAdditional: false,
20
25
  unicodeRegExp: true,
26
+ useDefaults: false,
27
+ addUsedSchema: false,
28
+ inlineRefs: true,
29
+ ownProperties: true,
30
+ discriminator: false,
31
+ code: {
32
+ es5: false,
33
+ esm: false,
34
+ optimize: 2,
35
+ },
21
36
  };
37
+ /**
38
+ * Create SchemaForge Registry: a json-schema validator with additional features
39
+ */
22
40
  function createSchemaForgeValidator(engineOptions, useAdditionalFormats = false) {
23
41
  engineOptions = {
24
42
  ...DEF_OPTIONS,
25
43
  ...engineOptions,
26
44
  };
45
+ const initialSchemas = engineOptions.schemas;
46
+ if (initialSchemas)
47
+ delete engineOptions.schemas;
27
48
  let rev = 0;
28
49
  let engine = new ajv_1.default(engineOptions);
50
+ engine.removeSchema();
51
+ if (initialSchemas)
52
+ engine.addSchema(initialSchemas);
29
53
  addJSDocKeywords(engine);
30
54
  if (useAdditionalFormats)
31
55
  engine = (0, ajv_formats_1.default)(engine);
@@ -37,7 +61,10 @@ function createSchemaForgeValidator(engineOptions, useAdditionalFormats = false)
37
61
  for (const env of (0, values_1.nonNullableValues)(engine.schemas)) {
38
62
  if (env.meta)
39
63
  continue;
40
- schemas.push(onSchema ? onSchema(env.schema) : env.schema);
64
+ const schema = onSchema
65
+ ? onSchema(env.schema)
66
+ : env.schema;
67
+ schemas.push(schema);
41
68
  }
42
69
  const opts = {
43
70
  ...engineOptions,
@@ -52,13 +79,16 @@ function createSchemaForgeValidator(engineOptions, useAdditionalFormats = false)
52
79
  function getValidator(ref) {
53
80
  if (typeof ref === 'string')
54
81
  return engine.getSchema(ref);
55
- return engine.compile(ref);
82
+ const result = engine.compile(ref);
83
+ checkIsSyncValidator(result);
84
+ return result;
56
85
  }
57
86
  /**
58
87
  * Check if schema exists
88
+ * WARN: this method may compile schema if it's not compiled yet
59
89
  */
60
90
  function hasValidator(ref) {
61
- return engine.getSchema(ref) != null;
91
+ return ref in engine.schemas || ref in engine.refs || engine.getSchema(ref) != null;
62
92
  }
63
93
  /**
64
94
  * Add root schema(s) to registry
@@ -68,10 +98,18 @@ function createSchemaForgeValidator(engineOptions, useAdditionalFormats = false)
68
98
  rev++;
69
99
  }
70
100
  /**
71
- * Remove root schema(s) from registry using Schema object, Reference or RegExp pattern to math Schema ID
101
+ * Remove root schema(s) from registry
102
+ */
103
+ function removeSchema(ref) {
104
+ for (const item of (0, as_array_1.asArray)(ref))
105
+ engine.removeSchema(item);
106
+ rev++;
107
+ }
108
+ /**
109
+ * Remove all schemas from registry
72
110
  */
73
- function removeSchema(schemaIdentity) {
74
- engine.removeSchema(schemaIdentity);
111
+ function clear() {
112
+ engine.removeSchema();
75
113
  rev++;
76
114
  }
77
115
  /**
@@ -119,7 +157,8 @@ function createSchemaForgeValidator(engineOptions, useAdditionalFormats = false)
119
157
  }
120
158
  return result.valid;
121
159
  }
122
- function forEachDefinitions(callback) {
160
+ function mapDefinitions(callback) {
161
+ const result = [];
123
162
  for (const [schemaId, env] of (0, entries_1.entries)(engine.schemas)) {
124
163
  if (env &&
125
164
  typeof env.schema === 'object' &&
@@ -127,17 +166,18 @@ function createSchemaForgeValidator(engineOptions, useAdditionalFormats = false)
127
166
  env.schema.definitions &&
128
167
  typeof env.schema.definitions === 'object') {
129
168
  for (const name of Object.keys(env.schema.definitions)) {
130
- callback(name, schemaId);
169
+ result.push(callback(name, schemaId));
131
170
  }
132
171
  }
133
172
  }
173
+ return result;
134
174
  }
135
175
  /**
136
176
  * List schema definitions
137
177
  */
138
178
  function listDefinitions(predicate) {
139
179
  const result = [];
140
- forEachDefinitions((name, schemaId) => {
180
+ mapDefinitions((name, schemaId) => {
141
181
  const info = (0, index_1.parseSchemaDefinitionInfo)(name, schemaId);
142
182
  if (predicate === undefined || predicate(info))
143
183
  result.push(info);
@@ -156,10 +196,40 @@ function createSchemaForgeValidator(engineOptions, useAdditionalFormats = false)
156
196
  function getRootSchema(schemaId) {
157
197
  return engine.schemas[schemaId]?.schema;
158
198
  }
199
+ /**
200
+ * Synchronously warm up validator cache
201
+ * This action is useful to pre-compile all schemas and their definitions
202
+ */
203
+ function warmupCacheSync() {
204
+ mapDefinitions((name, schemaId) => {
205
+ const ref = (0, index_1.buildSchemaDefinitionRef)(name, schemaId);
206
+ checkIsSyncValidator(engine.getSchema(ref));
207
+ });
208
+ }
209
+ /**
210
+ * Asynchronously warm up validator cache
211
+ * This action is useful to pre-compile all schemas and their definitions
212
+ */
213
+ async function warmupCache(schemasPerIteration = 5, delayMs = 1) {
214
+ await (0, chunk_1.chunk)(mapDefinitions((name, schemaId) => (0, index_1.buildSchemaDefinitionRef)(name, schemaId)), schemasPerIteration, async (items) => {
215
+ for (const ref of items)
216
+ engine.getSchema(ref);
217
+ return (0, delay_1.delay)(delayMs);
218
+ });
219
+ }
159
220
  return {
221
+ get compilationArtifactCount() {
222
+ const names = new Set([
223
+ //
224
+ ...Object.keys(engine.refs),
225
+ ...Object.keys(engine.schemas),
226
+ ]);
227
+ return names.size;
228
+ },
160
229
  get rev() {
161
230
  return rev;
162
231
  },
232
+ clear,
163
233
  clone,
164
234
  removeSchema,
165
235
  hasValidator,
@@ -171,6 +241,8 @@ function createSchemaForgeValidator(engineOptions, useAdditionalFormats = false)
171
241
  checkBySchema,
172
242
  validationErrorsText,
173
243
  listDefinitions,
244
+ warmupCache,
245
+ warmupCacheSync,
174
246
  };
175
247
  }
176
248
  function addJSDocKeywords(engine) {
@@ -181,6 +253,13 @@ function addJSDocKeywords(engine) {
181
253
  const IXNamePattern = '^ix_[a-z][a-zA-Z0-9_]+$';
182
254
  const EntityNamePattern = '^([a-zA-Z_][a-z0-9_]*\\.)?[a-z_][a-z0-9_]*$';
183
255
  const FakerModulePattern = '^[a-zA-Z.]+$';
256
+ engine.addKeyword({
257
+ keyword: 'version',
258
+ metaSchema: {
259
+ type: 'string',
260
+ },
261
+ dependencies: ['$id', '$schema'],
262
+ });
184
263
  engine.addKeyword({
185
264
  keyword: 'hash',
186
265
  metaSchema: {
@@ -265,3 +344,8 @@ function addJSDocKeywords(engine) {
265
344
  },
266
345
  });
267
346
  }
347
+ function checkIsSyncValidator(fn) {
348
+ if (typeof fn === 'function' && '$async' in fn) {
349
+ (0, error_1.raise)('[SchemaForge] Asynchronous validation schemas are not supported');
350
+ }
351
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsofist/schema-forge",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "Generate JSON schema from TypeScript types",
5
5
  "author": "Andrew Berdnikov <tsofistgudmen@gmail.com>",
6
6
  "license": "LGPL-3.0",
@@ -20,8 +20,8 @@
20
20
  "test:watch": "jest --watch"
21
21
  },
22
22
  "dependencies": {
23
- "@faker-js/faker": "^9.4.0",
24
- "@tsofist/stem": "^2.0.0",
23
+ "@faker-js/faker": "^9.6.0",
24
+ "@tsofist/stem": "^2.2.0",
25
25
  "ajv": "^8.17.1",
26
26
  "ajv-formats": "^3.0.1",
27
27
  "json-schema-faker": "^0.5.8",
@@ -31,13 +31,13 @@
31
31
  "devDependencies": {
32
32
  "@tsofist/web-buddy": "^1.21.0",
33
33
  "@types/jest": "^29.5.14",
34
- "@types/node": "^20.17.17",
35
- "@types/supertest": "^6.0.2",
34
+ "@types/node": "^20.17.28",
35
+ "@types/supertest": "^6.0.3",
36
36
  "jest": "^29.7.0",
37
37
  "rimraf": "^6.0.1",
38
- "supertest": "^7.0.0",
39
- "ts-jest": "^29.2.5",
40
- "typescript": "~5.7.3"
38
+ "supertest": "^7.1.0",
39
+ "ts-jest": "^29.3.0",
40
+ "typescript": "~5.8.2"
41
41
  },
42
42
  "publishConfig": {
43
43
  "registry": "https://registry.npmjs.org/",