@koa/router 15.0.0 → 15.1.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/dist/layer.mjs DELETED
@@ -1,436 +0,0 @@
1
- // src/layer.ts
2
- import { parse as parseUrl, format as formatUrl } from "url";
3
-
4
- // src/utils/path-to-regexp-wrapper.ts
5
- import { pathToRegexp, compile, parse } from "path-to-regexp";
6
- function compilePathToRegexp(path, options = {}) {
7
- const normalizedOptions = { ...options };
8
- if ("strict" in normalizedOptions && !("trailing" in normalizedOptions)) {
9
- normalizedOptions.trailing = normalizedOptions.strict !== true;
10
- delete normalizedOptions.strict;
11
- }
12
- delete normalizedOptions.pathAsRegExp;
13
- delete normalizedOptions.ignoreCaptures;
14
- delete normalizedOptions.prefix;
15
- const { regexp, keys } = pathToRegexp(path, normalizedOptions);
16
- return { regexp, keys };
17
- }
18
- function compilePath(path, options = {}) {
19
- return compile(path, options);
20
- }
21
- function parsePath(path, options) {
22
- return parse(path, options);
23
- }
24
- function normalizeLayerOptionsToPathToRegexp(options = {}) {
25
- const normalized = {
26
- sensitive: options.sensitive,
27
- end: options.end,
28
- strict: options.strict,
29
- trailing: options.trailing
30
- };
31
- if ("strict" in normalized && !("trailing" in normalized)) {
32
- normalized.trailing = normalized.strict !== true;
33
- delete normalized.strict;
34
- }
35
- for (const key of Object.keys(normalized)) {
36
- if (normalized[key] === void 0) {
37
- delete normalized[key];
38
- }
39
- }
40
- return normalized;
41
- }
42
-
43
- // src/layer.ts
44
- function safeDecodeURIComponent(text) {
45
- try {
46
- return decodeURIComponent(text);
47
- } catch {
48
- return text;
49
- }
50
- }
51
- var Layer = class {
52
- opts;
53
- name;
54
- methods;
55
- paramNames;
56
- stack;
57
- path;
58
- regexp;
59
- /**
60
- * Initialize a new routing Layer with given `method`, `path`, and `middleware`.
61
- *
62
- * @param path - Path string or regular expression
63
- * @param methods - Array of HTTP verbs
64
- * @param middleware - Layer callback/middleware or series of
65
- * @param opts - Layer options
66
- * @private
67
- */
68
- constructor(path, methods, middleware, options = {}) {
69
- this.opts = options;
70
- this.name = this.opts.name || void 0;
71
- this.methods = this._normalizeHttpMethods(methods);
72
- this.stack = this._normalizeAndValidateMiddleware(
73
- middleware,
74
- methods,
75
- path
76
- );
77
- this.path = path;
78
- this.paramNames = [];
79
- this._configurePathMatching();
80
- }
81
- /**
82
- * Normalize HTTP methods and add automatic HEAD support for GET
83
- * @private
84
- */
85
- _normalizeHttpMethods(methods) {
86
- const normalizedMethods = [];
87
- for (const method of methods) {
88
- const upperMethod = method.toUpperCase();
89
- normalizedMethods.push(upperMethod);
90
- if (upperMethod === "GET") {
91
- normalizedMethods.unshift("HEAD");
92
- }
93
- }
94
- return normalizedMethods;
95
- }
96
- /**
97
- * Normalize middleware to array and validate all are functions
98
- * @private
99
- */
100
- _normalizeAndValidateMiddleware(middleware, methods, path) {
101
- const middlewareArray = Array.isArray(middleware) ? middleware : [middleware];
102
- for (const middlewareFunction of middlewareArray) {
103
- const middlewareType = typeof middlewareFunction;
104
- if (middlewareType !== "function") {
105
- const routeIdentifier = this.opts.name || path;
106
- throw new Error(
107
- `${methods.toString()} \`${routeIdentifier}\`: \`middleware\` must be a function, not \`${middlewareType}\``
108
- );
109
- }
110
- }
111
- return middlewareArray;
112
- }
113
- /**
114
- * Configure path matching regexp and parameters
115
- * @private
116
- */
117
- _configurePathMatching() {
118
- if (this.opts.pathAsRegExp === true) {
119
- this.regexp = this.path instanceof RegExp ? this.path : new RegExp(this.path);
120
- } else if (this.path) {
121
- this._configurePathToRegexp();
122
- }
123
- }
124
- /**
125
- * Configure path-to-regexp for string paths
126
- * @private
127
- */
128
- _configurePathToRegexp() {
129
- const options = normalizeLayerOptionsToPathToRegexp(this.opts);
130
- const { regexp, keys } = compilePathToRegexp(this.path, options);
131
- this.regexp = regexp;
132
- this.paramNames = keys;
133
- }
134
- /**
135
- * Returns whether request `path` matches route.
136
- *
137
- * @param path - Request path
138
- * @returns Whether path matches
139
- * @private
140
- */
141
- match(path) {
142
- return this.regexp.test(path);
143
- }
144
- /**
145
- * Returns map of URL parameters for given `path` and `paramNames`.
146
- *
147
- * @param _path - Request path (not used, kept for API compatibility)
148
- * @param captures - Captured values from regexp
149
- * @param existingParams - Existing params to merge with
150
- * @returns Parameter map
151
- * @private
152
- */
153
- params(_path, captures, existingParameters = {}) {
154
- const parameterValues = { ...existingParameters };
155
- for (const [captureIndex, capturedValue] of captures.entries()) {
156
- const parameterDefinition = this.paramNames[captureIndex];
157
- if (parameterDefinition && capturedValue && capturedValue.length > 0) {
158
- const parameterName = parameterDefinition.name;
159
- parameterValues[parameterName] = safeDecodeURIComponent(capturedValue);
160
- }
161
- }
162
- return parameterValues;
163
- }
164
- /**
165
- * Returns array of regexp url path captures.
166
- *
167
- * @param path - Request path
168
- * @returns Array of captured values
169
- * @private
170
- */
171
- captures(path) {
172
- if (this.opts.ignoreCaptures) {
173
- return [];
174
- }
175
- const match = path.match(this.regexp);
176
- return match ? match.slice(1) : [];
177
- }
178
- /**
179
- * Generate URL for route using given `params`.
180
- *
181
- * @example
182
- *
183
- * ```javascript
184
- * const route = new Layer('/users/:id', ['GET'], fn);
185
- *
186
- * route.url({ id: 123 }); // => "/users/123"
187
- * ```
188
- *
189
- * @param args - URL parameters (various formats supported)
190
- * @returns Generated URL
191
- * @private
192
- */
193
- url(...arguments_) {
194
- const { params, options } = this._parseUrlArguments(arguments_);
195
- const cleanPath = this.path.replaceAll("(.*)", "");
196
- const pathCompiler = compilePath(cleanPath, {
197
- encode: encodeURIComponent,
198
- ...options
199
- });
200
- const parameterReplacements = this._buildParamReplacements(
201
- params,
202
- cleanPath
203
- );
204
- const generatedUrl = pathCompiler(parameterReplacements);
205
- if (options && options.query) {
206
- return this._addQueryString(generatedUrl, options.query);
207
- }
208
- return generatedUrl;
209
- }
210
- /**
211
- * Parse url() arguments into params and options
212
- * Supports multiple call signatures:
213
- * - url({ id: 1 })
214
- * - url(1, 2, 3)
215
- * - url({ query: {...} })
216
- * - url({ id: 1 }, { query: {...} })
217
- * @private
218
- */
219
- _parseUrlArguments(allArguments) {
220
- let parameters = allArguments[0];
221
- let options = allArguments[1];
222
- if (typeof parameters !== "object") {
223
- const argumentsList = [...allArguments];
224
- const lastArgument = argumentsList.at(-1);
225
- if (typeof lastArgument === "object") {
226
- options = lastArgument;
227
- parameters = argumentsList.slice(0, -1);
228
- } else {
229
- parameters = argumentsList;
230
- }
231
- } else if (parameters && parameters.query && !options) {
232
- options = parameters;
233
- parameters = {};
234
- }
235
- return { params: parameters, options };
236
- }
237
- /**
238
- * Build parameter replacements for URL generation
239
- * @private
240
- */
241
- _buildParamReplacements(parameters, cleanPath) {
242
- const { tokens } = parsePath(cleanPath);
243
- const hasNamedParameters = tokens.some(
244
- (token) => "name" in token && token.name
245
- );
246
- const parameterReplacements = {};
247
- if (Array.isArray(parameters)) {
248
- let parameterIndex = 0;
249
- for (const token of tokens) {
250
- if ("name" in token && token.name) {
251
- parameterReplacements[token.name] = String(
252
- parameters[parameterIndex++]
253
- );
254
- }
255
- }
256
- } else if (hasNamedParameters && typeof parameters === "object" && !parameters.query) {
257
- for (const [parameterName, parameterValue] of Object.entries(
258
- parameters
259
- )) {
260
- parameterReplacements[parameterName] = String(parameterValue);
261
- }
262
- }
263
- return parameterReplacements;
264
- }
265
- /**
266
- * Add query string to URL
267
- * @private
268
- */
269
- _addQueryString(baseUrl, query) {
270
- const parsedUrl = parseUrl(baseUrl);
271
- if (typeof query === "string") {
272
- parsedUrl.search = query;
273
- } else {
274
- parsedUrl.search = void 0;
275
- parsedUrl.query = query;
276
- }
277
- return formatUrl(parsedUrl);
278
- }
279
- /**
280
- * Run validations on route named parameters.
281
- *
282
- * @example
283
- *
284
- * ```javascript
285
- * router
286
- * .param('user', function (id, ctx, next) {
287
- * ctx.user = users[id];
288
- * if (!ctx.user) return ctx.status = 404;
289
- * next();
290
- * })
291
- * .get('/users/:user', function (ctx, next) {
292
- * ctx.body = ctx.user;
293
- * });
294
- * ```
295
- *
296
- * @param paramName - Parameter name
297
- * @param paramHandler - Middleware function
298
- * @returns This layer instance
299
- * @private
300
- */
301
- param(parameterName, parameterHandler) {
302
- const middlewareStack = this.stack;
303
- const routeParameterNames = this.paramNames;
304
- const parameterMiddleware = this._createParamMiddleware(
305
- parameterName,
306
- parameterHandler
307
- );
308
- const parameterNamesList = routeParameterNames.map(
309
- (parameterDefinition) => parameterDefinition.name
310
- );
311
- const parameterPosition = parameterNamesList.indexOf(parameterName);
312
- if (parameterPosition !== -1) {
313
- this._insertParamMiddleware(
314
- middlewareStack,
315
- parameterMiddleware,
316
- parameterNamesList,
317
- parameterPosition
318
- );
319
- }
320
- return this;
321
- }
322
- /**
323
- * Create param middleware with deduplication tracking
324
- * @private
325
- */
326
- _createParamMiddleware(parameterName, parameterHandler) {
327
- const middleware = function(context, next) {
328
- if (!context._matchedParams) {
329
- context._matchedParams = /* @__PURE__ */ new WeakMap();
330
- }
331
- if (context._matchedParams.has(parameterHandler)) {
332
- return next();
333
- }
334
- context._matchedParams.set(parameterHandler, true);
335
- return parameterHandler.call(
336
- this,
337
- context.params[parameterName],
338
- context,
339
- next
340
- );
341
- };
342
- middleware.param = parameterName;
343
- middleware._originalFn = parameterHandler;
344
- return middleware;
345
- }
346
- /**
347
- * Insert param middleware at the correct position in the stack
348
- * @private
349
- */
350
- _insertParamMiddleware(middlewareStack, parameterMiddleware, parameterNamesList, currentParameterPosition) {
351
- middlewareStack.some((existingMiddleware, stackIndex) => {
352
- if (!existingMiddleware.param) {
353
- middlewareStack.splice(stackIndex, 0, parameterMiddleware);
354
- return true;
355
- }
356
- const existingParameterPosition = parameterNamesList.indexOf(
357
- existingMiddleware.param
358
- );
359
- if (existingParameterPosition > currentParameterPosition) {
360
- middlewareStack.splice(stackIndex, 0, parameterMiddleware);
361
- return true;
362
- }
363
- return false;
364
- });
365
- }
366
- /**
367
- * Prefix route path.
368
- *
369
- * @param prefixPath - Prefix to prepend
370
- * @returns This layer instance
371
- * @private
372
- */
373
- setPrefix(prefixPath) {
374
- if (!this.path) {
375
- return this;
376
- }
377
- if (this.path instanceof RegExp) {
378
- return this;
379
- }
380
- this.path = this._applyPrefix(prefixPath);
381
- this._reconfigurePathMatching(prefixPath);
382
- return this;
383
- }
384
- /**
385
- * Apply prefix to the current path
386
- * @private
387
- */
388
- _applyPrefix(prefixPath) {
389
- const isRootPath = this.path === "/";
390
- const isStrictMode = this.opts.strict === true;
391
- const prefixHasParameters = prefixPath.includes(":");
392
- const pathIsRawRegex = this.opts.pathAsRegExp === true && typeof this.path === "string";
393
- if (prefixHasParameters && pathIsRawRegex) {
394
- const currentPath = this.path;
395
- if (currentPath === String.raw`(?:\/|$)` || currentPath === String.raw`(?:\/|$)`) {
396
- this.path = "{/*rest}";
397
- this.opts.pathAsRegExp = false;
398
- }
399
- }
400
- if (isRootPath && !isStrictMode) {
401
- return prefixPath;
402
- }
403
- return `${prefixPath}${this.path}`;
404
- }
405
- /**
406
- * Reconfigure path matching after prefix is applied
407
- * @private
408
- */
409
- _reconfigurePathMatching(prefixPath) {
410
- const treatAsRegExp = this.opts.pathAsRegExp === true;
411
- const prefixHasParameters = prefixPath && prefixPath.includes(":");
412
- if (prefixHasParameters && treatAsRegExp) {
413
- const options = normalizeLayerOptionsToPathToRegexp(this.opts);
414
- const { regexp, keys } = compilePathToRegexp(
415
- this.path,
416
- options
417
- );
418
- this.regexp = regexp;
419
- this.paramNames = keys;
420
- this.opts.pathAsRegExp = false;
421
- } else if (treatAsRegExp) {
422
- this.regexp = this.path instanceof RegExp ? this.path : new RegExp(this.path);
423
- } else {
424
- const options = normalizeLayerOptionsToPathToRegexp(this.opts);
425
- const { regexp, keys } = compilePathToRegexp(
426
- this.path,
427
- options
428
- );
429
- this.regexp = regexp;
430
- this.paramNames = keys;
431
- }
432
- }
433
- };
434
- export {
435
- Layer as default
436
- };
package/dist/router.d.mts DELETED
@@ -1,3 +0,0 @@
1
- import 'koa';
2
- export { R as default } from './layer.mjs';
3
- import 'path-to-regexp';
package/dist/router.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import 'koa';
2
- export { R as default } from './layer.js';
3
- import 'path-to-regexp';