@antelopejs/dms-frontend 0.9.3 → 0.10.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.
Files changed (140) hide show
  1. package/README.md +46 -29
  2. package/dist/commands/build-action.d.ts +3 -0
  3. package/dist/commands/build-action.js +77 -8
  4. package/dist/commands/build.js +7 -0
  5. package/dist/commands/clean-action.js +1 -1
  6. package/dist/commands/clean.js +1 -1
  7. package/dist/commands/dev-action.js +4 -3
  8. package/dist/commands/dev.js +1 -1
  9. package/dist/commands/prepare-action.js +1 -1
  10. package/dist/commands/start-action.js +3 -24
  11. package/dist/commands/workspace-task.js +2 -1
  12. package/dist/config.js +1 -0
  13. package/dist/derived-outputs.d.ts +1 -0
  14. package/dist/derived-outputs.js +6 -4
  15. package/dist/discovery.js +17 -6
  16. package/dist/entry-weight.js +15 -0
  17. package/dist/fs-sync.js +7 -4
  18. package/dist/help.js +1 -1
  19. package/dist/index.js +2 -1
  20. package/dist/internal/built-workspace.d.ts +4 -0
  21. package/dist/internal/built-workspace.js +43 -0
  22. package/dist/internal/ensure.d.ts +1 -0
  23. package/dist/internal/ensure.js +8 -0
  24. package/dist/internal/locale-catalogs.d.ts +8 -3
  25. package/dist/internal/locale-catalogs.js +16 -6
  26. package/dist/internal/manifest-decoder.d.ts +7 -0
  27. package/dist/internal/manifest-decoder.js +81 -0
  28. package/dist/internal/own-release.d.ts +5 -0
  29. package/dist/internal/own-release.js +20 -0
  30. package/dist/internal/plain-object.d.ts +1 -0
  31. package/dist/internal/plain-object.js +6 -0
  32. package/dist/internal/prepared-workspace.d.ts +12 -0
  33. package/dist/internal/prepared-workspace.js +74 -0
  34. package/dist/internal/text-list.d.ts +1 -0
  35. package/dist/internal/text-list.js +10 -0
  36. package/dist/internal/vite-manifest.d.ts +6 -0
  37. package/dist/internal/vite-manifest.js +24 -0
  38. package/dist/layer-watch.js +25 -24
  39. package/dist/layers.d.ts +1 -1
  40. package/dist/layers.js +3 -1
  41. package/dist/locale-files.d.ts +6 -3
  42. package/dist/locale-files.js +15 -9
  43. package/dist/manifest.d.ts +1 -1
  44. package/dist/manifest.js +46 -32
  45. package/dist/materialize.js +6 -2
  46. package/dist/module-set.js +2 -1
  47. package/dist/node-options.d.ts +1 -0
  48. package/dist/node-options.js +8 -0
  49. package/dist/output.d.ts +15 -9
  50. package/dist/output.js +30 -18
  51. package/dist/ports.d.ts +3 -1
  52. package/dist/ports.js +1 -1
  53. package/dist/private-values.d.ts +1 -1
  54. package/dist/private-values.js +5 -3
  55. package/dist/project-modules.d.ts +5 -1
  56. package/dist/project-modules.js +23 -15
  57. package/dist/renderer-range.js +5 -3
  58. package/dist/server-process.js +1 -1
  59. package/dist/verify-source-build.js +10 -2
  60. package/dist/verify-source-client-bundle.d.ts +1 -0
  61. package/dist/verify-source-client-bundle.js +30 -0
  62. package/dist/verify-source-runner.js +36 -94
  63. package/dist/verify-source-sources.js +49 -11
  64. package/dist/verify-source-workspace.d.ts +2 -0
  65. package/dist/verify-source-workspace.js +111 -0
  66. package/dist/workspace-setup.d.ts +1 -0
  67. package/dist/workspace-setup.js +4 -3
  68. package/package.json +11 -7
  69. package/skills/dms-frontend/SKILL.md +1 -1
  70. package/templates/vue/app-runtime.ts +2 -9
  71. package/templates/vue/backend-boundary.mjs +132 -0
  72. package/templates/vue/client-chunking.mjs +2 -0
  73. package/templates/vue/client-server-guard.mjs +16 -0
  74. package/templates/vue/compress-assets.mjs +9 -1
  75. package/templates/vue/entry-weight.mjs +29 -3
  76. package/templates/vue/frontend-build.ts +4 -3
  77. package/templates/vue/frontend-module.ts +1 -0
  78. package/templates/vue/frontend-runtime/module-registration.ts +2 -2
  79. package/templates/vue/frontend-runtime/state.ts +2 -2
  80. package/templates/vue/frontend-runtime/types.ts +8 -2
  81. package/templates/vue/head-order.mjs +10 -2
  82. package/templates/vue/icon-api.mjs +2 -0
  83. package/templates/vue/icon-scan.mjs +4 -0
  84. package/templates/vue/server/auth/backend.mjs +42 -5
  85. package/templates/vue/server/auth/client-ip.mjs +52 -4
  86. package/templates/vue/server/auth/establish-endpoints.mjs +11 -7
  87. package/templates/vue/server/auth/session.mjs +86 -4
  88. package/templates/vue/server/auth-declarations.mjs +46 -0
  89. package/templates/vue/server/auth-hooks.mjs +55 -7
  90. package/templates/vue/server/backend-proxies.mjs +78 -18
  91. package/templates/vue/server/backend-request.mjs +61 -0
  92. package/templates/vue/server/backend-response.mjs +23 -6
  93. package/templates/vue/server/built-files.mjs +7 -0
  94. package/templates/vue/server/cli-report.mjs +75 -12
  95. package/templates/vue/server/client-error.mjs +5 -0
  96. package/templates/vue/server/client-manifest.mjs +23 -1
  97. package/templates/vue/server/content.mjs +40 -10
  98. package/templates/vue/server/dev-private-files.mjs +17 -1
  99. package/templates/vue/server/dev-styles.mjs +38 -0
  100. package/templates/vue/server/email-bundle-watch.mjs +9 -3
  101. package/templates/vue/server/email.mjs +14 -6
  102. package/templates/vue/server/favicon.mjs +3 -0
  103. package/templates/vue/server/generated-files.mjs +221 -0
  104. package/templates/vue/server/homepage.mjs +21 -9
  105. package/templates/vue/server/html-tag.mjs +4 -0
  106. package/templates/vue/server/icons.mjs +93 -16
  107. package/templates/vue/server/in-place-render.mjs +35 -0
  108. package/templates/vue/server/inertia.mjs +76 -3
  109. package/templates/vue/server/module-event.mjs +118 -25
  110. package/templates/vue/server/module-route-types.mjs +78 -0
  111. package/templates/vue/server/module-routes.mjs +151 -96
  112. package/templates/vue/server/module-server.mjs +121 -40
  113. package/templates/vue/server/module-testing.mjs +36 -12
  114. package/templates/vue/server/page-props.mjs +94 -3
  115. package/templates/vue/server/page-redirects.mjs +37 -0
  116. package/templates/vue/server/path-patterns.mjs +35 -12
  117. package/templates/vue/server/proxy-compression.mjs +22 -0
  118. package/templates/vue/server/proxy-headers.mjs +12 -2
  119. package/templates/vue/server/proxy-redirects.mjs +21 -5
  120. package/templates/vue/server/proxy-request.mjs +58 -0
  121. package/templates/vue/server/proxy-transfers.mjs +44 -10
  122. package/templates/vue/server/public-endpoints.mjs +139 -28
  123. package/templates/vue/server/public-options.mjs +20 -6
  124. package/templates/vue/server/request-failure.mjs +24 -1
  125. package/templates/vue/server/request-routing.mjs +32 -10
  126. package/templates/vue/server/request-target.mjs +30 -11
  127. package/templates/vue/server/route-rules.mjs +48 -18
  128. package/templates/vue/server/ssr-module.mjs +32 -0
  129. package/templates/vue/server/static-files.mjs +35 -7
  130. package/templates/vue/server/type-guards.mjs +71 -0
  131. package/templates/vue/server/vite-errors.mjs +45 -7
  132. package/templates/vue/server/web-body.mjs +5 -1
  133. package/templates/vue/server-module.ts +12 -2
  134. package/templates/vue/server.mjs +162 -157
  135. package/templates/vue/sfc-global-types.mjs +112 -13
  136. package/templates/vue/ssr-renderer.ts +2 -2
  137. package/templates/vue/ui-inertia-page.mjs +16 -1
  138. package/templates/vue/ui-kbd.mjs +2 -1
  139. package/templates/vue/ui-request-state.mjs +2 -1
  140. package/templates/vue/vite.config.ts +9 -0
@@ -1,7 +1,23 @@
1
+ import { resolveAuth } from "./auth-declarations.mjs";
1
2
  import { resolveBackendProxies } from "./backend-proxies.mjs";
2
3
  import { compilePathPattern } from "./path-patterns.mjs";
3
4
  import { matchPublicEndpoint } from "./public-endpoints.mjs";
4
5
  import { ruleRejection } from "./route-rules.mjs";
6
+ import {
7
+ isBoolean,
8
+ isFunction,
9
+ isNonNullObject,
10
+ isString,
11
+ } from "./type-guards.mjs";
12
+
13
+ /** @typedef {(message: string) => void} Warn */
14
+ /**
15
+ * @import {
16
+ * DeclaredModule, ModuleDeclared, RouteRegistration,
17
+ * ServerDeclarations, ServerMethod, ServerModuleDeclaration, ServerRoute
18
+ * } from "./module-route-types.mjs"
19
+ * @import { PublicEndpoint } from "./public-endpoints.mjs"
20
+ */
5
21
 
6
22
  const METHODS = new Set([
7
23
  "GET",
@@ -29,6 +45,8 @@ const MODULE_API_PREFIX = /^\/api\/_[A-Za-z0-9-]+(?:\/|$)/;
29
45
  * Whether `pathname` names one of the files the renderer serves: Vite's URLs,
30
46
  * the built assets and the dependencies. No server route is ever there, and a
31
47
  * development page requests hundreds of them.
48
+ *
49
+ * @param {string} pathname
32
50
  */
33
51
  export function isRendererFilePath(pathname) {
34
52
  return ["/@", "/assets/", "/node_modules/"].some((prefix) =>
@@ -36,20 +54,25 @@ export function isRendererFilePath(pathname) {
36
54
  );
37
55
  }
38
56
 
39
- /** Whether a module route may serve `pathname`: never the renderer's own. */
57
+ /**
58
+ * Whether a module route may serve `pathname`: never the renderer's own.
59
+ *
60
+ * @param {string} pathname
61
+ */
40
62
  export function mayHaveModuleRoute(pathname) {
41
63
  return !RESERVED_PREFIXES.some(
42
64
  (prefix) => pathname === prefix.slice(0, -1) || pathname.startsWith(prefix),
43
65
  );
44
66
  }
45
67
 
68
+ /** @param {string} path */
46
69
  function normalizedPath(path) {
47
70
  return path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
48
71
  }
49
72
 
73
+ /** @param {string} path */
50
74
  function pathRejection(path) {
51
- if (typeof path !== "string" || !path.startsWith("/"))
52
- return "a path starts with /";
75
+ if (!isString(path) || !path.startsWith("/")) return "a path starts with /";
53
76
  const normalized = normalizedPath(path);
54
77
  if (!mayHaveModuleRoute(normalized))
55
78
  return `${RESERVED_PREFIXES.join(", ")} are the renderer's own paths`;
@@ -71,14 +94,11 @@ function pathRejection(path) {
71
94
  return undefined;
72
95
  }
73
96
 
97
+ /** @param {RouteRegistration["options"]} options */
74
98
  function optionsRejection(options) {
75
99
  if (options === undefined) return undefined;
76
- if (typeof options !== "object" || options === null)
77
- return "its options are not an object";
78
- if (
79
- options.sameOrigin !== undefined &&
80
- typeof options.sameOrigin !== "boolean"
81
- )
100
+ if (!isNonNullObject(options)) return "its options are not an object";
101
+ if (options.sameOrigin !== undefined && !isBoolean(options.sameOrigin))
82
102
  return "sameOrigin is not a boolean";
83
103
  if (
84
104
  options.bodyLimit !== undefined &&
@@ -90,11 +110,14 @@ function optionsRejection(options) {
90
110
 
91
111
  /**
92
112
  * Two declarations of one path whatever their parameters are named share a
93
- * shape.
113
+ * path identity.
114
+ *
115
+ * @param {string} path
94
116
  */
95
117
  function compilePath(path) {
96
118
  const normalized = normalizedPath(path);
97
119
  const segments = normalized === "/" ? [] : normalized.slice(1).split("/");
120
+ /** @type {string[]} */
98
121
  const params = [];
99
122
  const pattern = segments
100
123
  .map((segment) => {
@@ -106,7 +129,7 @@ function compilePath(path) {
106
129
  .join("");
107
130
  return {
108
131
  path: normalized,
109
- shape: segments
132
+ pathIdentity: segments
110
133
  .map((segment) => (PARAM_SEGMENT.test(segment) ? ":" : segment))
111
134
  .join("/"),
112
135
  params,
@@ -114,8 +137,14 @@ function compilePath(path) {
114
137
  };
115
138
  }
116
139
 
117
- /** What a module's setup declares, by kind, in declaration order. */
140
+ /**
141
+ * What a module's setup declares, by kind, in declaration order.
142
+ *
143
+ * @param {ServerModuleDeclaration["setup"]} setup
144
+ * @returns {ModuleDeclared}
145
+ */
118
146
  function collectDeclarations(setup) {
147
+ /** @type {ModuleDeclared} */
119
148
  const declared = { routes: [], auth: [], rules: [], proxies: [] };
120
149
  setup({
121
150
  registerRoute: (method, path, handler, options) =>
@@ -128,27 +157,43 @@ function collectDeclarations(setup) {
128
157
  return declared;
129
158
  }
130
159
 
160
+ /**
161
+ * @param {unknown} name
162
+ * @returns {name is ServerMethod}
163
+ */
164
+ function isMethod(name) {
165
+ return isString(name) && METHODS.has(name);
166
+ }
167
+
168
+ /** @param {unknown} method */
131
169
  function methodsOf(method) {
170
+ /** @type {readonly unknown[]} */
132
171
  const methods = Array.isArray(method) ? method : [method];
133
- return methods.length && methods.every((name) => METHODS.has(name))
134
- ? methods
135
- : undefined;
172
+ return methods.length && methods.every(isMethod) ? methods : undefined;
136
173
  }
137
174
 
175
+ /** @param {RouteRegistration} registration */
138
176
  function registrationRejection(registration) {
139
177
  if (!methodsOf(registration.method))
140
178
  return `the method is not one of ${[...METHODS].join(", ")}`;
141
- if (typeof registration.handler !== "function")
142
- return "its handler is not a function";
179
+ if (!isFunction(registration.handler)) return "its handler is not a function";
143
180
  return (
144
181
  pathRejection(registration.path) ?? optionsRejection(registration.options)
145
182
  );
146
183
  }
147
184
 
185
+ /**
186
+ * @param {string | readonly string[]} method
187
+ * @param {string} path
188
+ */
148
189
  function describeRoute(method, path) {
149
190
  return `"${[method].flat().join(", ")} ${path}"`;
150
191
  }
151
192
 
193
+ /**
194
+ * @param {string} id
195
+ * @param {ServerRoute} route
196
+ */
152
197
  function backendShadowWarning(id, route) {
153
198
  if (
154
199
  !BACKEND_PREFIX.test(route.path) ||
@@ -163,6 +208,11 @@ function backendShadowWarning(id, route) {
163
208
  );
164
209
  }
165
210
 
211
+ /**
212
+ * @param {string} id
213
+ * @param {ServerRoute} route
214
+ * @param {PublicEndpoint[]} publicEndpoints
215
+ */
166
216
  function publicEndpointWarning(id, route, publicEndpoints) {
167
217
  const sample = route.path.replaceAll(/\/:[^/]+/g, "/x");
168
218
  if (!matchPublicEndpoint(route.method, sample, publicEndpoints))
@@ -173,7 +223,12 @@ function publicEndpointWarning(id, route, publicEndpoints) {
173
223
  );
174
224
  }
175
225
 
176
- /** Runs a module's setup; a setup that throws declares nothing. */
226
+ /**
227
+ * Runs a module's setup; a setup that throws declares nothing.
228
+ *
229
+ * @param {ServerModuleDeclaration} declaration
230
+ * @param {Warn} warn
231
+ */
177
232
  function declarationsOfModule(declaration, warn) {
178
233
  try {
179
234
  return collectDeclarations(declaration.setup);
@@ -185,18 +240,24 @@ function declarationsOfModule(declaration, warn) {
185
240
  }
186
241
  }
187
242
 
243
+ /**
244
+ * @param {DeclaredModule} module
245
+ * @param {Warn} warn
246
+ * @returns {ServerRoute[]}
247
+ */
188
248
  function routesOfModule(module, warn) {
189
249
  const { id, priority = 0 } = module;
190
250
  return module.declared.routes.flatMap((registration, order) => {
251
+ const methods = methodsOf(registration.method);
191
252
  const reason = registrationRejection(registration);
192
- if (reason) {
253
+ if (reason || !methods) {
193
254
  warn(
194
255
  `Frontend module ${id} declares route ${describeRoute(registration.method, registration.path)}, which is ignored: ${reason}.`,
195
256
  );
196
257
  return [];
197
258
  }
198
259
  const compiled = compilePath(registration.path);
199
- return methodsOf(registration.method).map((method) => ({
260
+ return methods.map((method) => ({
200
261
  ...compiled,
201
262
  id,
202
263
  priority,
@@ -210,6 +271,10 @@ function routesOfModule(module, warn) {
210
271
  });
211
272
  }
212
273
 
274
+ /**
275
+ * @param {{ id: string, priority?: number }} left
276
+ * @param {{ id: string, priority?: number }} right
277
+ */
213
278
  function byPriority(left, right) {
214
279
  return (
215
280
  (right.priority ?? 0) - (left.priority ?? 0) ||
@@ -217,6 +282,10 @@ function byPriority(left, right) {
217
282
  );
218
283
  }
219
284
 
285
+ /**
286
+ * @param {ServerRoute} kept
287
+ * @param {ServerRoute} shadowed
288
+ */
220
289
  function shadowWarning(kept, shadowed) {
221
290
  const settled =
222
291
  kept.priority === shadowed.priority
@@ -228,6 +297,7 @@ function shadowWarning(kept, shadowed) {
228
297
  );
229
298
  }
230
299
 
300
+ /** @param {ServerRoute} route */
231
301
  function parameterCount(route) {
232
302
  return route.params.length;
233
303
  }
@@ -235,10 +305,15 @@ function parameterCount(route) {
235
305
  /**
236
306
  * Whether one request path can match both routes: as many segments, and at
237
307
  * each position the same literal or a parameter on either side.
308
+ *
309
+ * @param {ServerRoute} left
310
+ * @param {ServerRoute} right
238
311
  */
239
312
  function routesOverlap(left, right) {
240
- const leftSegments = left.shape === "" ? [] : left.shape.split("/");
241
- const rightSegments = right.shape === "" ? [] : right.shape.split("/");
313
+ const leftSegments =
314
+ left.pathIdentity === "" ? [] : left.pathIdentity.split("/");
315
+ const rightSegments =
316
+ right.pathIdentity === "" ? [] : right.pathIdentity.split("/");
242
317
  return (
243
318
  leftSegments.length === rightSegments.length &&
244
319
  leftSegments.every(
@@ -250,7 +325,12 @@ function routesOverlap(left, right) {
250
325
  );
251
326
  }
252
327
 
253
- /** `first` is tried before `second`: it answers what both can match. */
328
+ /**
329
+ * `first` is tried before `second`: it answers what both can match.
330
+ *
331
+ * @param {ServerRoute} first
332
+ * @param {ServerRoute} second
333
+ */
254
334
  function overlapWarning(first, second) {
255
335
  const settled =
256
336
  parameterCount(first) < parameterCount(second)
@@ -265,6 +345,7 @@ function overlapWarning(first, second) {
265
345
  );
266
346
  }
267
347
 
348
+ /** @param {ServerRoute} route */
268
349
  function rootParameterWarning(route) {
269
350
  return (
270
351
  `Frontend module ${route.id} serves ${describeRoute(route.method, route.path)}, whose first ` +
@@ -276,10 +357,13 @@ function rootParameterWarning(route) {
276
357
  /**
277
358
  * Reports what the order of `routes` settles silently: two modules' routes
278
359
  * that can match one request, and a route that catches every first segment.
360
+ *
361
+ * @param {ServerRoute[]} routes
362
+ * @param {Warn} warn
279
363
  */
280
364
  function warnOverlaps(routes, warn) {
281
365
  for (const [index, route] of routes.entries()) {
282
- if (route.shape.startsWith(":")) warn(rootParameterWarning(route));
366
+ if (route.pathIdentity.startsWith(":")) warn(rootParameterWarning(route));
283
367
  for (const later of routes.slice(index + 1))
284
368
  if (
285
369
  later.method === route.method &&
@@ -290,11 +374,17 @@ function warnOverlaps(routes, warn) {
290
374
  }
291
375
  }
292
376
 
377
+ /**
378
+ * @param {DeclaredModule[]} modules
379
+ * @param {Warn} warn
380
+ * @param {PublicEndpoint[]} publicEndpoints
381
+ */
293
382
  function resolveRoutes(modules, warn, publicEndpoints) {
383
+ /** @type {Map<string, ServerRoute>} */
294
384
  const kept = new Map();
295
385
  for (const module of modules) {
296
386
  for (const route of routesOfModule(module, warn)) {
297
- const key = `${route.method} ${route.shape}`;
387
+ const key = `${route.method} ${route.pathIdentity}`;
298
388
  const current = kept.get(key);
299
389
  if (current) {
300
390
  warn(shadowWarning(current, route));
@@ -317,92 +407,41 @@ function resolveRoutes(modules, warn, publicEndpoints) {
317
407
  return routes;
318
408
  }
319
409
 
320
- const AUTH_HOOKS = ["refreshSession", "publicSession"];
321
-
322
- function authRejection(auth) {
323
- if (typeof auth !== "object" || auth === null) return "it is not an object";
324
- for (const hook of AUTH_HOOKS)
325
- if (auth[hook] !== undefined && typeof auth[hook] !== "function")
326
- return `${hook} is not a function`;
327
- if (
328
- auth.signInPath !== undefined &&
329
- (typeof auth.signInPath !== "string" ||
330
- !compilePathPattern(auth.signInPath) ||
331
- auth.signInPath.includes("*"))
332
- )
333
- return "signInPath is not a page path";
334
- if (
335
- auth.signInExempt !== undefined &&
336
- !(
337
- Array.isArray(auth.signInExempt) &&
338
- auth.signInExempt.every((pattern) => compilePathPattern(pattern))
339
- )
340
- )
341
- return "signInExempt is not a list of /x, /x/* or /x/** patterns";
342
- return undefined;
343
- }
344
-
345
- /**
346
- * The auth hooks: those of the highest-priority module that registers them.
347
- * A module registering them twice, or after another one, is reported.
348
- */
349
- function resolveAuth(modules, warn) {
350
- let provider;
351
- for (const module of modules) {
352
- for (const auth of module.declared.auth) {
353
- const reason = authRejection(auth);
354
- if (reason) {
355
- warn(
356
- `Frontend module ${module.id} registers auth hooks, which are ignored: ${reason}.`,
357
- );
358
- continue;
359
- }
360
- if (provider) {
361
- warn(
362
- `Frontend module ${module.id} registers auth hooks, which are ignored: ${provider.id} (priority ${provider.priority ?? 0}) provides them.`,
363
- );
364
- continue;
365
- }
366
- provider = {
367
- id: module.id,
368
- priority: module.priority ?? 0,
369
- signInPath: auth.signInPath,
370
- signInExempt: auth.signInExempt?.map(compilePathPattern),
371
- refreshSession: auth.refreshSession,
372
- publicSession: auth.publicSession,
373
- };
374
- }
375
- }
376
- return provider;
377
- }
378
-
379
410
  /**
380
411
  * The route rules: of two declarations of the same pattern, the module of
381
412
  * higher priority keeps it, with a warning.
413
+ *
414
+ * @param {DeclaredModule[]} modules
415
+ * @param {Warn} warn
382
416
  */
383
417
  function resolveRules(modules, warn) {
418
+ /** @type {Map<string, import("./route-rules.mjs").ResolvedRouteRule>} */
384
419
  const kept = new Map();
385
420
  for (const module of modules) {
386
421
  for (const rules of module.declared.rules) {
387
- if (typeof rules !== "object" || rules === null) {
422
+ if (!isNonNullObject(rules)) {
388
423
  warn(
389
424
  `Frontend module ${module.id} declares route rules that are ignored: they are not an object.`,
390
425
  );
391
426
  continue;
392
427
  }
393
428
  for (const [pattern, rule] of Object.entries(rules)) {
394
- const reason = ruleRejection(pattern, rule);
429
+ const compiled = compilePathPattern(pattern);
395
430
  const current = kept.get(pattern);
396
- if (reason || current) {
431
+ const reason =
432
+ ruleRejection(pattern, rule) ??
433
+ (current &&
434
+ `${current.id} (priority ${current.priority}) declares it`);
435
+ if (!compiled || reason) {
397
436
  warn(
398
- `Frontend module ${module.id} declares the route rule "${pattern}", which is ignored: ${reason ?? `${current.id} (priority ${current.priority}) declares it`}.`,
437
+ `Frontend module ${module.id} declares the route rule "${pattern}", which is ignored: ${reason}.`,
399
438
  );
400
439
  continue;
401
440
  }
402
441
  kept.set(pattern, {
403
442
  id: module.id,
404
443
  priority: module.priority ?? 0,
405
- pattern: compilePathPattern(pattern),
444
+ pattern: compiled,
406
445
  rule,
407
446
  });
408
447
  }
@@ -435,12 +474,13 @@ function resolveRules(modules, warn) {
435
474
  * request reaches. Pure: the server hands it the declarations it loaded, and
436
475
  * `module-event.mjs` runs the handler a request reaches.
437
476
  *
438
- * @param declarations `{ id, priority, setup }` per module with a dms.server.ts
439
- * @param warn Reports a declaration problem
440
- * @param publicEndpoints The deployment's public endpoints (#108), to report a
477
+ * @param {readonly ServerModuleDeclaration[]} declarations `{ id, priority, setup }` per module with a dms.server.ts
478
+ * @param {Warn} warn Reports a declaration problem
479
+ * @param {PublicEndpoint[]} [publicEndpoints] The deployment's public endpoints (#108), to report a
441
480
  * module route answering one
442
- * @param manifestProxies `{ module, path, auth? }`, the backend proxies the
481
+ * @param {readonly import("./backend-proxies.mjs").ManifestProxy[]} [manifestProxies] `{ module, path, auth? }`, the backend proxies the
443
482
  * backend modules declare in the manifest
483
+ * @returns {ServerDeclarations}
444
484
  */
445
485
  export function resolveServerDeclarations(
446
486
  declarations,
@@ -466,7 +506,12 @@ export function resolveServerDeclarations(
466
506
  };
467
507
  }
468
508
 
509
+ /**
510
+ * @param {ServerRoute} route
511
+ * @param {RegExpExecArray} match
512
+ */
469
513
  function decodedParams(route, match) {
514
+ /** @type {Record<string, string>} */
470
515
  const params = {};
471
516
  for (const [index, name] of route.params.entries()) {
472
517
  try {
@@ -483,9 +528,9 @@ function decodedParams(route, match) {
483
528
  *
484
529
  * A `HEAD` no route declares is the `GET`'s, without its body.
485
530
  *
486
- * @param routes What `resolveServerDeclarations` returned
487
- * @param method The request's method
488
- * @param pathname The request's normalized path, without its query string
531
+ * @param {ServerRoute[]} routes What `resolveServerDeclarations` returned
532
+ * @param {string} method The request's method
533
+ * @param {string} pathname The request's normalized path, without its query string
489
534
  */
490
535
  export function matchModuleRoute(routes, method, pathname) {
491
536
  const reached = matchMethod(routes, method, pathname);
@@ -493,6 +538,12 @@ export function matchModuleRoute(routes, method, pathname) {
493
538
  return matchMethod(routes, "GET", pathname);
494
539
  }
495
540
 
541
+ /**
542
+ * @param {ServerRoute[]} routes
543
+ * @param {string} method
544
+ * @param {string} pathname
545
+ * @returns {{ route: ServerRoute, params: Record<string, string> } | undefined}
546
+ */
496
547
  function matchMethod(routes, method, pathname) {
497
548
  for (const route of routes) {
498
549
  if (route.method !== method) continue;
@@ -507,8 +558,12 @@ function matchMethod(routes, method, pathname) {
507
558
  /**
508
559
  * The methods the module routes answer on `pathname`: a path a module route
509
560
  * serves is that module's for every method, the others answered `405`.
561
+ *
562
+ * @param {ServerRoute[]} routes
563
+ * @param {string} pathname
510
564
  */
511
565
  export function moduleRouteMethods(routes, pathname) {
566
+ /** @type {Set<string>} */
512
567
  const methods = new Set();
513
568
  for (const route of routes)
514
569
  if (route.matcher.test(pathname)) methods.add(route.method);