@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.
Files changed (162) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -0
  4. package/dist/admin/admin-routes.d.ts +42 -0
  5. package/dist/admin/admin-routes.d.ts.map +1 -0
  6. package/dist/admin/admin-routes.js +472 -0
  7. package/dist/admin/admin-routes.js.map +1 -0
  8. package/dist/admin/admin-types.d.ts +170 -0
  9. package/dist/admin/admin-types.d.ts.map +1 -0
  10. package/dist/admin/admin-types.js +30 -0
  11. package/dist/admin/admin-types.js.map +1 -0
  12. package/dist/admin/banner.d.ts +11 -0
  13. package/dist/admin/banner.d.ts.map +1 -0
  14. package/dist/admin/banner.js +5 -0
  15. package/dist/admin/banner.js.map +1 -0
  16. package/dist/admin/header-parser.d.ts +14 -0
  17. package/dist/admin/header-parser.d.ts.map +1 -0
  18. package/dist/admin/header-parser.js +171 -0
  19. package/dist/admin/header-parser.js.map +1 -0
  20. package/dist/admin/inject.d.ts +48 -0
  21. package/dist/admin/inject.d.ts.map +1 -0
  22. package/dist/admin/inject.js +167 -0
  23. package/dist/admin/inject.js.map +1 -0
  24. package/dist/admin/overrides-store.d.ts +18 -0
  25. package/dist/admin/overrides-store.d.ts.map +1 -0
  26. package/dist/admin/overrides-store.js +100 -0
  27. package/dist/admin/overrides-store.js.map +1 -0
  28. package/dist/admin/resolve-cors.d.ts +22 -0
  29. package/dist/admin/resolve-cors.d.ts.map +1 -0
  30. package/dist/admin/resolve-cors.js +28 -0
  31. package/dist/admin/resolve-cors.js.map +1 -0
  32. package/dist/admin/scenarios-store.d.ts +15 -0
  33. package/dist/admin/scenarios-store.d.ts.map +1 -0
  34. package/dist/admin/scenarios-store.js +109 -0
  35. package/dist/admin/scenarios-store.js.map +1 -0
  36. package/dist/admin/ui.css +2 -0
  37. package/dist/admin/ui.html +13 -0
  38. package/dist/admin/ui.js +240 -0
  39. package/dist/bin-helpers.d.ts +46 -0
  40. package/dist/bin-helpers.d.ts.map +1 -0
  41. package/dist/bin-helpers.js +61 -0
  42. package/dist/bin-helpers.js.map +1 -0
  43. package/dist/bin.d.ts +3 -0
  44. package/dist/bin.d.ts.map +1 -0
  45. package/dist/bin.js +57 -0
  46. package/dist/bin.js.map +1 -0
  47. package/dist/cors.d.ts +57 -0
  48. package/dist/cors.d.ts.map +1 -0
  49. package/dist/cors.js +167 -0
  50. package/dist/cors.js.map +1 -0
  51. package/dist/define.d.ts +26 -0
  52. package/dist/define.d.ts.map +1 -0
  53. package/dist/define.js +55 -0
  54. package/dist/define.js.map +1 -0
  55. package/dist/index.d.ts +21 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +19 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/json-config.d.ts +170 -0
  60. package/dist/json-config.d.ts.map +1 -0
  61. package/dist/json-config.js +684 -0
  62. package/dist/json-config.js.map +1 -0
  63. package/dist/jsonc.d.ts +24 -0
  64. package/dist/jsonc.d.ts.map +1 -0
  65. package/dist/jsonc.js +125 -0
  66. package/dist/jsonc.js.map +1 -0
  67. package/dist/log.d.ts +57 -0
  68. package/dist/log.d.ts.map +1 -0
  69. package/dist/log.js +103 -0
  70. package/dist/log.js.map +1 -0
  71. package/dist/middleware/request-validation.d.ts +58 -0
  72. package/dist/middleware/request-validation.d.ts.map +1 -0
  73. package/dist/middleware/request-validation.js +328 -0
  74. package/dist/middleware/request-validation.js.map +1 -0
  75. package/dist/mock-mode.d.ts +95 -0
  76. package/dist/mock-mode.d.ts.map +1 -0
  77. package/dist/mock-mode.js +95 -0
  78. package/dist/mock-mode.js.map +1 -0
  79. package/dist/openapi/auto-schema.d.ts +83 -0
  80. package/dist/openapi/auto-schema.d.ts.map +1 -0
  81. package/dist/openapi/auto-schema.js +232 -0
  82. package/dist/openapi/auto-schema.js.map +1 -0
  83. package/dist/openapi/generate.d.ts +20 -0
  84. package/dist/openapi/generate.d.ts.map +1 -0
  85. package/dist/openapi/generate.js +121 -0
  86. package/dist/openapi/generate.js.map +1 -0
  87. package/dist/openapi/loader.d.ts +18 -0
  88. package/dist/openapi/loader.d.ts.map +1 -0
  89. package/dist/openapi/loader.js +25 -0
  90. package/dist/openapi/loader.js.map +1 -0
  91. package/dist/openapi/register.d.ts +58 -0
  92. package/dist/openapi/register.d.ts.map +1 -0
  93. package/dist/openapi/register.js +166 -0
  94. package/dist/openapi/register.js.map +1 -0
  95. package/dist/openapi/seed.d.ts +16 -0
  96. package/dist/openapi/seed.d.ts.map +1 -0
  97. package/dist/openapi/seed.js +58 -0
  98. package/dist/openapi/seed.js.map +1 -0
  99. package/dist/openapi/translate.d.ts +55 -0
  100. package/dist/openapi/translate.d.ts.map +1 -0
  101. package/dist/openapi/translate.js +319 -0
  102. package/dist/openapi/translate.js.map +1 -0
  103. package/dist/openapi/walker.d.ts +44 -0
  104. package/dist/openapi/walker.d.ts.map +1 -0
  105. package/dist/openapi/walker.js +15 -0
  106. package/dist/openapi/walker.js.map +1 -0
  107. package/dist/request.d.ts +19 -0
  108. package/dist/request.d.ts.map +1 -0
  109. package/dist/request.js +66 -0
  110. package/dist/request.js.map +1 -0
  111. package/dist/response.d.ts +13 -0
  112. package/dist/response.d.ts.map +1 -0
  113. package/dist/response.js +43 -0
  114. package/dist/response.js.map +1 -0
  115. package/dist/route-key.d.ts +60 -0
  116. package/dist/route-key.d.ts.map +1 -0
  117. package/dist/route-key.js +105 -0
  118. package/dist/route-key.js.map +1 -0
  119. package/dist/run.d.ts +27 -0
  120. package/dist/run.d.ts.map +1 -0
  121. package/dist/run.js +87 -0
  122. package/dist/run.js.map +1 -0
  123. package/dist/server.d.ts +60 -0
  124. package/dist/server.d.ts.map +1 -0
  125. package/dist/server.js +497 -0
  126. package/dist/server.js.map +1 -0
  127. package/dist/types.d.ts +274 -0
  128. package/dist/types.d.ts.map +1 -0
  129. package/dist/types.js +9 -0
  130. package/dist/types.js.map +1 -0
  131. package/dist/validation/validate.d.ts +38 -0
  132. package/dist/validation/validate.d.ts.map +1 -0
  133. package/dist/validation/validate.js +372 -0
  134. package/dist/validation/validate.js.map +1 -0
  135. package/docs/admin/error-mode.md +14 -0
  136. package/docs/admin/headers.md +63 -0
  137. package/docs/admin/index.md +111 -0
  138. package/docs/admin/overrides.md +23 -0
  139. package/docs/admin/rest-api.md +90 -0
  140. package/docs/admin/scenarios.md +28 -0
  141. package/docs/cli.md +76 -0
  142. package/docs/config/admin.md +62 -0
  143. package/docs/config/cors.md +72 -0
  144. package/docs/config/endpoints.md +162 -0
  145. package/docs/config/index.md +107 -0
  146. package/docs/config/log.md +43 -0
  147. package/docs/config/mock-mode.md +84 -0
  148. package/docs/config/openapi.md +8 -0
  149. package/docs/config/schema.md +34 -0
  150. package/docs/config/server.md +32 -0
  151. package/docs/config/validation.md +161 -0
  152. package/docs/design.md +517 -0
  153. package/docs/errors.md +192 -0
  154. package/docs/getting-started.md +146 -0
  155. package/docs/index.md +23 -0
  156. package/docs/openapi/auto-schema.md +146 -0
  157. package/docs/openapi/fallback.md +242 -0
  158. package/docs/openapi/keywords.md +78 -0
  159. package/docs/recipes.md +258 -0
  160. package/docs/stability.md +118 -0
  161. package/docs/troubleshooting.md +191 -0
  162. 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":"AA8BA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAUjD,iEAAiE;AACjE,IAAI,YAAY,GAAG,KAAK,CAAA;AAExB,0EAA0E;AAC1E,MAAM,iBAAiB,GAAwB,IAAI,GAAG,CAAC;IACrD,SAAS;IACT,MAAM;IACN,QAAQ;IACR,YAAY;CACb,CAAC,CAAA;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GACvB,wEAAwE;IACxE,4FAA4F;IAC5F,IAAI;IACJ,8BAA8B;IAC9B,uCAAuC;IACvC,uCAAuC;IACvC,IAAI;IACJ,sEAAsE;IACtE,oIAAoI;IACpI,IAAI;IACJ,8DAA8D;IAC9D,oFAAoF,CAAA;AAEtF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAY,EAA+B,EAAE;IACjF,IAAI,OAAO,GAAG,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,6DAA6D;YAC3D,8DAA8D;YAC9D,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;YACnB,GAAG,CACN,CAAA;IACH,CAAC;IACD,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CACb,6DAA6D;YAC3D,2BAA2B;YAC3B,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC;YACnE,GAAG,CACN,CAAA;IACH,CAAC;IACD,MAAM,GAAG,GAAG,GAA8B,CAAA;IAC1C,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CACb,uDAAuD,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG;gBACzE,gBAAgB,CAAC,GAAG,iBAAiB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACvD,CAAA;QACH,CAAC;IACH,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,gEAAgE;YAC9D,QAAQ,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CACnF,CAAA;IACH,CAAC;IACD,IAAI,GAAG,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,IAAI,CAAA;IACtC,IAAI,IAAI,GAAiC,QAAQ,CAAA;IACjD,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC3B,IACE,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;YAC5B,GAAG,CAAC,IAAI,KAAK,QAAQ;YACrB,GAAG,CAAC,IAAI,KAAK,QAAQ,EACrB,CAAC;YACD,IAAI,GAAG,GAAG,CAAC,IAAI,CAAA;QACjB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,mFAAmF;gBACjF,QAAQ,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CACtC,CAAA;QACH,CAAC;IACH,CAAC;IACD,MAAM,GAAG,GAA6E,EAAE,IAAI,EAAE,CAAA;IAC9F,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7B,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAA;QAChF,CAAC;QACD,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;QACvB,IAAI,CAAC,YAAY,EAAE,CAAC;YAAC,YAAY,GAAG,IAAI,CAAC;YAAC,OAAO,CAAC,IAAI,CAAC,uCAAuC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,kGAAkG,CAAC,CAAA;QAAC,CAAC;IAC/N,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;YACjG,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAA;QACjG,CAAC;QACD,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,UAAU,CAAA;IACjC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,IAAkD,EAAE;IACvF,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAC3C,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,GAAI,GAAyC,EAAE,IAAI,CAAA;QAC7D,IACE,IAAI,KAAK,sBAAsB;YAC/B,IAAI,KAAK,kBAAkB;YAC3B,GAAG,CAAC,QAAQ,CAAC,oBAAoB,CAAC;YAClC,GAAG,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EACnC,CAAC;YACD,0DAA0D;YAC1D,2DAA2D;YAC3D,iEAAiE;YACjE,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAA;QAC/B,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC,CAAA;AAED,MAAM,iBAAiB,GAAwB,IAAI,GAAG,CAAS;IAC7D,KAAK;IACL,MAAM;IACN,KAAK;IACL,QAAQ;IACR,OAAO;CACR,CAAC,CAAA;AAEF,gDAAgD;AAChD,MAAM,iBAAiB,GAAG,CAAC,OAAe,EAAU,EAAE,CACpD,OAAO;KACJ,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;KACvF,IAAI,CAAC,GAAG,CAAC,CAAA;AAEd;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAU,EAAE;IAChD,IAAI,CAAC,GAAG,UAAU,CAAA;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;QACxB,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IACzE,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,UAAU,GAAG,CAAC,MAAwC,EAAU,EAAE,CACtE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;KAChB,IAAI,EAAE;KACN,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/B,IAAI,CAAC,GAAG,CAAC,CAAA;AAEd,MAAM,UAAU,GAAG,CACjB,GAAyB,EACzB,MAAc,EACd,YAAoB,EACpB,GAAgB,EACR,EAAE;IACV,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,CAAA;IACvD,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;IAC3C,CAAC;IACD,+EAA+E;IAC/E,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,YAAY,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAA;IAC1F,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAA;AACtB,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,kBAAkB,GAAG,CACzB,SAAkC,EACsB,EAAE;IAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;SACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;SAClC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC;SACxD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACvB,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACrC,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,SAAS,CAAA;IACpC,MAAM,MAAM,GACV,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAGzB,EAAE,OAAO,EAAE,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IACxC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACtD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;AAC3B,CAAC,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EACxC,GAAW,EACX,GAAyB,EACzB,YAAiD,EACjD,YAAmC,EACF,EAAE;IACnC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAqB,CAAA;IACxC,MAAM,KAAK,GAAI,GAA2D,CAAC,KAAK,CAAA;IAChF,IAAI,CAAC,KAAK;QAAE,OAAO,GAAG,CAAA;IAEtB,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,IAAI,OAAO,KAAK,GAAG;YAAE,SAAQ;QAC7B,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;QAC1C,KAAK,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACvD,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,EAAY,CAAA;YACjD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,SAAQ;YAC5C,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,OAAO,EAAc,CAAA;YAC5D,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAQ;YACnC,MAAM,SAAS,GAAI,EAA8C,CAAC,SAAS,CAAA;YAC3E,IAAI,CAAC,SAAS;gBAAE,SAAQ;YACxB,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;YAC5C,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS;gBAAE,SAAQ;YACrD,kEAAkE;YAClE,4DAA4D;YAC5D,2DAA2D;YAC3D,yCAAyC;YACzC,MAAM,EAAE,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;YACrD,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,EAAE,CAAA;YACxC,MAAM,OAAO,GAAY,CAAC,GAAG,EAAE,EAAE;gBAC/B,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,CAAA;gBACvD,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;gBAC7C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;YACxB,CAAC,CAAA;YACD,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QACvB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA"}
@@ -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"}