@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.
- package/README.md +46 -29
- package/dist/commands/build-action.d.ts +3 -0
- package/dist/commands/build-action.js +77 -8
- package/dist/commands/build.js +7 -0
- package/dist/commands/clean-action.js +1 -1
- package/dist/commands/clean.js +1 -1
- package/dist/commands/dev-action.js +4 -3
- package/dist/commands/dev.js +1 -1
- package/dist/commands/prepare-action.js +1 -1
- package/dist/commands/start-action.js +3 -24
- package/dist/commands/workspace-task.js +2 -1
- package/dist/config.js +1 -0
- package/dist/derived-outputs.d.ts +1 -0
- package/dist/derived-outputs.js +6 -4
- package/dist/discovery.js +17 -6
- package/dist/entry-weight.js +15 -0
- package/dist/fs-sync.js +7 -4
- package/dist/help.js +1 -1
- package/dist/index.js +2 -1
- package/dist/internal/built-workspace.d.ts +4 -0
- package/dist/internal/built-workspace.js +43 -0
- package/dist/internal/ensure.d.ts +1 -0
- package/dist/internal/ensure.js +8 -0
- package/dist/internal/locale-catalogs.d.ts +8 -3
- package/dist/internal/locale-catalogs.js +16 -6
- package/dist/internal/manifest-decoder.d.ts +7 -0
- package/dist/internal/manifest-decoder.js +81 -0
- package/dist/internal/own-release.d.ts +5 -0
- package/dist/internal/own-release.js +20 -0
- package/dist/internal/plain-object.d.ts +1 -0
- package/dist/internal/plain-object.js +6 -0
- package/dist/internal/prepared-workspace.d.ts +12 -0
- package/dist/internal/prepared-workspace.js +74 -0
- package/dist/internal/text-list.d.ts +1 -0
- package/dist/internal/text-list.js +10 -0
- package/dist/internal/vite-manifest.d.ts +6 -0
- package/dist/internal/vite-manifest.js +24 -0
- package/dist/layer-watch.js +25 -24
- package/dist/layers.d.ts +1 -1
- package/dist/layers.js +3 -1
- package/dist/locale-files.d.ts +6 -3
- package/dist/locale-files.js +15 -9
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +46 -32
- package/dist/materialize.js +6 -2
- package/dist/module-set.js +2 -1
- package/dist/node-options.d.ts +1 -0
- package/dist/node-options.js +8 -0
- package/dist/output.d.ts +15 -9
- package/dist/output.js +30 -18
- package/dist/ports.d.ts +3 -1
- package/dist/ports.js +1 -1
- package/dist/private-values.d.ts +1 -1
- package/dist/private-values.js +5 -3
- package/dist/project-modules.d.ts +5 -1
- package/dist/project-modules.js +23 -15
- package/dist/renderer-range.js +5 -3
- package/dist/server-process.js +1 -1
- package/dist/verify-source-build.js +10 -2
- package/dist/verify-source-client-bundle.d.ts +1 -0
- package/dist/verify-source-client-bundle.js +30 -0
- package/dist/verify-source-runner.js +36 -94
- package/dist/verify-source-sources.js +49 -11
- package/dist/verify-source-workspace.d.ts +2 -0
- package/dist/verify-source-workspace.js +111 -0
- package/dist/workspace-setup.d.ts +1 -0
- package/dist/workspace-setup.js +4 -3
- package/package.json +11 -7
- package/skills/dms-frontend/SKILL.md +1 -1
- package/templates/vue/app-runtime.ts +2 -9
- package/templates/vue/backend-boundary.mjs +132 -0
- package/templates/vue/client-chunking.mjs +2 -0
- package/templates/vue/client-server-guard.mjs +16 -0
- package/templates/vue/compress-assets.mjs +9 -1
- package/templates/vue/entry-weight.mjs +29 -3
- package/templates/vue/frontend-build.ts +4 -3
- package/templates/vue/frontend-module.ts +1 -0
- package/templates/vue/frontend-runtime/module-registration.ts +2 -2
- package/templates/vue/frontend-runtime/state.ts +2 -2
- package/templates/vue/frontend-runtime/types.ts +8 -2
- package/templates/vue/head-order.mjs +10 -2
- package/templates/vue/icon-api.mjs +2 -0
- package/templates/vue/icon-scan.mjs +4 -0
- package/templates/vue/server/auth/backend.mjs +42 -5
- package/templates/vue/server/auth/client-ip.mjs +52 -4
- package/templates/vue/server/auth/establish-endpoints.mjs +11 -7
- package/templates/vue/server/auth/session.mjs +86 -4
- package/templates/vue/server/auth-declarations.mjs +46 -0
- package/templates/vue/server/auth-hooks.mjs +55 -7
- package/templates/vue/server/backend-proxies.mjs +78 -18
- package/templates/vue/server/backend-request.mjs +61 -0
- package/templates/vue/server/backend-response.mjs +23 -6
- package/templates/vue/server/built-files.mjs +7 -0
- package/templates/vue/server/cli-report.mjs +75 -12
- package/templates/vue/server/client-error.mjs +5 -0
- package/templates/vue/server/client-manifest.mjs +23 -1
- package/templates/vue/server/content.mjs +40 -10
- package/templates/vue/server/dev-private-files.mjs +17 -1
- package/templates/vue/server/dev-styles.mjs +38 -0
- package/templates/vue/server/email-bundle-watch.mjs +9 -3
- package/templates/vue/server/email.mjs +14 -6
- package/templates/vue/server/favicon.mjs +3 -0
- package/templates/vue/server/generated-files.mjs +221 -0
- package/templates/vue/server/homepage.mjs +21 -9
- package/templates/vue/server/html-tag.mjs +4 -0
- package/templates/vue/server/icons.mjs +93 -16
- package/templates/vue/server/in-place-render.mjs +35 -0
- package/templates/vue/server/inertia.mjs +76 -3
- package/templates/vue/server/module-event.mjs +118 -25
- package/templates/vue/server/module-route-types.mjs +78 -0
- package/templates/vue/server/module-routes.mjs +151 -96
- package/templates/vue/server/module-server.mjs +121 -40
- package/templates/vue/server/module-testing.mjs +36 -12
- package/templates/vue/server/page-props.mjs +94 -3
- package/templates/vue/server/page-redirects.mjs +37 -0
- package/templates/vue/server/path-patterns.mjs +35 -12
- package/templates/vue/server/proxy-compression.mjs +22 -0
- package/templates/vue/server/proxy-headers.mjs +12 -2
- package/templates/vue/server/proxy-redirects.mjs +21 -5
- package/templates/vue/server/proxy-request.mjs +58 -0
- package/templates/vue/server/proxy-transfers.mjs +44 -10
- package/templates/vue/server/public-endpoints.mjs +139 -28
- package/templates/vue/server/public-options.mjs +20 -6
- package/templates/vue/server/request-failure.mjs +24 -1
- package/templates/vue/server/request-routing.mjs +32 -10
- package/templates/vue/server/request-target.mjs +30 -11
- package/templates/vue/server/route-rules.mjs +48 -18
- package/templates/vue/server/ssr-module.mjs +32 -0
- package/templates/vue/server/static-files.mjs +35 -7
- package/templates/vue/server/type-guards.mjs +71 -0
- package/templates/vue/server/vite-errors.mjs +45 -7
- package/templates/vue/server/web-body.mjs +5 -1
- package/templates/vue/server-module.ts +12 -2
- package/templates/vue/server.mjs +162 -157
- package/templates/vue/sfc-global-types.mjs +112 -13
- package/templates/vue/ssr-renderer.ts +2 -2
- package/templates/vue/ui-inertia-page.mjs +16 -1
- package/templates/vue/ui-kbd.mjs +2 -1
- package/templates/vue/ui-request-state.mjs +2 -1
- 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
|
-
/**
|
|
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 (
|
|
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 (
|
|
77
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
/**
|
|
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(
|
|
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 (
|
|
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
|
-
/**
|
|
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
|
|
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 =
|
|
241
|
-
|
|
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
|
-
/**
|
|
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.
|
|
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.
|
|
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 (
|
|
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
|
|
429
|
+
const compiled = compilePathPattern(pattern);
|
|
395
430
|
const current = kept.get(pattern);
|
|
396
|
-
|
|
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
|
|
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:
|
|
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);
|