@kravc/schema 2.8.0-alpha.0 → 2.8.0-alpha.2

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.
@@ -0,0 +1,98 @@
1
+ import Schema from '../Schema';
2
+ /**
3
+ * Reads schema source from YAML file and returns a Schema instance.
4
+ *
5
+ * **Intent:** Load and parse a single YAML schema file, extracting the schema ID
6
+ * from the filename and creating a Schema instance for use in validation or
7
+ * schema composition.
8
+ *
9
+ * **Use Cases:**
10
+ * - Load individual schema files during application startup
11
+ * - Parse YAML schema definitions into Schema instances
12
+ * - Extract schema identifiers from file paths automatically
13
+ * - Support both enum and properties-based schemas from YAML files
14
+ *
15
+ * @param yamlPath - Absolute or relative path to the YAML schema file
16
+ * @returns A Schema instance with ID extracted from the filename (without .yaml extension)
17
+ *
18
+ * **Example:**
19
+ * ```typescript
20
+ * const schema = loadSync('/path/to/schemas/User.yaml');
21
+ * // schema.id === 'User'
22
+ * // schema.source contains the parsed YAML content
23
+ * ```
24
+ *
25
+ * **Example - Enum Schema:**
26
+ * ```typescript
27
+ * const statusSchema = loadSync('/path/to/schemas/Status.yaml');
28
+ * // If Status.yaml contains: { enum: ['PENDING', 'ACTIVE'] }
29
+ * // statusSchema.isEnum === true
30
+ * ```
31
+ */
32
+ export declare const loadSync: (yamlPath: string) => Schema;
33
+ /**
34
+ * Creates a map of schemas by ID, loading from YAML files and merging with programmatic schemas.
35
+ *
36
+ * **Intent:** Build a centralized schema registry that combines file-based YAML schemas
37
+ * with programmatically created Schema instances, providing a unified lookup mechanism
38
+ * by schema ID. This enables hybrid schema management where some schemas are defined
39
+ * in YAML files while others are created dynamically in code.
40
+ *
41
+ * **Use Cases:**
42
+ * - Initialize schema registries for validators from both files and code
43
+ * - Support schema composition where base schemas come from files and extended
44
+ * schemas are created programmatically
45
+ * - Enable schema overriding where programmatic schemas can replace file-based ones
46
+ * - Build schema maps for credential factories or API validation systems
47
+ * - Support development workflows where schemas evolve from YAML to code
48
+ *
49
+ * @param servicePath - Path to directory containing YAML schema files (searched recursively)
50
+ * @param modules - Array of Schema instances or other values (non-Schema values are filtered out)
51
+ * @returns Record mapping schema IDs to Schema instances, with modules schemas overriding YAML schemas
52
+ *
53
+ * **Example - Basic Usage:**
54
+ * ```typescript
55
+ * const schemasMap = createSchemasMap('/path/to/examples/schemas', []);
56
+ * // Returns: {
57
+ * // FavoriteItem: Schema { id: 'FavoriteItem', ... },
58
+ * // Profile: Schema { id: 'Profile', ... },
59
+ * // Status: Schema { id: 'Status', ... },
60
+ * // Preferences: Schema { id: 'Preferences', ... }
61
+ * // }
62
+ * ```
63
+ *
64
+ * **Example - Merging Programmatic Schemas:**
65
+ * ```typescript
66
+ * const customSchema = new Schema(
67
+ * { customField: { type: 'string' } },
68
+ * 'CustomSchema'
69
+ * );
70
+ * const schemasMap = createSchemasMap('/path/to/schemas', [customSchema]);
71
+ * // schemasMap contains both YAML schemas and CustomSchema
72
+ * ```
73
+ *
74
+ * **Example - Overriding YAML Schemas:**
75
+ * ```typescript
76
+ * const updatedProfile = new Schema(
77
+ * { name: { type: 'string' }, newField: { type: 'number' } },
78
+ * 'Profile'
79
+ * );
80
+ * const schemasMap = createSchemasMap('/path/to/schemas', [updatedProfile]);
81
+ * // schemasMap.Profile is the updatedProfile instance, not the YAML version
82
+ * ```
83
+ *
84
+ * **Example - Filtering Non-Schema Values:**
85
+ * ```typescript
86
+ * const schema = new Schema({ field: { type: 'string' } }, 'Test');
87
+ * const schemasMap = createSchemasMap('/path/to/schemas', [
88
+ * schema,
89
+ * 'not a schema',
90
+ * { id: 'fake' },
91
+ * null
92
+ * ]);
93
+ * // Only the Schema instance is included, other values are ignored
94
+ * ```
95
+ */
96
+ declare const createSchemasMap: (servicePath: string, modules: unknown[]) => Record<string, Schema>;
97
+ export default createSchemasMap;
98
+ //# sourceMappingURL=createSchemasMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createSchemasMap.d.ts","sourceRoot":"","sources":["../../src/helpers/createSchemasMap.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,WAAW,CAAC;AAM/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,QAAQ,GAAI,UAAU,MAAM,WAUxC,CAAC;AAuFF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,QAAA,MAAM,gBAAgB,GAAI,aAAa,MAAM,EAAE,SAAS,OAAO,EAAE,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAYxF,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadSync = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const Schema_1 = __importDefault(require("../Schema"));
9
+ const js_yaml_1 = require("js-yaml");
10
+ const lodash_1 = require("lodash");
11
+ const fs_1 = require("fs");
12
+ /**
13
+ * Reads schema source from YAML file and returns a Schema instance.
14
+ *
15
+ * **Intent:** Load and parse a single YAML schema file, extracting the schema ID
16
+ * from the filename and creating a Schema instance for use in validation or
17
+ * schema composition.
18
+ *
19
+ * **Use Cases:**
20
+ * - Load individual schema files during application startup
21
+ * - Parse YAML schema definitions into Schema instances
22
+ * - Extract schema identifiers from file paths automatically
23
+ * - Support both enum and properties-based schemas from YAML files
24
+ *
25
+ * @param yamlPath - Absolute or relative path to the YAML schema file
26
+ * @returns A Schema instance with ID extracted from the filename (without .yaml extension)
27
+ *
28
+ * **Example:**
29
+ * ```typescript
30
+ * const schema = loadSync('/path/to/schemas/User.yaml');
31
+ * // schema.id === 'User'
32
+ * // schema.source contains the parsed YAML content
33
+ * ```
34
+ *
35
+ * **Example - Enum Schema:**
36
+ * ```typescript
37
+ * const statusSchema = loadSync('/path/to/schemas/Status.yaml');
38
+ * // If Status.yaml contains: { enum: ['PENDING', 'ACTIVE'] }
39
+ * // statusSchema.isEnum === true
40
+ * ```
41
+ */
42
+ const loadSync = (yamlPath) => {
43
+ const schemaId = yamlPath
44
+ .split('/')
45
+ .reverse()[0]
46
+ .split('.yaml')[0];
47
+ const file = (0, fs_1.readFileSync)(yamlPath);
48
+ const source = (0, js_yaml_1.load)(file.toString());
49
+ return new Schema_1.default(source, schemaId);
50
+ };
51
+ exports.loadSync = loadSync;
52
+ /**
53
+ * Recursively lists all files in a directory and its subdirectories.
54
+ *
55
+ * **Intent:** Traverse a directory tree and collect all file paths, enabling
56
+ * discovery of schema files nested in subdirectories without manual path specification.
57
+ *
58
+ * **Use Cases:**
59
+ * - Find all schema files in a directory structure
60
+ * - Support organized schema layouts with nested folders
61
+ * - Enable schema discovery without hardcoding file paths
62
+ * - Prepare file list for filtering and processing
63
+ *
64
+ * @param servicePath - Path to the directory to traverse
65
+ * @returns Array of absolute file paths found in the directory tree
66
+ *
67
+ * **Example:**
68
+ * ```typescript
69
+ * const files = listFilesSync('/path/to/schemas');
70
+ * // Returns: [
71
+ * // '/path/to/schemas/User.yaml',
72
+ * // '/path/to/schemas/nested/Profile.yaml',
73
+ * // '/path/to/schemas/nested/deep/Status.yaml'
74
+ * // ]
75
+ * ```
76
+ *
77
+ * **Example - Flat Directory:**
78
+ * ```typescript
79
+ * const files = listFilesSync('/path/to/schemas');
80
+ * // Returns: [
81
+ * // '/path/to/schemas/User.yaml',
82
+ * // '/path/to/schemas/Profile.yaml'
83
+ * // ]
84
+ * ```
85
+ */
86
+ const listFilesSync = (servicePath) => (0, fs_1.readdirSync)(servicePath)
87
+ .reduce((filePaths, fileName) => (0, fs_1.statSync)(path_1.default.join(servicePath, fileName)).isDirectory() ?
88
+ filePaths.concat(listFilesSync(path_1.default.join(servicePath, fileName))) :
89
+ filePaths.concat(path_1.default.join(servicePath, fileName)), []);
90
+ /**
91
+ * Reads all YAML schema files from a directory and creates Schema instances.
92
+ *
93
+ * **Intent:** Bulk load schema definitions from YAML files in a directory,
94
+ * automatically discovering and parsing all schema files for use in schema
95
+ * registries or validators.
96
+ *
97
+ * **Use Cases:**
98
+ * - Load all schemas from a schemas directory at application startup
99
+ * - Initialize schema registries from file-based definitions
100
+ * - Support schema-as-code workflows where schemas are stored as YAML files
101
+ * - Enable automatic schema discovery without manual registration
102
+ *
103
+ * @param servicePath - Path to the directory containing YAML schema files
104
+ * @returns Array of Schema instances, one for each YAML file found
105
+ *
106
+ * **Example:**
107
+ * ```typescript
108
+ * const schemas = readSchemasSync('/path/to/examples/schemas');
109
+ * // Returns: [
110
+ * // Schema { id: 'FavoriteItem', ... },
111
+ * // Schema { id: 'Profile', ... },
112
+ * // Schema { id: 'Status', ... },
113
+ * // Schema { id: 'Preferences', ... }
114
+ * // ]
115
+ * ```
116
+ *
117
+ * **Example - With Nested Directories:**
118
+ * ```typescript
119
+ * const schemas = readSchemasSync('/path/to/schemas');
120
+ * // Automatically finds schemas in subdirectories:
121
+ * // - /path/to/schemas/User.yaml
122
+ * // - /path/to/schemas/nested/Profile.yaml
123
+ * ```
124
+ */
125
+ const readSchemasSync = (servicePath) => listFilesSync(servicePath)
126
+ .filter((fileName) => fileName.endsWith('.yaml'))
127
+ .map((schemaPath) => (0, exports.loadSync)(schemaPath));
128
+ /**
129
+ * Creates a map of schemas by ID, loading from YAML files and merging with programmatic schemas.
130
+ *
131
+ * **Intent:** Build a centralized schema registry that combines file-based YAML schemas
132
+ * with programmatically created Schema instances, providing a unified lookup mechanism
133
+ * by schema ID. This enables hybrid schema management where some schemas are defined
134
+ * in YAML files while others are created dynamically in code.
135
+ *
136
+ * **Use Cases:**
137
+ * - Initialize schema registries for validators from both files and code
138
+ * - Support schema composition where base schemas come from files and extended
139
+ * schemas are created programmatically
140
+ * - Enable schema overriding where programmatic schemas can replace file-based ones
141
+ * - Build schema maps for credential factories or API validation systems
142
+ * - Support development workflows where schemas evolve from YAML to code
143
+ *
144
+ * @param servicePath - Path to directory containing YAML schema files (searched recursively)
145
+ * @param modules - Array of Schema instances or other values (non-Schema values are filtered out)
146
+ * @returns Record mapping schema IDs to Schema instances, with modules schemas overriding YAML schemas
147
+ *
148
+ * **Example - Basic Usage:**
149
+ * ```typescript
150
+ * const schemasMap = createSchemasMap('/path/to/examples/schemas', []);
151
+ * // Returns: {
152
+ * // FavoriteItem: Schema { id: 'FavoriteItem', ... },
153
+ * // Profile: Schema { id: 'Profile', ... },
154
+ * // Status: Schema { id: 'Status', ... },
155
+ * // Preferences: Schema { id: 'Preferences', ... }
156
+ * // }
157
+ * ```
158
+ *
159
+ * **Example - Merging Programmatic Schemas:**
160
+ * ```typescript
161
+ * const customSchema = new Schema(
162
+ * { customField: { type: 'string' } },
163
+ * 'CustomSchema'
164
+ * );
165
+ * const schemasMap = createSchemasMap('/path/to/schemas', [customSchema]);
166
+ * // schemasMap contains both YAML schemas and CustomSchema
167
+ * ```
168
+ *
169
+ * **Example - Overriding YAML Schemas:**
170
+ * ```typescript
171
+ * const updatedProfile = new Schema(
172
+ * { name: { type: 'string' }, newField: { type: 'number' } },
173
+ * 'Profile'
174
+ * );
175
+ * const schemasMap = createSchemasMap('/path/to/schemas', [updatedProfile]);
176
+ * // schemasMap.Profile is the updatedProfile instance, not the YAML version
177
+ * ```
178
+ *
179
+ * **Example - Filtering Non-Schema Values:**
180
+ * ```typescript
181
+ * const schema = new Schema({ field: { type: 'string' } }, 'Test');
182
+ * const schemasMap = createSchemasMap('/path/to/schemas', [
183
+ * schema,
184
+ * 'not a schema',
185
+ * { id: 'fake' },
186
+ * null
187
+ * ]);
188
+ * // Only the Schema instance is included, other values are ignored
189
+ * ```
190
+ */
191
+ const createSchemasMap = (servicePath, modules) => {
192
+ const yamlSchemas = readSchemasSync(servicePath);
193
+ const schemasMap = (0, lodash_1.keyBy)(yamlSchemas, 'id');
194
+ const schemas = modules
195
+ .filter(schema => schema instanceof Schema_1.default);
196
+ for (const schema of schemas) {
197
+ schemasMap[schema.id] = schema;
198
+ }
199
+ return schemasMap;
200
+ };
201
+ exports.default = createSchemasMap;
202
+ //# sourceMappingURL=createSchemasMap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createSchemasMap.js","sourceRoot":"","sources":["../../src/helpers/createSchemasMap.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AACxB,uDAA+B;AAC/B,qCAA+B;AAC/B,mCAA+B;AAE/B,2BAAyD;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACI,MAAM,QAAQ,GAAG,CAAC,QAAgB,EAAE,EAAE;IAC3C,MAAM,QAAQ,GAAG,QAAQ;SACtB,KAAK,CAAC,GAAG,CAAC;SACV,OAAO,EAAE,CAAC,CAAC,CAAC;SACZ,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAErB,MAAM,IAAI,GAAG,IAAA,iBAAY,EAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,IAAA,cAAI,EAAC,IAAI,CAAC,QAAQ,EAAE,CAAkC,CAAC;IAEtE,OAAO,IAAI,gBAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACtC,CAAC,CAAC;AAVW,QAAA,QAAQ,YAUnB;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,aAAa,GAAG,CAAC,WAAmB,EAAY,EAAE,CACtD,IAAA,gBAAW,EAAC,WAAW,CAAC;KACrB,MAAM,CACL,CAAC,SAAmB,EAAE,QAAgB,EAAE,EAAE,CACxC,IAAA,aAAQ,EACN,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/C,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,SAAS,CAAC,MAAM,CAAC,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CACpD,EACH,EAAE,CAAC,CAAC;AAEV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,eAAe,GAAG,CAAC,WAAmB,EAAE,EAAE,CAC9C,aAAa,CAAC,WAAW,CAAC;KACvB,MAAM,CAAC,CAAC,QAAgB,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;KACxD,GAAG,CAAC,CAAC,UAAkB,EAAE,EAAE,CAAC,IAAA,gBAAQ,EAAC,UAAU,CAAC,CAAC,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,MAAM,gBAAgB,GAAG,CAAC,WAAmB,EAAE,OAAkB,EAA0B,EAAE;IAC3F,MAAM,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,IAAA,cAAK,EAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAE5C,MAAM,OAAO,GAAG,OAAO;SACpB,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,YAAY,gBAAM,CAAC,CAAC;IAE9C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;IACjC,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAEF,kBAAe,gBAAgB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
+ import got from './helpers/got';
1
2
  import Schema from './Schema';
2
3
  import Validator from './Validator';
3
4
  import documentLoader from './ld/documentLoader';
4
5
  import ValidationError from './ValidationError';
5
6
  import CredentialFactory from './CredentialFactory';
6
- export { Schema, Validator, documentLoader, ValidationError, CredentialFactory };
7
+ import createSchemasMap, { loadSync } from './helpers/createSchemasMap';
8
+ export { got, Schema, loadSync, Validator, documentLoader, ValidationError, createSchemasMap, CredentialFactory };
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,UAAU,CAAC;AAC9B,OAAO,SAAS,MAAM,aAAa,CAAC;AACpC,OAAO,cAAc,MAAM,qBAAqB,CAAC;AACjD,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAChD,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EACL,MAAM,EACN,SAAS,EACT,cAAc,EACd,eAAe,EACf,iBAAiB,EAClB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,eAAe,CAAC;AAChC,OAAO,MAAM,MAAM,UAAU,CAAC;AAC9B,OAAO,SAAS,MAAM,aAAa,CAAC;AACpC,OAAO,cAAc,MAAM,qBAAqB,CAAC;AACjD,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAChD,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,gBAAgB,EAAE,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAExE,OAAO,EACL,GAAG,EACH,MAAM,EACN,QAAQ,EACR,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EAClB,CAAC"}
package/dist/index.js CHANGED
@@ -1,9 +1,44 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
5
38
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.CredentialFactory = exports.ValidationError = exports.documentLoader = exports.Validator = exports.Schema = void 0;
39
+ exports.CredentialFactory = exports.createSchemasMap = exports.ValidationError = exports.documentLoader = exports.Validator = exports.loadSync = exports.Schema = exports.got = void 0;
40
+ const got_1 = __importDefault(require("./helpers/got"));
41
+ exports.got = got_1.default;
7
42
  const Schema_1 = __importDefault(require("./Schema"));
8
43
  exports.Schema = Schema_1.default;
9
44
  const Validator_1 = __importDefault(require("./Validator"));
@@ -14,4 +49,7 @@ const ValidationError_1 = __importDefault(require("./ValidationError"));
14
49
  exports.ValidationError = ValidationError_1.default;
15
50
  const CredentialFactory_1 = __importDefault(require("./CredentialFactory"));
16
51
  exports.CredentialFactory = CredentialFactory_1.default;
52
+ const createSchemasMap_1 = __importStar(require("./helpers/createSchemasMap"));
53
+ exports.createSchemasMap = createSchemasMap_1.default;
54
+ Object.defineProperty(exports, "loadSync", { enumerable: true, get: function () { return createSchemasMap_1.loadSync; } });
17
55
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA8B;AAO5B,iBAPK,gBAAM,CAOL;AANR,4DAAoC;AAOlC,oBAPK,mBAAS,CAOL;AANX,yEAAiD;AAO/C,yBAPK,wBAAc,CAOL;AANhB,wEAAgD;AAO9C,0BAPK,yBAAe,CAOL;AANjB,4EAAoD;AAOlD,4BAPK,2BAAiB,CAOL"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wDAAgC;AAS9B,cATK,aAAG,CASL;AARL,sDAA8B;AAS5B,iBATK,gBAAM,CASL;AARR,4DAAoC;AAUlC,oBAVK,mBAAS,CAUL;AATX,yEAAiD;AAU/C,yBAVK,wBAAc,CAUL;AAThB,wEAAgD;AAU9C,0BAVK,yBAAe,CAUL;AATjB,4EAAoD;AAWlD,4BAXK,2BAAiB,CAWL;AAVnB,+EAAwE;AAStE,2BATK,0BAAgB,CASL;AAJhB,yFALyB,2BAAQ,OAKzB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kravc/schema",
3
- "version": "2.8.0-alpha.0",
3
+ "version": "2.8.0-alpha.2",
4
4
  "description": "Advanced JSON schema manipulation and validation library.",
5
5
  "keywords": [
6
6
  "JSON",
@@ -0,0 +1,238 @@
1
+ import path from 'path';
2
+ import createSchemasMap from '../createSchemasMap';
3
+ import Schema from '../../Schema';
4
+
5
+ describe('createSchemasMap', () => {
6
+ const examplesSchemasPath = path.join(__dirname, '../../../examples/schemas');
7
+
8
+ describe('loading schemas from YAML files', () => {
9
+ it('should load all YAML schemas from the examples/schemas directory', () => {
10
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
11
+
12
+ expect(Object.keys(schemasMap).length).toBeGreaterThan(0);
13
+ expect(schemasMap).toHaveProperty('FavoriteItem');
14
+ expect(schemasMap).toHaveProperty('Profile');
15
+ expect(schemasMap).toHaveProperty('Status');
16
+ expect(schemasMap).toHaveProperty('Preferences');
17
+ });
18
+
19
+ it('should create Schema instances from YAML files', () => {
20
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
21
+
22
+ expect(schemasMap.FavoriteItem).toBeInstanceOf(Schema);
23
+ expect(schemasMap.Profile).toBeInstanceOf(Schema);
24
+ expect(schemasMap.Status).toBeInstanceOf(Schema);
25
+ expect(schemasMap.Preferences).toBeInstanceOf(Schema);
26
+ });
27
+
28
+ it('should extract schema ID from YAML filename', () => {
29
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
30
+
31
+ expect(schemasMap.FavoriteItem.id).toBe('FavoriteItem');
32
+ expect(schemasMap.Profile.id).toBe('Profile');
33
+ expect(schemasMap.Status.id).toBe('Status');
34
+ expect(schemasMap.Preferences.id).toBe('Preferences');
35
+ });
36
+
37
+ it('should handle enum schemas correctly', () => {
38
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
39
+
40
+ expect(schemasMap.Status).toBeInstanceOf(Schema);
41
+ expect(schemasMap.Status.isEnum).toBe(true);
42
+ });
43
+
44
+ it('should handle properties schemas correctly', () => {
45
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
46
+
47
+ expect(schemasMap.Profile).toBeInstanceOf(Schema);
48
+ expect(schemasMap.Profile.isEnum).toBe(false);
49
+ expect(schemasMap.FavoriteItem).toBeInstanceOf(Schema);
50
+ expect(schemasMap.FavoriteItem.isEnum).toBe(false);
51
+ });
52
+ });
53
+
54
+ describe('merging modules array', () => {
55
+ it('should merge Schema instances from modules array', () => {
56
+ const customSchema = new Schema(
57
+ { customField: { type: 'string' } },
58
+ 'CustomSchema'
59
+ );
60
+
61
+ const schemasMap = createSchemasMap(examplesSchemasPath, [customSchema]);
62
+
63
+ expect(schemasMap.CustomSchema).toBeInstanceOf(Schema);
64
+ expect(schemasMap.CustomSchema.id).toBe('CustomSchema');
65
+ });
66
+
67
+ it('should override YAML schemas with modules schemas if same ID', () => {
68
+ const customProfile = new Schema(
69
+ { customField: { type: 'string' } },
70
+ 'Profile'
71
+ );
72
+
73
+ const schemasMap = createSchemasMap(examplesSchemasPath, [customProfile]);
74
+
75
+ expect(schemasMap.Profile).toBeInstanceOf(Schema);
76
+ expect(schemasMap.Profile.id).toBe('Profile');
77
+ // The custom schema should override the YAML one
78
+ const source = schemasMap.Profile.source;
79
+ expect(source).toHaveProperty('customField');
80
+ });
81
+
82
+ it('should filter out non-Schema instances from modules', () => {
83
+ const customSchema = new Schema(
84
+ { customField: { type: 'string' } },
85
+ 'CustomSchema'
86
+ );
87
+ const notASchema = { id: 'NotASchema', someProperty: 'value' };
88
+ const alsoNotASchema = 'string value';
89
+
90
+ const schemasMap = createSchemasMap(examplesSchemasPath, [
91
+ customSchema,
92
+ notASchema,
93
+ alsoNotASchema,
94
+ null,
95
+ undefined,
96
+ ]);
97
+
98
+ expect(schemasMap.CustomSchema).toBeInstanceOf(Schema);
99
+ expect(schemasMap).not.toHaveProperty('NotASchema');
100
+ });
101
+
102
+ it('should handle empty modules array', () => {
103
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
104
+
105
+ expect(Object.keys(schemasMap).length).toBeGreaterThan(0);
106
+ expect(schemasMap.FavoriteItem).toBeInstanceOf(Schema);
107
+ });
108
+
109
+ it('should merge multiple Schema instances from modules', () => {
110
+ const schema1 = new Schema({ field1: { type: 'string' } }, 'Schema1');
111
+ const schema2 = new Schema({ field2: { type: 'number' } }, 'Schema2');
112
+ const schema3 = new Schema({ field3: { type: 'boolean' } }, 'Schema3');
113
+
114
+ const schemasMap = createSchemasMap(examplesSchemasPath, [
115
+ schema1,
116
+ schema2,
117
+ schema3,
118
+ ]);
119
+
120
+ expect(schemasMap.Schema1).toBeInstanceOf(Schema);
121
+ expect(schemasMap.Schema2).toBeInstanceOf(Schema);
122
+ expect(schemasMap.Schema3).toBeInstanceOf(Schema);
123
+ expect(schemasMap.Schema1.id).toBe('Schema1');
124
+ expect(schemasMap.Schema2.id).toBe('Schema2');
125
+ expect(schemasMap.Schema3.id).toBe('Schema3');
126
+ });
127
+ });
128
+
129
+ describe('return value structure', () => {
130
+ it('should return a Record<string, Schema>', () => {
131
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
132
+
133
+ expect(typeof schemasMap).toBe('object');
134
+ expect(schemasMap).not.toBeNull();
135
+ expect(Array.isArray(schemasMap)).toBe(false);
136
+
137
+ // Check that all values are Schema instances
138
+ Object.values(schemasMap).forEach((schema) => {
139
+ expect(schema).toBeInstanceOf(Schema);
140
+ });
141
+ });
142
+
143
+ it('should use schema ID as keys in the map', () => {
144
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
145
+
146
+ Object.entries(schemasMap).forEach(([key, schema]) => {
147
+ expect(schema.id).toBe(key);
148
+ });
149
+ });
150
+ });
151
+
152
+ describe('edge cases', () => {
153
+ it('should handle empty directory gracefully', () => {
154
+ const emptyDir = path.join(__dirname, '../../../examples');
155
+ // This directory might have subdirectories but no YAML files directly
156
+ // We'll test with a path that exists but may have no YAML files
157
+ const schemasMap = createSchemasMap(emptyDir, []);
158
+
159
+ expect(typeof schemasMap).toBe('object');
160
+ // If there are no YAML files, the map should be empty or contain only what's in modules
161
+ });
162
+
163
+ it('should ignore non-YAML files', () => {
164
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
165
+
166
+ // Should only contain schemas from YAML files, not TypeScript files
167
+ Object.keys(schemasMap).forEach((key) => {
168
+ expect(key).not.toBe('FavoriteItemSchema');
169
+ expect(key).not.toBe('ProfileSchema');
170
+ expect(key).not.toBe('PreferencesSchema');
171
+ expect(key).not.toBe('StatusSchema');
172
+ });
173
+ });
174
+
175
+ it('should handle modules array with only non-Schema values', () => {
176
+ const schemasMap = createSchemasMap(examplesSchemasPath, [
177
+ 'string',
178
+ 123,
179
+ {},
180
+ null,
181
+ undefined,
182
+ ]);
183
+
184
+ // Should still have YAML schemas
185
+ expect(schemasMap.FavoriteItem).toBeInstanceOf(Schema);
186
+ expect(schemasMap.Profile).toBeInstanceOf(Schema);
187
+ });
188
+ });
189
+
190
+ describe('schema content validation', () => {
191
+ it('should correctly parse FavoriteItem schema', () => {
192
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
193
+ const favoriteItem = schemasMap.FavoriteItem;
194
+
195
+ expect(favoriteItem).toBeInstanceOf(Schema);
196
+ expect(favoriteItem.isEnum).toBe(false);
197
+ const source = favoriteItem.source;
198
+ expect(source).toHaveProperty('id');
199
+ expect(source).toHaveProperty('name');
200
+ expect(source).toHaveProperty('status');
201
+ });
202
+
203
+ it('should correctly parse Profile schema', () => {
204
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
205
+ const profile = schemasMap.Profile;
206
+
207
+ expect(profile).toBeInstanceOf(Schema);
208
+ expect(profile.isEnum).toBe(false);
209
+ const source = profile.source;
210
+ expect(source).toHaveProperty('name');
211
+ expect(source).toHaveProperty('status');
212
+ expect(source).toHaveProperty('gender');
213
+ });
214
+
215
+ it('should correctly parse Status enum schema', () => {
216
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
217
+ const status = schemasMap.Status;
218
+
219
+ expect(status).toBeInstanceOf(Schema);
220
+ expect(status.isEnum).toBe(true);
221
+ const source = status.source;
222
+ expect(source).toHaveProperty('enum');
223
+ expect(Array.isArray(source.enum)).toBe(true);
224
+ });
225
+
226
+ it('should correctly parse Preferences schema', () => {
227
+ const schemasMap = createSchemasMap(examplesSchemasPath, []);
228
+ const preferences = schemasMap.Preferences;
229
+
230
+ expect(preferences).toBeInstanceOf(Schema);
231
+ expect(preferences.isEnum).toBe(false);
232
+ const source = preferences.source;
233
+ expect(source).toHaveProperty('age');
234
+ expect(source).toHaveProperty('height');
235
+ expect(source).toHaveProperty('isNotificationEnabled');
236
+ });
237
+ });
238
+ });
@@ -0,0 +1,212 @@
1
+ import path from 'path';
2
+ import Schema from '../Schema';
3
+ import { load } from 'js-yaml';
4
+ import { keyBy } from 'lodash';
5
+ import type { EnumSchema, PropertiesSchema } from './JsonSchema';
6
+ import { readFileSync, readdirSync, statSync } from 'fs';
7
+
8
+ /**
9
+ * Reads schema source from YAML file and returns a Schema instance.
10
+ *
11
+ * **Intent:** Load and parse a single YAML schema file, extracting the schema ID
12
+ * from the filename and creating a Schema instance for use in validation or
13
+ * schema composition.
14
+ *
15
+ * **Use Cases:**
16
+ * - Load individual schema files during application startup
17
+ * - Parse YAML schema definitions into Schema instances
18
+ * - Extract schema identifiers from file paths automatically
19
+ * - Support both enum and properties-based schemas from YAML files
20
+ *
21
+ * @param yamlPath - Absolute or relative path to the YAML schema file
22
+ * @returns A Schema instance with ID extracted from the filename (without .yaml extension)
23
+ *
24
+ * **Example:**
25
+ * ```typescript
26
+ * const schema = loadSync('/path/to/schemas/User.yaml');
27
+ * // schema.id === 'User'
28
+ * // schema.source contains the parsed YAML content
29
+ * ```
30
+ *
31
+ * **Example - Enum Schema:**
32
+ * ```typescript
33
+ * const statusSchema = loadSync('/path/to/schemas/Status.yaml');
34
+ * // If Status.yaml contains: { enum: ['PENDING', 'ACTIVE'] }
35
+ * // statusSchema.isEnum === true
36
+ * ```
37
+ */
38
+ export const loadSync = (yamlPath: string) => {
39
+ const schemaId = yamlPath
40
+ .split('/')
41
+ .reverse()[0]
42
+ .split('.yaml')[0];
43
+
44
+ const file = readFileSync(yamlPath);
45
+ const source = load(file.toString()) as EnumSchema | PropertiesSchema;
46
+
47
+ return new Schema(source, schemaId);
48
+ };
49
+
50
+ /**
51
+ * Recursively lists all files in a directory and its subdirectories.
52
+ *
53
+ * **Intent:** Traverse a directory tree and collect all file paths, enabling
54
+ * discovery of schema files nested in subdirectories without manual path specification.
55
+ *
56
+ * **Use Cases:**
57
+ * - Find all schema files in a directory structure
58
+ * - Support organized schema layouts with nested folders
59
+ * - Enable schema discovery without hardcoding file paths
60
+ * - Prepare file list for filtering and processing
61
+ *
62
+ * @param servicePath - Path to the directory to traverse
63
+ * @returns Array of absolute file paths found in the directory tree
64
+ *
65
+ * **Example:**
66
+ * ```typescript
67
+ * const files = listFilesSync('/path/to/schemas');
68
+ * // Returns: [
69
+ * // '/path/to/schemas/User.yaml',
70
+ * // '/path/to/schemas/nested/Profile.yaml',
71
+ * // '/path/to/schemas/nested/deep/Status.yaml'
72
+ * // ]
73
+ * ```
74
+ *
75
+ * **Example - Flat Directory:**
76
+ * ```typescript
77
+ * const files = listFilesSync('/path/to/schemas');
78
+ * // Returns: [
79
+ * // '/path/to/schemas/User.yaml',
80
+ * // '/path/to/schemas/Profile.yaml'
81
+ * // ]
82
+ * ```
83
+ */
84
+ const listFilesSync = (servicePath: string): string[] =>
85
+ readdirSync(servicePath)
86
+ .reduce(
87
+ (filePaths: string[], fileName: string) =>
88
+ statSync(
89
+ path.join(servicePath, fileName)).isDirectory() ?
90
+ filePaths.concat(listFilesSync(path.join(servicePath, fileName))) :
91
+ filePaths.concat(path.join(servicePath, fileName)
92
+ )
93
+ , []);
94
+
95
+ /**
96
+ * Reads all YAML schema files from a directory and creates Schema instances.
97
+ *
98
+ * **Intent:** Bulk load schema definitions from YAML files in a directory,
99
+ * automatically discovering and parsing all schema files for use in schema
100
+ * registries or validators.
101
+ *
102
+ * **Use Cases:**
103
+ * - Load all schemas from a schemas directory at application startup
104
+ * - Initialize schema registries from file-based definitions
105
+ * - Support schema-as-code workflows where schemas are stored as YAML files
106
+ * - Enable automatic schema discovery without manual registration
107
+ *
108
+ * @param servicePath - Path to the directory containing YAML schema files
109
+ * @returns Array of Schema instances, one for each YAML file found
110
+ *
111
+ * **Example:**
112
+ * ```typescript
113
+ * const schemas = readSchemasSync('/path/to/examples/schemas');
114
+ * // Returns: [
115
+ * // Schema { id: 'FavoriteItem', ... },
116
+ * // Schema { id: 'Profile', ... },
117
+ * // Schema { id: 'Status', ... },
118
+ * // Schema { id: 'Preferences', ... }
119
+ * // ]
120
+ * ```
121
+ *
122
+ * **Example - With Nested Directories:**
123
+ * ```typescript
124
+ * const schemas = readSchemasSync('/path/to/schemas');
125
+ * // Automatically finds schemas in subdirectories:
126
+ * // - /path/to/schemas/User.yaml
127
+ * // - /path/to/schemas/nested/Profile.yaml
128
+ * ```
129
+ */
130
+ const readSchemasSync = (servicePath: string) =>
131
+ listFilesSync(servicePath)
132
+ .filter((fileName: string) => fileName.endsWith('.yaml'))
133
+ .map((schemaPath: string) => loadSync(schemaPath));
134
+
135
+ /**
136
+ * Creates a map of schemas by ID, loading from YAML files and merging with programmatic schemas.
137
+ *
138
+ * **Intent:** Build a centralized schema registry that combines file-based YAML schemas
139
+ * with programmatically created Schema instances, providing a unified lookup mechanism
140
+ * by schema ID. This enables hybrid schema management where some schemas are defined
141
+ * in YAML files while others are created dynamically in code.
142
+ *
143
+ * **Use Cases:**
144
+ * - Initialize schema registries for validators from both files and code
145
+ * - Support schema composition where base schemas come from files and extended
146
+ * schemas are created programmatically
147
+ * - Enable schema overriding where programmatic schemas can replace file-based ones
148
+ * - Build schema maps for credential factories or API validation systems
149
+ * - Support development workflows where schemas evolve from YAML to code
150
+ *
151
+ * @param servicePath - Path to directory containing YAML schema files (searched recursively)
152
+ * @param modules - Array of Schema instances or other values (non-Schema values are filtered out)
153
+ * @returns Record mapping schema IDs to Schema instances, with modules schemas overriding YAML schemas
154
+ *
155
+ * **Example - Basic Usage:**
156
+ * ```typescript
157
+ * const schemasMap = createSchemasMap('/path/to/examples/schemas', []);
158
+ * // Returns: {
159
+ * // FavoriteItem: Schema { id: 'FavoriteItem', ... },
160
+ * // Profile: Schema { id: 'Profile', ... },
161
+ * // Status: Schema { id: 'Status', ... },
162
+ * // Preferences: Schema { id: 'Preferences', ... }
163
+ * // }
164
+ * ```
165
+ *
166
+ * **Example - Merging Programmatic Schemas:**
167
+ * ```typescript
168
+ * const customSchema = new Schema(
169
+ * { customField: { type: 'string' } },
170
+ * 'CustomSchema'
171
+ * );
172
+ * const schemasMap = createSchemasMap('/path/to/schemas', [customSchema]);
173
+ * // schemasMap contains both YAML schemas and CustomSchema
174
+ * ```
175
+ *
176
+ * **Example - Overriding YAML Schemas:**
177
+ * ```typescript
178
+ * const updatedProfile = new Schema(
179
+ * { name: { type: 'string' }, newField: { type: 'number' } },
180
+ * 'Profile'
181
+ * );
182
+ * const schemasMap = createSchemasMap('/path/to/schemas', [updatedProfile]);
183
+ * // schemasMap.Profile is the updatedProfile instance, not the YAML version
184
+ * ```
185
+ *
186
+ * **Example - Filtering Non-Schema Values:**
187
+ * ```typescript
188
+ * const schema = new Schema({ field: { type: 'string' } }, 'Test');
189
+ * const schemasMap = createSchemasMap('/path/to/schemas', [
190
+ * schema,
191
+ * 'not a schema',
192
+ * { id: 'fake' },
193
+ * null
194
+ * ]);
195
+ * // Only the Schema instance is included, other values are ignored
196
+ * ```
197
+ */
198
+ const createSchemasMap = (servicePath: string, modules: unknown[]): Record<string, Schema> => {
199
+ const yamlSchemas = readSchemasSync(servicePath);
200
+ const schemasMap = keyBy(yamlSchemas, 'id');
201
+
202
+ const schemas = modules
203
+ .filter(schema => schema instanceof Schema);
204
+
205
+ for (const schema of schemas) {
206
+ schemasMap[schema.id] = schema;
207
+ }
208
+
209
+ return schemasMap;
210
+ };
211
+
212
+ export default createSchemasMap;
package/src/index.ts CHANGED
@@ -1,13 +1,18 @@
1
+ import got from './helpers/got';
1
2
  import Schema from './Schema';
2
3
  import Validator from './Validator';
3
4
  import documentLoader from './ld/documentLoader';
4
5
  import ValidationError from './ValidationError';
5
6
  import CredentialFactory from './CredentialFactory';
7
+ import createSchemasMap, { loadSync } from './helpers/createSchemasMap';
6
8
 
7
9
  export {
10
+ got,
8
11
  Schema,
12
+ loadSync,
9
13
  Validator,
10
14
  documentLoader,
11
15
  ValidationError,
16
+ createSchemasMap,
12
17
  CredentialFactory
13
18
  };