@mokup/runtime 1.0.2 → 1.0.3
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/dist/index.cjs +89 -86
- package/dist/index.d.cts +319 -0
- package/dist/index.d.mts +319 -0
- package/dist/index.d.ts +319 -0
- package/dist/index.mjs +89 -86
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { Context, Hono } from '@mokup/shared/hono';
|
|
2
2
|
export { Context, MiddlewareHandler, handle } from '@mokup/shared/hono';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Tokenized route segment for matching.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import type { RouteToken } from '@mokup/runtime'
|
|
9
|
+
*
|
|
10
|
+
* const token: RouteToken = { type: 'param', name: 'id' }
|
|
11
|
+
*/
|
|
4
12
|
type RouteToken = {
|
|
5
13
|
type: 'static';
|
|
6
14
|
value: string;
|
|
@@ -14,6 +22,20 @@ type RouteToken = {
|
|
|
14
22
|
type: 'optional-catchall';
|
|
15
23
|
name: string;
|
|
16
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Parsed route template with tokens and score.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* import type { ParsedRouteTemplate } from '@mokup/runtime'
|
|
30
|
+
*
|
|
31
|
+
* const parsed: ParsedRouteTemplate = {
|
|
32
|
+
* template: '/users/[id]',
|
|
33
|
+
* tokens: [{ type: 'param', name: 'id' }],
|
|
34
|
+
* score: [3],
|
|
35
|
+
* errors: [],
|
|
36
|
+
* warnings: [],
|
|
37
|
+
* }
|
|
38
|
+
*/
|
|
17
39
|
interface ParsedRouteTemplate {
|
|
18
40
|
template: string;
|
|
19
41
|
tokens: RouteToken[];
|
|
@@ -21,36 +43,196 @@ interface ParsedRouteTemplate {
|
|
|
21
43
|
errors: string[];
|
|
22
44
|
warnings: string[];
|
|
23
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Compute a score array for route tokens.
|
|
48
|
+
*
|
|
49
|
+
* @param tokens - Parsed route tokens.
|
|
50
|
+
* @returns A numeric score array used for sorting.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* import { scoreRouteTokens } from '@mokup/runtime'
|
|
54
|
+
*
|
|
55
|
+
* const score = scoreRouteTokens([{ type: 'static', value: 'users' }])
|
|
56
|
+
*/
|
|
24
57
|
declare function scoreRouteTokens(tokens: RouteToken[]): (4 | 3 | 2 | 1 | 0)[];
|
|
58
|
+
/**
|
|
59
|
+
* Compare two route scores (higher wins).
|
|
60
|
+
*
|
|
61
|
+
* @param a - Score array for route A.
|
|
62
|
+
* @param b - Score array for route B.
|
|
63
|
+
* @returns Negative when A is higher priority.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* import { compareRouteScore } from '@mokup/runtime'
|
|
67
|
+
*
|
|
68
|
+
* const order = compareRouteScore([4], [3])
|
|
69
|
+
*/
|
|
25
70
|
declare function compareRouteScore(a: number[], b: number[]): number;
|
|
71
|
+
/**
|
|
72
|
+
* Normalize a URL path by stripping query/hash and trailing slash.
|
|
73
|
+
*
|
|
74
|
+
* @param value - Raw path or URL.
|
|
75
|
+
* @returns Normalized pathname.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* import { normalizePathname } from '@mokup/runtime'
|
|
79
|
+
*
|
|
80
|
+
* const pathname = normalizePathname('/users/?q=1')
|
|
81
|
+
*/
|
|
26
82
|
declare function normalizePathname(value: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* Parse a route template into tokens, score, and diagnostics.
|
|
85
|
+
*
|
|
86
|
+
* @param template - Route template string.
|
|
87
|
+
* @returns Parsed template details.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* import { parseRouteTemplate } from '@mokup/runtime'
|
|
91
|
+
*
|
|
92
|
+
* const parsed = parseRouteTemplate('/users/[id]')
|
|
93
|
+
*/
|
|
27
94
|
declare function parseRouteTemplate(template: string): ParsedRouteTemplate;
|
|
95
|
+
/**
|
|
96
|
+
* Match route tokens against a pathname and extract params.
|
|
97
|
+
*
|
|
98
|
+
* @param tokens - Tokens for the template.
|
|
99
|
+
* @param pathname - Request pathname.
|
|
100
|
+
* @returns Params object or null if no match.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* import { matchRouteTokens } from '@mokup/runtime'
|
|
104
|
+
*
|
|
105
|
+
* const match = matchRouteTokens(
|
|
106
|
+
* [{ type: 'param', name: 'id' }],
|
|
107
|
+
* '/123',
|
|
108
|
+
* )
|
|
109
|
+
*/
|
|
28
110
|
declare function matchRouteTokens(tokens: RouteToken[], pathname: string): {
|
|
29
111
|
params: Record<string, string | string[]>;
|
|
30
112
|
} | null;
|
|
31
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Supported HTTP methods for mock routes.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* import type { HttpMethod } from '@mokup/runtime'
|
|
119
|
+
*
|
|
120
|
+
* const method: HttpMethod = 'GET'
|
|
121
|
+
*/
|
|
32
122
|
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
|
|
123
|
+
/**
|
|
124
|
+
* Serialized manifest describing all mock routes.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* import type { Manifest } from '@mokup/runtime'
|
|
128
|
+
*
|
|
129
|
+
* const manifest: Manifest = {
|
|
130
|
+
* version: 1,
|
|
131
|
+
* routes: [],
|
|
132
|
+
* }
|
|
133
|
+
*/
|
|
33
134
|
interface Manifest {
|
|
135
|
+
/**
|
|
136
|
+
* Manifest schema version.
|
|
137
|
+
*
|
|
138
|
+
* @default 1
|
|
139
|
+
*/
|
|
34
140
|
version: 1;
|
|
141
|
+
/**
|
|
142
|
+
* Route entries in this manifest.
|
|
143
|
+
*
|
|
144
|
+
* @default []
|
|
145
|
+
*/
|
|
35
146
|
routes: ManifestRoute[];
|
|
36
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* One route entry inside the manifest.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* import type { ManifestRoute } from '@mokup/runtime'
|
|
153
|
+
*
|
|
154
|
+
* const route: ManifestRoute = {
|
|
155
|
+
* method: 'GET',
|
|
156
|
+
* url: '/api/ping',
|
|
157
|
+
* response: { type: 'json', body: { ok: true } },
|
|
158
|
+
* }
|
|
159
|
+
*/
|
|
37
160
|
interface ManifestRoute {
|
|
161
|
+
/** HTTP method of the route. */
|
|
38
162
|
method: HttpMethod;
|
|
163
|
+
/** URL template for the route. */
|
|
39
164
|
url: string;
|
|
165
|
+
/** Pre-parsed route tokens (optional optimization). */
|
|
40
166
|
tokens?: RouteToken[];
|
|
167
|
+
/** Route score used for sorting and matching. */
|
|
41
168
|
score?: number[];
|
|
169
|
+
/** Source file path for the route handler. */
|
|
42
170
|
source?: string;
|
|
171
|
+
/**
|
|
172
|
+
* Override response status code.
|
|
173
|
+
*
|
|
174
|
+
* @default 200
|
|
175
|
+
*/
|
|
43
176
|
status?: number;
|
|
177
|
+
/**
|
|
178
|
+
* Additional headers to apply to the response.
|
|
179
|
+
*
|
|
180
|
+
* @default {}
|
|
181
|
+
*/
|
|
44
182
|
headers?: Record<string, string>;
|
|
183
|
+
/**
|
|
184
|
+
* Delay in milliseconds before responding.
|
|
185
|
+
*
|
|
186
|
+
* @default 0
|
|
187
|
+
*/
|
|
45
188
|
delay?: number;
|
|
189
|
+
/**
|
|
190
|
+
* Middleware module references to apply before the handler.
|
|
191
|
+
*
|
|
192
|
+
* @default []
|
|
193
|
+
*/
|
|
46
194
|
middleware?: ManifestModuleRef[];
|
|
195
|
+
/** Response definition for this route. */
|
|
47
196
|
response: ManifestResponse;
|
|
48
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Reference to a module export produced by mokup build.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* import type { ManifestModuleRef } from '@mokup/runtime'
|
|
203
|
+
*
|
|
204
|
+
* const ref: ManifestModuleRef = {
|
|
205
|
+
* module: './mokup-handlers/mock/ping.get.mjs',
|
|
206
|
+
* exportName: 'default',
|
|
207
|
+
* }
|
|
208
|
+
*/
|
|
49
209
|
interface ManifestModuleRef {
|
|
210
|
+
/** Module path (absolute or relative to moduleBase). */
|
|
50
211
|
module: string;
|
|
212
|
+
/**
|
|
213
|
+
* Named export to load from the module.
|
|
214
|
+
*
|
|
215
|
+
* @default "default"
|
|
216
|
+
*/
|
|
51
217
|
exportName?: string;
|
|
218
|
+
/**
|
|
219
|
+
* Optional rule index when multiple rules exist in one module.
|
|
220
|
+
*
|
|
221
|
+
* @default 0
|
|
222
|
+
*/
|
|
52
223
|
ruleIndex?: number;
|
|
53
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Response descriptor stored in the manifest.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* import type { ManifestResponse } from '@mokup/runtime'
|
|
230
|
+
*
|
|
231
|
+
* const response: ManifestResponse = {
|
|
232
|
+
* type: 'text',
|
|
233
|
+
* body: 'ok',
|
|
234
|
+
* }
|
|
235
|
+
*/
|
|
54
236
|
type ManifestResponse = {
|
|
55
237
|
type: 'json';
|
|
56
238
|
body: unknown;
|
|
@@ -64,33 +246,170 @@ type ManifestResponse = {
|
|
|
64
246
|
} | ({
|
|
65
247
|
type: 'module';
|
|
66
248
|
} & ManifestModuleRef);
|
|
249
|
+
/**
|
|
250
|
+
* Normalized request input for runtime execution.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* import type { RuntimeRequest } from '@mokup/runtime'
|
|
254
|
+
*
|
|
255
|
+
* const request: RuntimeRequest = {
|
|
256
|
+
* method: 'GET',
|
|
257
|
+
* path: '/api/ping',
|
|
258
|
+
* query: {},
|
|
259
|
+
* headers: {},
|
|
260
|
+
* body: null,
|
|
261
|
+
* }
|
|
262
|
+
*/
|
|
67
263
|
interface RuntimeRequest {
|
|
264
|
+
/** HTTP method. */
|
|
68
265
|
method: string;
|
|
266
|
+
/** Request path (no origin). */
|
|
69
267
|
path: string;
|
|
268
|
+
/** Parsed query parameters. */
|
|
70
269
|
query: Record<string, string | string[]>;
|
|
270
|
+
/** Normalized request headers. */
|
|
71
271
|
headers: Record<string, string>;
|
|
272
|
+
/** Parsed request body. */
|
|
72
273
|
body: unknown;
|
|
274
|
+
/** Raw body text (if available). */
|
|
73
275
|
rawBody?: string;
|
|
276
|
+
/** Path params captured during matching. */
|
|
74
277
|
params?: Record<string, string | string[]>;
|
|
75
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* Normalized runtime response output.
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* import type { RuntimeResult } from '@mokup/runtime'
|
|
284
|
+
*
|
|
285
|
+
* const result: RuntimeResult = {
|
|
286
|
+
* status: 200,
|
|
287
|
+
* headers: { 'content-type': 'application/json' },
|
|
288
|
+
* body: '{"ok":true}',
|
|
289
|
+
* }
|
|
290
|
+
*/
|
|
76
291
|
interface RuntimeResult {
|
|
292
|
+
/**
|
|
293
|
+
* HTTP status code.
|
|
294
|
+
*
|
|
295
|
+
* @default 200
|
|
296
|
+
*/
|
|
77
297
|
status: number;
|
|
298
|
+
/**
|
|
299
|
+
* Response headers.
|
|
300
|
+
*
|
|
301
|
+
* @default {}
|
|
302
|
+
*/
|
|
78
303
|
headers: Record<string, string>;
|
|
304
|
+
/** Response body as text or binary. */
|
|
79
305
|
body: string | Uint8Array | null;
|
|
80
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Static response values supported by route handlers.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* import type { RouteStaticResponse } from '@mokup/runtime'
|
|
312
|
+
*
|
|
313
|
+
* const value: RouteStaticResponse = { ok: true }
|
|
314
|
+
*/
|
|
81
315
|
type RouteStaticResponse = string | number | boolean | bigint | symbol | null | undefined | object;
|
|
316
|
+
/**
|
|
317
|
+
* Allowed return values from a route handler.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* import type { RouteHandlerResult } from '@mokup/runtime'
|
|
321
|
+
*
|
|
322
|
+
* const result: RouteHandlerResult = 'ok'
|
|
323
|
+
*/
|
|
82
324
|
type RouteHandlerResult = RouteStaticResponse | Response;
|
|
325
|
+
/**
|
|
326
|
+
* Function signature for request handlers.
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* import type { RequestHandler } from '@mokup/runtime'
|
|
330
|
+
*
|
|
331
|
+
* const handler: RequestHandler = (c) => {
|
|
332
|
+
* return { ok: true }
|
|
333
|
+
* }
|
|
334
|
+
*/
|
|
83
335
|
type RequestHandler = (context: Context) => RouteHandlerResult | Promise<RouteHandlerResult>;
|
|
336
|
+
/**
|
|
337
|
+
* Route response as a static value or handler function.
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* import type { RouteResponse } from '@mokup/runtime'
|
|
341
|
+
*
|
|
342
|
+
* const response: RouteResponse = (c) => c.text('ok')
|
|
343
|
+
*/
|
|
84
344
|
type RouteResponse = RouteStaticResponse | RequestHandler;
|
|
85
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Runtime configuration options.
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* import type { RuntimeOptions } from '@mokup/runtime'
|
|
351
|
+
*
|
|
352
|
+
* const options: RuntimeOptions = {
|
|
353
|
+
* manifest: { version: 1, routes: [] },
|
|
354
|
+
* }
|
|
355
|
+
*/
|
|
86
356
|
interface RuntimeOptions {
|
|
357
|
+
/**
|
|
358
|
+
* Manifest object or async loader.
|
|
359
|
+
*/
|
|
87
360
|
manifest: Manifest | (() => Promise<Manifest>);
|
|
361
|
+
/**
|
|
362
|
+
* Base directory for resolving module paths.
|
|
363
|
+
*
|
|
364
|
+
* @default undefined
|
|
365
|
+
*/
|
|
88
366
|
moduleBase?: string | URL;
|
|
367
|
+
/**
|
|
368
|
+
* Map of module exports for in-memory execution.
|
|
369
|
+
*
|
|
370
|
+
* @default undefined
|
|
371
|
+
*/
|
|
89
372
|
moduleMap?: ModuleMap;
|
|
90
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Map of module path to exported members.
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* import type { ModuleMap } from '@mokup/runtime'
|
|
379
|
+
*
|
|
380
|
+
* const moduleMap: ModuleMap = {
|
|
381
|
+
* './handlers/ping.mjs': { default: () => ({ ok: true }) },
|
|
382
|
+
* }
|
|
383
|
+
*/
|
|
91
384
|
type ModuleMap = Record<string, Record<string, unknown>>;
|
|
92
385
|
|
|
386
|
+
/**
|
|
387
|
+
* Build a Hono app from a manifest, loading module handlers as needed.
|
|
388
|
+
*
|
|
389
|
+
* @param options - Runtime options including the manifest.
|
|
390
|
+
* @returns A Hono app with routes registered.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* import { createRuntimeApp } from '@mokup/runtime'
|
|
394
|
+
*
|
|
395
|
+
* const app = await createRuntimeApp({
|
|
396
|
+
* manifest: { version: 1, routes: [] },
|
|
397
|
+
* })
|
|
398
|
+
*/
|
|
93
399
|
declare function createRuntimeApp(options: RuntimeOptions): Promise<Hono>;
|
|
400
|
+
/**
|
|
401
|
+
* Create a cached runtime handler for fetching and request simulation.
|
|
402
|
+
*
|
|
403
|
+
* @param options - Runtime options including the manifest.
|
|
404
|
+
* @returns Runtime helper with fetch and match helpers.
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* import { createRuntime } from '@mokup/runtime'
|
|
408
|
+
*
|
|
409
|
+
* const runtime = createRuntime({
|
|
410
|
+
* manifest: { version: 1, routes: [] },
|
|
411
|
+
* })
|
|
412
|
+
*/
|
|
94
413
|
declare function createRuntime(options: RuntimeOptions): {
|
|
95
414
|
handle: (req: RuntimeRequest) => Promise<RuntimeResult | null>;
|
|
96
415
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -190,6 +190,29 @@ function matchRouteTokens(tokens, pathname) {
|
|
|
190
190
|
return { params };
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
const methodSet = /* @__PURE__ */ new Set([
|
|
194
|
+
"GET",
|
|
195
|
+
"POST",
|
|
196
|
+
"PUT",
|
|
197
|
+
"PATCH",
|
|
198
|
+
"DELETE",
|
|
199
|
+
"OPTIONS",
|
|
200
|
+
"HEAD"
|
|
201
|
+
]);
|
|
202
|
+
function normalizeMethod(method) {
|
|
203
|
+
if (!method) {
|
|
204
|
+
return void 0;
|
|
205
|
+
}
|
|
206
|
+
const normalized = method.toUpperCase();
|
|
207
|
+
if (methodSet.has(normalized)) {
|
|
208
|
+
return normalized;
|
|
209
|
+
}
|
|
210
|
+
return void 0;
|
|
211
|
+
}
|
|
212
|
+
function delay(ms) {
|
|
213
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
214
|
+
}
|
|
215
|
+
|
|
193
216
|
function resolveModuleUrl(modulePath, moduleBase) {
|
|
194
217
|
if (/^(?:data|http|https|file):/.test(modulePath)) {
|
|
195
218
|
return modulePath;
|
|
@@ -325,29 +348,6 @@ async function loadModuleMiddleware(middleware, middlewareCache, moduleBase, mod
|
|
|
325
348
|
return handlers[0];
|
|
326
349
|
}
|
|
327
350
|
|
|
328
|
-
const methodSet = /* @__PURE__ */ new Set([
|
|
329
|
-
"GET",
|
|
330
|
-
"POST",
|
|
331
|
-
"PUT",
|
|
332
|
-
"PATCH",
|
|
333
|
-
"DELETE",
|
|
334
|
-
"OPTIONS",
|
|
335
|
-
"HEAD"
|
|
336
|
-
]);
|
|
337
|
-
function normalizeMethod(method) {
|
|
338
|
-
if (!method) {
|
|
339
|
-
return void 0;
|
|
340
|
-
}
|
|
341
|
-
const normalized = method.toUpperCase();
|
|
342
|
-
if (methodSet.has(normalized)) {
|
|
343
|
-
return normalized;
|
|
344
|
-
}
|
|
345
|
-
return void 0;
|
|
346
|
-
}
|
|
347
|
-
function delay(ms) {
|
|
348
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
349
|
-
}
|
|
350
|
-
|
|
351
351
|
function decodeBase64(value) {
|
|
352
352
|
if (typeof atob === "function") {
|
|
353
353
|
const binary = atob(value);
|
|
@@ -360,50 +360,6 @@ function decodeBase64(value) {
|
|
|
360
360
|
throw new Error("Base64 decoding is not supported in this runtime.");
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
-
function toHonoPath(tokens) {
|
|
364
|
-
if (!tokens || tokens.length === 0) {
|
|
365
|
-
return "/";
|
|
366
|
-
}
|
|
367
|
-
const segments = tokens.map((token) => {
|
|
368
|
-
if (token.type === "static") {
|
|
369
|
-
return token.value;
|
|
370
|
-
}
|
|
371
|
-
if (token.type === "param") {
|
|
372
|
-
return `:${token.name}`;
|
|
373
|
-
}
|
|
374
|
-
if (token.type === "catchall") {
|
|
375
|
-
return `:${token.name}{.+}`;
|
|
376
|
-
}
|
|
377
|
-
return `:${token.name}{.+}?`;
|
|
378
|
-
});
|
|
379
|
-
return `/${segments.join("/")}`;
|
|
380
|
-
}
|
|
381
|
-
function compileRoutes(manifest) {
|
|
382
|
-
const compiled = [];
|
|
383
|
-
for (const route of manifest.routes) {
|
|
384
|
-
const method = normalizeMethod(route.method) ?? "GET";
|
|
385
|
-
const parsed = route.tokens ? {
|
|
386
|
-
tokens: route.tokens,
|
|
387
|
-
score: route.score ?? scoreRouteTokens(route.tokens),
|
|
388
|
-
errors: []
|
|
389
|
-
} : parseRouteTemplate(route.url);
|
|
390
|
-
if (parsed.errors.length > 0) {
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
compiled.push({
|
|
394
|
-
route,
|
|
395
|
-
method,
|
|
396
|
-
tokens: route.tokens ?? parsed.tokens,
|
|
397
|
-
score: route.score ?? parsed.score
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
return compiled.sort((a, b) => {
|
|
401
|
-
if (a.method !== b.method) {
|
|
402
|
-
return a.method.localeCompare(b.method);
|
|
403
|
-
}
|
|
404
|
-
return compareRouteScore(a.score, b.score);
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
363
|
function shouldTreatAsText(contentType) {
|
|
408
364
|
const normalized = contentType.toLowerCase();
|
|
409
365
|
if (!normalized) {
|
|
@@ -485,6 +441,52 @@ function resolveResponse(value, fallback) {
|
|
|
485
441
|
}
|
|
486
442
|
return fallback;
|
|
487
443
|
}
|
|
444
|
+
|
|
445
|
+
function toHonoPath(tokens) {
|
|
446
|
+
if (!tokens || tokens.length === 0) {
|
|
447
|
+
return "/";
|
|
448
|
+
}
|
|
449
|
+
const segments = tokens.map((token) => {
|
|
450
|
+
if (token.type === "static") {
|
|
451
|
+
return token.value;
|
|
452
|
+
}
|
|
453
|
+
if (token.type === "param") {
|
|
454
|
+
return `:${token.name}`;
|
|
455
|
+
}
|
|
456
|
+
if (token.type === "catchall") {
|
|
457
|
+
return `:${token.name}{.+}`;
|
|
458
|
+
}
|
|
459
|
+
return `:${token.name}{.+}?`;
|
|
460
|
+
});
|
|
461
|
+
return `/${segments.join("/")}`;
|
|
462
|
+
}
|
|
463
|
+
function compileRoutes(manifest) {
|
|
464
|
+
const compiled = [];
|
|
465
|
+
for (const route of manifest.routes) {
|
|
466
|
+
const method = normalizeMethod(route.method) ?? "GET";
|
|
467
|
+
const parsed = route.tokens ? {
|
|
468
|
+
tokens: route.tokens,
|
|
469
|
+
score: route.score ?? scoreRouteTokens(route.tokens),
|
|
470
|
+
errors: []
|
|
471
|
+
} : parseRouteTemplate(route.url);
|
|
472
|
+
if (parsed.errors.length > 0) {
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
compiled.push({
|
|
476
|
+
route,
|
|
477
|
+
method,
|
|
478
|
+
tokens: route.tokens ?? parsed.tokens,
|
|
479
|
+
score: route.score ?? parsed.score
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
return compiled.sort((a, b) => {
|
|
483
|
+
if (a.method !== b.method) {
|
|
484
|
+
return a.method.localeCompare(b.method);
|
|
485
|
+
}
|
|
486
|
+
return compareRouteScore(a.score, b.score);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
|
|
488
490
|
function normalizeHandlerValue(c, value) {
|
|
489
491
|
if (value instanceof Response) {
|
|
490
492
|
return value;
|
|
@@ -551,7 +553,7 @@ function createFinalizeMiddleware(route) {
|
|
|
551
553
|
};
|
|
552
554
|
}
|
|
553
555
|
async function buildApp(params) {
|
|
554
|
-
const
|
|
556
|
+
const manifest = typeof params.manifest === "function" ? await params.manifest() : params.manifest;
|
|
555
557
|
const app = new Hono({ router: new PatternRouter(), strict: false });
|
|
556
558
|
const compiled = compileRoutes(manifest);
|
|
557
559
|
for (const entry of compiled) {
|
|
@@ -559,9 +561,9 @@ async function buildApp(params) {
|
|
|
559
561
|
for (const middleware of entry.route.middleware ?? []) {
|
|
560
562
|
const handler2 = await loadModuleMiddleware(
|
|
561
563
|
middleware,
|
|
562
|
-
middlewareCache,
|
|
563
|
-
moduleBase,
|
|
564
|
-
moduleMap
|
|
564
|
+
params.middlewareCache,
|
|
565
|
+
params.moduleBase,
|
|
566
|
+
params.moduleMap
|
|
565
567
|
);
|
|
566
568
|
if (handler2) {
|
|
567
569
|
middlewares.push(handler2);
|
|
@@ -569,9 +571,9 @@ async function buildApp(params) {
|
|
|
569
571
|
}
|
|
570
572
|
const handler = createRouteHandler({
|
|
571
573
|
route: entry.route,
|
|
572
|
-
moduleCache,
|
|
573
|
-
...typeof moduleBase !== "undefined" ? { moduleBase } : {},
|
|
574
|
-
...typeof moduleMap !== "undefined" ? { moduleMap } : {}
|
|
574
|
+
moduleCache: params.moduleCache,
|
|
575
|
+
...typeof params.moduleBase !== "undefined" ? { moduleBase: params.moduleBase } : {},
|
|
576
|
+
...typeof params.moduleMap !== "undefined" ? { moduleMap: params.moduleMap } : {}
|
|
575
577
|
});
|
|
576
578
|
app.on(
|
|
577
579
|
entry.method,
|
|
@@ -583,18 +585,7 @@ async function buildApp(params) {
|
|
|
583
585
|
}
|
|
584
586
|
return app;
|
|
585
587
|
}
|
|
586
|
-
|
|
587
|
-
const manifest = typeof options.manifest === "function" ? await options.manifest() : options.manifest;
|
|
588
|
-
const moduleCache = /* @__PURE__ */ new Map();
|
|
589
|
-
const middlewareCache = /* @__PURE__ */ new Map();
|
|
590
|
-
return await buildApp({
|
|
591
|
-
manifest,
|
|
592
|
-
moduleCache,
|
|
593
|
-
middlewareCache,
|
|
594
|
-
...typeof options.moduleBase !== "undefined" ? { moduleBase: options.moduleBase } : {},
|
|
595
|
-
...typeof options.moduleMap !== "undefined" ? { moduleMap: options.moduleMap } : {}
|
|
596
|
-
});
|
|
597
|
-
}
|
|
588
|
+
|
|
598
589
|
function appendQueryParams(url, query) {
|
|
599
590
|
for (const [key, value] of Object.entries(query)) {
|
|
600
591
|
if (Array.isArray(value)) {
|
|
@@ -670,6 +661,18 @@ function routeNeedsModuleBase(route, moduleMap) {
|
|
|
670
661
|
}
|
|
671
662
|
return false;
|
|
672
663
|
}
|
|
664
|
+
|
|
665
|
+
async function createRuntimeApp(options) {
|
|
666
|
+
const moduleCache = /* @__PURE__ */ new Map();
|
|
667
|
+
const middlewareCache = /* @__PURE__ */ new Map();
|
|
668
|
+
return await buildApp({
|
|
669
|
+
manifest: options.manifest,
|
|
670
|
+
moduleCache,
|
|
671
|
+
middlewareCache,
|
|
672
|
+
...typeof options.moduleBase !== "undefined" ? { moduleBase: options.moduleBase } : {},
|
|
673
|
+
...typeof options.moduleMap !== "undefined" ? { moduleMap: options.moduleMap } : {}
|
|
674
|
+
});
|
|
675
|
+
}
|
|
673
676
|
function createRuntime(options) {
|
|
674
677
|
let manifestCache = null;
|
|
675
678
|
let appPromise = null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mokup/runtime",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"description": "Cross-runtime mock matching and response handling for mokup.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://mokup.icebreaker.top",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"dist"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@mokup/shared": "1.0.
|
|
30
|
+
"@mokup/shared": "1.0.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"typescript": "^5.9.3",
|