@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,183 @@
1
+ /**
2
+ * Walks ZodType._def trees to produce conformant fixture data.
3
+ * Handles the Zod 4 type system for deterministic data generation.
4
+ */
5
+ import { createSeededRandom, FIXTURE_NOW, recentDate, seededUUID, seedFromPath } from './seed.js';
6
+ import { applySemanticRules } from './semantic-generator.js';
7
+ /**
8
+ * Walk a serialized Zod schema definition and produce conformant data.
9
+ * @param schema - The serialized schema definition
10
+ * @param fieldName - The field name (for semantic heuristics)
11
+ * @param rng - The seeded random function
12
+ * @param index - The item index for list generation
13
+ * @returns A conformant value matching the schema
14
+ */
15
+ export function walkSchema(schema, fieldName, rng, index) {
16
+ switch (schema.type) {
17
+ case 'ZodObject':
18
+ return walkObject(schema, rng, index);
19
+ case 'ZodArray':
20
+ return walkArray(schema, fieldName, rng, index);
21
+ case 'ZodString':
22
+ return walkString(schema, fieldName, rng, index);
23
+ case 'ZodNumber':
24
+ return walkNumber(schema, fieldName, rng, index);
25
+ case 'ZodBoolean':
26
+ return rng() > 0.5;
27
+ case 'ZodEnum':
28
+ if (schema.enumValues && schema.enumValues.length > 0) {
29
+ return schema.enumValues[Math.floor(rng() * schema.enumValues.length)];
30
+ }
31
+ return 'unknown';
32
+ case 'ZodOptional':
33
+ case 'ZodNullable':
34
+ // Always generate the inner value (we want populated data for screenshots)
35
+ if (schema.innerType) {
36
+ return walkSchema(schema.innerType, fieldName, rng, index);
37
+ }
38
+ return null;
39
+ case 'ZodDefault':
40
+ // Use the default value
41
+ if (schema.defaultValue !== undefined) {
42
+ return schema.defaultValue;
43
+ }
44
+ if (schema.innerType) {
45
+ return walkSchema(schema.innerType, fieldName, rng, index);
46
+ }
47
+ return null;
48
+ case 'ZodUnion':
49
+ // Pick the first option
50
+ if (schema.innerType) {
51
+ return walkSchema(schema.innerType, fieldName, rng, index);
52
+ }
53
+ return null;
54
+ case 'ZodLiteral':
55
+ return schema.defaultValue;
56
+ case 'ZodUnknown':
57
+ case 'ZodAny':
58
+ return null;
59
+ default:
60
+ // Fallback: try semantic rules
61
+ return applySemanticRules(fieldName, rng, index) ?? `mock-${fieldName}`;
62
+ }
63
+ }
64
+ /**
65
+ * Walk a ZodObject schema to produce a conformant object.
66
+ * @param schema
67
+ * @param rng
68
+ * @param index
69
+ */
70
+ function walkObject(schema, rng, index) {
71
+ const result = {};
72
+ if (schema.shape) {
73
+ for (const [key, fieldSchema] of Object.entries(schema.shape)) {
74
+ result[key] = walkSchema(fieldSchema, key, rng, index);
75
+ }
76
+ }
77
+ return result;
78
+ }
79
+ /**
80
+ * Walk a ZodArray schema to produce a conformant array.
81
+ * @param schema
82
+ * @param fieldName
83
+ * @param rng
84
+ * @param index
85
+ */
86
+ function walkArray(schema, fieldName, rng, index) {
87
+ const count = schema.constraints?.min ?? 2;
88
+ const elementType = schema.elementType ?? { type: 'ZodString' };
89
+ const result = [];
90
+ for (let i = 0; i < count; i++) {
91
+ result.push(walkSchema(elementType, fieldName, rng, index + i));
92
+ }
93
+ return result;
94
+ }
95
+ /**
96
+ * Walk a ZodString schema, applying semantic rules for realistic values.
97
+ * @param _schema
98
+ * @param fieldName
99
+ * @param rng
100
+ * @param index
101
+ */
102
+ function walkString(_schema, fieldName, rng, index) {
103
+ // Try semantic rule first
104
+ const semantic = applySemanticRules(fieldName, rng, index);
105
+ if (typeof semantic === 'string') {
106
+ return semantic;
107
+ }
108
+ // Fallback to a generic string
109
+ return `mock-${fieldName}-${index}`;
110
+ }
111
+ /**
112
+ * Walk a ZodNumber schema, applying semantic rules and constraints.
113
+ * @param schema
114
+ * @param fieldName
115
+ * @param rng
116
+ * @param index
117
+ */
118
+ function walkNumber(schema, fieldName, rng, index) {
119
+ // Try semantic rule first
120
+ const semantic = applySemanticRules(fieldName, rng, index);
121
+ if (typeof semantic === 'number') {
122
+ return semantic;
123
+ }
124
+ const c = schema.constraints;
125
+ let min = c?.min ?? 0;
126
+ const max = c?.max ?? 1000;
127
+ if (c?.positive && min <= 0) {
128
+ min = 1;
129
+ }
130
+ let value = rng() * (max - min) + min;
131
+ if (c?.int) {
132
+ value = Math.floor(value);
133
+ }
134
+ else {
135
+ value = Math.round(value * 100) / 100;
136
+ }
137
+ return value;
138
+ }
139
+ /**
140
+ * Generate a single conformant record from a schema, enriched with
141
+ * standard fields (id, created_at, updated_at).
142
+ * @param schema - The serialized Zod schema definition
143
+ * @param rng - The seeded random function
144
+ * @param index - The item index
145
+ * @returns A record with all schema fields plus standard fields
146
+ */
147
+ export function generateRecord(schema, rng, index) {
148
+ const base = {
149
+ id: seededUUID(rng),
150
+ // Anchored to FIXTURE_NOW (not the wall clock) so generated records are
151
+ // byte-stable across runs — the module's determinism promise held for
152
+ // seed.ts dates but this function used Date.now(), so timestamps (and any
153
+ // screenshot rendering them) changed on every run. created_at is always
154
+ // <= updated_at.
155
+ created_at: recentDate(rng),
156
+ updated_at: FIXTURE_NOW.toISOString(),
157
+ };
158
+ if (schema && schema.type === 'ZodObject' && schema.shape) {
159
+ for (const [key, fieldSchema] of Object.entries(schema.shape)) {
160
+ base[key] = walkSchema(fieldSchema, key, rng, index);
161
+ }
162
+ }
163
+ return base;
164
+ }
165
+ /**
166
+ * Generate multiple records from a schema definition.
167
+ * @param schema - The serialized Zod schema definition
168
+ * @param count - Number of records to generate
169
+ * @param appType - App type for seed derivation
170
+ * @param path - Endpoint path for seed derivation
171
+ * @param config - Optional fixture configuration
172
+ * @returns An array of conformant records
173
+ */
174
+ export function generateRecords(schema, count, appType, path, config) {
175
+ const seed = config?.seed ?? seedFromPath(appType, path);
176
+ const rng = createSeededRandom(seed);
177
+ const records = [];
178
+ for (let i = 0; i < count; i++) {
179
+ records.push(generateRecord(schema, rng, i));
180
+ }
181
+ return records;
182
+ }
183
+ //# sourceMappingURL=zod-walker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-walker.js","sourceRoot":"","sources":["../../src/fixtures/zod-walker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Mock API server with deterministic fixture data for testing, screenshots, and E2E.
3
+ *
4
+ * Provides a lightweight Express server that serves realistic fixture responses
5
+ * for any molecule app type. Supports per-request state control via query params
6
+ * and headers for testing success, empty, error, and unauthorized states.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { createMockServer } from '@molecule/api-mock-server'
11
+ *
12
+ * const server = await createMockServer({
13
+ * appType: 'personal-finance',
14
+ * fixturesPath: './api/fixtures', // directory of *.json fixture files
15
+ * port: 4000,
16
+ * })
17
+ *
18
+ * // Control state programmatically ('GET /accounts' and 'GET /api/accounts'
19
+ * // are equivalent keys)
20
+ * server.setState('GET /accounts', { state: 'error', statusCode: 500 })
21
+ * server.setState('GET /transactions', { state: 'empty' })
22
+ * server.setDefaultState('empty') // flips every endpoint at once
23
+ *
24
+ * // Undo an endpoint override so ?_state / the default control it again —
25
+ * // setState(key, { state: 'success' }) is NOT the same thing (see @remarks)
26
+ * server.clearState('GET /accounts')
27
+ *
28
+ * // Teardown
29
+ * await server.close()
30
+ * ```
31
+ *
32
+ * @remarks
33
+ * The server uses deterministic seeded PRNG for stable fixture data, making
34
+ * screenshot comparisons reliable. Fixture data comes from the JSON files in
35
+ * `fixturesPath` (array files become CRUD resources; `reports`/`storefront`/
36
+ * `admin` object files become sub-endpoint groups).
37
+ *
38
+ * Omitting `fixturesPath` makes the server resolve
39
+ * `mlcl/templates/apps/<appType>/api/fixtures/` by walking up from `process.cwd()`
40
+ * — that only works inside the molecule workspace. In a scaffolded project,
41
+ * always pass `fixturesPath`.
42
+ *
43
+ * Requests to `/api/*` paths with no matching fixture endpoint return an empty
44
+ * success (`200 []` for GET) so pages still render — the response carries an
45
+ * `X-Mock-Unmatched: true` header so a typo'd endpoint can be told apart from
46
+ * an endpoint that legitimately returned empty data. Similarly, an invalid
47
+ * `?_state`/`X-Mock-State` value is ignored (the default state is served) but
48
+ * labeled with an `X-Mock-Invalid-State` response header, so a typo'd state
49
+ * control is detectable instead of silently looking like "state applied".
50
+ *
51
+ * State precedence, per request, highest first: (1) an endpoint-level
52
+ * `server.setState(key, state)` override, (2) a per-request `?_state` query
53
+ * param / `X-Mock-State` header, (3) `server.setDefaultState(...)` / the
54
+ * configured `defaultState`. A `setState()` override is PERSISTENT — a
55
+ * forgotten override from an earlier test silently beats every later
56
+ * `?_state` on that same endpoint. Calling `setState(key, { state: 'success'
57
+ * })` again does NOT remove the override (it replaces it with one that looks
58
+ * like the default, still outranking `?_state`); call `server.clearState(key)`
59
+ * to actually remove it and hand control back to per-request/default state.
60
+ * `setDefaultState()` only changes the fallback and never clears endpoint
61
+ * overrides.
62
+ *
63
+ * Response delay (`defaultDelay`, `?_delay`/`X-Mock-Delay`, or a `delay` in
64
+ * `setState`/`setDefaultState`) is capped at `MAX_MOCK_DELAY_MS` (60s) — an
65
+ * oversized value (e.g. a units mistake applying `*1000` twice) is clamped
66
+ * and logged with `console.warn` instead of hanging the request until the
67
+ * client gives up, which in an E2E harness reads as an inexplicable page
68
+ * timeout rather than a mock misconfiguration.
69
+ *
70
+ * @module
71
+ */
72
+ export * from './browser-guard.js';
73
+ export * from './fixtures/index.js';
74
+ export * from './scanner/index.js';
75
+ export * from './server/index.js';
76
+ export * from './states/index.js';
77
+ export * from './types.js';
78
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Mock API server with deterministic fixture data for testing, screenshots, and E2E.
3
+ *
4
+ * Provides a lightweight Express server that serves realistic fixture responses
5
+ * for any molecule app type. Supports per-request state control via query params
6
+ * and headers for testing success, empty, error, and unauthorized states.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { createMockServer } from '@molecule/api-mock-server'
11
+ *
12
+ * const server = await createMockServer({
13
+ * appType: 'personal-finance',
14
+ * fixturesPath: './api/fixtures', // directory of *.json fixture files
15
+ * port: 4000,
16
+ * })
17
+ *
18
+ * // Control state programmatically ('GET /accounts' and 'GET /api/accounts'
19
+ * // are equivalent keys)
20
+ * server.setState('GET /accounts', { state: 'error', statusCode: 500 })
21
+ * server.setState('GET /transactions', { state: 'empty' })
22
+ * server.setDefaultState('empty') // flips every endpoint at once
23
+ *
24
+ * // Undo an endpoint override so ?_state / the default control it again —
25
+ * // setState(key, { state: 'success' }) is NOT the same thing (see @remarks)
26
+ * server.clearState('GET /accounts')
27
+ *
28
+ * // Teardown
29
+ * await server.close()
30
+ * ```
31
+ *
32
+ * @remarks
33
+ * The server uses deterministic seeded PRNG for stable fixture data, making
34
+ * screenshot comparisons reliable. Fixture data comes from the JSON files in
35
+ * `fixturesPath` (array files become CRUD resources; `reports`/`storefront`/
36
+ * `admin` object files become sub-endpoint groups).
37
+ *
38
+ * Omitting `fixturesPath` makes the server resolve
39
+ * `mlcl/templates/apps/<appType>/api/fixtures/` by walking up from `process.cwd()`
40
+ * — that only works inside the molecule workspace. In a scaffolded project,
41
+ * always pass `fixturesPath`.
42
+ *
43
+ * Requests to `/api/*` paths with no matching fixture endpoint return an empty
44
+ * success (`200 []` for GET) so pages still render — the response carries an
45
+ * `X-Mock-Unmatched: true` header so a typo'd endpoint can be told apart from
46
+ * an endpoint that legitimately returned empty data. Similarly, an invalid
47
+ * `?_state`/`X-Mock-State` value is ignored (the default state is served) but
48
+ * labeled with an `X-Mock-Invalid-State` response header, so a typo'd state
49
+ * control is detectable instead of silently looking like "state applied".
50
+ *
51
+ * State precedence, per request, highest first: (1) an endpoint-level
52
+ * `server.setState(key, state)` override, (2) a per-request `?_state` query
53
+ * param / `X-Mock-State` header, (3) `server.setDefaultState(...)` / the
54
+ * configured `defaultState`. A `setState()` override is PERSISTENT — a
55
+ * forgotten override from an earlier test silently beats every later
56
+ * `?_state` on that same endpoint. Calling `setState(key, { state: 'success'
57
+ * })` again does NOT remove the override (it replaces it with one that looks
58
+ * like the default, still outranking `?_state`); call `server.clearState(key)`
59
+ * to actually remove it and hand control back to per-request/default state.
60
+ * `setDefaultState()` only changes the fallback and never clears endpoint
61
+ * overrides.
62
+ *
63
+ * Response delay (`defaultDelay`, `?_delay`/`X-Mock-Delay`, or a `delay` in
64
+ * `setState`/`setDefaultState`) is capped at `MAX_MOCK_DELAY_MS` (60s) — an
65
+ * oversized value (e.g. a units mistake applying `*1000` twice) is clamped
66
+ * and logged with `console.warn` instead of hanging the request until the
67
+ * client gives up, which in an E2E harness reads as an inexplicable page
68
+ * timeout rather than a mock misconfiguration.
69
+ *
70
+ * @module
71
+ */
72
+ export * from './browser-guard.js';
73
+ export * from './fixtures/index.js';
74
+ export * from './scanner/index.js';
75
+ export * from './server/index.js';
76
+ export * from './states/index.js';
77
+ export * from './types.js';
78
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Handler scanner module for discovering endpoints from template files.
3
+ * @module
4
+ */
5
+ export * from './scanner.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,cAAc,CAAA"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Handler scanner module for discovering endpoints from template files.
3
+ * @module
4
+ */
5
+ export * from './scanner.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,cAAc,CAAA"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Static analysis scanner that reads handler files from mlcl templates
3
+ * and produces endpoint definitions. Uses regex-based AST parsing.
4
+ */
5
+ import type { HandlerScanResult } from '../types.js';
6
+ /**
7
+ * Scan all handler files for a given app type and produce endpoint definitions.
8
+ * @param handlersPath - Path to the handlers directory
9
+ * @param appType - The app type name
10
+ * @returns The scan result with all discovered endpoints
11
+ */
12
+ export declare function scanHandlers(handlersPath: string, appType: string): HandlerScanResult;
13
+ /**
14
+ * Resolve the handlers path for a given app type.
15
+ * Searches standard locations in the mlcl templates directory.
16
+ * @param appType - The app type name
17
+ * @param workspaceRoot - The workspace root directory
18
+ * @returns The resolved handlers path, or undefined if not found
19
+ */
20
+ export declare function resolveHandlersPath(appType: string, workspaceRoot?: string): string | undefined;
21
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/scanner/scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAEV,iBAAiB,EAIlB,MAAM,aAAa,CAAA;AAoMpB;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,iBAAiB,CA2GrF;AAgLD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAkB/F"}