@loozo/angular-router 0.0.3 → 0.0.5
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 +246 -124
- package/fesm2022/loozo-angular-router-_core.mjs +19 -41
- package/fesm2022/loozo-angular-router-_core.mjs.map +1 -1
- package/fesm2022/loozo-angular-router-_generated.mjs +148 -315
- package/fesm2022/loozo-angular-router-_generated.mjs.map +1 -1
- package/fesm2022/loozo-angular-router.mjs +2 -2
- package/node/src/builders/application.js +12 -3
- package/node/src/builders/options.js +9 -3
- package/node/src/builders/serve.js +21 -14
- package/node/src/generation/emit.js +26 -8
- package/node/src/generation/filesystem.js +261 -69
- package/node/src/generation/generate.d.ts +1 -1
- package/node/src/generation/generate.js +6 -1
- package/node/src/generation/sources.js +7 -2
- package/node/src/generation/validate.js +15 -24
- package/package.json +1 -1
- package/schemas/application.json +29 -20
- package/schemas/serve.json +142 -7
- package/snippets/angular.json +29 -0
- package/snippets/generated/destinations.generated.ts +126 -0
- package/snippets/generated/routes.generated.ts +35 -0
- package/snippets/src/catalog.ts +20 -0
- package/snippets/src/docs-url.ts +8 -0
- package/snippets/src/index.html +11 -0
- package/snippets/src/main.ts +6 -0
- package/snippets/src/product-url.ts +8 -0
- package/snippets/src/routes/products/$productId/page.ts +20 -0
- package/snippets/src/routes/products/$productId/route.generated.ts +24 -0
- package/snippets/src/routes/products/$productId/route.ts +7 -0
- package/snippets/src/routes/reports/$reportId/page.ts +9 -0
- package/snippets/src/routes/reports/$reportId/route.generated.ts +24 -0
- package/snippets/src/routes/reports/$reportId/route.ts +6 -0
- package/snippets/src/routes/teams/$teamId/items/$id/inputs.ts +15 -0
- package/snippets/src/routes/teams/$teamId/items/$id/links.ts +13 -0
- package/snippets/src/routes/teams/$teamId/items/$id/page.ts +9 -0
- package/snippets/src/routes/teams/$teamId/items/$id/route.generated.ts +24 -0
- package/snippets/src/routes/teams/$teamId/items/$id/route.ts +18 -0
- package/snippets/src/routes/teams/$teamId/members/$memberId/page.ts +9 -0
- package/snippets/src/routes/teams/$teamId/members/$memberId/route.generated.ts +24 -0
- package/snippets/src/routes/teams/$teamId/members/$memberId/route.ts +7 -0
- package/snippets/src/routes/teams/$teamId/old-items/$id/route.ts +13 -0
- package/snippets/src/routes/teams/$teamId/route.generated.ts +24 -0
- package/snippets/src/routes/teams/$teamId/route.ts +6 -0
- package/snippets/src/shell.ts +9 -0
- package/snippets/src/slug.ts +14 -0
- package/snippets/tsconfig.app.json +14 -0
- package/snippets/tsconfig.routes.json +5 -0
- package/types/loozo-angular-router-_core.d.ts +141 -99
- package/types/loozo-angular-router-_generated.d.ts +48 -31
- package/types/loozo-angular-router.d.ts +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { DestroyRef,
|
|
2
|
-
import { ActivatedRoute,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { DestroyRef, computed, inject, signal } from "@angular/core";
|
|
2
|
+
import { ActivatedRoute, RedirectCommand, Router, UrlTree } from "@angular/router";
|
|
3
|
+
import { from, isObservable, map, merge } from "rxjs";
|
|
4
|
+
import { codecFallback, createUrl, routeMetadata } from "@loozo/angular-router/_core";
|
|
5
5
|
function hasIdentity(snapshot, id) {
|
|
6
6
|
const data = snapshot.routeConfig?.data;
|
|
7
7
|
return !!data && Object.hasOwn(data, "__loozoAngularRouterRouteId") && data.__loozoAngularRouterRouteId === id;
|
|
@@ -9,17 +9,33 @@ function hasIdentity(snapshot, id) {
|
|
|
9
9
|
function assertIdentity(snapshot, id) {
|
|
10
10
|
if (!hasIdentity(snapshot, id)) throw new Error(`Generated route wiring identity mismatch: ${id}`);
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
var InputError = class extends Error {
|
|
13
|
+
key;
|
|
14
|
+
reason;
|
|
15
|
+
source = "path";
|
|
16
|
+
constructor(key, reason, cause) {
|
|
17
|
+
super(`Invalid path input ${key}: ${reason}`, { cause });
|
|
18
|
+
this.key = key;
|
|
19
|
+
this.reason = reason;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function validateRaw(key, raws, array) {
|
|
23
|
+
if (!array && raws.length === 0) throw new InputError(key, "missing");
|
|
24
|
+
if (!array && raws.length > 1) throw new InputError(key, "repeated");
|
|
25
|
+
if (raws.some((raw) => typeof raw !== "string")) throw new InputError(key, "invalid", /* @__PURE__ */ new TypeError("Raw input must be a decoded string"));
|
|
18
26
|
}
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
function parse(codec, raw) {
|
|
28
|
+
let value;
|
|
29
|
+
try {
|
|
30
|
+
value = codec.parse(raw);
|
|
31
|
+
} catch (cause) {
|
|
32
|
+
return { cause };
|
|
33
|
+
}
|
|
34
|
+
if (value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function") {
|
|
35
|
+
Promise.resolve(value).catch(() => void 0);
|
|
36
|
+
throw new TypeError("ParamCodec.parse must return its value synchronously, not a Promise");
|
|
37
|
+
}
|
|
38
|
+
return { value };
|
|
23
39
|
}
|
|
24
40
|
function pathJobs(definition, url) {
|
|
25
41
|
const metadata = routeMetadata(definition);
|
|
@@ -31,9 +47,8 @@ function pathJobs(definition, url) {
|
|
|
31
47
|
const array = segment.kind === "catchall";
|
|
32
48
|
if (array) raws = url.slice(index).map((value) => value.path);
|
|
33
49
|
else if (index < url.length) raws = [url[index].path];
|
|
34
|
-
validateRaw(
|
|
50
|
+
validateRaw(segment.name, raws, array);
|
|
35
51
|
local.set(segment.name, {
|
|
36
|
-
source: "path",
|
|
37
52
|
key: segment.name,
|
|
38
53
|
raws,
|
|
39
54
|
array,
|
|
@@ -42,75 +57,63 @@ function pathJobs(definition, url) {
|
|
|
42
57
|
}
|
|
43
58
|
return [...local.values()];
|
|
44
59
|
}
|
|
45
|
-
function
|
|
60
|
+
function runPathJobs(jobs) {
|
|
61
|
+
const params = Object.create(null);
|
|
62
|
+
for (const job of jobs) {
|
|
63
|
+
const values = [];
|
|
64
|
+
for (const raw of job.raws) {
|
|
65
|
+
const result = parse(job.codec, raw);
|
|
66
|
+
if ("cause" in result) throw new InputError(job.key, "invalid", result.cause);
|
|
67
|
+
values.push(result.value);
|
|
68
|
+
}
|
|
69
|
+
if (job.array) params[job.key] = Object.freeze(values);
|
|
70
|
+
else params[job.key] = values[0];
|
|
71
|
+
}
|
|
72
|
+
return Object.freeze(params);
|
|
73
|
+
}
|
|
74
|
+
function parseSearch(definition, snapshot) {
|
|
46
75
|
const metadata = routeMetadata(definition);
|
|
47
|
-
const
|
|
76
|
+
const search = Object.create(null);
|
|
48
77
|
for (const key of Object.keys(metadata.search)) {
|
|
49
78
|
const codec = metadata.search[key];
|
|
50
79
|
const raws = snapshot.queryParamMap.getAll(key);
|
|
51
|
-
if (raws.length ===
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
array: false,
|
|
58
|
-
codec
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
return jobs;
|
|
62
|
-
}
|
|
63
|
-
async function runJobs(jobs) {
|
|
64
|
-
const params = Object.create(null);
|
|
65
|
-
const search = Object.create(null);
|
|
66
|
-
const pending = [];
|
|
67
|
-
for (const job of jobs) pending.push((async () => {
|
|
68
|
-
try {
|
|
69
|
-
const elements = [];
|
|
70
|
-
for (const raw of job.raws) elements.push((async () => job.codec.parse(raw))());
|
|
71
|
-
const values = await Promise.all(elements);
|
|
72
|
-
let value = values[0];
|
|
73
|
-
if (job.array) value = Object.freeze(values);
|
|
74
|
-
if (job.source === "path") params[job.key] = value;
|
|
75
|
-
else search[job.key] = value;
|
|
76
|
-
} catch (cause) {
|
|
77
|
-
throw inputError(job.source, job.key, "invalid", cause);
|
|
80
|
+
if (raws.length === 1 && typeof raws[0] === "string") {
|
|
81
|
+
const result = parse(codec, raws[0]);
|
|
82
|
+
if ("value" in result) {
|
|
83
|
+
search[key] = result.value;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
78
86
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
search: Object.freeze(search)
|
|
84
|
-
});
|
|
87
|
+
const fallback = codecFallback(codec);
|
|
88
|
+
if (fallback) search[key] = fallback.value;
|
|
89
|
+
}
|
|
90
|
+
return Object.freeze(search);
|
|
85
91
|
}
|
|
86
92
|
const full = /* @__PURE__ */ new WeakMap();
|
|
93
|
+
const paths = /* @__PURE__ */ new WeakMap();
|
|
87
94
|
const matching = /* @__PURE__ */ new WeakMap();
|
|
88
|
-
function
|
|
95
|
+
function memo(cache, definition, snapshot, run) {
|
|
89
96
|
let snapshots = cache.get(definition);
|
|
90
97
|
if (!snapshots) {
|
|
91
98
|
snapshots = /* @__PURE__ */ new WeakMap();
|
|
92
99
|
cache.set(definition, snapshots);
|
|
93
100
|
}
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (!(error instanceof Error)) error = new Error("Route input failure", { cause });
|
|
106
|
-
result.outcome = { error };
|
|
107
|
-
});
|
|
108
|
-
return result;
|
|
101
|
+
let outcome = snapshots.get(snapshot);
|
|
102
|
+
if (!outcome) {
|
|
103
|
+
try {
|
|
104
|
+
outcome = { value: run() };
|
|
105
|
+
} catch (error) {
|
|
106
|
+
outcome = { error };
|
|
107
|
+
}
|
|
108
|
+
snapshots.set(snapshot, outcome);
|
|
109
|
+
}
|
|
110
|
+
if ("error" in outcome) throw outcome.error;
|
|
111
|
+
return outcome.value;
|
|
109
112
|
}
|
|
110
|
-
function
|
|
113
|
+
function parseInputs(set, id, snapshot) {
|
|
111
114
|
assertIdentity(snapshot, id);
|
|
112
115
|
const binding = set.records[id];
|
|
113
|
-
return
|
|
116
|
+
return memo(full, binding.definition(), snapshot, () => {
|
|
114
117
|
const jobs = [];
|
|
115
118
|
for (const ownerId of binding.owners) {
|
|
116
119
|
const definition = set.records[ownerId].definition();
|
|
@@ -120,50 +123,40 @@ function fullOperation(set, id, snapshot) {
|
|
|
120
123
|
assertIdentity(owner, ownerId);
|
|
121
124
|
jobs.push(...pathJobs(definition, owner.url));
|
|
122
125
|
}
|
|
123
|
-
jobs
|
|
124
|
-
return
|
|
126
|
+
const params = runPathJobs(jobs);
|
|
127
|
+
return Object.freeze({
|
|
128
|
+
params,
|
|
129
|
+
search: parseSearch(binding.definition(), snapshot)
|
|
130
|
+
});
|
|
125
131
|
});
|
|
126
132
|
}
|
|
127
|
-
function
|
|
128
|
-
return fullOperation(set, id, snapshot).promise;
|
|
129
|
-
}
|
|
130
|
-
function matchingInputs(set, id, snapshot) {
|
|
133
|
+
function pathInputs(set, id, snapshot) {
|
|
131
134
|
assertIdentity(snapshot, id);
|
|
132
135
|
const definition = set.records[id].definition();
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
try {
|
|
137
|
+
return { params: memo(paths, definition, snapshot, () => runPathJobs(pathJobs(definition, snapshot.url))) };
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error instanceof InputError) return { error };
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
137
142
|
}
|
|
138
|
-
function
|
|
139
|
-
const
|
|
140
|
-
return
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
else if (isPromiseResult(result)) subscription = from(Promise.resolve(result)).subscribe(subscriber);
|
|
148
|
-
else {
|
|
149
|
-
subscriber.next(result);
|
|
150
|
-
subscriber.complete();
|
|
151
|
-
}
|
|
152
|
-
} catch (error) {
|
|
153
|
-
subscriber.error(error);
|
|
154
|
-
}
|
|
155
|
-
}, (error) => subscriber.error(error));
|
|
156
|
-
return () => subscription?.unsubscribe();
|
|
143
|
+
function matchingInputs(set, id, snapshot) {
|
|
144
|
+
const definition = set.records[id].definition();
|
|
145
|
+
return memo(matching, definition, snapshot, () => {
|
|
146
|
+
const path = pathInputs(set, id, snapshot);
|
|
147
|
+
if ("error" in path) return;
|
|
148
|
+
return Object.freeze({
|
|
149
|
+
params: path.params,
|
|
150
|
+
search: parseSearch(definition, snapshot)
|
|
151
|
+
});
|
|
157
152
|
});
|
|
158
153
|
}
|
|
159
154
|
function callbacks(set, id) {
|
|
160
|
-
const
|
|
155
|
+
const metadata = routeMetadata(set.records[id].definition());
|
|
156
|
+
const config = metadata.config;
|
|
161
157
|
const result = {};
|
|
162
158
|
const activate = config.canActivate;
|
|
163
|
-
if (activate) result.canActivate = activate.map((callback) => (snapshot, state) =>
|
|
164
|
-
assertIdentity(snapshot, id);
|
|
165
|
-
return bridge(() => parseInputs(set, id, snapshot), (inputs) => callback(inputs, snapshot, state));
|
|
166
|
-
});
|
|
159
|
+
if (activate) result.canActivate = activate.map((callback) => (snapshot, state) => callback(parseInputs(set, id, snapshot), snapshot, state));
|
|
167
160
|
const children = config.canActivateChild;
|
|
168
161
|
if (children) result.canActivateChild = children.map((callback) => (child, state) => {
|
|
169
162
|
let parent;
|
|
@@ -175,27 +168,50 @@ function callbacks(set, id) {
|
|
|
175
168
|
}
|
|
176
169
|
}
|
|
177
170
|
if (!parent) throw new Error(`Generated guard ancestor missing: ${id}`);
|
|
178
|
-
|
|
179
|
-
return bridge(() => parseInputs(set, id, parent), (inputs) => callback(inputs, child, state));
|
|
171
|
+
return callback(parseInputs(set, id, parent), child, state);
|
|
180
172
|
});
|
|
181
173
|
const matching = config.canMatch;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
174
|
+
const canMatch = [];
|
|
175
|
+
if (metadata.localSegments.some((segment) => segment.kind !== "static") || Object.keys(metadata.search).length > 0) {
|
|
176
|
+
const handler = config.onInvalidParams;
|
|
177
|
+
canMatch.push((route, segments, snapshot) => {
|
|
178
|
+
const path = pathInputs(set, id, snapshot);
|
|
179
|
+
if (!("error" in path)) {
|
|
180
|
+
matchingInputs(set, id, snapshot);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
if (!handler) return false;
|
|
184
|
+
const decision = handler(path.error, route, segments, snapshot);
|
|
185
|
+
if (isObservable(decision) || isPromiseResult(decision)) return from(decision).pipe(map(invalidParamsResult));
|
|
186
|
+
return invalidParamsResult(decision);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (matching) for (const callback of matching) canMatch.push((route, segments, snapshot) => {
|
|
190
|
+
const inputs = matchingInputs(set, id, snapshot);
|
|
191
|
+
if (inputs === void 0) return false;
|
|
192
|
+
return callback(inputs, route, segments, snapshot);
|
|
185
193
|
});
|
|
194
|
+
if (canMatch.length > 0) result.canMatch = canMatch;
|
|
186
195
|
const resolvers = config.resolve;
|
|
187
196
|
if (resolvers) {
|
|
188
197
|
const resolve = Object.create(null);
|
|
189
|
-
for (const key of Reflect.ownKeys(resolvers)) resolve[key] = (snapshot, state) =>
|
|
190
|
-
assertIdentity(snapshot, id);
|
|
191
|
-
return bridge(() => parseInputs(set, id, snapshot), (inputs) => resolvers[key](inputs, snapshot, state));
|
|
192
|
-
};
|
|
198
|
+
for (const key of Reflect.ownKeys(resolvers)) resolve[key] = (snapshot, state) => resolvers[key](parseInputs(set, id, snapshot), snapshot, state);
|
|
193
199
|
result.resolve = resolve;
|
|
194
200
|
}
|
|
201
|
+
const title = config.title;
|
|
202
|
+
if (typeof title === "function") result.title = (snapshot, state) => title(parseInputs(set, id, snapshot), snapshot, state);
|
|
203
|
+
else if (title !== void 0) result.title = title;
|
|
195
204
|
return result;
|
|
196
205
|
}
|
|
206
|
+
function isPromiseResult(value) {
|
|
207
|
+
return !!value && typeof value.then === "function";
|
|
208
|
+
}
|
|
209
|
+
function invalidParamsResult(decision) {
|
|
210
|
+
if (decision === false || decision instanceof UrlTree || decision instanceof RedirectCommand) return decision;
|
|
211
|
+
throw new TypeError("onInvalidParams must return false, a UrlTree or a RedirectCommand");
|
|
212
|
+
}
|
|
197
213
|
function redirect(definition) {
|
|
198
|
-
return
|
|
214
|
+
return (snapshot) => {
|
|
199
215
|
const router = inject(Router);
|
|
200
216
|
const source = routeMetadata(definition());
|
|
201
217
|
const config = source.config.redirect;
|
|
@@ -217,39 +233,24 @@ function redirect(definition) {
|
|
|
217
233
|
const input = raw.get(key);
|
|
218
234
|
const raws = input?.values ?? [];
|
|
219
235
|
const array = input?.array ?? false;
|
|
220
|
-
validateRaw(
|
|
236
|
+
validateRaw(key, raws, array);
|
|
221
237
|
jobs.push({
|
|
222
|
-
source: "path",
|
|
223
238
|
key,
|
|
224
239
|
raws,
|
|
225
240
|
array,
|
|
226
241
|
codec: source.params[key]
|
|
227
242
|
});
|
|
228
243
|
}
|
|
229
|
-
|
|
230
|
-
const codec = target.search[key];
|
|
231
|
-
const raws = snapshot.queryParamMap.getAll(key);
|
|
232
|
-
if (raws.length === 0 && codecPresence(codec) === "optional") continue;
|
|
233
|
-
validateRaw("search", key, raws, false);
|
|
234
|
-
jobs.push({
|
|
235
|
-
source: "search",
|
|
236
|
-
key,
|
|
237
|
-
raws,
|
|
238
|
-
array: false,
|
|
239
|
-
codec
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
const logical = await runJobs(jobs);
|
|
244
|
+
const logical = runPathJobs(jobs);
|
|
243
245
|
const params = Object.create(null);
|
|
244
|
-
for (const [key, sourceKey] of Object.entries(config.params)) params[key] = logical
|
|
246
|
+
for (const [key, sourceKey] of Object.entries(config.params)) params[key] = logical[sourceKey];
|
|
245
247
|
const path = createUrlForPath(config.to, params);
|
|
246
248
|
const replacement = Object.create(null);
|
|
247
249
|
if (config.search.mode === "replace") {
|
|
248
250
|
const values = config.search.values;
|
|
249
251
|
for (const key of Object.keys(target.search).sort()) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const raw = codec.stringify(values[key]);
|
|
252
|
+
if (!Object.hasOwn(values, key) || values[key] === void 0) continue;
|
|
253
|
+
const raw = target.search[key].stringify(values[key]);
|
|
253
254
|
if (typeof raw !== "string") throw new TypeError(`Redirect search codec must stringify to a string: ${key}`);
|
|
254
255
|
replacement[key] = raw;
|
|
255
256
|
}
|
|
@@ -288,190 +289,23 @@ function createUrlForPath(definition, params) {
|
|
|
288
289
|
function observeRoute(set, id) {
|
|
289
290
|
const activated = inject(ActivatedRoute);
|
|
290
291
|
const router = inject(Router);
|
|
291
|
-
const destroy = inject(DestroyRef);
|
|
292
|
-
const tasks = inject(PendingTasks);
|
|
293
292
|
assertIdentity(activated.snapshot, id);
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}, ...ngDevMode ? [{ debugName: "paired" }] : /* istanbul ignore next */ []);
|
|
298
|
-
const data = signal({
|
|
299
|
-
status: "loading",
|
|
300
|
-
value: void 0
|
|
301
|
-
}, ...ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []);
|
|
302
|
-
let survivor;
|
|
303
|
-
let work;
|
|
304
|
-
let commitment = -1;
|
|
305
|
-
let pending;
|
|
306
|
-
let visible = false;
|
|
307
|
-
let destroyed = false;
|
|
308
|
-
let release;
|
|
309
|
-
let acquiring = false;
|
|
310
|
-
function track() {
|
|
311
|
-
if (release || destroyed || acquiring) return;
|
|
312
|
-
acquiring = true;
|
|
313
|
-
const cleanup = tasks.add();
|
|
314
|
-
acquiring = false;
|
|
315
|
-
if (destroyed) {
|
|
316
|
-
cleanup();
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
release = cleanup;
|
|
320
|
-
}
|
|
321
|
-
function untrack() {
|
|
322
|
-
const cleanup = release;
|
|
323
|
-
release = void 0;
|
|
324
|
-
cleanup?.();
|
|
325
|
-
}
|
|
326
|
-
function publish() {
|
|
327
|
-
if (!visible || destroyed || !work) return;
|
|
328
|
-
if (!work.outcome) {
|
|
329
|
-
paired.set({
|
|
330
|
-
status: "loading",
|
|
331
|
-
value: void 0
|
|
332
|
-
});
|
|
333
|
-
track();
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
if ("value" in work.outcome) paired.set({
|
|
337
|
-
status: "resolved",
|
|
338
|
-
value: work.outcome.value
|
|
339
|
-
});
|
|
340
|
-
else paired.set({
|
|
341
|
-
status: "error",
|
|
342
|
-
error: work.outcome.error
|
|
343
|
-
});
|
|
344
|
-
untrack();
|
|
345
|
-
}
|
|
346
|
-
function adopt(snapshot) {
|
|
347
|
-
assertIdentity(snapshot, id);
|
|
348
|
-
if (snapshot === survivor) return;
|
|
349
|
-
survivor = snapshot;
|
|
350
|
-
const joined = fullOperation(set, id, snapshot);
|
|
351
|
-
work = joined;
|
|
352
|
-
if (!joined.outcome) track();
|
|
353
|
-
joined.promise.then(() => {
|
|
354
|
-
if (work === joined) publish();
|
|
355
|
-
}, () => {
|
|
356
|
-
if (work === joined) publish();
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
function reconcile() {
|
|
360
|
-
const successful = router.lastSuccessfulNavigation();
|
|
361
|
-
if (successful && successful.id > commitment) {
|
|
362
|
-
commitment = successful.id;
|
|
363
|
-
adopt(activated.snapshot);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
function restore() {
|
|
367
|
-
if (destroyed) return;
|
|
368
|
-
reconcile();
|
|
369
|
-
if (!survivor) adopt(activated.snapshot);
|
|
370
|
-
const local = Object.create(null);
|
|
371
|
-
const resolvers = routeMetadata(set.records[id].definition()).config.resolve ?? {};
|
|
372
|
-
for (const key of Reflect.ownKeys(resolvers)) if (Object.hasOwn(survivor.data, key)) local[key] = survivor.data[key];
|
|
373
|
-
data.set({
|
|
374
|
-
status: "resolved",
|
|
375
|
-
value: Object.freeze(local)
|
|
376
|
-
});
|
|
377
|
-
visible = true;
|
|
378
|
-
publish();
|
|
379
|
-
}
|
|
380
|
-
function idleHandoff() {
|
|
381
|
-
queueMicrotask(() => {
|
|
382
|
-
if (destroyed || visible) return;
|
|
383
|
-
const current = router.currentNavigation();
|
|
384
|
-
const successful = router.lastSuccessfulNavigation();
|
|
385
|
-
if (!current || current.id === successful?.id) {
|
|
386
|
-
pending = void 0;
|
|
387
|
-
restore();
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
function eligible(id) {
|
|
392
|
-
const current = router.currentNavigation();
|
|
393
|
-
const successful = router.lastSuccessfulNavigation();
|
|
394
|
-
if (current && current.id > id && current.id !== successful?.id) return false;
|
|
395
|
-
return pending === void 0 || pending === id;
|
|
396
|
-
}
|
|
397
|
-
const subscription = router.events.subscribe((event) => {
|
|
398
|
-
if (destroyed) return;
|
|
399
|
-
if (event instanceof NavigationStart) {
|
|
400
|
-
pending = event.id;
|
|
401
|
-
visible = false;
|
|
402
|
-
reconcile();
|
|
403
|
-
paired.set({
|
|
404
|
-
status: "loading",
|
|
405
|
-
value: void 0
|
|
406
|
-
});
|
|
407
|
-
data.set({
|
|
408
|
-
status: "loading",
|
|
409
|
-
value: void 0
|
|
410
|
-
});
|
|
411
|
-
} else if (event instanceof NavigationEnd) {
|
|
412
|
-
reconcile();
|
|
413
|
-
if (eligible(event.id)) {
|
|
414
|
-
pending = void 0;
|
|
415
|
-
restore();
|
|
416
|
-
}
|
|
417
|
-
} else if (event instanceof NavigationCancel) {
|
|
418
|
-
reconcile();
|
|
419
|
-
if (event.code === NavigationCancellationCode.Redirect || event.code === NavigationCancellationCode.SupersededByNewNavigation) return;
|
|
420
|
-
if (eligible(event.id)) {
|
|
421
|
-
pending = void 0;
|
|
422
|
-
restore();
|
|
423
|
-
}
|
|
424
|
-
} else if (event instanceof NavigationSkipped) {
|
|
425
|
-
reconcile();
|
|
426
|
-
if (eligible(event.id)) {
|
|
427
|
-
pending = void 0;
|
|
428
|
-
restore();
|
|
429
|
-
}
|
|
430
|
-
} else if (event instanceof NavigationError && eligible(event.id)) {
|
|
431
|
-
reconcile();
|
|
432
|
-
pending = void 0;
|
|
433
|
-
visible = false;
|
|
434
|
-
let error = event.error;
|
|
435
|
-
if (!(error instanceof Error)) error = new Error("Navigation failure", { cause: event.error });
|
|
436
|
-
paired.set({
|
|
437
|
-
status: "error",
|
|
438
|
-
error
|
|
439
|
-
});
|
|
440
|
-
data.set({
|
|
441
|
-
status: "error",
|
|
442
|
-
error
|
|
443
|
-
});
|
|
444
|
-
untrack();
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
destroy.onDestroy(() => {
|
|
448
|
-
destroyed = true;
|
|
449
|
-
visible = false;
|
|
450
|
-
subscription.unsubscribe();
|
|
451
|
-
untrack();
|
|
293
|
+
const snapshot = signal(activated.snapshot, ...ngDevMode ? [{ debugName: "snapshot" }] : /* istanbul ignore next */ []);
|
|
294
|
+
const subscription = merge(activated.queryParams, activated.fragment, activated.params, activated.url, activated.data, router.events).subscribe(() => {
|
|
295
|
+
snapshot.set(activated.snapshot);
|
|
452
296
|
});
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const successful = router.lastSuccessfulNavigation();
|
|
457
|
-
if (!current || current.id === successful?.id) restore();
|
|
458
|
-
else {
|
|
459
|
-
pending = current.id;
|
|
460
|
-
idleHandoff();
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
function project(key) {
|
|
464
|
-
const state = paired();
|
|
465
|
-
if (state.status === "error") return state;
|
|
466
|
-
return {
|
|
467
|
-
status: state.status,
|
|
468
|
-
value: state.value?.[key]
|
|
469
|
-
};
|
|
470
|
-
}
|
|
297
|
+
inject(DestroyRef).onDestroy(() => subscription.unsubscribe());
|
|
298
|
+
const inputs = computed(() => parseInputs(set, id, snapshot()), ...ngDevMode ? [{ debugName: "inputs" }] : /* istanbul ignore next */ []);
|
|
299
|
+
const resolvers = routeMetadata(set.records[id].definition()).config.resolve ?? {};
|
|
471
300
|
return {
|
|
472
|
-
params:
|
|
473
|
-
search:
|
|
474
|
-
data:
|
|
301
|
+
params: computed(() => inputs().params),
|
|
302
|
+
search: computed(() => inputs().search),
|
|
303
|
+
data: computed(() => {
|
|
304
|
+
const data = snapshot().data;
|
|
305
|
+
const own = Object.create(null);
|
|
306
|
+
for (const key of Reflect.ownKeys(resolvers)) if (Object.hasOwn(data, key)) own[key] = data[key];
|
|
307
|
+
return Object.freeze(own);
|
|
308
|
+
})
|
|
475
309
|
};
|
|
476
310
|
}
|
|
477
311
|
const destinations = /* @__PURE__ */ new WeakMap();
|
|
@@ -488,7 +322,6 @@ function nativeOptions(definition) {
|
|
|
488
322
|
const options = {};
|
|
489
323
|
for (const key of [
|
|
490
324
|
"providers",
|
|
491
|
-
"title",
|
|
492
325
|
"data",
|
|
493
326
|
"canDeactivate",
|
|
494
327
|
"runGuardsAndResolvers"
|