@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,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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"}
|
package/dist/index.d.ts
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.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 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,cAAc,CAAA"}
|
|
@@ -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"}
|