@molecule/api-mock-server 1.0.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 (67) hide show
  1. package/LICENSE +115 -0
  2. package/README.md +853 -0
  3. package/dist/browser-guard.d.ts +2 -0
  4. package/dist/browser-guard.d.ts.map +1 -0
  5. package/dist/browser-guard.js +19 -0
  6. package/dist/browser-guard.js.map +1 -0
  7. package/dist/cli.d.ts +11 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +152 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/fixtures/app-fixtures.d.ts +54 -0
  12. package/dist/fixtures/app-fixtures.d.ts.map +1 -0
  13. package/dist/fixtures/app-fixtures.js +601 -0
  14. package/dist/fixtures/app-fixtures.js.map +1 -0
  15. package/dist/fixtures/index.d.ts +9 -0
  16. package/dist/fixtures/index.d.ts.map +1 -0
  17. package/dist/fixtures/index.js +9 -0
  18. package/dist/fixtures/index.js.map +1 -0
  19. package/dist/fixtures/seed.d.ts +74 -0
  20. package/dist/fixtures/seed.d.ts.map +1 -0
  21. package/dist/fixtures/seed.js +112 -0
  22. package/dist/fixtures/seed.js.map +1 -0
  23. package/dist/fixtures/semantic-generator.d.ts +20 -0
  24. package/dist/fixtures/semantic-generator.d.ts.map +1 -0
  25. package/dist/fixtures/semantic-generator.js +539 -0
  26. package/dist/fixtures/semantic-generator.js.map +1 -0
  27. package/dist/fixtures/zod-walker.d.ts +34 -0
  28. package/dist/fixtures/zod-walker.d.ts.map +1 -0
  29. package/dist/fixtures/zod-walker.js +183 -0
  30. package/dist/fixtures/zod-walker.js.map +1 -0
  31. package/dist/index.d.ts +78 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +78 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/scanner/index.d.ts +6 -0
  36. package/dist/scanner/index.d.ts.map +1 -0
  37. package/dist/scanner/index.js +6 -0
  38. package/dist/scanner/index.js.map +1 -0
  39. package/dist/scanner/scanner.d.ts +21 -0
  40. package/dist/scanner/scanner.d.ts.map +1 -0
  41. package/dist/scanner/scanner.js +463 -0
  42. package/dist/scanner/scanner.js.map +1 -0
  43. package/dist/server/index.d.ts +7 -0
  44. package/dist/server/index.d.ts.map +1 -0
  45. package/dist/server/index.js +7 -0
  46. package/dist/server/index.js.map +1 -0
  47. package/dist/server/middleware.d.ts +51 -0
  48. package/dist/server/middleware.d.ts.map +1 -0
  49. package/dist/server/middleware.js +124 -0
  50. package/dist/server/middleware.js.map +1 -0
  51. package/dist/server/server.d.ts +29 -0
  52. package/dist/server/server.d.ts.map +1 -0
  53. package/dist/server/server.js +314 -0
  54. package/dist/server/server.js.map +1 -0
  55. package/dist/states/index.d.ts +6 -0
  56. package/dist/states/index.d.ts.map +1 -0
  57. package/dist/states/index.js +6 -0
  58. package/dist/states/index.js.map +1 -0
  59. package/dist/states/states.d.ts +57 -0
  60. package/dist/states/states.d.ts.map +1 -0
  61. package/dist/states/states.js +89 -0
  62. package/dist/states/states.js.map +1 -0
  63. package/dist/types.d.ts +214 -0
  64. package/dist/types.d.ts.map +1 -0
  65. package/dist/types.js +6 -0
  66. package/dist/types.js.map +1 -0
  67. package/package.json +66 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * State control middleware for the mock server.
3
+ * Allows per-request state overrides via query params or headers.
4
+ *
5
+ * Query params: ?_state=error&_delay=2000&_status=500
6
+ * Headers: X-Mock-State: error, X-Mock-Delay: 2000
7
+ */
8
+ /**
9
+ * Express middleware that extracts state control signals from the request
10
+ * and attaches them to res.locals for the route handler to use.
11
+ * @param defaultState - The default state to use when no override is provided.
12
+ * Pass a function to have the default resolved per-request (a live getter) —
13
+ * required for `MockServer.setDefaultState()` to take effect after startup,
14
+ * since a plain object is captured once at middleware-creation time.
15
+ * @returns Express middleware function
16
+ */
17
+ export function stateControlMiddleware(defaultState = { state: 'success' }) {
18
+ return (req, res, next) => {
19
+ // A repeated query param (?_state=a&_state=b) parses as an array — only
20
+ // accept plain strings so a malformed URL can't crash the middleware.
21
+ const queryString = (name) => {
22
+ const value = req.query[name];
23
+ return typeof value === 'string' ? value : undefined;
24
+ };
25
+ // Extract state from query params or headers
26
+ const stateParam = queryString('_state') || req.get('X-Mock-State');
27
+ const delayParam = queryString('_delay') || req.get('X-Mock-Delay');
28
+ const statusParam = queryString('_status') || req.get('X-Mock-Status');
29
+ const base = typeof defaultState === 'function' ? defaultState() : defaultState;
30
+ const state = { ...base };
31
+ if (stateParam) {
32
+ const normalized = stateParam.toLowerCase().trim();
33
+ if (['success', 'empty', 'error', 'unauthorized'].includes(normalized)) {
34
+ state.state = normalized;
35
+ }
36
+ else {
37
+ // A typo'd ?_state (e.g. `?_state=eror`) would otherwise silently serve
38
+ // the DEFAULT state — indistinguishable from the override never having
39
+ // been sent, which reads as "the mock ignores my state control". Label
40
+ // the response so a caller can tell "invalid state value" apart from
41
+ // "state applied".
42
+ res.setHeader('X-Mock-Invalid-State', stateParam);
43
+ }
44
+ }
45
+ if (delayParam) {
46
+ const delay = Number(delayParam);
47
+ if (!isNaN(delay) && delay >= 0) {
48
+ state.delay = delay;
49
+ }
50
+ }
51
+ if (statusParam) {
52
+ const status = Number(statusParam);
53
+ if (!isNaN(status) && status >= 100 && status < 600) {
54
+ state.statusCode = status;
55
+ }
56
+ }
57
+ // Attach to res.locals for route handlers
58
+ res.locals.mockState = state;
59
+ next();
60
+ };
61
+ }
62
+ /**
63
+ * CORS middleware that sets permissive CORS headers for development.
64
+ * @returns Express middleware function
65
+ */
66
+ export function corsMiddleware() {
67
+ return (_req, res, next) => {
68
+ res.setHeader('Access-Control-Allow-Origin', '*');
69
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH');
70
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Mock-State, X-Mock-Delay, X-Mock-Status');
71
+ res.setHeader('Access-Control-Max-Age', '86400');
72
+ if (_req.method === 'OPTIONS') {
73
+ res.status(204).end();
74
+ return;
75
+ }
76
+ next();
77
+ };
78
+ }
79
+ /**
80
+ * Request logging middleware for the mock server.
81
+ * @returns Express middleware function
82
+ */
83
+ export function loggingMiddleware() {
84
+ return (req, _res, next) => {
85
+ const state = _res.locals.mockState?.state ?? 'success';
86
+ const timestamp = new Date().toISOString().substring(11, 23);
87
+ console.log(`[mock-server ${timestamp}] ${req.method} ${req.path} (state: ${state})`);
88
+ next();
89
+ };
90
+ }
91
+ /**
92
+ * Maximum delay, in ms, that {@link applyDelay} will actually wait — a
93
+ * requested delay above this is clamped (and logged) rather than honored
94
+ * verbatim. Guards against a stray oversized `?_delay` / `X-Mock-Delay` /
95
+ * `defaultDelay` / `setState({ delay })` value (e.g. a units mistake
96
+ * applying `*1000` twice) hanging a request until the CLIENT gives up —
97
+ * which in an E2E harness presents as an inexplicable page timeout rather
98
+ * than an obvious mock misconfiguration.
99
+ */
100
+ export const MAX_MOCK_DELAY_MS = 60_000;
101
+ /**
102
+ * Apply a delay if specified in the response state. The requested delay is
103
+ * capped at {@link MAX_MOCK_DELAY_MS} — a value above the cap is clamped and
104
+ * a warning is logged (via `console.warn`, immediately, before waiting —
105
+ * not after — so the clamp is visible in server logs right when the
106
+ * oversized delay is requested rather than a minute later).
107
+ * @param state - The response state that may contain a delay
108
+ * @returns A promise that resolves after the (possibly clamped) delay, or
109
+ * immediately if no delay was requested
110
+ */
111
+ export function applyDelay(state) {
112
+ const requested = state.delay;
113
+ if (!requested || requested <= 0) {
114
+ return Promise.resolve();
115
+ }
116
+ const delay = Math.min(requested, MAX_MOCK_DELAY_MS);
117
+ if (delay !== requested) {
118
+ console.warn(`[mock-server] requested delay ${requested}ms exceeds the ${MAX_MOCK_DELAY_MS}ms cap — ` +
119
+ `clamping to ${delay}ms. Check for a units mistake (e.g. seconds*1000 applied twice) ` +
120
+ `in ?_delay / X-Mock-Delay / defaultDelay / setState({ delay }).`);
121
+ }
122
+ return new Promise((resolve) => setTimeout(resolve, delay));
123
+ }
124
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Express-based mock HTTP server that serves fixture responses.
3
+ * Supports programmatic control of response states and delays.
4
+ */
5
+ import type { MockServer, MockServerConfig } from '../types.js';
6
+ /**
7
+ * Create and start a mock API server for the given app type.
8
+ * The server discovers endpoints by scanning handler templates and
9
+ * serves deterministic fixture data for each discovered route.
10
+ *
11
+ * @param config - Server configuration
12
+ * @returns A running MockServer instance with control methods
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const server = await createMockServer({
17
+ * appType: 'personal-finance',
18
+ * port: 4000,
19
+ * })
20
+ *
21
+ * // Control state programmatically
22
+ * server.setState('GET /accounts', { state: 'error', statusCode: 500 })
23
+ *
24
+ * // Teardown
25
+ * await server.close()
26
+ * ```
27
+ */
28
+ export declare function createMockServer(config: MockServerConfig): Promise<MockServer>;
29
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,OAAO,KAAK,EAGV,UAAU,EACV,gBAAgB,EAEjB,MAAM,aAAa,CAAA;AAQpB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CA8LpF"}
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Express-based mock HTTP server that serves fixture responses.
3
+ * Supports programmatic control of response states and delays.
4
+ */
5
+ import { existsSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import express from 'express';
8
+ import { buildFixtureSet, generateFixtures } from '../fixtures/app-fixtures.js';
9
+ import { resolveHandlersPath, scanHandlers } from '../scanner/scanner.js';
10
+ import { getResponseBody, getStatusCode } from '../states/states.js';
11
+ import { applyDelay, corsMiddleware, loggingMiddleware, stateControlMiddleware, } from './middleware.js';
12
+ /**
13
+ * Create and start a mock API server for the given app type.
14
+ * The server discovers endpoints by scanning handler templates and
15
+ * serves deterministic fixture data for each discovered route.
16
+ *
17
+ * @param config - Server configuration
18
+ * @returns A running MockServer instance with control methods
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const server = await createMockServer({
23
+ * appType: 'personal-finance',
24
+ * port: 4000,
25
+ * })
26
+ *
27
+ * // Control state programmatically
28
+ * server.setState('GET /accounts', { state: 'error', statusCode: 500 })
29
+ *
30
+ * // Teardown
31
+ * await server.close()
32
+ * ```
33
+ */
34
+ export async function createMockServer(config) {
35
+ const { appType, fixturesPath, port = 4000, defaultDelay = 0, defaultState = 'success', endpointStates = {}, handlersPath, customFixtures, logging = true, } = config;
36
+ // Build fixture set
37
+ let fixtures;
38
+ if (customFixtures) {
39
+ fixtures = customFixtures;
40
+ }
41
+ else {
42
+ // Generate fixtures from directory path or throw
43
+ const resolvedFixturesPath = fixturesPath ?? resolveFixturesPath(appType);
44
+ if (!resolvedFixturesPath) {
45
+ throw new Error(`No fixture data available for app type: ${appType}. Pass fixturesPath ` +
46
+ `(a directory of *.json fixture files, e.g. './api/fixtures'). ` +
47
+ `Resolving by appType alone looks up mlcl/templates/apps/${appType}/api/fixtures/ ` +
48
+ `and only works inside the molecule workspace — in a scaffolded project, fixturesPath is required.`);
49
+ }
50
+ const generated = generateFixtures(resolvedFixturesPath, appType);
51
+ if (!generated) {
52
+ throw new Error(`No fixture data available at path: ${resolvedFixturesPath} — ` +
53
+ `the directory is missing or contains no *.json fixture files.`);
54
+ }
55
+ fixtures = generated;
56
+ // If handler files exist, scan them and merge any endpoints not already covered
57
+ const resolvedHandlersPath = handlersPath ?? resolveHandlersPath(appType);
58
+ if (resolvedHandlersPath) {
59
+ try {
60
+ const scanResult = scanHandlers(resolvedHandlersPath, appType);
61
+ const scanned = buildFixtureSet(appType, scanResult.endpoints, resolvedFixturesPath);
62
+ if (scanned) {
63
+ // Directory fixtures key endpoints as `GET /api/x`; the scanner keys
64
+ // them as `GET /x` (the router.use prefix has no /api). Normalize so
65
+ // the two sets reconcile instead of double-registering.
66
+ const norm = (k) => k.replace(/^(\w+) (?!\/api\/)\//, '$1 /api/');
67
+ const dirKeys = new Set([...fixtures.endpoints.keys()].map(norm));
68
+ for (const [key, fixture] of scanned.endpoints) {
69
+ const nk = norm(key);
70
+ if (!dirKeys.has(nk)) {
71
+ // Scanner-discovered endpoint not covered by a fixture file.
72
+ fixtures.endpoints.set(nk, fixture);
73
+ }
74
+ else if (fixture.endpoint.responseHints.isPaginated) {
75
+ // Endpoint exists in both: the directory fixture defaults a list
76
+ // GET to a bare array, but the handler actually returns a
77
+ // `{ data, total }` envelope — re-wrap the directory fixture's
78
+ // data so the response shape matches what app pages expect.
79
+ const dir = fixtures.endpoints.get(nk);
80
+ if (dir && Array.isArray(dir.successResponse)) {
81
+ const arr = dir.successResponse;
82
+ dir.successResponse = { data: arr, total: arr.length };
83
+ dir.emptyResponse = { data: [], total: 0 };
84
+ dir.endpoint.responseHints.isPaginated = true;
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ catch (error) {
91
+ // Scanner enrichment is best-effort: a scan failure must not stop the
92
+ // fixture-file endpoints from serving. But a SILENT failure made an
93
+ // explicitly passed handlersPath look ignored (its endpoints just
94
+ // "missing" with no signal), so surface the reason when logging is on.
95
+ if (logging) {
96
+ console.warn(`[mock-server] handler scan failed for ${resolvedHandlersPath} — serving fixture-file endpoints only: ${error.message}`);
97
+ }
98
+ }
99
+ }
100
+ }
101
+ // Per-endpoint state overrides (mutable at runtime)
102
+ const stateOverrides = new Map(Object.entries(endpointStates));
103
+ let currentDefaultState = {
104
+ state: defaultState,
105
+ delay: defaultDelay,
106
+ };
107
+ // Create Express app
108
+ const app = express();
109
+ app.use(express.json());
110
+ app.use(corsMiddleware());
111
+ // Pass a getter, not the object: setDefaultState() reassigns
112
+ // currentDefaultState, and a captured object would freeze the default at
113
+ // its startup value (making setDefaultState a silent no-op).
114
+ app.use(stateControlMiddleware(() => currentDefaultState));
115
+ if (logging) {
116
+ app.use(loggingMiddleware());
117
+ }
118
+ // Register routes from fixtures — static paths before `:param` siblings.
119
+ // Express matches in registration order, so the dir-CRUD `GET /profile/:id`
120
+ // route would otherwise shadow a scanner-discovered `GET /profile/me`
121
+ // (`:id` = 'me') and serve the raw fixture record instead of the handler's
122
+ // synthesized single-object response shape. Sort is stable, so ordering
123
+ // within the same param count is preserved.
124
+ const routeEntries = [...fixtures.endpoints.entries()].sort(([, a], [, b]) => paramCount(a.endpoint.path) - paramCount(b.endpoint.path));
125
+ for (const [key, fixture] of routeEntries) {
126
+ registerRoute(app, key, fixture, stateOverrides, () => currentDefaultState);
127
+ }
128
+ // Health check endpoint
129
+ app.get('/health', (_req, res) => {
130
+ res.json({ status: 'ok', appType, endpoints: fixtures.endpoints.size });
131
+ });
132
+ // Catch-all for unmatched API routes (Express 5 path-to-regexp syntax).
133
+ // Unmatched routes intentionally return an empty SUCCESS (screenshot/E2E
134
+ // pages must render, not 404) — but that makes a typo'd endpoint look
135
+ // identical to legitimately-empty data. The X-Mock-Unmatched header (and a
136
+ // warn log) lets a caller/debugger tell "no such fixture endpoint" apart
137
+ // from "endpoint exists and its data is empty".
138
+ app.all('/api/{*path}', (req, res) => {
139
+ res.setHeader('X-Mock-Unmatched', 'true');
140
+ if (logging) {
141
+ console.warn(`[mock-server] no fixture endpoint matches ${req.method} ${req.path} — serving default empty response (X-Mock-Unmatched: true)`);
142
+ }
143
+ const state = res.locals.mockState;
144
+ if (state?.state === 'error') {
145
+ res.status(500).json({ error: 'Internal server error' });
146
+ return;
147
+ }
148
+ if (state?.state === 'unauthorized') {
149
+ res.status(401).json({ error: 'Unauthorized' });
150
+ return;
151
+ }
152
+ // Return empty success for unmatched routes
153
+ if (req.method === 'GET') {
154
+ res.json([]);
155
+ }
156
+ else if (req.method === 'DELETE') {
157
+ res.status(204).end();
158
+ }
159
+ else {
160
+ res.json({});
161
+ }
162
+ });
163
+ // Start server
164
+ const server = await startServer(app, port);
165
+ const actualPort = server.address().port;
166
+ if (logging) {
167
+ console.log(`\n Mock API server running at http://localhost:${actualPort}`);
168
+ console.log(` App type: ${appType}`);
169
+ console.log(` Endpoints: ${fixtures.endpoints.size}`);
170
+ console.log(` Default state: ${defaultState}\n`);
171
+ }
172
+ return {
173
+ port: actualPort,
174
+ appType,
175
+ setState(endpointKey, state) {
176
+ stateOverrides.set(endpointKey, state);
177
+ },
178
+ clearState(endpointKey) {
179
+ stateOverrides.delete(endpointKey);
180
+ },
181
+ setDefaultState(state) {
182
+ currentDefaultState = { state, delay: defaultDelay };
183
+ },
184
+ getFixtures() {
185
+ return fixtures;
186
+ },
187
+ async close() {
188
+ return new Promise((resolve, reject) => {
189
+ server.close((err) => {
190
+ if (err)
191
+ reject(err);
192
+ else
193
+ resolve();
194
+ });
195
+ });
196
+ },
197
+ };
198
+ }
199
+ /**
200
+ * Resolve a fixtures directory path from an app type name.
201
+ * Searches standard locations in the mlcl templates directory.
202
+ * @param appType - The app type name
203
+ * @returns The resolved fixtures path, or undefined if not found
204
+ */
205
+ function resolveFixturesPath(appType) {
206
+ const root = findWorkspaceRoot();
207
+ if (!root)
208
+ return undefined;
209
+ const candidate = join(root, 'mlcl', 'templates', 'apps', appType, 'api', 'fixtures');
210
+ if (existsSync(candidate))
211
+ return candidate;
212
+ return undefined;
213
+ }
214
+ /**
215
+ * Attempt to find the workspace root by walking up from cwd.
216
+ */
217
+ function findWorkspaceRoot() {
218
+ let dir = process.cwd();
219
+ for (let i = 0; i < 10; i++) {
220
+ if (existsSync(join(dir, 'mlcl')) && existsSync(join(dir, 'molecule'))) {
221
+ return dir;
222
+ }
223
+ const parent = join(dir, '..');
224
+ if (parent === dir)
225
+ break;
226
+ dir = parent;
227
+ }
228
+ return undefined;
229
+ }
230
+ /**
231
+ * Number of `:param` segments in a route path — used to register static
232
+ * paths before parameterized siblings that would otherwise shadow them.
233
+ * @param path - The route path (e.g. '/profile/:id')
234
+ */
235
+ function paramCount(path) {
236
+ return path.split('/').filter((segment) => segment.startsWith(':')).length;
237
+ }
238
+ /**
239
+ * Register an Express route for a fixture endpoint.
240
+ * @param app
241
+ * @param key
242
+ * @param fixture
243
+ * @param stateOverrides
244
+ * @param getDefault
245
+ */
246
+ function registerRoute(app, key, fixture, stateOverrides, getDefault) {
247
+ const { method, path } = fixture.endpoint;
248
+ // Prefix with /api if not already
249
+ const routePath = path.startsWith('/api') ? path : `/api${path}`;
250
+ // Convert :paramName to Express param syntax (already correct format)
251
+ // Build both key forms for state override lookup
252
+ const apiKey = key.replace(/ \//, ' /api/').replace(' /api/api/', ' /api/');
253
+ const bareKey = key.replace(/ \/api\//, ' /');
254
+ const handler = async (_req, res) => {
255
+ // Determine effective state: per-endpoint override > per-request > default
256
+ const endpointOverride = stateOverrides.get(key) ?? stateOverrides.get(apiKey) ?? stateOverrides.get(bareKey);
257
+ const requestState = res.locals.mockState;
258
+ let state;
259
+ if (endpointOverride) {
260
+ // Per-endpoint override wins, but inherit delay from request if not set
261
+ state = {
262
+ ...requestState,
263
+ ...endpointOverride,
264
+ };
265
+ }
266
+ else if (requestState) {
267
+ state = requestState;
268
+ }
269
+ else {
270
+ state = getDefault();
271
+ }
272
+ // Apply delay
273
+ await applyDelay(state);
274
+ // Get status code and body
275
+ const statusCode = getStatusCode(state, method);
276
+ const body = getResponseBody(state, method, fixture);
277
+ if (statusCode === 204 || body === null) {
278
+ res.status(statusCode).end();
279
+ return;
280
+ }
281
+ res.status(statusCode).json(body);
282
+ };
283
+ switch (method) {
284
+ case 'GET':
285
+ app.get(routePath, handler);
286
+ break;
287
+ case 'POST':
288
+ app.post(routePath, handler);
289
+ break;
290
+ case 'PUT':
291
+ app.put(routePath, handler);
292
+ break;
293
+ case 'PATCH':
294
+ app.patch(routePath, handler);
295
+ break;
296
+ case 'DELETE':
297
+ app.delete(routePath, handler);
298
+ break;
299
+ }
300
+ }
301
+ /**
302
+ * Start the Express server, with special handling for port 0 (random port).
303
+ * @param app
304
+ * @param port
305
+ */
306
+ function startServer(app, port) {
307
+ return new Promise((resolve, reject) => {
308
+ const server = app.listen(port, () => {
309
+ resolve(server);
310
+ });
311
+ server.on('error', reject);
312
+ });
313
+ }
314
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Response state management for the mock server.
3
+ * @module
4
+ */
5
+ export * from './states.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/states/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,aAAa,CAAA"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Response state management for the mock server.
3
+ * @module
4
+ */
5
+ export * from './states.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/states/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,aAAa,CAAA"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Configurable response states for the mock server.
3
+ * Provides standard response shapes for success, empty, error, and unauthorized states.
4
+ */
5
+ import type { HttpMethod, ResponseState } from '../types.js';
6
+ /** Default response states for each scenario */
7
+ export declare const DEFAULT_STATES: {
8
+ readonly success: {
9
+ readonly state: "success";
10
+ };
11
+ readonly empty: {
12
+ readonly state: "empty";
13
+ };
14
+ readonly error: {
15
+ readonly state: "error";
16
+ readonly statusCode: 500;
17
+ };
18
+ readonly unauthorized: {
19
+ readonly state: "unauthorized";
20
+ readonly statusCode: 401;
21
+ };
22
+ };
23
+ /**
24
+ * Get the HTTP status code for a given state and method.
25
+ * @param state - The response state
26
+ * @param method - The HTTP method
27
+ * @returns The appropriate HTTP status code
28
+ */
29
+ export declare function getStatusCode(state: ResponseState, method: HttpMethod): number;
30
+ /**
31
+ * Get the response body for a given state, using the endpoint fixture data.
32
+ * @param state - The response state
33
+ * @param method - The HTTP method
34
+ * @param fixture - The fixture data containing success, empty, and error responses
35
+ * @param fixture.successResponse
36
+ * @param fixture.emptyResponse
37
+ * @param fixture.errorResponse
38
+ * @param fixture.errorResponse.error
39
+ * @returns The response body, or null for 204 responses
40
+ */
41
+ export declare function getResponseBody(state: ResponseState, method: HttpMethod, fixture: {
42
+ successResponse: unknown;
43
+ emptyResponse: unknown;
44
+ errorResponse: {
45
+ error: string;
46
+ };
47
+ }): unknown;
48
+ /**
49
+ * Parse a state string into a ResponseState object.
50
+ * @param stateStr - The state string (e.g. 'success', 'error', 'empty', 'unauthorized')
51
+ * @returns The parsed ResponseState. An unrecognized string falls back to
52
+ * `DEFAULT_STATES.success` — the same forgiving behavior as the per-request
53
+ * `?_state` middleware (which additionally labels the response with an
54
+ * `X-Mock-Invalid-State` header so typos are detectable).
55
+ */
56
+ export declare function parseState(stateStr: string): ResponseState;
57
+ //# sourceMappingURL=states.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../src/states/states.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE5D,gDAAgD;AAChD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;CAKjB,CAAA;AAEV;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAuB9E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE;IACP,eAAe,EAAE,OAAO,CAAA;IACxB,aAAa,EAAE,OAAO,CAAA;IACtB,aAAa,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CACjC,GACA,OAAO,CAkBT;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAc1D"}