@databehave/server 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.
- package/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/admin/admin-routes.d.ts +42 -0
- package/dist/admin/admin-routes.d.ts.map +1 -0
- package/dist/admin/admin-routes.js +472 -0
- package/dist/admin/admin-routes.js.map +1 -0
- package/dist/admin/admin-types.d.ts +170 -0
- package/dist/admin/admin-types.d.ts.map +1 -0
- package/dist/admin/admin-types.js +30 -0
- package/dist/admin/admin-types.js.map +1 -0
- package/dist/admin/banner.d.ts +11 -0
- package/dist/admin/banner.d.ts.map +1 -0
- package/dist/admin/banner.js +5 -0
- package/dist/admin/banner.js.map +1 -0
- package/dist/admin/header-parser.d.ts +14 -0
- package/dist/admin/header-parser.d.ts.map +1 -0
- package/dist/admin/header-parser.js +171 -0
- package/dist/admin/header-parser.js.map +1 -0
- package/dist/admin/inject.d.ts +48 -0
- package/dist/admin/inject.d.ts.map +1 -0
- package/dist/admin/inject.js +167 -0
- package/dist/admin/inject.js.map +1 -0
- package/dist/admin/overrides-store.d.ts +18 -0
- package/dist/admin/overrides-store.d.ts.map +1 -0
- package/dist/admin/overrides-store.js +100 -0
- package/dist/admin/overrides-store.js.map +1 -0
- package/dist/admin/resolve-cors.d.ts +22 -0
- package/dist/admin/resolve-cors.d.ts.map +1 -0
- package/dist/admin/resolve-cors.js +28 -0
- package/dist/admin/resolve-cors.js.map +1 -0
- package/dist/admin/scenarios-store.d.ts +15 -0
- package/dist/admin/scenarios-store.d.ts.map +1 -0
- package/dist/admin/scenarios-store.js +109 -0
- package/dist/admin/scenarios-store.js.map +1 -0
- package/dist/admin/ui.css +2 -0
- package/dist/admin/ui.html +13 -0
- package/dist/admin/ui.js +240 -0
- package/dist/bin-helpers.d.ts +46 -0
- package/dist/bin-helpers.d.ts.map +1 -0
- package/dist/bin-helpers.js +61 -0
- package/dist/bin-helpers.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +57 -0
- package/dist/bin.js.map +1 -0
- package/dist/cors.d.ts +57 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +167 -0
- package/dist/cors.js.map +1 -0
- package/dist/define.d.ts +26 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/define.js +55 -0
- package/dist/define.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/json-config.d.ts +170 -0
- package/dist/json-config.d.ts.map +1 -0
- package/dist/json-config.js +684 -0
- package/dist/json-config.js.map +1 -0
- package/dist/jsonc.d.ts +24 -0
- package/dist/jsonc.d.ts.map +1 -0
- package/dist/jsonc.js +125 -0
- package/dist/jsonc.js.map +1 -0
- package/dist/log.d.ts +57 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +103 -0
- package/dist/log.js.map +1 -0
- package/dist/middleware/request-validation.d.ts +58 -0
- package/dist/middleware/request-validation.d.ts.map +1 -0
- package/dist/middleware/request-validation.js +328 -0
- package/dist/middleware/request-validation.js.map +1 -0
- package/dist/mock-mode.d.ts +95 -0
- package/dist/mock-mode.d.ts.map +1 -0
- package/dist/mock-mode.js +95 -0
- package/dist/mock-mode.js.map +1 -0
- package/dist/openapi/auto-schema.d.ts +83 -0
- package/dist/openapi/auto-schema.d.ts.map +1 -0
- package/dist/openapi/auto-schema.js +232 -0
- package/dist/openapi/auto-schema.js.map +1 -0
- package/dist/openapi/generate.d.ts +20 -0
- package/dist/openapi/generate.d.ts.map +1 -0
- package/dist/openapi/generate.js +121 -0
- package/dist/openapi/generate.js.map +1 -0
- package/dist/openapi/loader.d.ts +18 -0
- package/dist/openapi/loader.d.ts.map +1 -0
- package/dist/openapi/loader.js +25 -0
- package/dist/openapi/loader.js.map +1 -0
- package/dist/openapi/register.d.ts +58 -0
- package/dist/openapi/register.d.ts.map +1 -0
- package/dist/openapi/register.js +166 -0
- package/dist/openapi/register.js.map +1 -0
- package/dist/openapi/seed.d.ts +16 -0
- package/dist/openapi/seed.d.ts.map +1 -0
- package/dist/openapi/seed.js +58 -0
- package/dist/openapi/seed.js.map +1 -0
- package/dist/openapi/translate.d.ts +55 -0
- package/dist/openapi/translate.d.ts.map +1 -0
- package/dist/openapi/translate.js +319 -0
- package/dist/openapi/translate.js.map +1 -0
- package/dist/openapi/walker.d.ts +44 -0
- package/dist/openapi/walker.d.ts.map +1 -0
- package/dist/openapi/walker.js +15 -0
- package/dist/openapi/walker.js.map +1 -0
- package/dist/request.d.ts +19 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +66 -0
- package/dist/request.js.map +1 -0
- package/dist/response.d.ts +13 -0
- package/dist/response.d.ts.map +1 -0
- package/dist/response.js +43 -0
- package/dist/response.js.map +1 -0
- package/dist/route-key.d.ts +60 -0
- package/dist/route-key.d.ts.map +1 -0
- package/dist/route-key.js +105 -0
- package/dist/route-key.js.map +1 -0
- package/dist/run.d.ts +27 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +87 -0
- package/dist/run.js.map +1 -0
- package/dist/server.d.ts +60 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +497 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +274 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/validation/validate.d.ts +38 -0
- package/dist/validation/validate.d.ts.map +1 -0
- package/dist/validation/validate.js +372 -0
- package/dist/validation/validate.js.map +1 -0
- package/docs/admin/error-mode.md +14 -0
- package/docs/admin/headers.md +63 -0
- package/docs/admin/index.md +111 -0
- package/docs/admin/overrides.md +23 -0
- package/docs/admin/rest-api.md +90 -0
- package/docs/admin/scenarios.md +28 -0
- package/docs/cli.md +76 -0
- package/docs/config/admin.md +62 -0
- package/docs/config/cors.md +72 -0
- package/docs/config/endpoints.md +162 -0
- package/docs/config/index.md +107 -0
- package/docs/config/log.md +43 -0
- package/docs/config/mock-mode.md +84 -0
- package/docs/config/openapi.md +8 -0
- package/docs/config/schema.md +34 -0
- package/docs/config/server.md +32 -0
- package/docs/config/validation.md +161 -0
- package/docs/design.md +517 -0
- package/docs/errors.md +192 -0
- package/docs/getting-started.md +146 -0
- package/docs/index.md +23 -0
- package/docs/openapi/auto-schema.md +146 -0
- package/docs/openapi/fallback.md +242 -0
- package/docs/openapi/keywords.md +78 -0
- package/docs/recipes.md +258 -0
- package/docs/stability.md +118 -0
- package/docs/troubleshooting.md +191 -0
- package/package.json +80 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { translateOasToIR } from './translate.js';
|
|
2
|
+
/** Single-shot guard for the `schema.locale` no-op soft warn. */
|
|
3
|
+
let localeWarned = false;
|
|
4
|
+
/** Knobs accepted on the `schema:` JSONC object (alongside `enabled`). */
|
|
5
|
+
const KNOWN_SCHEMA_KEYS = new Set([
|
|
6
|
+
'enabled',
|
|
7
|
+
'seed',
|
|
8
|
+
'locale',
|
|
9
|
+
'arrayCount',
|
|
10
|
+
]);
|
|
11
|
+
/**
|
|
12
|
+
* Friendly install-hint emitted when `config.schema.enabled === true`
|
|
13
|
+
* but the `@databehave/schema` peer is not installed. Verbatim from
|
|
14
|
+
* the brief — names the package, the field, the file, all three
|
|
15
|
+
* install commands, the README anchor, and the opt-out instruction.
|
|
16
|
+
*/
|
|
17
|
+
export const INSTALL_HINT = '[@databehave/server] Auto-schema mode is enabled in databehave.jsonc\n' +
|
|
18
|
+
'("schema": { "enabled": true } is set), but the data-generation engine is not installed.\n' +
|
|
19
|
+
'\n' +
|
|
20
|
+
' npm i @databehave/schema\n' +
|
|
21
|
+
' # or: pnpm add @databehave/schema\n' +
|
|
22
|
+
' # or: yarn add @databehave/schema\n' +
|
|
23
|
+
'\n' +
|
|
24
|
+
'This enables realistic, seeded mock data derived from your OpenAPI\n' +
|
|
25
|
+
'document. See: https://github.com/ochairo/databehave/blob/main/packages/server/docs/openapi/auto-schema.md#missing-install-error\n' +
|
|
26
|
+
'\n' +
|
|
27
|
+
'To keep the default zero-dep placeholder mode instead, set\n' +
|
|
28
|
+
'"schema": { "enabled": false } or remove the "schema" field from databehave.jsonc.';
|
|
29
|
+
/**
|
|
30
|
+
* Validate the raw JSONC `schema:` value and normalise to the
|
|
31
|
+
* resolved shape. The value MUST be an object with an explicit
|
|
32
|
+
* `enabled: boolean`. The boolean shorthand (`schema: true` /
|
|
33
|
+
* `schema: false`) was removed — callers receive a migration
|
|
34
|
+
* error pointing at the new shape. Returns `null` when
|
|
35
|
+
* `enabled === false` so the caller can short-circuit auto-mode.
|
|
36
|
+
* Unknown keys throw with a message pointing at the offending key.
|
|
37
|
+
*/
|
|
38
|
+
export const normaliseSchemaConfig = (raw) => {
|
|
39
|
+
if (typeof raw === 'boolean') {
|
|
40
|
+
throw new Error('@databehave/server/config: "schema" must be an object like ' +
|
|
41
|
+
'{ "enabled": true }; the boolean shorthand was removed (got ' +
|
|
42
|
+
JSON.stringify(raw) +
|
|
43
|
+
')');
|
|
44
|
+
}
|
|
45
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
46
|
+
throw new Error('@databehave/server/config: "schema" must be an object like ' +
|
|
47
|
+
'{ "enabled": true } (got ' +
|
|
48
|
+
(raw === null ? 'null' : Array.isArray(raw) ? 'array' : typeof raw) +
|
|
49
|
+
')');
|
|
50
|
+
}
|
|
51
|
+
const obj = raw;
|
|
52
|
+
for (const k of Object.keys(obj)) {
|
|
53
|
+
if (!KNOWN_SCHEMA_KEYS.has(k)) {
|
|
54
|
+
throw new Error(`@databehave/server/config: unknown key in "schema": ${JSON.stringify(k)} ` +
|
|
55
|
+
`(valid keys: ${[...KNOWN_SCHEMA_KEYS].join(', ')})`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (typeof obj.enabled !== 'boolean') {
|
|
59
|
+
throw new Error('@databehave/server/config: "schema.enabled" must be a boolean ' +
|
|
60
|
+
`(got ${obj.enabled === undefined ? 'undefined' : JSON.stringify(obj.enabled)})`);
|
|
61
|
+
}
|
|
62
|
+
if (obj.enabled === false)
|
|
63
|
+
return null;
|
|
64
|
+
let seed = 'stable';
|
|
65
|
+
if (obj.seed !== undefined) {
|
|
66
|
+
if (typeof obj.seed === 'number' ||
|
|
67
|
+
obj.seed === 'stable' ||
|
|
68
|
+
obj.seed === 'random') {
|
|
69
|
+
seed = obj.seed;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new Error('@databehave/server/config: "schema.seed" must be a number, "stable", or "random" ' +
|
|
73
|
+
`(got ${JSON.stringify(obj.seed)})`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const out = { seed };
|
|
77
|
+
if (obj.locale !== undefined) {
|
|
78
|
+
if (typeof obj.locale !== 'string') {
|
|
79
|
+
throw new Error('@databehave/server/config: "schema.locale" must be a string');
|
|
80
|
+
}
|
|
81
|
+
out.locale = obj.locale;
|
|
82
|
+
if (!localeWarned) {
|
|
83
|
+
localeWarned = true;
|
|
84
|
+
console.warn(`[@databehave/server] schema.locale (${JSON.stringify(obj.locale)}) is currently parsed but not consumed by the data-generation engine; the value will be ignored.`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (obj.arrayCount !== undefined) {
|
|
88
|
+
if (typeof obj.arrayCount !== 'number' || !Number.isFinite(obj.arrayCount) || obj.arrayCount < 0) {
|
|
89
|
+
throw new Error('@databehave/server/config: "schema.arrayCount" must be a non-negative number');
|
|
90
|
+
}
|
|
91
|
+
out.arrayCount = obj.arrayCount;
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Load the optional peer `@databehave/schema` package. On
|
|
97
|
+
* "Cannot find module" failures the native Node error is suppressed
|
|
98
|
+
* and replaced with the friendly install hint above. Other errors
|
|
99
|
+
* (e.g. broken install) re-throw verbatim — those are real bugs.
|
|
100
|
+
*/
|
|
101
|
+
export const loadSchemaModule = async () => {
|
|
102
|
+
try {
|
|
103
|
+
return await import('@databehave/schema');
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
107
|
+
const code = err?.code;
|
|
108
|
+
if (code === 'ERR_MODULE_NOT_FOUND' ||
|
|
109
|
+
code === 'MODULE_NOT_FOUND' ||
|
|
110
|
+
msg.includes('Cannot find module') ||
|
|
111
|
+
msg.includes('Cannot find package')) {
|
|
112
|
+
// Suppress the native error — users see only the friendly
|
|
113
|
+
// template. Wrapping `cause: err` would re-leak the native
|
|
114
|
+
// text into Node's default stack-printer; intentionally drop it.
|
|
115
|
+
throw new Error(INSTALL_HINT);
|
|
116
|
+
}
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const SUPPORTED_METHODS = new Set([
|
|
121
|
+
'get',
|
|
122
|
+
'post',
|
|
123
|
+
'put',
|
|
124
|
+
'delete',
|
|
125
|
+
'patch',
|
|
126
|
+
]);
|
|
127
|
+
/** Convert OAS `/users/{id}` → `/users/:id`. */
|
|
128
|
+
const oasPathToInternal = (oasPath) => oasPath
|
|
129
|
+
.split('/')
|
|
130
|
+
.map((seg) => (seg.startsWith('{') && seg.endsWith('}') ? `:${seg.slice(1, -1)}` : seg))
|
|
131
|
+
.join('/');
|
|
132
|
+
/**
|
|
133
|
+
* FNV-1a 32-bit hash. Tiny, zero-dep, stable across machines —
|
|
134
|
+
* exactly what the per-request `seed: 'stable'` derivation needs.
|
|
135
|
+
*
|
|
136
|
+
* Input format: `${METHOD} ${endpointPath}|${sortedQuery}|${sortedPathParams}`,
|
|
137
|
+
* where sorted means lex-sorted keys joined by `&` as `key=value`.
|
|
138
|
+
*
|
|
139
|
+
* Example input → output (uint32):
|
|
140
|
+
* `GET /users/:id|page=2|id=42` → 0x9b1ed8c5
|
|
141
|
+
*/
|
|
142
|
+
export const hashSeed = (input) => {
|
|
143
|
+
let h = 0x811c9dc5;
|
|
144
|
+
for (let i = 0; i < input.length; i++) {
|
|
145
|
+
h ^= input.charCodeAt(i);
|
|
146
|
+
h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;
|
|
147
|
+
}
|
|
148
|
+
return h >>> 0;
|
|
149
|
+
};
|
|
150
|
+
const sortedJoin = (record) => Object.keys(record)
|
|
151
|
+
.sort()
|
|
152
|
+
.map((k) => `${k}=${record[k]}`)
|
|
153
|
+
.join('&');
|
|
154
|
+
const deriveSeed = (cfg, method, endpointPath, req) => {
|
|
155
|
+
if (typeof cfg.seed === 'number')
|
|
156
|
+
return cfg.seed >>> 0;
|
|
157
|
+
if (cfg.seed === 'random') {
|
|
158
|
+
return (Math.random() * 0xffffffff) >>> 0;
|
|
159
|
+
}
|
|
160
|
+
// 'stable': hash of `${METHOD} ${endpointPath}|${sortedQuery}|${sortedParams}`
|
|
161
|
+
const key = `${method} ${endpointPath}|${sortedJoin(req.query)}|${sortedJoin(req.params)}`;
|
|
162
|
+
return hashSeed(key);
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Find the response schema node that the OAS-only generator would
|
|
166
|
+
* pick: prefer 200, then the smallest declared 2xx, mirroring
|
|
167
|
+
* `register.ts`. 204 → no body (signal via `null`).
|
|
168
|
+
*/
|
|
169
|
+
const pickResponseSchema = (responses) => {
|
|
170
|
+
const twoXx = Object.keys(responses)
|
|
171
|
+
.map((s) => Number.parseInt(s, 10))
|
|
172
|
+
.filter((n) => Number.isFinite(n) && n >= 200 && n < 300)
|
|
173
|
+
.sort((a, b) => (a === 200 ? -1 : b === 200 ? 1 : a - b));
|
|
174
|
+
const status = twoXx[0];
|
|
175
|
+
if (status === undefined)
|
|
176
|
+
return null;
|
|
177
|
+
if (status === 204)
|
|
178
|
+
return 'no-body';
|
|
179
|
+
const schema = responses[String(status)]?.content?.['application/json']?.schema;
|
|
180
|
+
if (!schema || typeof schema !== 'object')
|
|
181
|
+
return null;
|
|
182
|
+
return { schema, status };
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Build a route-key → handler map for every OAS path/method whose
|
|
186
|
+
* response carries a usable JSON schema and is NOT already covered
|
|
187
|
+
* by hand-written `endpoints`. Each handler is closed over the
|
|
188
|
+
* cached schema module and pre-translated IR.
|
|
189
|
+
*
|
|
190
|
+
* `declaredKeys` is the set of route keys the user already declared
|
|
191
|
+
* via `endpoints`; auto-mode skips those so hand-written routes
|
|
192
|
+
* always win.
|
|
193
|
+
*/
|
|
194
|
+
export const buildAutoSchemaRoutes = async (doc, cfg, schemaModule, declaredKeys) => {
|
|
195
|
+
const out = new Map();
|
|
196
|
+
const paths = doc.paths;
|
|
197
|
+
if (!paths)
|
|
198
|
+
return out;
|
|
199
|
+
for (const [oasPath, methods] of Object.entries(paths)) {
|
|
200
|
+
if (oasPath === '/')
|
|
201
|
+
continue;
|
|
202
|
+
const kitPath = oasPathToInternal(oasPath);
|
|
203
|
+
for (const [methodName, op] of Object.entries(methods)) {
|
|
204
|
+
const method = methodName.toLowerCase();
|
|
205
|
+
if (!SUPPORTED_METHODS.has(method))
|
|
206
|
+
continue;
|
|
207
|
+
const key = `${method.toUpperCase()} ${kitPath}`;
|
|
208
|
+
if (declaredKeys.has(key))
|
|
209
|
+
continue;
|
|
210
|
+
const responses = op.responses;
|
|
211
|
+
if (!responses)
|
|
212
|
+
continue;
|
|
213
|
+
const picked = pickResponseSchema(responses);
|
|
214
|
+
if (picked === null || picked === 'no-body')
|
|
215
|
+
continue;
|
|
216
|
+
// Translation throws at server-creation on unsupported keywords —
|
|
217
|
+
// FAIL LOUD, never silently fall through to the placeholder
|
|
218
|
+
// generator (the user opted in to auto-schema; downgrading
|
|
219
|
+
// would produce confusing mixed output).
|
|
220
|
+
const ir = await translateOasToIR(picked.schema, doc);
|
|
221
|
+
const upperMethod = method.toUpperCase();
|
|
222
|
+
const handler = (req) => {
|
|
223
|
+
const seed = deriveSeed(cfg, upperMethod, kitPath, req);
|
|
224
|
+
const value = schemaModule.mock(ir, { seed });
|
|
225
|
+
return { json: value };
|
|
226
|
+
};
|
|
227
|
+
out.set(key, handler);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return out;
|
|
231
|
+
};
|
|
232
|
+
//# sourceMappingURL=auto-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-schema.js","sourceRoot":"","sources":["../../src/openapi/auto-schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-server, zero-dep mock-data generator for OpenAPI-driven mocks.
|
|
3
|
+
*
|
|
4
|
+
* OpenAPI node → generateFromOasSchema() → deterministic JSON value
|
|
5
|
+
*
|
|
6
|
+
* `@databehave/server` runs standalone for OAS-only consumers — this
|
|
7
|
+
* file is the entire mock-body engine and pulls no DSL dependency at
|
|
8
|
+
* runtime. Output is deterministic placeholder data so tests can pin
|
|
9
|
+
* exact values (`"string"`, `0`, `false`, …) and OAS gaps surface
|
|
10
|
+
* loudly via `onWalkError` in `register.ts`.
|
|
11
|
+
*
|
|
12
|
+
* Single file. No external imports. Sibling-relative only.
|
|
13
|
+
*/
|
|
14
|
+
import type { OasDoc, OasNode } from './walker.js';
|
|
15
|
+
/** Reserved for a future seeded mode (brief item #11). Currently ignored. */
|
|
16
|
+
export interface GenerateOptions {
|
|
17
|
+
readonly seed?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare const generateFromOasSchema: (node: OasNode, doc?: OasDoc, _opts?: GenerateOptions, seen?: Set<string>) => unknown;
|
|
20
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/openapi/generate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAElD,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAsDD,eAAO,MAAM,qBAAqB,GAChC,MAAM,OAAO,EACb,MAAK,MAAW,EAEhB,QAAO,eAAoB,EAC3B,OAAM,GAAG,CAAC,MAAM,CAAqB,KACpC,OAqEF,CAAA"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const FORMAT_STRINGS = {
|
|
2
|
+
date: '2024-01-01',
|
|
3
|
+
'date-time': '2024-01-01T00:00:00Z',
|
|
4
|
+
email: 'user@example.com',
|
|
5
|
+
uuid: '00000000-0000-4000-8000-000000000000',
|
|
6
|
+
uri: 'https://example.com/',
|
|
7
|
+
};
|
|
8
|
+
const KNOWN_TYPES = new Set([
|
|
9
|
+
'string',
|
|
10
|
+
'integer',
|
|
11
|
+
'number',
|
|
12
|
+
'boolean',
|
|
13
|
+
'array',
|
|
14
|
+
'object',
|
|
15
|
+
'null',
|
|
16
|
+
]);
|
|
17
|
+
const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
18
|
+
const resolveRef = (doc, ref) => {
|
|
19
|
+
if (!ref.startsWith('#/')) {
|
|
20
|
+
throw new Error(`@databehave/server/openapi: remote $ref not supported by OAS-only generator: ${ref}`);
|
|
21
|
+
}
|
|
22
|
+
const m = /^#\/components\/schemas\/(.+)$/.exec(ref);
|
|
23
|
+
if (!m) {
|
|
24
|
+
throw new Error(`@databehave/server/openapi: unsupported $ref: ${ref}`);
|
|
25
|
+
}
|
|
26
|
+
const name = decodeURIComponent(m[1]);
|
|
27
|
+
const target = doc.components?.schemas?.[name];
|
|
28
|
+
if (!target) {
|
|
29
|
+
throw new Error(`@databehave/server/openapi: $ref not found: ${ref}`);
|
|
30
|
+
}
|
|
31
|
+
return target;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Pick the concrete (non-`null`) type when OAS 3.1 declares
|
|
35
|
+
* `["string","null"]`. OAS 3.0 `nullable: true` is treated the same
|
|
36
|
+
* way: the generator never emits `null` from a nullable widening —
|
|
37
|
+
* callers that want explicit `null` use `examples` or `enum`.
|
|
38
|
+
*/
|
|
39
|
+
const pickType = (node) => {
|
|
40
|
+
if (Array.isArray(node.type)) {
|
|
41
|
+
return node.type.find((t) => t !== 'null');
|
|
42
|
+
}
|
|
43
|
+
return node.type;
|
|
44
|
+
};
|
|
45
|
+
export const generateFromOasSchema = (node, doc = {},
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
47
|
+
_opts = {}, seen = new Set()) => {
|
|
48
|
+
const ex = node.example;
|
|
49
|
+
if (ex !== undefined)
|
|
50
|
+
return ex;
|
|
51
|
+
const examples = node.examples;
|
|
52
|
+
if (Array.isArray(examples) && examples.length > 0)
|
|
53
|
+
return examples[0];
|
|
54
|
+
if (Array.isArray(node.enum) && node.enum.length > 0)
|
|
55
|
+
return node.enum[0];
|
|
56
|
+
if (node.const !== undefined)
|
|
57
|
+
return node.const;
|
|
58
|
+
if (node.$ref) {
|
|
59
|
+
if (seen.has(node.$ref))
|
|
60
|
+
return {};
|
|
61
|
+
const next = new Set(seen);
|
|
62
|
+
next.add(node.$ref);
|
|
63
|
+
return generateFromOasSchema(resolveRef(doc, node.$ref), doc, _opts, next);
|
|
64
|
+
}
|
|
65
|
+
if (Array.isArray(node.allOf) && node.allOf.length > 0) {
|
|
66
|
+
let merged = {};
|
|
67
|
+
for (const branch of node.allOf) {
|
|
68
|
+
const v = generateFromOasSchema(branch, doc, _opts, seen);
|
|
69
|
+
if (isPlainObject(v))
|
|
70
|
+
merged = { ...merged, ...v };
|
|
71
|
+
else
|
|
72
|
+
return v;
|
|
73
|
+
}
|
|
74
|
+
return merged;
|
|
75
|
+
}
|
|
76
|
+
if (Array.isArray(node.oneOf) && node.oneOf.length > 0) {
|
|
77
|
+
return generateFromOasSchema(node.oneOf[0], doc, _opts, seen);
|
|
78
|
+
}
|
|
79
|
+
if (Array.isArray(node.anyOf) && node.anyOf.length > 0) {
|
|
80
|
+
return generateFromOasSchema(node.anyOf[0], doc, _opts, seen);
|
|
81
|
+
}
|
|
82
|
+
let t = pickType(node);
|
|
83
|
+
if (!t && node.properties)
|
|
84
|
+
t = 'object';
|
|
85
|
+
if (!t && node.items !== undefined)
|
|
86
|
+
t = 'array';
|
|
87
|
+
if (!t)
|
|
88
|
+
return {};
|
|
89
|
+
if (typeof t === 'string' && !KNOWN_TYPES.has(t)) {
|
|
90
|
+
throw new Error(`@databehave/server/openapi: unsupported OpenAPI node: ${JSON.stringify(node)}`);
|
|
91
|
+
}
|
|
92
|
+
if (t === 'null')
|
|
93
|
+
return null;
|
|
94
|
+
if (t === 'string') {
|
|
95
|
+
if (node.format && FORMAT_STRINGS[node.format]) {
|
|
96
|
+
return FORMAT_STRINGS[node.format];
|
|
97
|
+
}
|
|
98
|
+
return 'string';
|
|
99
|
+
}
|
|
100
|
+
if (t === 'integer' || t === 'number') {
|
|
101
|
+
return typeof node.minimum === 'number' ? node.minimum : 0;
|
|
102
|
+
}
|
|
103
|
+
if (t === 'boolean')
|
|
104
|
+
return false;
|
|
105
|
+
if (t === 'array') {
|
|
106
|
+
const itemNode = node.items ?? {};
|
|
107
|
+
const count = typeof node.minItems === 'number' && node.minItems > 0 ? node.minItems : 1;
|
|
108
|
+
const out = [];
|
|
109
|
+
for (let i = 0; i < count; i += 1) {
|
|
110
|
+
out.push(generateFromOasSchema(itemNode, doc, _opts, seen));
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
// object
|
|
115
|
+
const out = {};
|
|
116
|
+
for (const [k, v] of Object.entries(node.properties ?? {})) {
|
|
117
|
+
out[k] = generateFromOasSchema(v, doc, _opts, seen);
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/openapi/generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a JSON OpenAPI document.
|
|
3
|
+
*
|
|
4
|
+
* Consumers hand `@databehave/server` the raw text — how they obtained it
|
|
5
|
+
* (`fs.readFile`, `fetch`, bundler `?raw` import) is up to them.
|
|
6
|
+
*
|
|
7
|
+
* `@databehave/server` is JSON-only. Convert YAML up-front
|
|
8
|
+
* (`yq -o=json spec.yaml > spec.json`) and pass the JSON bytes through.
|
|
9
|
+
*/
|
|
10
|
+
import type { OasDoc } from './walker.js';
|
|
11
|
+
/**
|
|
12
|
+
* Parse a JSON OpenAPI document.
|
|
13
|
+
*
|
|
14
|
+
* `sourcePath` is optional metadata used only to enrich error messages
|
|
15
|
+
* and to refuse YAML inputs by extension with an actionable hint.
|
|
16
|
+
*/
|
|
17
|
+
export declare const parseOpenApi: (text: string, sourcePath?: string) => OasDoc;
|
|
18
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/openapi/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,aAAa,MAAM,KAAG,MAoBhE,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a JSON OpenAPI document.
|
|
3
|
+
*
|
|
4
|
+
* `sourcePath` is optional metadata used only to enrich error messages
|
|
5
|
+
* and to refuse YAML inputs by extension with an actionable hint.
|
|
6
|
+
*/
|
|
7
|
+
export const parseOpenApi = (text, sourcePath) => {
|
|
8
|
+
// Strip a leading UTF-8 BOM (U+FEFF) if present — common when the
|
|
9
|
+
// OAS document was authored in Windows tooling or saved as
|
|
10
|
+
// "UTF-8 with BOM". Without this, the very first `JSON.parse`
|
|
11
|
+
// throws "Unexpected token" with no actionable hint.
|
|
12
|
+
const stripped = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
13
|
+
if (sourcePath !== undefined && /\.ya?ml$/i.test(sourcePath)) {
|
|
14
|
+
throw new Error('JSON-only spec loader. Convert YAML first: yq -o=json <file>.yaml > <file>.json');
|
|
15
|
+
}
|
|
16
|
+
const where = sourcePath !== undefined ? ` at ${sourcePath}` : '';
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(stripped);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
22
|
+
throw new Error(`@databehave/server/openapi: failed to parse JSON OpenAPI document${where}: ${msg}`);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/openapi/loader.ts"],"names":[],"mappings":"AAWA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,UAAmB,EAAU,EAAE;IACxE,kEAAkE;IAClE,2DAA2D;IAC3D,8DAA8D;IAC9D,qDAAqD;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACrE,IAAI,UAAU,KAAK,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CACb,iFAAiF,CAClF,CAAA;IACH,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACjE,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAW,CAAA;IACvC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAC5D,MAAM,IAAI,KAAK,CACb,oEAAoE,KAAK,KAAK,GAAG,EAAE,CACpF,CAAA;IACH,CAAC;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a route table of `Handler`s from an OpenAPI document.
|
|
3
|
+
*
|
|
4
|
+
* The walker visits every `paths.*.<method>` entry. For each that
|
|
5
|
+
* has a non-empty 200 response schema, a deterministic mock body is
|
|
6
|
+
* generated once at boot (so OAS gaps surface at boot, not on the
|
|
7
|
+
* first request) and the handler re-serves it per request.
|
|
8
|
+
*
|
|
9
|
+
* Determinism: the OAS-only generator emits structural placeholder
|
|
10
|
+
* values, so same schema → same body. Per-request seeding (path /
|
|
11
|
+
* query params) is reserved for a future seeded mode (brief #11)
|
|
12
|
+
* and currently has no effect on the body.
|
|
13
|
+
*
|
|
14
|
+
* Endpoints without a usable schema get a tiny stub: `{}` for GET,
|
|
15
|
+
* `{ success: true, message: null }` for mutating methods.
|
|
16
|
+
*
|
|
17
|
+
* Pure: this module never reads files or talks to the HTTP layer. It produces
|
|
18
|
+
* plain `Handler` callables that @databehave/server registers like any
|
|
19
|
+
* hand-written route.
|
|
20
|
+
*/
|
|
21
|
+
import type { Handler, Method, RouteKey } from '../types.js';
|
|
22
|
+
import type { OasDoc } from './walker.js';
|
|
23
|
+
export interface OpenApiRoutesOptions {
|
|
24
|
+
/** Route keys (`'METHOD /path'`) the OAS walker must NOT register. */
|
|
25
|
+
readonly skip?: ReadonlySet<RouteKey>;
|
|
26
|
+
/** Optional callback for walker failures. Defaults to silent stub fallback. */
|
|
27
|
+
readonly onWalkError?: (method: Method, routePath: string, err: unknown) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Optional callback fired when the OAS declares a response with an
|
|
30
|
+
* empty / permissive schema (`schema: {}`). Per JSON Schema this
|
|
31
|
+
* matches any value, so we still serve a deterministic stub — but
|
|
32
|
+
* the caller usually wants to surface it so the spec gap is fixed.
|
|
33
|
+
*/
|
|
34
|
+
readonly onEmptySchema?: (method: Method, routePath: string, status: number) => void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build a per-route per-status JSON body generator map from the OAS
|
|
38
|
+
* `responses` table. Used by mock-mode to synthesize an error body
|
|
39
|
+
* from the schema declared for that status code.
|
|
40
|
+
*
|
|
41
|
+
* Each generator is deterministic: same `(routeKey, status)` always
|
|
42
|
+
* yields the same body. Routes with `:param` segments are matched
|
|
43
|
+
* by exact path key only — runtime path-param substitution is the
|
|
44
|
+
* caller's responsibility.
|
|
45
|
+
*
|
|
46
|
+
* `onWalkError` mirrors `buildOpenApiRoutes` so spec gaps in error
|
|
47
|
+
* responses surface the same way as gaps in success responses.
|
|
48
|
+
*/
|
|
49
|
+
export declare const buildOpenApiResponseGenerators: (doc: OasDoc, options?: {
|
|
50
|
+
onWalkError?: OpenApiRoutesOptions["onWalkError"];
|
|
51
|
+
}) => Map<RouteKey, Map<number, () => unknown>>;
|
|
52
|
+
/**
|
|
53
|
+
* Produce a `Map<RouteKey, Handler>` covering every OAS
|
|
54
|
+
* `paths.<p>.<m>` not in `skip`. Failing schemas fall back to the
|
|
55
|
+
* stub body and report via `onWalkError` (default: ignore).
|
|
56
|
+
*/
|
|
57
|
+
export declare const buildOpenApiRoutes: (doc: OasDoc, options?: OpenApiRoutesOptions) => Map<RouteKey, Handler>;
|
|
58
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/openapi/register.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAG5D,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,aAAa,CAAA;AAwClD,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,WAAW,CAAC,EAAE,CACrB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,OAAO,KACT,IAAI,CAAA;IACT;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,KACX,IAAI,CAAA;CACV;AAmBD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,8BAA8B,GACzC,KAAK,MAAM,EACX,UAAS;IAAE,WAAW,CAAC,EAAE,oBAAoB,CAAC,aAAa,CAAC,CAAA;CAAO,KAClE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,CA+C1C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,KAAK,MAAM,EACX,UAAS,oBAAyB,KACjC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAuDvB,CAAA"}
|