@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.
- package/LICENSE +115 -0
- package/README.md +853 -0
- package/dist/browser-guard.d.ts +2 -0
- package/dist/browser-guard.d.ts.map +1 -0
- package/dist/browser-guard.js +19 -0
- package/dist/browser-guard.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +152 -0
- package/dist/cli.js.map +1 -0
- package/dist/fixtures/app-fixtures.d.ts +54 -0
- package/dist/fixtures/app-fixtures.d.ts.map +1 -0
- package/dist/fixtures/app-fixtures.js +601 -0
- package/dist/fixtures/app-fixtures.js.map +1 -0
- package/dist/fixtures/index.d.ts +9 -0
- package/dist/fixtures/index.d.ts.map +1 -0
- package/dist/fixtures/index.js +9 -0
- package/dist/fixtures/index.js.map +1 -0
- package/dist/fixtures/seed.d.ts +74 -0
- package/dist/fixtures/seed.d.ts.map +1 -0
- package/dist/fixtures/seed.js +112 -0
- package/dist/fixtures/seed.js.map +1 -0
- package/dist/fixtures/semantic-generator.d.ts +20 -0
- package/dist/fixtures/semantic-generator.d.ts.map +1 -0
- package/dist/fixtures/semantic-generator.js +539 -0
- package/dist/fixtures/semantic-generator.js.map +1 -0
- package/dist/fixtures/zod-walker.d.ts +34 -0
- package/dist/fixtures/zod-walker.d.ts.map +1 -0
- package/dist/fixtures/zod-walker.js +183 -0
- package/dist/fixtures/zod-walker.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/scanner/index.d.ts +6 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +6 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/scanner.d.ts +21 -0
- package/dist/scanner/scanner.d.ts.map +1 -0
- package/dist/scanner/scanner.js +463 -0
- package/dist/scanner/scanner.js.map +1 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +7 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/middleware.d.ts +51 -0
- package/dist/server/middleware.d.ts.map +1 -0
- package/dist/server/middleware.js +124 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/server.d.ts +29 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +314 -0
- package/dist/server/server.js.map +1 -0
- package/dist/states/index.d.ts +6 -0
- package/dist/states/index.d.ts.map +1 -0
- package/dist/states/index.js +6 -0
- package/dist/states/index.js.map +1 -0
- package/dist/states/states.d.ts +57 -0
- package/dist/states/states.d.ts.map +1 -0
- package/dist/states/states.js +89 -0
- package/dist/states/states.js.map +1 -0
- package/dist/types.d.ts +214 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- 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 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/states/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,aAAa,CAAA"}
|
|
@@ -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"}
|