@schmock/core 2.2.3 → 2.3.1
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 +21 -0
- package/README.md +35 -0
- package/dist/abort.d.ts +3 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +30 -0
- package/dist/binary.d.ts +8 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +9 -0
- package/dist/builder.d.ts +31 -10
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +804 -203
- package/dist/constants.d.ts +38 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +123 -2
- package/dist/errors.d.ts +14 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +41 -6
- package/dist/helpers.d.ts +9 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +18 -2
- package/dist/http-helpers.d.ts +52 -5
- package/dist/http-helpers.d.ts.map +1 -1
- package/dist/http-helpers.js +153 -37
- package/dist/index.d.ts +381 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/interceptor.d.ts +11 -1
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +354 -156
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +12 -3
- package/dist/plugin-pipeline.d.ts +11 -4
- package/dist/plugin-pipeline.d.ts.map +1 -1
- package/dist/plugin-pipeline.js +117 -39
- package/dist/response-normalizer.d.ts +16 -0
- package/dist/response-normalizer.d.ts.map +1 -0
- package/dist/response-normalizer.js +316 -0
- package/dist/response-parser.d.ts.map +1 -1
- package/dist/response-parser.js +85 -18
- package/dist/route-matcher.d.ts +3 -0
- package/dist/route-matcher.d.ts.map +1 -1
- package/dist/route-matcher.js +12 -6
- package/dist/types.d.ts +7 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +18 -8
- package/src/audit-core-builder.test.ts +0 -218
- package/src/audit-response-guard.test.ts +0 -38
- package/src/builder.test.ts +0 -289
- package/src/builder.ts +0 -701
- package/src/constants.test.ts +0 -99
- package/src/constants.ts +0 -73
- package/src/debug.test.ts +0 -241
- package/src/delay.test.ts +0 -319
- package/src/dist-shape.test.ts +0 -35
- package/src/errors.test.ts +0 -223
- package/src/errors.ts +0 -130
- package/src/factory.test.ts +0 -133
- package/src/helpers.test.ts +0 -147
- package/src/helpers.ts +0 -58
- package/src/http-helpers.ts +0 -113
- package/src/index.ts +0 -151
- package/src/interceptor.test.ts +0 -291
- package/src/interceptor.ts +0 -320
- package/src/namespace.test.ts +0 -274
- package/src/parser.property.test.ts +0 -594
- package/src/parser.test.ts +0 -148
- package/src/parser.ts +0 -64
- package/src/plugin-pipeline.ts +0 -103
- package/src/plugin-system.test.ts +0 -602
- package/src/response-parser.ts +0 -75
- package/src/response-parsing.test.ts +0 -333
- package/src/route-matcher.ts +0 -69
- package/src/route-matching.test.ts +0 -394
- package/src/server.test.ts +0 -165
- package/src/smart-defaults.test.ts +0 -361
- package/src/steps/async-support.steps.ts +0 -400
- package/src/steps/audit-core-builder.steps.ts +0 -188
- package/src/steps/audit-onerror-tuple.steps.ts +0 -99
- package/src/steps/basic-usage.steps.ts +0 -245
- package/src/steps/developer-experience.steps.ts +0 -204
- package/src/steps/error-handling.steps.ts +0 -345
- package/src/steps/fluent-api.steps.ts +0 -255
- package/src/steps/http-methods.steps.ts +0 -331
- package/src/steps/interceptor.steps.ts +0 -260
- package/src/steps/lifecycle-events.steps.ts +0 -142
- package/src/steps/performance-reliability.steps.ts +0 -423
- package/src/steps/plugin-integration.steps.ts +0 -280
- package/src/steps/request-history.steps.ts +0 -276
- package/src/steps/response-delay.steps.ts +0 -88
- package/src/steps/route-key-format.steps.ts +0 -99
- package/src/steps/standalone-server.steps.ts +0 -233
- package/src/steps/state-concurrency.steps.ts +0 -739
- package/src/steps/stateful-workflows.steps.ts +0 -353
- package/src/types.ts +0 -36
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { schmock } from "./index";
|
|
3
|
-
|
|
4
|
-
describe("response parsing", () => {
|
|
5
|
-
describe("tuple response formats", () => {
|
|
6
|
-
it("treats single-element array [status] as data, not tuple", async () => {
|
|
7
|
-
const mock = schmock();
|
|
8
|
-
mock("GET /status-only", () => [204] as [number]);
|
|
9
|
-
|
|
10
|
-
const response = await mock.handle("GET", "/status-only");
|
|
11
|
-
|
|
12
|
-
expect(response.status).toBe(200);
|
|
13
|
-
expect(response.body).toEqual([204]);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("handles [status, body] tuple", async () => {
|
|
17
|
-
const mock = schmock();
|
|
18
|
-
mock("POST /create", () => [201, { id: 123, created: true }]);
|
|
19
|
-
|
|
20
|
-
const response = await mock.handle("POST", "/create");
|
|
21
|
-
|
|
22
|
-
expect(response.status).toBe(201);
|
|
23
|
-
expect(response.body).toEqual({ id: 123, created: true });
|
|
24
|
-
expect(response.headers).toEqual({});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("handles [status, body, headers] tuple", async () => {
|
|
28
|
-
const mock = schmock();
|
|
29
|
-
mock(
|
|
30
|
-
"POST /upload",
|
|
31
|
-
() =>
|
|
32
|
-
[
|
|
33
|
-
201,
|
|
34
|
-
{ fileId: "abc123" },
|
|
35
|
-
{
|
|
36
|
-
Location: "/files/abc123",
|
|
37
|
-
"Content-Type": "application/json",
|
|
38
|
-
},
|
|
39
|
-
] as [number, any, Record<string, string>],
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const response = await mock.handle("POST", "/upload");
|
|
43
|
-
|
|
44
|
-
expect(response.status).toBe(201);
|
|
45
|
-
expect(response.body).toEqual({ fileId: "abc123" });
|
|
46
|
-
expect(response.headers).toEqual({
|
|
47
|
-
Location: "/files/abc123",
|
|
48
|
-
"Content-Type": "application/json",
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("handles empty headers object in tuple", async () => {
|
|
53
|
-
const mock = schmock();
|
|
54
|
-
mock("GET /test", () => [200, "OK", {}]);
|
|
55
|
-
|
|
56
|
-
const response = await mock.handle("GET", "/test");
|
|
57
|
-
|
|
58
|
-
expect(response.status).toBe(200);
|
|
59
|
-
expect(response.body).toBe("OK");
|
|
60
|
-
expect(response.headers).toEqual({});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("treats arrays with more than 3 elements as data, not tuple", async () => {
|
|
64
|
-
const mock = schmock();
|
|
65
|
-
mock(
|
|
66
|
-
"GET /extra",
|
|
67
|
-
() => [200, "data", {}, "ignored", "also-ignored"] as any,
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
const response = await mock.handle("GET", "/extra");
|
|
71
|
-
|
|
72
|
-
expect(response.status).toBe(200);
|
|
73
|
-
expect(response.body).toEqual([
|
|
74
|
-
200,
|
|
75
|
-
"data",
|
|
76
|
-
{},
|
|
77
|
-
"ignored",
|
|
78
|
-
"also-ignored",
|
|
79
|
-
]);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("treats non-numeric first element as body, not status", async () => {
|
|
83
|
-
const mock = schmock();
|
|
84
|
-
mock("GET /array-body", () => ["item1", "item2", "item3"]);
|
|
85
|
-
|
|
86
|
-
const response = await mock.handle("GET", "/array-body");
|
|
87
|
-
|
|
88
|
-
expect(response.status).toBe(200);
|
|
89
|
-
expect(response.body).toEqual(["item1", "item2", "item3"]);
|
|
90
|
-
expect(response.headers).toEqual({ "content-type": "application/json" });
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe("various response types", () => {
|
|
95
|
-
it("handles string responses", async () => {
|
|
96
|
-
const mock = schmock();
|
|
97
|
-
mock("GET /text", () => "Simple text response");
|
|
98
|
-
|
|
99
|
-
const response = await mock.handle("GET", "/text");
|
|
100
|
-
|
|
101
|
-
expect(response.status).toBe(200);
|
|
102
|
-
expect(response.body).toBe("Simple text response");
|
|
103
|
-
expect(response.headers).toEqual({ "content-type": "application/json" });
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("handles number responses", async () => {
|
|
107
|
-
const mock = schmock();
|
|
108
|
-
mock("GET /number", () => 42);
|
|
109
|
-
|
|
110
|
-
const response = await mock.handle("GET", "/number");
|
|
111
|
-
|
|
112
|
-
expect(response.status).toBe(200);
|
|
113
|
-
expect(response.body).toBe(42);
|
|
114
|
-
expect(response.headers).toEqual({ "content-type": "application/json" });
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it("handles boolean responses", async () => {
|
|
118
|
-
const mock = schmock();
|
|
119
|
-
mock("GET /bool", () => true);
|
|
120
|
-
|
|
121
|
-
const response = await mock.handle("GET", "/bool");
|
|
122
|
-
|
|
123
|
-
expect(response.status).toBe(200);
|
|
124
|
-
expect(response.body).toBe(true);
|
|
125
|
-
expect(response.headers).toEqual({ "content-type": "application/json" });
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("handles null responses", async () => {
|
|
129
|
-
const mock = schmock();
|
|
130
|
-
mock("GET /null", () => null);
|
|
131
|
-
|
|
132
|
-
const response = await mock.handle("GET", "/null");
|
|
133
|
-
|
|
134
|
-
expect(response.status).toBe(204);
|
|
135
|
-
expect(response.body).toBeUndefined();
|
|
136
|
-
expect(response.headers).toEqual({ "content-type": "application/json" });
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("handles undefined responses", async () => {
|
|
140
|
-
const mock = schmock();
|
|
141
|
-
mock("GET /undefined", () => undefined);
|
|
142
|
-
|
|
143
|
-
const response = await mock.handle("GET", "/undefined");
|
|
144
|
-
|
|
145
|
-
expect(response.status).toBe(204);
|
|
146
|
-
expect(response.body).toBeUndefined();
|
|
147
|
-
expect(response.headers).toEqual({ "content-type": "application/json" });
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("handles complex object responses", async () => {
|
|
151
|
-
const complexObject = {
|
|
152
|
-
data: {
|
|
153
|
-
users: [
|
|
154
|
-
{ id: 1, name: "Alice", tags: ["admin", "active"] },
|
|
155
|
-
{ id: 2, name: "Bob", meta: { lastLogin: "2023-01-01" } },
|
|
156
|
-
],
|
|
157
|
-
pagination: {
|
|
158
|
-
page: 1,
|
|
159
|
-
limit: 10,
|
|
160
|
-
total: 2,
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
timestamp: new Date("2023-01-01T00:00:00Z"),
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const mock = schmock();
|
|
167
|
-
mock("GET /complex", () => complexObject);
|
|
168
|
-
|
|
169
|
-
const response = await mock.handle("GET", "/complex");
|
|
170
|
-
|
|
171
|
-
expect(response.status).toBe(200);
|
|
172
|
-
expect(response.body).toEqual(complexObject);
|
|
173
|
-
expect(response.headers).toEqual({ "content-type": "application/json" });
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it("handles empty array responses", async () => {
|
|
177
|
-
const mock = schmock();
|
|
178
|
-
mock("GET /empty-array", () => []);
|
|
179
|
-
|
|
180
|
-
const response = await mock.handle("GET", "/empty-array");
|
|
181
|
-
|
|
182
|
-
expect(response.status).toBe(200);
|
|
183
|
-
expect(response.body).toEqual([]);
|
|
184
|
-
expect(response.headers).toEqual({ "content-type": "application/json" });
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("handles empty object responses", async () => {
|
|
188
|
-
const mock = schmock();
|
|
189
|
-
mock("GET /empty-object", () => ({}));
|
|
190
|
-
|
|
191
|
-
const response = await mock.handle("GET", "/empty-object");
|
|
192
|
-
|
|
193
|
-
expect(response.status).toBe(200);
|
|
194
|
-
expect(response.body).toEqual({});
|
|
195
|
-
expect(response.headers).toEqual({ "content-type": "application/json" });
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
describe("async response functions", () => {
|
|
200
|
-
it("handles async response functions", async () => {
|
|
201
|
-
const mock = schmock();
|
|
202
|
-
mock("GET /async", async () => {
|
|
203
|
-
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
204
|
-
return { async: true };
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
const response = await mock.handle("GET", "/async");
|
|
208
|
-
|
|
209
|
-
expect(response.status).toBe(200);
|
|
210
|
-
expect(response.body).toEqual({ async: true });
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("handles async tuple responses", async () => {
|
|
214
|
-
const mock = schmock();
|
|
215
|
-
mock("POST /async-create", async () => {
|
|
216
|
-
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
217
|
-
return [201, { created: true }, { "X-Async": "true" }];
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
const response = await mock.handle("POST", "/async-create");
|
|
221
|
-
|
|
222
|
-
expect(response.status).toBe(201);
|
|
223
|
-
expect(response.body).toEqual({ created: true });
|
|
224
|
-
expect(response.headers).toEqual({ "X-Async": "true" });
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
describe("status code boundaries", () => {
|
|
229
|
-
it("status code 100 (lower boundary) is accepted as tuple", async () => {
|
|
230
|
-
const mock = schmock();
|
|
231
|
-
mock("GET /continue", () => [100, "Continue"]);
|
|
232
|
-
|
|
233
|
-
const response = await mock.handle("GET", "/continue");
|
|
234
|
-
expect(response.status).toBe(100);
|
|
235
|
-
expect(response.body).toBe("Continue");
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
it("status code 599 (upper boundary) is accepted as tuple", async () => {
|
|
239
|
-
const mock = schmock();
|
|
240
|
-
mock("GET /custom", () => [599, { message: "custom status" }]);
|
|
241
|
-
|
|
242
|
-
const response = await mock.handle("GET", "/custom");
|
|
243
|
-
expect(response.status).toBe(599);
|
|
244
|
-
expect(response.body).toEqual({ message: "custom status" });
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it("float status like 200.5 is accepted as tuple (number check)", async () => {
|
|
248
|
-
const mock = schmock();
|
|
249
|
-
mock("GET /float", () => [200.5, { ok: true }]);
|
|
250
|
-
|
|
251
|
-
const response = await mock.handle("GET", "/float");
|
|
252
|
-
// isStatusTuple checks typeof === 'number' and 100 <= n <= 599
|
|
253
|
-
// 200.5 passes both checks, so it's treated as a tuple
|
|
254
|
-
expect(response.status).toBe(200.5);
|
|
255
|
-
expect(response.body).toEqual({ ok: true });
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it("array [201, null, undefined] — tuple with undefined headers defaults to empty", async () => {
|
|
259
|
-
const mock = schmock();
|
|
260
|
-
mock("GET /null-headers", () => [201, null, undefined] as any);
|
|
261
|
-
|
|
262
|
-
const response = await mock.handle("GET", "/null-headers");
|
|
263
|
-
// isStatusTuple: length 3, first element 201 (number, 100-599) -> true
|
|
264
|
-
// destructure: [status=201, body=null, headers=undefined]
|
|
265
|
-
// headers defaults to {} via `headers = {}`
|
|
266
|
-
expect(response.status).toBe(201);
|
|
267
|
-
expect(response.body).toBeUndefined(); // null body becomes undefined
|
|
268
|
-
expect(response.headers).toEqual({});
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it("non-tuple array [1, 2, 3, 4] with length > 3 treated as body", async () => {
|
|
272
|
-
const mock = schmock();
|
|
273
|
-
mock("GET /long-array", () => [1, 2, 3, 4] as any);
|
|
274
|
-
|
|
275
|
-
const response = await mock.handle("GET", "/long-array");
|
|
276
|
-
// isStatusTuple requires length 2 or 3, so length 4 => not a tuple => body
|
|
277
|
-
expect(response.status).toBe(200);
|
|
278
|
-
expect(response.body).toEqual([1, 2, 3, 4]);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it("status 99 (below 100) is NOT treated as a tuple", async () => {
|
|
282
|
-
const mock = schmock();
|
|
283
|
-
mock("GET /below-range", () => [99, { data: true }]);
|
|
284
|
-
|
|
285
|
-
const response = await mock.handle("GET", "/below-range");
|
|
286
|
-
// isStatusTuple requires value[0] >= 100, so 99 fails => treated as body
|
|
287
|
-
expect(response.status).toBe(200);
|
|
288
|
-
expect(response.body).toEqual([99, { data: true }]);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
it("status 600 (above 599) is NOT treated as a tuple", async () => {
|
|
292
|
-
const mock = schmock();
|
|
293
|
-
mock("GET /above-range", () => [600, { data: true }]);
|
|
294
|
-
|
|
295
|
-
const response = await mock.handle("GET", "/above-range");
|
|
296
|
-
// isStatusTuple requires value[0] <= 599, so 600 fails => treated as body
|
|
297
|
-
expect(response.status).toBe(200);
|
|
298
|
-
expect(response.body).toEqual([600, { data: true }]);
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
describe("edge cases", () => {
|
|
303
|
-
it("handles response with circular references gracefully", async () => {
|
|
304
|
-
const mock = schmock();
|
|
305
|
-
mock("GET /circular", () => {
|
|
306
|
-
const obj: any = { name: "test" };
|
|
307
|
-
obj.self = obj; // Create circular reference
|
|
308
|
-
return obj;
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
const response = await mock.handle("GET", "/circular");
|
|
312
|
-
|
|
313
|
-
expect(response.status).toBe(200);
|
|
314
|
-
expect(response.body).toHaveProperty("name", "test");
|
|
315
|
-
expect(response.body).toHaveProperty("self");
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it("preserves functions in response objects", async () => {
|
|
319
|
-
const mock = schmock();
|
|
320
|
-
mock("GET /with-functions", () => ({
|
|
321
|
-
data: "test",
|
|
322
|
-
fn: () => "function result",
|
|
323
|
-
}));
|
|
324
|
-
|
|
325
|
-
const response = await mock.handle("GET", "/with-functions");
|
|
326
|
-
|
|
327
|
-
expect(response.status).toBe(200);
|
|
328
|
-
expect(response.body.data).toBe("test");
|
|
329
|
-
expect(typeof response.body.fn).toBe("function");
|
|
330
|
-
expect(response.body.fn()).toBe("function result");
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
});
|
package/src/route-matcher.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { normalizePath } from "./constants.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Compiled callable route with pattern matching
|
|
5
|
-
*/
|
|
6
|
-
export interface CompiledCallableRoute {
|
|
7
|
-
pattern: RegExp;
|
|
8
|
-
params: string[];
|
|
9
|
-
method: Schmock.HttpMethod;
|
|
10
|
-
path: string;
|
|
11
|
-
generator: Schmock.Generator;
|
|
12
|
-
config: Schmock.RouteConfig;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function isGeneratorFunction(
|
|
16
|
-
gen: Schmock.Generator,
|
|
17
|
-
): gen is Schmock.GeneratorFunction {
|
|
18
|
-
return typeof gen === "function";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Find a route that matches the given method and path
|
|
23
|
-
* Uses two-pass matching: static routes first, then parameterized routes
|
|
24
|
-
* Matches routes in registration order (first registered wins)
|
|
25
|
-
*/
|
|
26
|
-
export function findRoute(
|
|
27
|
-
method: Schmock.HttpMethod,
|
|
28
|
-
path: string,
|
|
29
|
-
staticRoutes: Map<string, CompiledCallableRoute>,
|
|
30
|
-
routes: CompiledCallableRoute[],
|
|
31
|
-
): CompiledCallableRoute | undefined {
|
|
32
|
-
// O(1) lookup for static routes
|
|
33
|
-
const staticMatch = staticRoutes.get(`${method} ${normalizePath(path)}`);
|
|
34
|
-
if (staticMatch) {
|
|
35
|
-
return staticMatch;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Fall through to parameterized route scan
|
|
39
|
-
for (const route of routes) {
|
|
40
|
-
if (
|
|
41
|
-
route.method === method &&
|
|
42
|
-
route.params.length > 0 &&
|
|
43
|
-
route.pattern.test(path)
|
|
44
|
-
) {
|
|
45
|
-
return route;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Extract parameter values from path based on route pattern
|
|
54
|
-
* Maps capture groups from regex match to parameter names
|
|
55
|
-
*/
|
|
56
|
-
export function extractParams(
|
|
57
|
-
route: CompiledCallableRoute,
|
|
58
|
-
path: string,
|
|
59
|
-
): Record<string, string> {
|
|
60
|
-
const match = path.match(route.pattern);
|
|
61
|
-
if (!match) return {};
|
|
62
|
-
|
|
63
|
-
const params: Record<string, string> = {};
|
|
64
|
-
route.params.forEach((param, index) => {
|
|
65
|
-
params[param] = match[index + 1];
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
return params;
|
|
69
|
-
}
|