@kravc/schema 2.8.0-alpha.0 → 2.8.0-alpha.1
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/helpers/createSchemasMap.d.ts +67 -0
- package/dist/helpers/createSchemasMap.d.ts.map +1 -0
- package/dist/helpers/createSchemasMap.js +200 -0
- package/dist/helpers/createSchemasMap.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/helpers/__tests__/createSchemasMap.test.ts +238 -0
- package/src/helpers/createSchemasMap.ts +212 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import Schema from '../Schema';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a map of schemas by ID, loading from YAML files and merging with programmatic schemas.
|
|
4
|
+
*
|
|
5
|
+
* **Intent:** Build a centralized schema registry that combines file-based YAML schemas
|
|
6
|
+
* with programmatically created Schema instances, providing a unified lookup mechanism
|
|
7
|
+
* by schema ID. This enables hybrid schema management where some schemas are defined
|
|
8
|
+
* in YAML files while others are created dynamically in code.
|
|
9
|
+
*
|
|
10
|
+
* **Use Cases:**
|
|
11
|
+
* - Initialize schema registries for validators from both files and code
|
|
12
|
+
* - Support schema composition where base schemas come from files and extended
|
|
13
|
+
* schemas are created programmatically
|
|
14
|
+
* - Enable schema overriding where programmatic schemas can replace file-based ones
|
|
15
|
+
* - Build schema maps for credential factories or API validation systems
|
|
16
|
+
* - Support development workflows where schemas evolve from YAML to code
|
|
17
|
+
*
|
|
18
|
+
* @param servicePath - Path to directory containing YAML schema files (searched recursively)
|
|
19
|
+
* @param modules - Array of Schema instances or other values (non-Schema values are filtered out)
|
|
20
|
+
* @returns Record mapping schema IDs to Schema instances, with modules schemas overriding YAML schemas
|
|
21
|
+
*
|
|
22
|
+
* **Example - Basic Usage:**
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const schemasMap = createSchemasMap('/path/to/examples/schemas', []);
|
|
25
|
+
* // Returns: {
|
|
26
|
+
* // FavoriteItem: Schema { id: 'FavoriteItem', ... },
|
|
27
|
+
* // Profile: Schema { id: 'Profile', ... },
|
|
28
|
+
* // Status: Schema { id: 'Status', ... },
|
|
29
|
+
* // Preferences: Schema { id: 'Preferences', ... }
|
|
30
|
+
* // }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* **Example - Merging Programmatic Schemas:**
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const customSchema = new Schema(
|
|
36
|
+
* { customField: { type: 'string' } },
|
|
37
|
+
* 'CustomSchema'
|
|
38
|
+
* );
|
|
39
|
+
* const schemasMap = createSchemasMap('/path/to/schemas', [customSchema]);
|
|
40
|
+
* // schemasMap contains both YAML schemas and CustomSchema
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* **Example - Overriding YAML Schemas:**
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const updatedProfile = new Schema(
|
|
46
|
+
* { name: { type: 'string' }, newField: { type: 'number' } },
|
|
47
|
+
* 'Profile'
|
|
48
|
+
* );
|
|
49
|
+
* const schemasMap = createSchemasMap('/path/to/schemas', [updatedProfile]);
|
|
50
|
+
* // schemasMap.Profile is the updatedProfile instance, not the YAML version
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* **Example - Filtering Non-Schema Values:**
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const schema = new Schema({ field: { type: 'string' } }, 'Test');
|
|
56
|
+
* const schemasMap = createSchemasMap('/path/to/schemas', [
|
|
57
|
+
* schema,
|
|
58
|
+
* 'not a schema',
|
|
59
|
+
* { id: 'fake' },
|
|
60
|
+
* null
|
|
61
|
+
* ]);
|
|
62
|
+
* // Only the Schema instance is included, other values are ignored
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
declare const createSchemasMap: (servicePath: string, modules: unknown[]) => Record<string, Schema>;
|
|
66
|
+
export default createSchemasMap;
|
|
67
|
+
//# 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;AAqI/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,200 @@
|
|
|
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
|
+
const path_1 = __importDefault(require("path"));
|
|
7
|
+
const Schema_1 = __importDefault(require("../Schema"));
|
|
8
|
+
const js_yaml_1 = require("js-yaml");
|
|
9
|
+
const lodash_1 = require("lodash");
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
/**
|
|
12
|
+
* Reads schema source from YAML file and returns a Schema instance.
|
|
13
|
+
*
|
|
14
|
+
* **Intent:** Load and parse a single YAML schema file, extracting the schema ID
|
|
15
|
+
* from the filename and creating a Schema instance for use in validation or
|
|
16
|
+
* schema composition.
|
|
17
|
+
*
|
|
18
|
+
* **Use Cases:**
|
|
19
|
+
* - Load individual schema files during application startup
|
|
20
|
+
* - Parse YAML schema definitions into Schema instances
|
|
21
|
+
* - Extract schema identifiers from file paths automatically
|
|
22
|
+
* - Support both enum and properties-based schemas from YAML files
|
|
23
|
+
*
|
|
24
|
+
* @param yamlPath - Absolute or relative path to the YAML schema file
|
|
25
|
+
* @returns A Schema instance with ID extracted from the filename (without .yaml extension)
|
|
26
|
+
*
|
|
27
|
+
* **Example:**
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const schema = loadSync('/path/to/schemas/User.yaml');
|
|
30
|
+
* // schema.id === 'User'
|
|
31
|
+
* // schema.source contains the parsed YAML content
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* **Example - Enum Schema:**
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const statusSchema = loadSync('/path/to/schemas/Status.yaml');
|
|
37
|
+
* // If Status.yaml contains: { enum: ['PENDING', 'ACTIVE'] }
|
|
38
|
+
* // statusSchema.isEnum === true
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
const loadSync = (yamlPath) => {
|
|
42
|
+
const schemaId = yamlPath
|
|
43
|
+
.split('/')
|
|
44
|
+
.reverse()[0]
|
|
45
|
+
.split('.yaml')[0];
|
|
46
|
+
const file = (0, fs_1.readFileSync)(yamlPath);
|
|
47
|
+
const source = (0, js_yaml_1.load)(file.toString());
|
|
48
|
+
return new Schema_1.default(source, schemaId);
|
|
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) => (0, fs_1.readdirSync)(servicePath)
|
|
85
|
+
.reduce((filePaths, fileName) => (0, fs_1.statSync)(path_1.default.join(servicePath, fileName)).isDirectory() ?
|
|
86
|
+
filePaths.concat(listFilesSync(path_1.default.join(servicePath, fileName))) :
|
|
87
|
+
filePaths.concat(path_1.default.join(servicePath, fileName)), []);
|
|
88
|
+
/**
|
|
89
|
+
* Reads all YAML schema files from a directory and creates Schema instances.
|
|
90
|
+
*
|
|
91
|
+
* **Intent:** Bulk load schema definitions from YAML files in a directory,
|
|
92
|
+
* automatically discovering and parsing all schema files for use in schema
|
|
93
|
+
* registries or validators.
|
|
94
|
+
*
|
|
95
|
+
* **Use Cases:**
|
|
96
|
+
* - Load all schemas from a schemas directory at application startup
|
|
97
|
+
* - Initialize schema registries from file-based definitions
|
|
98
|
+
* - Support schema-as-code workflows where schemas are stored as YAML files
|
|
99
|
+
* - Enable automatic schema discovery without manual registration
|
|
100
|
+
*
|
|
101
|
+
* @param servicePath - Path to the directory containing YAML schema files
|
|
102
|
+
* @returns Array of Schema instances, one for each YAML file found
|
|
103
|
+
*
|
|
104
|
+
* **Example:**
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const schemas = readSchemasSync('/path/to/examples/schemas');
|
|
107
|
+
* // Returns: [
|
|
108
|
+
* // Schema { id: 'FavoriteItem', ... },
|
|
109
|
+
* // Schema { id: 'Profile', ... },
|
|
110
|
+
* // Schema { id: 'Status', ... },
|
|
111
|
+
* // Schema { id: 'Preferences', ... }
|
|
112
|
+
* // ]
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* **Example - With Nested Directories:**
|
|
116
|
+
* ```typescript
|
|
117
|
+
* const schemas = readSchemasSync('/path/to/schemas');
|
|
118
|
+
* // Automatically finds schemas in subdirectories:
|
|
119
|
+
* // - /path/to/schemas/User.yaml
|
|
120
|
+
* // - /path/to/schemas/nested/Profile.yaml
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
const readSchemasSync = (servicePath) => listFilesSync(servicePath)
|
|
124
|
+
.filter((fileName) => fileName.endsWith('.yaml'))
|
|
125
|
+
.map((schemaPath) => loadSync(schemaPath));
|
|
126
|
+
/**
|
|
127
|
+
* Creates a map of schemas by ID, loading from YAML files and merging with programmatic schemas.
|
|
128
|
+
*
|
|
129
|
+
* **Intent:** Build a centralized schema registry that combines file-based YAML schemas
|
|
130
|
+
* with programmatically created Schema instances, providing a unified lookup mechanism
|
|
131
|
+
* by schema ID. This enables hybrid schema management where some schemas are defined
|
|
132
|
+
* in YAML files while others are created dynamically in code.
|
|
133
|
+
*
|
|
134
|
+
* **Use Cases:**
|
|
135
|
+
* - Initialize schema registries for validators from both files and code
|
|
136
|
+
* - Support schema composition where base schemas come from files and extended
|
|
137
|
+
* schemas are created programmatically
|
|
138
|
+
* - Enable schema overriding where programmatic schemas can replace file-based ones
|
|
139
|
+
* - Build schema maps for credential factories or API validation systems
|
|
140
|
+
* - Support development workflows where schemas evolve from YAML to code
|
|
141
|
+
*
|
|
142
|
+
* @param servicePath - Path to directory containing YAML schema files (searched recursively)
|
|
143
|
+
* @param modules - Array of Schema instances or other values (non-Schema values are filtered out)
|
|
144
|
+
* @returns Record mapping schema IDs to Schema instances, with modules schemas overriding YAML schemas
|
|
145
|
+
*
|
|
146
|
+
* **Example - Basic Usage:**
|
|
147
|
+
* ```typescript
|
|
148
|
+
* const schemasMap = createSchemasMap('/path/to/examples/schemas', []);
|
|
149
|
+
* // Returns: {
|
|
150
|
+
* // FavoriteItem: Schema { id: 'FavoriteItem', ... },
|
|
151
|
+
* // Profile: Schema { id: 'Profile', ... },
|
|
152
|
+
* // Status: Schema { id: 'Status', ... },
|
|
153
|
+
* // Preferences: Schema { id: 'Preferences', ... }
|
|
154
|
+
* // }
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* **Example - Merging Programmatic Schemas:**
|
|
158
|
+
* ```typescript
|
|
159
|
+
* const customSchema = new Schema(
|
|
160
|
+
* { customField: { type: 'string' } },
|
|
161
|
+
* 'CustomSchema'
|
|
162
|
+
* );
|
|
163
|
+
* const schemasMap = createSchemasMap('/path/to/schemas', [customSchema]);
|
|
164
|
+
* // schemasMap contains both YAML schemas and CustomSchema
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* **Example - Overriding YAML Schemas:**
|
|
168
|
+
* ```typescript
|
|
169
|
+
* const updatedProfile = new Schema(
|
|
170
|
+
* { name: { type: 'string' }, newField: { type: 'number' } },
|
|
171
|
+
* 'Profile'
|
|
172
|
+
* );
|
|
173
|
+
* const schemasMap = createSchemasMap('/path/to/schemas', [updatedProfile]);
|
|
174
|
+
* // schemasMap.Profile is the updatedProfile instance, not the YAML version
|
|
175
|
+
* ```
|
|
176
|
+
*
|
|
177
|
+
* **Example - Filtering Non-Schema Values:**
|
|
178
|
+
* ```typescript
|
|
179
|
+
* const schema = new Schema({ field: { type: 'string' } }, 'Test');
|
|
180
|
+
* const schemasMap = createSchemasMap('/path/to/schemas', [
|
|
181
|
+
* schema,
|
|
182
|
+
* 'not a schema',
|
|
183
|
+
* { id: 'fake' },
|
|
184
|
+
* null
|
|
185
|
+
* ]);
|
|
186
|
+
* // Only the Schema instance is included, other values are ignored
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
const createSchemasMap = (servicePath, modules) => {
|
|
190
|
+
const yamlSchemas = readSchemasSync(servicePath);
|
|
191
|
+
const schemasMap = (0, lodash_1.keyBy)(yamlSchemas, 'id');
|
|
192
|
+
const schemas = modules
|
|
193
|
+
.filter(schema => schema instanceof Schema_1.default);
|
|
194
|
+
for (const schema of schemas) {
|
|
195
|
+
schemasMap[schema.id] = schema;
|
|
196
|
+
}
|
|
197
|
+
return schemasMap;
|
|
198
|
+
};
|
|
199
|
+
exports.default = createSchemasMap;
|
|
200
|
+
//# 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;AACH,MAAM,QAAQ,GAAG,CAAC,QAAgB,EAAE,EAAE;IACpC,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;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,QAAQ,CAAC,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';
|
|
6
|
+
import createSchemasMap from './helpers/createSchemasMap';
|
|
5
7
|
import CredentialFactory from './CredentialFactory';
|
|
6
|
-
export { Schema, Validator, documentLoader, ValidationError, CredentialFactory };
|
|
8
|
+
export { got, Schema, Validator, documentLoader, ValidationError, createSchemasMap, CredentialFactory };
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,gBAAgB,MAAM,4BAA4B,CAAC;AAC1D,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EACL,GAAG,EACH,MAAM,EACN,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EAClB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.CredentialFactory = exports.ValidationError = exports.documentLoader = exports.Validator = exports.Schema = void 0;
|
|
6
|
+
exports.CredentialFactory = exports.createSchemasMap = exports.ValidationError = exports.documentLoader = exports.Validator = exports.Schema = exports.got = void 0;
|
|
7
|
+
const got_1 = __importDefault(require("./helpers/got"));
|
|
8
|
+
exports.got = got_1.default;
|
|
7
9
|
const Schema_1 = __importDefault(require("./Schema"));
|
|
8
10
|
exports.Schema = Schema_1.default;
|
|
9
11
|
const Validator_1 = __importDefault(require("./Validator"));
|
|
@@ -12,6 +14,8 @@ const documentLoader_1 = __importDefault(require("./ld/documentLoader"));
|
|
|
12
14
|
exports.documentLoader = documentLoader_1.default;
|
|
13
15
|
const ValidationError_1 = __importDefault(require("./ValidationError"));
|
|
14
16
|
exports.ValidationError = ValidationError_1.default;
|
|
17
|
+
const createSchemasMap_1 = __importDefault(require("./helpers/createSchemasMap"));
|
|
18
|
+
exports.createSchemasMap = createSchemasMap_1.default;
|
|
15
19
|
const CredentialFactory_1 = __importDefault(require("./CredentialFactory"));
|
|
16
20
|
exports.CredentialFactory = CredentialFactory_1.default;
|
|
17
21
|
//# 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;
|
|
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;AASlC,oBATK,mBAAS,CASL;AARX,yEAAiD;AAS/C,yBATK,wBAAc,CASL;AARhB,wEAAgD;AAS9C,0BATK,yBAAe,CASL;AARjB,kFAA0D;AASxD,2BATK,0BAAgB,CASL;AARlB,4EAAoD;AASlD,4BATK,2BAAiB,CASL"}
|
package/package.json
CHANGED
|
@@ -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
|
+
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,17 @@
|
|
|
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';
|
|
6
|
+
import createSchemasMap from './helpers/createSchemasMap';
|
|
5
7
|
import CredentialFactory from './CredentialFactory';
|
|
6
8
|
|
|
7
9
|
export {
|
|
10
|
+
got,
|
|
8
11
|
Schema,
|
|
9
12
|
Validator,
|
|
10
13
|
documentLoader,
|
|
11
14
|
ValidationError,
|
|
15
|
+
createSchemasMap,
|
|
12
16
|
CredentialFactory
|
|
13
17
|
};
|