japa-openapi-assertions 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi_assertions.js","sourceRoot":"","sources":["../src/openapi_assertions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { PathMatchResult } from './types.js';
2
+ /**
3
+ * Match a request path against OpenAPI path definitions.
4
+ *
5
+ * Handles path parameters (e.g., /pets/{petId} matches /pets/123)
6
+ * and scores matches by specificity (exact segments preferred over parameters).
7
+ */
8
+ export declare function matchPath(requestPath: string, specPaths: string[]): PathMatchResult | null;
9
+ /**
10
+ * Get the base path from a server URL.
11
+ * E.g., "http://localhost:3333/api/v1" -> "/api/v1"
12
+ */
13
+ export declare function extractBasePath(serverUrl: string): string;
14
+ /**
15
+ * Normalize a path by removing base path prefix if present.
16
+ */
17
+ export declare function removeBasePath(requestPath: string, basePath: string): string;
18
+ //# sourceMappingURL=path_matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path_matcher.d.ts","sourceRoot":"","sources":["../src/path_matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EAAE,GAClB,eAAe,GAAG,IAAI,CAwBxB;AA6CD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAQzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAW5E"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Match a request path against OpenAPI path definitions.
3
+ *
4
+ * Handles path parameters (e.g., /pets/{petId} matches /pets/123)
5
+ * and scores matches by specificity (exact segments preferred over parameters).
6
+ */
7
+ export function matchPath(requestPath, specPaths) {
8
+ // Clean the request path: remove query string and trailing slash
9
+ const cleanPath = requestPath.split('?')[0].replace(/\/$/, '') || '/';
10
+ const matches = [];
11
+ for (const specPath of specPaths) {
12
+ const result = tryMatch(cleanPath, specPath);
13
+ if (result) {
14
+ matches.push(result);
15
+ }
16
+ }
17
+ if (matches.length === 0) {
18
+ return null;
19
+ }
20
+ // Sort by score (higher = more specific = fewer parameters)
21
+ matches.sort((a, b) => b.score - a.score);
22
+ return {
23
+ matched: matches[0].specPath,
24
+ params: matches[0].params,
25
+ };
26
+ }
27
+ /**
28
+ * Try to match a request path against a single spec path.
29
+ * Returns match result with score, or null if no match.
30
+ */
31
+ function tryMatch(requestPath, specPath) {
32
+ const requestSegments = requestPath.split('/').filter(Boolean);
33
+ const specSegments = specPath.split('/').filter(Boolean);
34
+ // Must have same number of segments
35
+ if (requestSegments.length !== specSegments.length) {
36
+ return null;
37
+ }
38
+ const params = {};
39
+ let score = 0;
40
+ for (let i = 0; i < specSegments.length; i++) {
41
+ const specSegment = specSegments[i];
42
+ const requestSegment = requestSegments[i];
43
+ // Check if it's a path parameter
44
+ const paramMatch = specSegment.match(/^\{(\w+)\}$/);
45
+ if (paramMatch) {
46
+ // It's a parameter - extract the value
47
+ params[paramMatch[1]] = requestSegment;
48
+ // Parameters score lower than exact matches
49
+ score += 1;
50
+ }
51
+ else if (specSegment === requestSegment) {
52
+ // Exact match - higher score
53
+ score += 10;
54
+ }
55
+ else {
56
+ // No match
57
+ return null;
58
+ }
59
+ }
60
+ return { specPath, params, score };
61
+ }
62
+ /**
63
+ * Get the base path from a server URL.
64
+ * E.g., "http://localhost:3333/api/v1" -> "/api/v1"
65
+ */
66
+ export function extractBasePath(serverUrl) {
67
+ try {
68
+ const url = new URL(serverUrl);
69
+ return url.pathname.replace(/\/$/, '') || '/';
70
+ }
71
+ catch {
72
+ // If it's not a valid URL, assume it's already a path
73
+ return serverUrl.replace(/\/$/, '') || '/';
74
+ }
75
+ }
76
+ /**
77
+ * Normalize a path by removing base path prefix if present.
78
+ */
79
+ export function removeBasePath(requestPath, basePath) {
80
+ if (basePath === '/' || basePath === '') {
81
+ return requestPath;
82
+ }
83
+ if (requestPath.startsWith(basePath)) {
84
+ const remaining = requestPath.slice(basePath.length);
85
+ return remaining.startsWith('/') ? remaining : `/${remaining}`;
86
+ }
87
+ return requestPath;
88
+ }
89
+ //# sourceMappingURL=path_matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path_matcher.js","sourceRoot":"","sources":["../src/path_matcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { CompiledValidators, ParsedResponse, ValidationResult } from './types.js';
2
+ /**
3
+ * Validate an HTTP response against compiled OpenAPI validators.
4
+ */
5
+ export declare function validateResponse(response: unknown, validators: CompiledValidators): ValidationResult;
6
+ /**
7
+ * Parse response from various HTTP client formats.
8
+ */
9
+ export declare function parseResponse(response: unknown): ParsedResponse;
10
+ //# sourceMappingURL=response_validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response_validator.d.ts","sourceRoot":"","sources":["../src/response_validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAEjB,MAAM,YAAY,CAAA;AAGnB;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,kBAAkB,GAC7B,gBAAgB,CAyElB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,cAAc,CA8D/D"}
@@ -0,0 +1,173 @@
1
+ import { matchPath } from './path_matcher.js';
2
+ /**
3
+ * Validate an HTTP response against compiled OpenAPI validators.
4
+ */
5
+ export function validateResponse(response, validators) {
6
+ // Parse the response from various HTTP client formats
7
+ const parsed = parseResponse(response);
8
+ // Find matching path in spec
9
+ const specPaths = Object.keys(validators);
10
+ const pathMatch = matchPath(parsed.path, specPaths);
11
+ if (!pathMatch) {
12
+ return {
13
+ valid: false,
14
+ errors: [{
15
+ path: '',
16
+ message: `No matching path found in OpenAPI spec for ${parsed.method} ${parsed.path}`,
17
+ actual: parsed.path,
18
+ expected: specPaths,
19
+ }],
20
+ };
21
+ }
22
+ const method = parsed.method.toLowerCase();
23
+ const statusCode = String(parsed.status);
24
+ // Get validators for this path/method
25
+ const pathValidators = validators[pathMatch.matched]?.[method];
26
+ if (!pathValidators) {
27
+ return {
28
+ valid: false,
29
+ errors: [{
30
+ path: '',
31
+ message: `No ${parsed.method} operation defined for ${pathMatch.matched}`,
32
+ actual: method,
33
+ }],
34
+ };
35
+ }
36
+ // Get validator for this status code
37
+ // Try exact match first, then 'default', then 2XX/4XX/5XX patterns
38
+ const responseValidator = pathValidators.responses[statusCode]
39
+ || pathValidators.responses['default']
40
+ || pathValidators.responses[`${statusCode[0]}XX`];
41
+ if (!responseValidator) {
42
+ return {
43
+ valid: false,
44
+ errors: [{
45
+ path: '',
46
+ message: `No response schema defined for ${parsed.method} ${pathMatch.matched} with status ${statusCode}`,
47
+ actual: statusCode,
48
+ expected: Object.keys(pathValidators.responses),
49
+ }],
50
+ };
51
+ }
52
+ // Validate response body if there's a body validator
53
+ if (responseValidator.body) {
54
+ const valid = responseValidator.body(parsed.body);
55
+ if (!valid && responseValidator.body.errors) {
56
+ const errors = responseValidator.body.errors.map((err) => ({
57
+ path: err.instancePath || '',
58
+ message: err.message || 'Validation failed',
59
+ keyword: err.keyword,
60
+ expected: err.params,
61
+ actual: getValueAtPath(parsed.body, err.instancePath || ''),
62
+ }));
63
+ return { valid: false, errors };
64
+ }
65
+ }
66
+ return { valid: true };
67
+ }
68
+ /**
69
+ * Parse response from various HTTP client formats.
70
+ */
71
+ export function parseResponse(response) {
72
+ if (!response || typeof response !== 'object') {
73
+ throw new Error('Invalid response: expected an object');
74
+ }
75
+ const res = response;
76
+ // Japa @japa/api-client format
77
+ // The response object itself has the properties we need
78
+ if ('response' in res && res.response && typeof res.response === 'object') {
79
+ // This is the ApiResponse wrapper, unwrap it
80
+ return parseResponse(res.response);
81
+ }
82
+ // Japa api-client direct response (has .request and .body())
83
+ if ('request' in res && 'statusCode' in res) {
84
+ const request = res.request;
85
+ return {
86
+ method: String(request.method || 'GET').toUpperCase(),
87
+ path: extractPath(request.url || '/'),
88
+ status: Number(res.statusCode) || 0,
89
+ headers: normalizeHeaders(res.headers || {}),
90
+ body: typeof res.body === 'function' ? res.body() : res.body,
91
+ };
92
+ }
93
+ // Axios format (has .data, .config, .status)
94
+ if ('data' in res && 'config' in res && 'status' in res) {
95
+ const config = res.config;
96
+ return {
97
+ method: String(config.method || 'GET').toUpperCase(),
98
+ path: extractPath(config.url || '/'),
99
+ status: Number(res.status) || 0,
100
+ headers: normalizeHeaders(res.headers || {}),
101
+ body: res.data,
102
+ };
103
+ }
104
+ // Supertest format (has .body, .req, .statusCode)
105
+ if ('body' in res && 'req' in res && 'statusCode' in res) {
106
+ const req = res.req;
107
+ return {
108
+ method: String(req.method || 'GET').toUpperCase(),
109
+ path: extractPath(req.path || '/'),
110
+ status: Number(res.statusCode) || 0,
111
+ headers: normalizeHeaders(res.headers || {}),
112
+ body: res.body,
113
+ };
114
+ }
115
+ // Generic format - try to extract what we can
116
+ if ('status' in res || 'statusCode' in res) {
117
+ return {
118
+ method: String(res.method || 'GET').toUpperCase(),
119
+ path: extractPath(res.url || res.path || '/'),
120
+ status: Number(res.status || res.statusCode) || 0,
121
+ headers: normalizeHeaders(res.headers || {}),
122
+ body: res.body ?? res.data ?? null,
123
+ };
124
+ }
125
+ throw new Error('Unknown response format: could not extract method, path, status, or body');
126
+ }
127
+ /**
128
+ * Extract path from a URL string.
129
+ */
130
+ function extractPath(url) {
131
+ try {
132
+ // If it's a full URL, parse it
133
+ if (url.startsWith('http://') || url.startsWith('https://')) {
134
+ const parsed = new URL(url);
135
+ return parsed.pathname;
136
+ }
137
+ // Otherwise, split on query string
138
+ return url.split('?')[0];
139
+ }
140
+ catch {
141
+ return url.split('?')[0];
142
+ }
143
+ }
144
+ /**
145
+ * Normalize headers to lowercase keys with string values.
146
+ */
147
+ function normalizeHeaders(headers) {
148
+ const result = {};
149
+ for (const [key, value] of Object.entries(headers)) {
150
+ result[key.toLowerCase()] = String(value);
151
+ }
152
+ return result;
153
+ }
154
+ /**
155
+ * Get value at a JSON path.
156
+ */
157
+ function getValueAtPath(obj, path) {
158
+ if (!path || path === '')
159
+ return obj;
160
+ if (!obj || typeof obj !== 'object')
161
+ return undefined;
162
+ const parts = path.split('/').filter(Boolean);
163
+ let current = obj;
164
+ for (const part of parts) {
165
+ if (current === null || current === undefined)
166
+ return undefined;
167
+ if (typeof current !== 'object')
168
+ return undefined;
169
+ current = current[part];
170
+ }
171
+ return current;
172
+ }
173
+ //# sourceMappingURL=response_validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response_validator.js","sourceRoot":"","sources":["../src/response_validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { CompiledValidators, CoverageEntry } from './types.js';
2
+ /**
3
+ * Build validators from OpenAPI specification files (async version).
4
+ *
5
+ * @param specPaths - Paths to OpenAPI spec files (JSON or YAML)
6
+ * @returns Compiled validators and coverage entries
7
+ */
8
+ export declare function buildValidators(specPaths: (string | URL)[]): Promise<{
9
+ validators: CompiledValidators;
10
+ coverageEntries: CoverageEntry[];
11
+ }>;
12
+ /**
13
+ * Build validators from OpenAPI specification files (sync version).
14
+ *
15
+ * @param specPaths - Paths to OpenAPI spec files (JSON or YAML)
16
+ * @returns Compiled validators and coverage entries
17
+ */
18
+ export declare function buildValidatorsSync(specPaths: (string | URL)[]): {
19
+ validators: CompiledValidators;
20
+ coverageEntries: CoverageEntry[];
21
+ };
22
+ //# sourceMappingURL=schema_builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema_builder.d.ts","sourceRoot":"","sources":["../src/schema_builder.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,kBAAkB,EAGlB,aAAa,EACd,MAAM,YAAY,CAAA;AAUnB;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,GAC1B,OAAO,CAAC;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,eAAe,EAAE,aAAa,EAAE,CAAA;CAAE,CAAC,CAG/E;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,GAC1B;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,eAAe,EAAE,aAAa,EAAE,CAAA;CAAE,CAyBtE"}
@@ -0,0 +1,215 @@
1
+ import AjvModule from 'ajv/dist/2020.js';
2
+ import addFormatsModule from 'ajv-formats';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { readFileSync } from 'node:fs';
5
+ // Handle ESM/CJS interop - get the actual constructor/function
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ const Ajv2020 = AjvModule.default ?? AjvModule;
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ const addFormats = addFormatsModule.default ?? addFormatsModule;
10
+ const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'trace'];
11
+ /**
12
+ * Build validators from OpenAPI specification files (async version).
13
+ *
14
+ * @param specPaths - Paths to OpenAPI spec files (JSON or YAML)
15
+ * @returns Compiled validators and coverage entries
16
+ */
17
+ export async function buildValidators(specPaths) {
18
+ // Delegate to sync version - the async wrapper is kept for API compatibility
19
+ return buildValidatorsSync(specPaths);
20
+ }
21
+ /**
22
+ * Build validators from OpenAPI specification files (sync version).
23
+ *
24
+ * @param specPaths - Paths to OpenAPI spec files (JSON or YAML)
25
+ * @returns Compiled validators and coverage entries
26
+ */
27
+ export function buildValidatorsSync(specPaths) {
28
+ const validators = {};
29
+ const coverageEntries = [];
30
+ // Create AJV instance with JSON Schema 2020-12 support
31
+ const ajv = new Ajv2020({
32
+ allErrors: true,
33
+ strict: false,
34
+ validateFormats: true,
35
+ });
36
+ addFormats(ajv);
37
+ // Process each spec file
38
+ for (const specPath of specPaths) {
39
+ const filePath = specPath instanceof URL ? fileURLToPath(specPath) : specPath;
40
+ const spec = loadAndValidateSpecSync(filePath);
41
+ // Resolve all $refs in the spec
42
+ const resolvedSpec = resolveRefsSync(spec);
43
+ // Build validators for each path
44
+ buildPathValidators(resolvedSpec, validators, coverageEntries, ajv);
45
+ }
46
+ return { validators, coverageEntries };
47
+ }
48
+ /**
49
+ * Load and validate an OpenAPI specification file (sync version).
50
+ */
51
+ function loadAndValidateSpecSync(filePath) {
52
+ const content = readFileSync(filePath, 'utf-8');
53
+ // Parse JSON (YAML support could be added with js-yaml)
54
+ let spec;
55
+ try {
56
+ spec = JSON.parse(content);
57
+ }
58
+ catch {
59
+ throw new Error(`Failed to parse OpenAPI spec at ${filePath}: Invalid JSON`);
60
+ }
61
+ // Note: We skip async validation here since the Validator.validate() is async
62
+ // and we need synchronous operation. The spec will still be validated when
63
+ // resolving refs, and AJV will catch schema issues during compilation.
64
+ return spec;
65
+ }
66
+ /**
67
+ * Resolve all $ref pointers in the spec (sync version).
68
+ * Uses a workaround since the Validator is async.
69
+ */
70
+ function resolveRefsSync(spec) {
71
+ // For synchronous operation, we'll resolve refs manually
72
+ // by recursively resolving $ref pointers within the spec
73
+ return resolveRefsInObject(spec, spec);
74
+ }
75
+ /**
76
+ * Recursively resolve $ref pointers in an object.
77
+ */
78
+ function resolveRefsInObject(obj, root) {
79
+ if (!obj || typeof obj !== 'object')
80
+ return obj;
81
+ if (Array.isArray(obj)) {
82
+ return obj.map((item) => resolveRefsInObject(item, root));
83
+ }
84
+ const record = obj;
85
+ // Check for $ref
86
+ if (typeof record.$ref === 'string') {
87
+ const ref = record.$ref;
88
+ // Only handle local refs (starting with #/)
89
+ if (ref.startsWith('#/')) {
90
+ const resolved = resolveLocalRef(ref, root);
91
+ if (resolved !== undefined) {
92
+ // Merge resolved ref with any additional properties (except $ref)
93
+ const { $ref: _ref, ...rest } = record;
94
+ if (Object.keys(rest).length > 0) {
95
+ return { ...resolveRefsInObject(resolved, root), ...rest };
96
+ }
97
+ return resolveRefsInObject(resolved, root);
98
+ }
99
+ }
100
+ }
101
+ // Recursively process all properties
102
+ const result = {};
103
+ for (const [key, value] of Object.entries(record)) {
104
+ result[key] = resolveRefsInObject(value, root);
105
+ }
106
+ return result;
107
+ }
108
+ /**
109
+ * Resolve a local JSON pointer reference.
110
+ */
111
+ function resolveLocalRef(ref, root) {
112
+ // Remove the #/ prefix and split into path segments
113
+ const path = ref.slice(2).split('/');
114
+ let current = root;
115
+ for (const segment of path) {
116
+ // Decode JSON pointer escapes
117
+ const decoded = segment.replace(/~1/g, '/').replace(/~0/g, '~');
118
+ if (current && typeof current === 'object' && decoded in current) {
119
+ current = current[decoded];
120
+ }
121
+ else {
122
+ return undefined;
123
+ }
124
+ }
125
+ return current;
126
+ }
127
+ /**
128
+ * Build validators for all paths in a spec.
129
+ */
130
+ function buildPathValidators(spec, validators, coverageEntries,
131
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
+ ajv) {
133
+ if (!spec.paths) {
134
+ return;
135
+ }
136
+ for (const [path, pathItem] of Object.entries(spec.paths)) {
137
+ if (!pathItem)
138
+ continue;
139
+ validators[path] = validators[path] || {};
140
+ for (const method of HTTP_METHODS) {
141
+ const operation = pathItem[method];
142
+ if (!operation?.responses)
143
+ continue;
144
+ validators[path][method] = { responses: {} };
145
+ const statuses = [];
146
+ for (const [statusCode, response] of Object.entries(operation.responses)) {
147
+ statuses.push(statusCode);
148
+ // Build body validator if there's a JSON schema
149
+ const jsonContent = response.content?.['application/json'];
150
+ if (jsonContent?.schema) {
151
+ try {
152
+ const bodyValidator = compileSchema(ajv, jsonContent.schema, `${path}:${method}:${statusCode}:body`);
153
+ validators[path][method].responses[statusCode] = {
154
+ body: bodyValidator,
155
+ };
156
+ }
157
+ catch (err) {
158
+ console.warn(`Warning: Failed to compile schema for ${method.toUpperCase()} ${path} ${statusCode}:`, err);
159
+ }
160
+ }
161
+ else {
162
+ validators[path][method].responses[statusCode] = {};
163
+ }
164
+ }
165
+ // Track for coverage
166
+ if (statuses.length > 0) {
167
+ coverageEntries.push({
168
+ route: path,
169
+ method: method.toUpperCase(),
170
+ statuses,
171
+ });
172
+ }
173
+ }
174
+ }
175
+ }
176
+ /**
177
+ * Compile a JSON schema into a validator function.
178
+ */
179
+ function compileSchema(
180
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
+ ajv, schema, _schemaId) {
182
+ // Clone the schema to avoid mutating the original
183
+ const schemaCopy = JSON.parse(JSON.stringify(schema));
184
+ // Remove all $id fields recursively - resolved refs may have invalid patterns
185
+ // AJV 2020-12 is strict about $id format - must match "^[^#]*#?$"
186
+ removeInvalidIds(schemaCopy);
187
+ return ajv.compile(schemaCopy);
188
+ }
189
+ /**
190
+ * Recursively remove $id fields that don't match JSON Schema 2020-12 pattern.
191
+ */
192
+ function removeInvalidIds(obj) {
193
+ if (!obj || typeof obj !== 'object')
194
+ return;
195
+ if (Array.isArray(obj)) {
196
+ for (const item of obj) {
197
+ removeInvalidIds(item);
198
+ }
199
+ return;
200
+ }
201
+ const record = obj;
202
+ // Remove $id if it contains # anywhere except at the very end
203
+ if (typeof record.$id === 'string') {
204
+ const id = record.$id;
205
+ // Valid pattern: ^[^#]*#?$ (no # except optionally at the end)
206
+ if (id.includes('#') && !id.match(/^[^#]*#?$/)) {
207
+ delete record.$id;
208
+ }
209
+ }
210
+ // Recurse into all properties
211
+ for (const value of Object.values(record)) {
212
+ removeInvalidIds(value);
213
+ }
214
+ }
215
+ //# sourceMappingURL=schema_builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema_builder.js","sourceRoot":"","sources":["../src/schema_builder.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,kBAAkB,CAAA;AACxC,OAAO,gBAAgB,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAStC,+DAA+D;AAC/D,8DAA8D;AAC9D,MAAM,OAAO,GAAI,SAAiB,CAAC,OAAO,IAAI,SAAS,CAAA;AACvD,8DAA8D;AAC9D,MAAM,UAAU,GAAI,gBAAwB,CAAC,OAAO,IAAI,gBAAgB,CAAA;AAExE,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAU,CAAA;AAEnG;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAA2B;IAE3B,6EAA6E;IAC7E,OAAO,mBAAmB,CAAC,SAAS,CAAC,CAAA;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAA2B;IAE3B,MAAM,UAAU,GAAuB,EAAE,CAAA;IACzC,MAAM,eAAe,GAAoB,EAAE,CAAA;IAE3C,uDAAuD;IACvD,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC;QACtB,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,KAAK;QACb,eAAe,EAAE,IAAI;KACtB,CAAC,CAAA;IACF,UAAU,CAAC,GAAG,CAAC,CAAA;IAEf,yBAAyB;IACzB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,QAAQ,YAAY,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;QAC7E,MAAM,IAAI,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAA;QAE9C,gCAAgC;QAChC,MAAM,YAAY,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;QAE1C,iCAAiC;QACjC,mBAAmB,CAAC,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,GAAG,CAAC,CAAA;IACrE,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,CAAA;AACxC,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,QAAgB;IAC/C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAE/C,wDAAwD;IACxD,IAAI,IAA6B,CAAA;IACjC,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,mCAAmC,QAAQ,gBAAgB,CAAC,CAAA;IAC9E,CAAC;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,uEAAuE;IAEvE,OAAO,IAAkC,CAAA;AAC3C,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,IAAqB;IAC5C,yDAAyD;IACzD,yDAAyD;IACzD,OAAO,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAoB,CAAA;AAC3D,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAC,GAAY,EAAE,IAAqB;IAC9D,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAA;IAE/C,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;IAC3D,CAAC;IAED,MAAM,MAAM,GAAG,GAA8B,CAAA;IAE7C,iBAAiB;IACjB,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAA;QACvB,4CAA4C;QAC5C,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC3C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,kEAAkE;gBAClE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAA;gBACtC,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACjC,OAAO,EAAE,GAAG,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAW,EAAE,GAAG,IAAI,EAAE,CAAA;gBACtE,CAAC;gBACD,OAAO,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;YAC5C,CAAC;QACH,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,MAAM,MAAM,GAA4B,EAAE,CAAA;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,MAAM,CAAC,GAAG,CAAC,GAAG,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAChD,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,GAAW,EAAE,IAAqB;IACzD,oDAAoD;IACpD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAEpC,IAAI,OAAO,GAAY,IAAI,CAAA;IAC3B,KAAK,MAAM,OAAO,IAAI,IAAI,EAAE,CAAC;QAC3B,8BAA8B;QAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAC/D,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,IAAK,OAAmC,EAAE,CAAC;YAC9F,OAAO,GAAI,OAAmC,CAAC,OAAO,CAAC,CAAA;QACzD,CAAC;aAAM,CAAC;YACN,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAC1B,IAAqB,EACrB,UAA8B,EAC9B,eAAgC;AAChC,8DAA8D;AAC9D,GAAQ;IAER,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAChB,OAAM;IACR,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,QAAQ;YAAE,SAAQ;QAEvB,UAAU,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QAEzC,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;YAClC,MAAM,SAAS,GAAI,QAA4B,CAAC,MAAM,CAAC,CAAA;YACvD,IAAI,CAAC,SAAS,EAAE,SAAS;gBAAE,SAAQ;YAEnC,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;YAC5C,MAAM,QAAQ,GAAa,EAAE,CAAA;YAE7B,KAAK,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzE,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBAEzB,gDAAgD;gBAChD,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,kBAAkB,CAAC,CAAA;gBAC1D,IAAI,WAAW,EAAE,MAAM,EAAE,CAAC;oBACxB,IAAI,CAAC;wBACH,MAAM,aAAa,GAAG,aAAa,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE,GAAG,IAAI,IAAI,MAAM,IAAI,UAAU,OAAO,CAAC,CAAA;wBACpG,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG;4BAC/C,IAAI,EAAE,aAAa;yBACpB,CAAA;oBACH,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,OAAO,CAAC,IAAI,CAAC,yCAAyC,MAAM,CAAC,WAAW,EAAE,IAAI,IAAI,IAAI,UAAU,GAAG,EAAE,GAAG,CAAC,CAAA;oBAC3G,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,CAAA;gBACrD,CAAC;YACH,CAAC;YAED,qBAAqB;YACrB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,eAAe,CAAC,IAAI,CAAC;oBACnB,KAAK,EAAE,IAAI;oBACX,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE;oBAC5B,QAAQ;iBACT,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,aAAa;AACpB,8DAA8D;AAC9D,GAAQ,EACR,MAAe,EACf,SAAiB;IAEjB,kDAAkD;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;IAErD,8EAA8E;IAC9E,kEAAkE;IAClE,gBAAgB,CAAC,UAAU,CAAC,CAAA;IAE5B,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;AAChC,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAM;IAE3C,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;YACvB,gBAAgB,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC;QACD,OAAM;IACR,CAAC;IAED,MAAM,MAAM,GAAG,GAA8B,CAAA;IAE7C,8DAA8D;IAC9D,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAA;QACrB,+DAA+D;QAC/D,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/C,OAAO,MAAM,CAAC,GAAG,CAAA;QACnB,CAAC;IACH,CAAC;IAED,8BAA8B;IAC9B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,gBAAgB,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;AACH,CAAC"}