@loozo/angular-router 0.0.2 → 0.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/README.md +442 -79
- package/builders.json +2 -2
- package/fesm2022/loozo-angular-router-_core.mjs +348 -589
- package/fesm2022/loozo-angular-router-_core.mjs.map +1 -1
- package/fesm2022/loozo-angular-router-_generated.mjs +492 -617
- package/fesm2022/loozo-angular-router-_generated.mjs.map +1 -1
- package/fesm2022/loozo-angular-router.mjs +2 -19
- package/node/_core/src/path.d.ts +17 -0
- package/node/src/builders/application.js +9 -6
- package/node/src/builders/options.js +38 -58
- package/node/src/builders/serve.js +51 -8
- package/node/src/generation/anchor.d.ts +13 -0
- package/node/src/generation/anchor.js +92 -0
- package/node/src/generation/compiler.d.ts +6 -0
- package/node/src/generation/compiler.js +68 -0
- package/node/src/generation/emit.d.ts +3 -0
- package/node/src/generation/files.d.ts +26 -0
- package/node/src/generation/files.js +14 -3
- package/node/src/generation/filesystem.d.ts +7 -0
- package/node/src/generation/generate.d.ts +16 -0
- package/node/src/generation/generate.js +5 -4
- package/node/src/generation/index.d.ts +16 -0
- package/node/src/generation/index.js +15 -0
- package/node/src/generation/overlay.d.ts +3 -0
- package/node/src/generation/selectors.d.ts +2 -0
- package/node/src/generation/sources.d.ts +50 -0
- package/node/src/generation/sources.js +50 -10
- package/node/src/generation/validate.d.ts +12 -0
- package/node/src/generation/validate.js +38 -33
- package/package.json +7 -2
- package/schemas/application.json +743 -8
- package/schemas/serve.json +11 -32
- package/types/loozo-angular-router-_core.d.ts +159 -181
- package/types/loozo-angular-router-_generated.d.ts +131 -109
- package/types/loozo-angular-router.d.ts +2 -1
- package/.npmignore +0 -3
- package/fesm2022/loozo-angular-router.mjs.map +0 -1
|
@@ -1,633 +1,392 @@
|
|
|
1
|
-
const presence = new WeakMap();
|
|
1
|
+
const presence = /* @__PURE__ */ new WeakMap();
|
|
2
2
|
function codecPresence(codec) {
|
|
3
|
-
|
|
3
|
+
return presence.get(codec);
|
|
4
4
|
}
|
|
5
5
|
function validateCodec(codec) {
|
|
6
|
-
|
|
7
|
-
(typeof codec !== "object" && typeof codec !== "function") ||
|
|
8
|
-
typeof codec.parse !== "function" ||
|
|
9
|
-
typeof codec.stringify !== "function") {
|
|
10
|
-
throw new TypeError("Codec must have callable parse and stringify methods");
|
|
11
|
-
}
|
|
6
|
+
if (codec === null || typeof codec !== "object" && typeof codec !== "function" || typeof codec.parse !== "function" || typeof codec.stringify !== "function") throw new TypeError("Codec must have callable parse and stringify methods");
|
|
12
7
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Preserves strings verbatim, including empty strings and literal percent text.
|
|
15
|
-
*
|
|
16
|
-
* @remarks
|
|
17
|
-
* Empty, dot and dotdot values are valid codec values but cannot be serialized
|
|
18
|
-
* into path segments by URL creation. Search values may contain them.
|
|
19
|
-
*/
|
|
20
8
|
const stringParam = {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return value;
|
|
29
|
-
},
|
|
9
|
+
parse(raw) {
|
|
10
|
+
return raw;
|
|
11
|
+
},
|
|
12
|
+
stringify(value) {
|
|
13
|
+
if (typeof value !== "string") throw new TypeError("Expected a string");
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
30
16
|
};
|
|
31
|
-
/**
|
|
32
|
-
* Parses nonblank text with JavaScript `Number` and requires a finite result.
|
|
33
|
-
*
|
|
34
|
-
* @remarks
|
|
35
|
-
* Whitespace around numbers, exponent notation and hexadecimal text are accepted.
|
|
36
|
-
* Blank text, NaN and infinities throw. Stringification requires a finite number
|
|
37
|
-
* and uses its JavaScript string representation (including `-0` becoming `"0"`).
|
|
38
|
-
*/
|
|
39
17
|
const numberParam = {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
throw new Error("Expected a finite number");
|
|
50
|
-
}
|
|
51
|
-
return String(value);
|
|
52
|
-
},
|
|
18
|
+
parse(raw) {
|
|
19
|
+
const value = Number(raw);
|
|
20
|
+
if (raw.trim() === "" || !Number.isFinite(value)) throw new Error("Expected a finite number");
|
|
21
|
+
return value;
|
|
22
|
+
},
|
|
23
|
+
stringify(value) {
|
|
24
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error("Expected a finite number");
|
|
25
|
+
return String(value);
|
|
26
|
+
}
|
|
53
27
|
};
|
|
54
|
-
/**
|
|
55
|
-
* Parses only lowercase `"true"` and `"false"`, and emits those same strings.
|
|
56
|
-
* Other representations and nonboolean values throw.
|
|
57
|
-
*/
|
|
58
28
|
const booleanParam = {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (value === true) {
|
|
70
|
-
return "true";
|
|
71
|
-
}
|
|
72
|
-
if (value === false) {
|
|
73
|
-
return "false";
|
|
74
|
-
}
|
|
75
|
-
throw new Error("Expected a boolean");
|
|
76
|
-
},
|
|
29
|
+
parse(raw) {
|
|
30
|
+
if (raw === "true") return true;
|
|
31
|
+
if (raw === "false") return false;
|
|
32
|
+
throw new Error("Expected true or false");
|
|
33
|
+
},
|
|
34
|
+
stringify(value) {
|
|
35
|
+
if (value === true) return "true";
|
|
36
|
+
if (value === false) return "false";
|
|
37
|
+
throw new Error("Expected a boolean");
|
|
38
|
+
}
|
|
77
39
|
};
|
|
78
|
-
/**
|
|
79
|
-
* Creates a case-sensitive string codec from a snapshotted list of members.
|
|
80
|
-
*
|
|
81
|
-
* @typeParam Values - The literal list from which the value union is inferred.
|
|
82
|
-
* @param values - Allowed decoded strings; an empty string is valid when listed.
|
|
83
|
-
* @returns A codec whose parser and stringifier reject nonmembers.
|
|
84
|
-
* @throws TypeError if the list or its entries are malformed.
|
|
85
|
-
*
|
|
86
|
-
* @example
|
|
87
|
-
* ```ts
|
|
88
|
-
* const tab = enumParam(["details", "reviews"]);
|
|
89
|
-
* tab.parse("reviews"); // "reviews"
|
|
90
|
-
* ```
|
|
91
|
-
*/
|
|
92
40
|
function enumParam(values) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
},
|
|
110
|
-
stringify(value) {
|
|
111
|
-
if (!members.has(value)) {
|
|
112
|
-
throw new Error("Unknown enum value");
|
|
113
|
-
}
|
|
114
|
-
return value;
|
|
115
|
-
},
|
|
116
|
-
};
|
|
41
|
+
if (!Array.isArray(values)) throw new TypeError("Enum values must be a string list");
|
|
42
|
+
const members = /* @__PURE__ */ new Set();
|
|
43
|
+
for (const value of values) {
|
|
44
|
+
if (typeof value !== "string") throw new TypeError("Enum values must be strings");
|
|
45
|
+
members.add(value);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
parse(raw) {
|
|
49
|
+
if (!members.has(raw)) throw new Error("Unknown enum value");
|
|
50
|
+
return raw;
|
|
51
|
+
},
|
|
52
|
+
stringify(value) {
|
|
53
|
+
if (!members.has(value)) throw new Error("Unknown enum value");
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
117
57
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Makes a search key optional without changing the handling of present values.
|
|
120
|
-
*
|
|
121
|
-
* @remarks
|
|
122
|
-
* Generated parsing omits absent keys; URL creation omits absent or `undefined`
|
|
123
|
-
* values. The wrapper's parser delegates present strings unchanged, preserving
|
|
124
|
-
* promises and failures. Its stringifier cannot serialize `undefined` directly.
|
|
125
|
-
* This wrapper is not a path codec. For optional nullable values, wrap
|
|
126
|
-
* {@link nullableSearchParam} with this function, not the other way around.
|
|
127
|
-
*
|
|
128
|
-
* @typeParam Value - The wrapped codec's logical value.
|
|
129
|
-
* @param codec - The codec used for present search values.
|
|
130
|
-
* @returns A search-only codec allowing omission.
|
|
131
|
-
* @throws TypeError if the codec lacks callable parse/stringify methods.
|
|
132
|
-
*/
|
|
133
58
|
function optionalSearchParam(codec) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
presence.set(wrapper, "optional");
|
|
147
|
-
return wrapper;
|
|
59
|
+
validateCodec(codec);
|
|
60
|
+
const wrapper = {
|
|
61
|
+
parse(raw) {
|
|
62
|
+
return codec.parse(raw);
|
|
63
|
+
},
|
|
64
|
+
stringify(value) {
|
|
65
|
+
if (value === void 0) throw new TypeError("Omitted search has no scalar representation");
|
|
66
|
+
return codec.stringify(value);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
presence.set(wrapper, "optional");
|
|
70
|
+
return wrapper;
|
|
148
71
|
}
|
|
149
|
-
/**
|
|
150
|
-
* Adds explicit null to a search codec, represented by the decoded text `"null"`.
|
|
151
|
-
*
|
|
152
|
-
* @remarks
|
|
153
|
-
* The key remains required unless wrapped with {@link optionalSearchParam}.
|
|
154
|
-
* Non-null values delegate unchanged, including promise/failure identity. The
|
|
155
|
-
* text `"null"` is reserved by this wrapper even if the inner codec accepts it.
|
|
156
|
-
* This wrapper is not a path codec and cannot wrap an optional search codec.
|
|
157
|
-
*
|
|
158
|
-
* @typeParam Value - The wrapped codec's non-null logical value.
|
|
159
|
-
* @param codec - The codec used for values other than null.
|
|
160
|
-
* @returns A search-only codec accepting and serializing null.
|
|
161
|
-
* @throws TypeError if the codec is malformed or is an optional search wrapper.
|
|
162
|
-
*
|
|
163
|
-
* @example
|
|
164
|
-
* ```ts
|
|
165
|
-
* const score = optionalSearchParam(nullableSearchParam(numberParam));
|
|
166
|
-
* // Omission, null, and a present number remain distinct.
|
|
167
|
-
* ```
|
|
168
|
-
*/
|
|
169
72
|
function nullableSearchParam(codec) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
return codec.stringify(value);
|
|
186
|
-
},
|
|
187
|
-
};
|
|
188
|
-
presence.set(wrapper, "nullable");
|
|
189
|
-
return wrapper;
|
|
73
|
+
validateCodec(codec);
|
|
74
|
+
if (codecPresence(codec) === "optional") throw new TypeError("Nullable cannot wrap optional");
|
|
75
|
+
const wrapper = {
|
|
76
|
+
parse(raw) {
|
|
77
|
+
if (raw === "null") return null;
|
|
78
|
+
return codec.parse(raw);
|
|
79
|
+
},
|
|
80
|
+
stringify(value) {
|
|
81
|
+
if (value === null) return "null";
|
|
82
|
+
return codec.stringify(value);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
presence.set(wrapper, "nullable");
|
|
86
|
+
return wrapper;
|
|
190
87
|
}
|
|
191
|
-
|
|
192
88
|
function parsePath(path) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
226
|
-
if (part === "" ||
|
|
227
|
-
part === "." ||
|
|
228
|
-
part === ".." ||
|
|
229
|
-
part === "*" ||
|
|
230
|
-
part === "**" ||
|
|
231
|
-
part.startsWith(":")) {
|
|
232
|
-
throw new TypeError(`Invalid static segment: ${part}`);
|
|
233
|
-
}
|
|
234
|
-
if (!part.isWellFormed()) {
|
|
235
|
-
throw new TypeError("Static path Unicode must be well formed");
|
|
236
|
-
}
|
|
237
|
-
segments.push(Object.freeze({ kind: "static", text: part }));
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return Object.freeze(segments);
|
|
89
|
+
if (typeof path !== "string") throw new TypeError("Path must be a string");
|
|
90
|
+
if (path === "") return Object.freeze([]);
|
|
91
|
+
const segments = [];
|
|
92
|
+
const scalarNames = /* @__PURE__ */ new Set();
|
|
93
|
+
const parts = path.split("/");
|
|
94
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
95
|
+
const part = parts[index];
|
|
96
|
+
if (part.startsWith("$")) {
|
|
97
|
+
let name = part.slice(1);
|
|
98
|
+
let kind = "scalar";
|
|
99
|
+
if (name.endsWith("[]")) {
|
|
100
|
+
name = name.slice(0, -2);
|
|
101
|
+
kind = "catchall";
|
|
102
|
+
}
|
|
103
|
+
if (name === "" || name === "__proto__" || name.startsWith("...") || name.includes("?") || name.includes("*") || name.includes("[]")) throw new TypeError(`Invalid parameter name: ${name}`);
|
|
104
|
+
if (kind === "catchall" && (index !== parts.length - 1 || scalarNames.has(name))) throw new TypeError("Catchall must be terminal and cannot repeat a scalar name");
|
|
105
|
+
scalarNames.add(name);
|
|
106
|
+
segments.push(Object.freeze({
|
|
107
|
+
kind,
|
|
108
|
+
name
|
|
109
|
+
}));
|
|
110
|
+
} else {
|
|
111
|
+
if (part === "" || part === "." || part === ".." || part === "*" || part === "**" || part.startsWith(":")) throw new TypeError(`Invalid static segment: ${part}`);
|
|
112
|
+
if (!part.isWellFormed()) throw new TypeError("Static path Unicode must be well formed");
|
|
113
|
+
segments.push(Object.freeze({
|
|
114
|
+
kind: "static",
|
|
115
|
+
text: part
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return Object.freeze(segments);
|
|
241
120
|
}
|
|
242
|
-
|
|
243
121
|
function ownRecord(value, label, allowSymbols = false) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
throw new TypeError(`${label} cannot contain inherited entries`);
|
|
250
|
-
}
|
|
251
|
-
for (const key of Reflect.ownKeys(value)) {
|
|
252
|
-
if ((!allowSymbols && typeof key !== "string") ||
|
|
253
|
-
!Object.prototype.propertyIsEnumerable.call(value, key)) {
|
|
254
|
-
throw new TypeError(`${label} contains an unsupported key`);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
return value;
|
|
122
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${label} must be an own record`);
|
|
123
|
+
const prototype = Object.getPrototypeOf(value);
|
|
124
|
+
if (prototype !== Object.prototype && prototype !== null) throw new TypeError(`${label} cannot contain inherited entries`);
|
|
125
|
+
for (const key of Reflect.ownKeys(value)) if (!allowSymbols && typeof key !== "string" || !Object.prototype.propertyIsEnumerable.call(value, key)) throw new TypeError(`${label} contains an unsupported key`);
|
|
126
|
+
return value;
|
|
258
127
|
}
|
|
259
128
|
function checkKeys(record, allowed, label) {
|
|
260
|
-
|
|
261
|
-
if (!allowed.has(key)) {
|
|
262
|
-
throw new TypeError(`Unknown ${label} key: ${key}`);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
129
|
+
for (const key of Object.keys(record)) if (!allowed.has(key)) throw new TypeError(`Unknown ${label} key: ${key}`);
|
|
265
130
|
}
|
|
266
131
|
function has(record, key) {
|
|
267
|
-
|
|
132
|
+
return Object.hasOwn(record, key);
|
|
268
133
|
}
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
"data",
|
|
134
|
+
const routes = /* @__PURE__ */ new WeakMap();
|
|
135
|
+
const configKeys = /* @__PURE__ */ new Set([
|
|
136
|
+
"parent",
|
|
137
|
+
"params",
|
|
138
|
+
"search",
|
|
139
|
+
"resolve",
|
|
140
|
+
"runGuardsAndResolvers",
|
|
141
|
+
"redirect",
|
|
142
|
+
"fragments",
|
|
143
|
+
"canActivate",
|
|
144
|
+
"canActivateChild",
|
|
145
|
+
"canMatch",
|
|
146
|
+
"canDeactivate",
|
|
147
|
+
"providers",
|
|
148
|
+
"title",
|
|
149
|
+
"data"
|
|
286
150
|
]);
|
|
287
|
-
const policies = new Set([
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
151
|
+
const policies = /* @__PURE__ */ new Set([
|
|
152
|
+
"pathParamsChange",
|
|
153
|
+
"pathParamsOrQueryParamsChange",
|
|
154
|
+
"paramsChange",
|
|
155
|
+
"paramsOrQueryParamsChange",
|
|
156
|
+
"always"
|
|
293
157
|
]);
|
|
294
158
|
const identityKey = "__loozoAngularRouterRouteId";
|
|
295
159
|
function routeMetadata(route) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
return routes.get(route);
|
|
160
|
+
if (route === null || typeof route !== "object" || !routes.has(route)) throw new TypeError("Expected a genuine route from this package instance");
|
|
161
|
+
return routes.get(route);
|
|
300
162
|
}
|
|
301
163
|
function copyCodecs(value, path) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
return Object.freeze(result);
|
|
164
|
+
const record = ownRecord(value, "Codec map");
|
|
165
|
+
const result = Object.create(null);
|
|
166
|
+
for (const key of Object.keys(record)) {
|
|
167
|
+
const codec = record[key];
|
|
168
|
+
validateCodec(codec);
|
|
169
|
+
if (path && codecPresence(codec) !== void 0) throw new TypeError("Search wrappers cannot be path codecs");
|
|
170
|
+
result[key] = codec;
|
|
171
|
+
}
|
|
172
|
+
return Object.freeze(result);
|
|
313
173
|
}
|
|
314
174
|
function snapshotRedirect(value, source) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (typeof fragment.value !== "string" || !target.fragments.includes(fragment.value)) {
|
|
367
|
-
throw new TypeError("Redirect fragment must belong to target declaration");
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
else if (fragment.mode === "preserve" || fragment.mode === "drop") {
|
|
371
|
-
checkKeys(fragment, new Set(["mode"]), "redirect fragment");
|
|
372
|
-
}
|
|
373
|
-
else {
|
|
374
|
-
throw new TypeError("Invalid redirect fragment policy");
|
|
375
|
-
}
|
|
376
|
-
return Object.freeze({
|
|
377
|
-
to: redirect.to,
|
|
378
|
-
params: Object.freeze(mapping),
|
|
379
|
-
search: Object.freeze(searchSnapshot),
|
|
380
|
-
fragment: Object.freeze({ ...fragment }),
|
|
381
|
-
});
|
|
175
|
+
const redirect = ownRecord(value, "Redirect");
|
|
176
|
+
checkKeys(redirect, /* @__PURE__ */ new Set([
|
|
177
|
+
"to",
|
|
178
|
+
"params",
|
|
179
|
+
"search",
|
|
180
|
+
"fragment"
|
|
181
|
+
]), "redirect");
|
|
182
|
+
const target = routeMetadata(redirect.to);
|
|
183
|
+
const mapping = Object.create(null);
|
|
184
|
+
if (has(redirect, "params")) {
|
|
185
|
+
const supplied = ownRecord(redirect.params, "Redirect params");
|
|
186
|
+
checkKeys(supplied, new Set(Object.keys(target.params)), "redirect params");
|
|
187
|
+
for (const key of Object.keys(target.params)) {
|
|
188
|
+
const sourceKey = supplied[key];
|
|
189
|
+
if (!has(supplied, key) || typeof sourceKey !== "string" || !has(source.params, sourceKey)) throw new TypeError(`Missing or invalid redirect mapping: ${key}`);
|
|
190
|
+
mapping[key] = sourceKey;
|
|
191
|
+
}
|
|
192
|
+
} else for (const key of Object.keys(target.params)) {
|
|
193
|
+
if (!has(source.params, key)) throw new TypeError(`Cannot forward redirect param: ${key}`);
|
|
194
|
+
mapping[key] = key;
|
|
195
|
+
}
|
|
196
|
+
const search = ownRecord(redirect.search, "Redirect search");
|
|
197
|
+
let searchSnapshot;
|
|
198
|
+
if (search.mode === "replace") {
|
|
199
|
+
checkKeys(search, /* @__PURE__ */ new Set(["mode", "values"]), "redirect search");
|
|
200
|
+
const values = ownRecord(search.values, "Redirect replacement");
|
|
201
|
+
validateSearchValues(target.search, values);
|
|
202
|
+
searchSnapshot = {
|
|
203
|
+
mode: "replace",
|
|
204
|
+
values: Object.freeze({ ...values })
|
|
205
|
+
};
|
|
206
|
+
} else if (search.mode === "preserve" || search.mode === "drop") {
|
|
207
|
+
checkKeys(search, /* @__PURE__ */ new Set(["mode"]), "redirect search");
|
|
208
|
+
for (const key of Object.keys(target.search)) {
|
|
209
|
+
if (codecPresence(target.search[key]) === "optional") continue;
|
|
210
|
+
if (search.mode === "drop" || !has(source.search, key) || codecPresence(source.search[key]) === "optional") throw new TypeError(`Redirect cannot guarantee required search: ${key}`);
|
|
211
|
+
}
|
|
212
|
+
searchSnapshot = { mode: search.mode };
|
|
213
|
+
} else throw new TypeError("Invalid redirect search policy");
|
|
214
|
+
const fragment = ownRecord(redirect.fragment, "Redirect fragment");
|
|
215
|
+
if (fragment.mode === "replace") {
|
|
216
|
+
checkKeys(fragment, /* @__PURE__ */ new Set(["mode", "value"]), "redirect fragment");
|
|
217
|
+
if (typeof fragment.value !== "string" || !target.fragments.includes(fragment.value)) throw new TypeError("Redirect fragment must belong to target declaration");
|
|
218
|
+
} else if (fragment.mode === "preserve" || fragment.mode === "drop") checkKeys(fragment, /* @__PURE__ */ new Set(["mode"]), "redirect fragment");
|
|
219
|
+
else throw new TypeError("Invalid redirect fragment policy");
|
|
220
|
+
return Object.freeze({
|
|
221
|
+
to: redirect.to,
|
|
222
|
+
params: Object.freeze(mapping),
|
|
223
|
+
search: Object.freeze(searchSnapshot),
|
|
224
|
+
fragment: Object.freeze({ ...fragment })
|
|
225
|
+
});
|
|
382
226
|
}
|
|
383
227
|
function validateSearchValues(codecs, values) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if (codecPresence(codecs[key]) !== "optional" && !has(values, key)) {
|
|
387
|
-
throw new TypeError(`Missing required search: ${key}`);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
228
|
+
checkKeys(values, new Set(Object.keys(codecs)), "search values");
|
|
229
|
+
for (const key of Object.keys(codecs)) if (codecPresence(codecs[key]) !== "optional" && !has(values, key)) throw new TypeError(`Missing required search: ${key}`);
|
|
390
230
|
}
|
|
391
231
|
function defineRoute(path, config) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
throw new TypeError("Static data cannot set generated identity");
|
|
494
|
-
}
|
|
495
|
-
retained.data = Object.freeze({ ...data });
|
|
496
|
-
}
|
|
497
|
-
const fragments = [];
|
|
498
|
-
if (has(supplied, "fragments")) {
|
|
499
|
-
if (!Array.isArray(supplied.fragments)) {
|
|
500
|
-
throw new TypeError("Fragments must be a string list");
|
|
501
|
-
}
|
|
502
|
-
for (const name of supplied.fragments) {
|
|
503
|
-
if (typeof name !== "string") {
|
|
504
|
-
throw new TypeError("Fragment names must be strings");
|
|
505
|
-
}
|
|
506
|
-
fragments.push(name);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
retained.fragments = Object.freeze(fragments);
|
|
510
|
-
retained.params = localParams;
|
|
511
|
-
retained.search = localSearch;
|
|
512
|
-
const metadata = {
|
|
513
|
-
parent,
|
|
514
|
-
localSegments,
|
|
515
|
-
segments: Object.freeze([...(inherited?.segments ?? []), ...localSegments]),
|
|
516
|
-
localParams,
|
|
517
|
-
localSearch,
|
|
518
|
-
params: Object.freeze({ ...inherited?.params, ...localParams }),
|
|
519
|
-
search: Object.freeze({ ...inherited?.search, ...localSearch }),
|
|
520
|
-
fragments: retained.fragments,
|
|
521
|
-
config: retained,
|
|
522
|
-
};
|
|
523
|
-
if (has(supplied, "redirect")) {
|
|
524
|
-
if (has(supplied, "canActivate") || has(supplied, "canMatch")) {
|
|
525
|
-
throw new TypeError("Redirect cannot declare own canActivate or canMatch");
|
|
526
|
-
}
|
|
527
|
-
retained.redirect = snapshotRedirect(supplied.redirect, metadata);
|
|
528
|
-
}
|
|
529
|
-
Object.freeze(retained);
|
|
530
|
-
routes.set(result, Object.freeze(metadata));
|
|
531
|
-
return Object.freeze(result);
|
|
232
|
+
const supplied = ownRecord(config, "Route config");
|
|
233
|
+
checkKeys(supplied, configKeys, "route config");
|
|
234
|
+
const localSegments = parsePath(path);
|
|
235
|
+
let parent;
|
|
236
|
+
let inherited;
|
|
237
|
+
if (has(supplied, "parent")) {
|
|
238
|
+
inherited = routeMetadata(supplied.parent);
|
|
239
|
+
parent = supplied.parent;
|
|
240
|
+
for (const segment of inherited.segments) if (segment.kind === "catchall") throw new TypeError("Catchall declarations cannot have children");
|
|
241
|
+
}
|
|
242
|
+
const localNames = /* @__PURE__ */ new Set();
|
|
243
|
+
for (const segment of localSegments) if (segment.kind !== "static") localNames.add(segment.name);
|
|
244
|
+
let localParams = Object.freeze(Object.create(null));
|
|
245
|
+
if (localNames.size === 0) {
|
|
246
|
+
if (has(supplied, "params")) throw new TypeError("Static paths cannot declare params");
|
|
247
|
+
} else {
|
|
248
|
+
localParams = copyCodecs(supplied.params, true);
|
|
249
|
+
checkKeys(localParams, localNames, "local params");
|
|
250
|
+
for (const key of localNames) {
|
|
251
|
+
if (!has(localParams, key)) throw new TypeError(`Missing local codec: ${key}`);
|
|
252
|
+
if (inherited && has(inherited.params, key)) throw new TypeError(`Inherited param already owned: ${key}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
let localSearch = Object.freeze(Object.create(null));
|
|
256
|
+
if (has(supplied, "search")) localSearch = copyCodecs(supplied.search, false);
|
|
257
|
+
for (const key of Object.keys(localSearch)) if (inherited && has(inherited.search, key)) throw new TypeError(`Inherited search already owned: ${key}`);
|
|
258
|
+
const retained = { ...supplied };
|
|
259
|
+
const result = { path };
|
|
260
|
+
if (has(supplied, "resolve")) {
|
|
261
|
+
const resolve = ownRecord(supplied.resolve, "Resolvers", true);
|
|
262
|
+
const copied = Object.create(null);
|
|
263
|
+
for (const key of Reflect.ownKeys(resolve)) {
|
|
264
|
+
if (key === identityKey || typeof resolve[key] !== "function") throw new TypeError(`Invalid resolver: ${String(key)}`);
|
|
265
|
+
copied[key] = resolve[key];
|
|
266
|
+
}
|
|
267
|
+
result.resolve = Object.freeze(copied);
|
|
268
|
+
retained.resolve = result.resolve;
|
|
269
|
+
}
|
|
270
|
+
if (has(supplied, "runGuardsAndResolvers")) {
|
|
271
|
+
const policy = supplied.runGuardsAndResolvers;
|
|
272
|
+
if (typeof policy !== "function" && (typeof policy !== "string" || !policies.has(policy))) throw new TypeError("Invalid native rerun policy");
|
|
273
|
+
result.runGuardsAndResolvers = policy;
|
|
274
|
+
}
|
|
275
|
+
for (const key of [
|
|
276
|
+
"canActivate",
|
|
277
|
+
"canActivateChild",
|
|
278
|
+
"canMatch",
|
|
279
|
+
"canDeactivate",
|
|
280
|
+
"providers"
|
|
281
|
+
]) {
|
|
282
|
+
if (!has(supplied, key)) continue;
|
|
283
|
+
const list = supplied[key];
|
|
284
|
+
if (!Array.isArray(list)) throw new TypeError(`${key} must be an array`);
|
|
285
|
+
if (key === "canActivate" || key === "canActivateChild" || key === "canMatch") {
|
|
286
|
+
for (const callback of list) if (typeof callback !== "function") throw new TypeError(`${key} entries must be callbacks`);
|
|
287
|
+
} else if (key === "canDeactivate") {
|
|
288
|
+
for (const guard of list) if (guard === null || typeof guard !== "function" && typeof guard !== "object" && typeof guard !== "string") throw new TypeError("canDeactivate entries must be native callbacks or tokens");
|
|
289
|
+
}
|
|
290
|
+
retained[key] = Object.freeze([...list]);
|
|
291
|
+
}
|
|
292
|
+
if (has(supplied, "title") && typeof supplied.title !== "string" && typeof supplied.title !== "function") throw new TypeError("Invalid native title");
|
|
293
|
+
if (has(supplied, "data")) {
|
|
294
|
+
const data = ownRecord(supplied.data, "Static data", true);
|
|
295
|
+
if (has(data, identityKey)) throw new TypeError("Static data cannot set generated identity");
|
|
296
|
+
retained.data = Object.freeze({ ...data });
|
|
297
|
+
}
|
|
298
|
+
const fragments = [];
|
|
299
|
+
if (has(supplied, "fragments")) {
|
|
300
|
+
if (!Array.isArray(supplied.fragments)) throw new TypeError("Fragments must be a string list");
|
|
301
|
+
for (const name of supplied.fragments) {
|
|
302
|
+
if (typeof name !== "string") throw new TypeError("Fragment names must be strings");
|
|
303
|
+
fragments.push(name);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
retained.fragments = Object.freeze(fragments);
|
|
307
|
+
retained.params = localParams;
|
|
308
|
+
retained.search = localSearch;
|
|
309
|
+
const metadata = {
|
|
310
|
+
parent,
|
|
311
|
+
localSegments,
|
|
312
|
+
segments: Object.freeze([...inherited?.segments ?? [], ...localSegments]),
|
|
313
|
+
localParams,
|
|
314
|
+
localSearch,
|
|
315
|
+
params: Object.freeze({
|
|
316
|
+
...inherited?.params,
|
|
317
|
+
...localParams
|
|
318
|
+
}),
|
|
319
|
+
search: Object.freeze({
|
|
320
|
+
...inherited?.search,
|
|
321
|
+
...localSearch
|
|
322
|
+
}),
|
|
323
|
+
fragments: retained.fragments,
|
|
324
|
+
config: retained
|
|
325
|
+
};
|
|
326
|
+
if (has(supplied, "redirect")) {
|
|
327
|
+
if (has(supplied, "canActivate") || has(supplied, "canMatch")) throw new TypeError("Redirect cannot declare own canActivate or canMatch");
|
|
328
|
+
retained.redirect = snapshotRedirect(supplied.redirect, metadata);
|
|
329
|
+
}
|
|
330
|
+
Object.freeze(retained);
|
|
331
|
+
routes.set(result, Object.freeze(metadata));
|
|
332
|
+
return Object.freeze(result);
|
|
532
333
|
}
|
|
533
|
-
|
|
534
334
|
function encode(value) {
|
|
535
|
-
|
|
335
|
+
return encodeURIComponent(value).replaceAll("(", "%28").replaceAll(")", "%29");
|
|
536
336
|
}
|
|
537
337
|
function stringify(codec, value, path) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (path && (text === "" || text === "." || text === "..")) {
|
|
543
|
-
throw new TypeError("Path output cannot be empty, dot or dotdot");
|
|
544
|
-
}
|
|
545
|
-
return encode(text);
|
|
338
|
+
const text = codec.stringify(value);
|
|
339
|
+
if (typeof text !== "string") throw new TypeError("Codec stringify must return a string");
|
|
340
|
+
if (path && (text === "" || text === "." || text === "..")) throw new TypeError("Path output cannot be empty, dot or dotdot");
|
|
341
|
+
return encode(text);
|
|
546
342
|
}
|
|
547
343
|
function createUrl(route, values) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
if (segment.kind === "static") {
|
|
594
|
-
parts.push(encode(segment.text));
|
|
595
|
-
}
|
|
596
|
-
else if (segment.kind === "scalar") {
|
|
597
|
-
parts.push(stringify(metadata.params[segment.name], params[segment.name], true));
|
|
598
|
-
}
|
|
599
|
-
else {
|
|
600
|
-
for (const value of params[segment.name]) {
|
|
601
|
-
parts.push(stringify(metadata.params[segment.name], value, true));
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
let url = `/${parts.join("/")}`;
|
|
606
|
-
const query = [];
|
|
607
|
-
for (const key of searchKeys) {
|
|
608
|
-
const codec = metadata.search[key];
|
|
609
|
-
const optional = codecPresence(codec) === "optional";
|
|
610
|
-
if (optional && !has(search, key)) {
|
|
611
|
-
continue;
|
|
612
|
-
}
|
|
613
|
-
const value = search[key];
|
|
614
|
-
if (optional && value === undefined) {
|
|
615
|
-
continue;
|
|
616
|
-
}
|
|
617
|
-
query.push(`${encode(key)}=${stringify(codec, value, false)}`);
|
|
618
|
-
}
|
|
619
|
-
if (query.length > 0) {
|
|
620
|
-
url += `?${query.join("&")}`;
|
|
621
|
-
}
|
|
622
|
-
if (has(options, "fragment")) {
|
|
623
|
-
url += `#${encode(options.fragment)}`;
|
|
624
|
-
}
|
|
625
|
-
return url;
|
|
344
|
+
const metadata = routeMetadata(route);
|
|
345
|
+
const options = ownRecord(values, "URL options");
|
|
346
|
+
checkKeys(options, /* @__PURE__ */ new Set([
|
|
347
|
+
"params",
|
|
348
|
+
"search",
|
|
349
|
+
"fragment"
|
|
350
|
+
]), "URL options");
|
|
351
|
+
const paramKeys = Object.keys(metadata.params);
|
|
352
|
+
let params = Object.create(null);
|
|
353
|
+
if (paramKeys.length === 0) {
|
|
354
|
+
if (has(options, "params")) throw new TypeError("Route has no path params");
|
|
355
|
+
} else {
|
|
356
|
+
params = ownRecord(options.params, "Path values");
|
|
357
|
+
checkKeys(params, new Set(paramKeys), "path values");
|
|
358
|
+
for (const key of paramKeys) if (!has(params, key)) throw new TypeError(`Missing path value: ${key}`);
|
|
359
|
+
}
|
|
360
|
+
const searchKeys = Object.keys(metadata.search).sort();
|
|
361
|
+
let search = Object.create(null);
|
|
362
|
+
if (searchKeys.length === 0) {
|
|
363
|
+
if (has(options, "search")) throw new TypeError("Route has no search declarations");
|
|
364
|
+
} else {
|
|
365
|
+
if (has(options, "search")) search = ownRecord(options.search, "Search values");
|
|
366
|
+
validateSearchValues(metadata.search, search);
|
|
367
|
+
}
|
|
368
|
+
if (has(options, "fragment")) {
|
|
369
|
+
if (typeof options.fragment !== "string" || !metadata.fragments.includes(options.fragment)) throw new TypeError("Fragment must belong to this declaration");
|
|
370
|
+
}
|
|
371
|
+
for (const segment of metadata.segments) if (segment.kind === "catchall" && !Array.isArray(params[segment.name])) throw new TypeError(`Catchall requires an array: ${segment.name}`);
|
|
372
|
+
const parts = [];
|
|
373
|
+
for (const segment of metadata.segments) if (segment.kind === "static") parts.push(encode(segment.text));
|
|
374
|
+
else if (segment.kind === "scalar") parts.push(stringify(metadata.params[segment.name], params[segment.name], true));
|
|
375
|
+
else for (const value of params[segment.name]) parts.push(stringify(metadata.params[segment.name], value, true));
|
|
376
|
+
let url = `/${parts.join("/")}`;
|
|
377
|
+
const query = [];
|
|
378
|
+
for (const key of searchKeys) {
|
|
379
|
+
const codec = metadata.search[key];
|
|
380
|
+
const optional = codecPresence(codec) === "optional";
|
|
381
|
+
if (optional && !has(search, key)) continue;
|
|
382
|
+
const value = search[key];
|
|
383
|
+
if (optional && value === void 0) continue;
|
|
384
|
+
query.push(`${encode(key)}=${stringify(codec, value, false)}`);
|
|
385
|
+
}
|
|
386
|
+
if (query.length > 0) url += `?${query.join("&")}`;
|
|
387
|
+
if (has(options, "fragment")) url += `#${encode(options.fragment)}`;
|
|
388
|
+
return url;
|
|
626
389
|
}
|
|
627
|
-
|
|
628
|
-
/**
|
|
629
|
-
* Generated bundle index. Do not edit.
|
|
630
|
-
*/
|
|
631
|
-
|
|
632
390
|
export { booleanParam, codecPresence, createUrl, defineRoute, enumParam, nullableSearchParam, numberParam, optionalSearchParam, routeMetadata, stringParam };
|
|
633
|
-
|
|
391
|
+
|
|
392
|
+
//# sourceMappingURL=loozo-angular-router-_core.mjs.map
|