@analogjs/router 2.4.0-beta.9 → 3.0.0-alpha.10
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/fesm2022/analogjs-router-server-actions.mjs +327 -31
- package/fesm2022/analogjs-router-server.mjs +155 -176
- package/fesm2022/analogjs-router-tanstack-query.mjs +58 -0
- package/fesm2022/analogjs-router-tokens.mjs +17 -16
- package/fesm2022/analogjs-router.mjs +521 -812
- package/fesm2022/debug.page.mjs +121 -0
- package/fesm2022/routes.mjs +301 -0
- package/package.json +44 -26
- package/server/actions/package.json +4 -0
- package/server/package.json +4 -0
- package/tokens/package.json +4 -0
- package/types/server/actions/src/actions.d.ts +13 -0
- package/types/server/actions/src/define-action.d.ts +54 -0
- package/types/server/actions/src/define-page-load.d.ts +55 -0
- package/types/server/actions/src/define-server-route.d.ts +68 -0
- package/types/server/actions/src/index.d.ts +9 -0
- package/types/server/actions/src/parse-request-data.d.ts +9 -0
- package/types/server/actions/src/validate.d.ts +8 -0
- package/types/server/src/index.d.ts +4 -0
- package/types/server/src/provide-server-context.d.ts +11 -0
- package/types/server/src/render.d.ts +12 -0
- package/types/server/src/server-component-render.d.ts +4 -0
- package/types/server/src/tokens.d.ts +7 -0
- package/types/src/index.d.ts +16 -0
- package/types/src/lib/cache-key.d.ts +3 -0
- package/types/src/lib/constants.d.ts +2 -0
- package/types/src/lib/cookie-interceptor.d.ts +4 -0
- package/types/src/lib/debug/debug.page.d.ts +18 -0
- package/types/src/lib/debug/index.d.ts +10 -0
- package/types/src/lib/debug/routes.d.ts +10 -0
- package/types/src/lib/define-route.d.ts +46 -0
- package/types/src/lib/endpoints.d.ts +5 -0
- package/types/src/lib/form-action.directive.d.ts +25 -0
- package/types/src/lib/get-load-resolver.d.ts +8 -0
- package/types/src/lib/inject-load.d.ts +9 -0
- package/types/src/lib/inject-route-endpoint-url.d.ts +2 -0
- package/types/src/lib/markdown-helpers.d.ts +2 -0
- package/types/src/lib/meta-tags.d.ts +33 -0
- package/types/src/lib/models.d.ts +29 -0
- package/types/src/lib/provide-file-router.d.ts +18 -0
- package/types/src/lib/request-context.d.ts +13 -0
- package/types/src/lib/route-config.d.ts +2 -0
- package/types/src/lib/route-types.d.ts +12 -0
- package/types/src/lib/routes.d.ts +19 -0
- package/types/src/lib/server.component.d.ts +33 -0
- package/types/tanstack-query/src/index.d.ts +2 -0
- package/types/tanstack-query/src/provide-analog-query.d.ts +4 -0
- package/types/tanstack-query/src/provide-server-analog-query.d.ts +2 -0
- package/types/tanstack-query/src/server-query.d.ts +16 -0
- package/types/tokens/src/index.d.ts +23 -0
- package/fesm2022/analogjs-router-debug.page-jzggTA45.mjs +0 -91
- package/fesm2022/analogjs-router-debug.page-jzggTA45.mjs.map +0 -1
- package/fesm2022/analogjs-router-server-actions.mjs.map +0 -1
- package/fesm2022/analogjs-router-server.mjs.map +0 -1
- package/fesm2022/analogjs-router-tokens.mjs.map +0 -1
- package/fesm2022/analogjs-router.mjs.map +0 -1
- package/types/analogjs-router-server-actions.d.ts +0 -16
- package/types/analogjs-router-server.d.ts +0 -28
- package/types/analogjs-router-tokens.d.ts +0 -22
- package/types/analogjs-router.d.ts +0 -268
|
@@ -1,846 +1,555 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { isPlatformServer } from
|
|
10
|
-
|
|
11
|
-
const ROUTE_META_TAGS_KEY = Symbol('@analogjs/router Route Meta Tags Key');
|
|
12
|
-
const CHARSET_KEY = 'charset';
|
|
13
|
-
const HTTP_EQUIV_KEY = 'httpEquiv';
|
|
14
|
-
// httpEquiv selector key needs to be in kebab case format
|
|
15
|
-
const HTTP_EQUIV_SELECTOR_KEY = 'http-equiv';
|
|
16
|
-
const NAME_KEY = 'name';
|
|
17
|
-
const PROPERTY_KEY = 'property';
|
|
18
|
-
const CONTENT_KEY = 'content';
|
|
19
|
-
const ITEMPROP_KEY = 'itemprop';
|
|
20
|
-
function updateMetaTagsOnRouteChange() {
|
|
21
|
-
const router = inject(Router);
|
|
22
|
-
const metaService = inject(Meta);
|
|
23
|
-
router.events
|
|
24
|
-
.pipe(filter((event) => event instanceof NavigationEnd))
|
|
25
|
-
.subscribe(() => {
|
|
26
|
-
const metaTagMap = getMetaTagMap(router.routerState.snapshot.root);
|
|
27
|
-
for (const metaTagSelector in metaTagMap) {
|
|
28
|
-
const metaTag = metaTagMap[metaTagSelector];
|
|
29
|
-
metaService.updateTag(metaTag, metaTagSelector);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
function getMetaTagMap(route) {
|
|
34
|
-
const metaTagMap = {};
|
|
35
|
-
let currentRoute = route;
|
|
36
|
-
while (currentRoute) {
|
|
37
|
-
const metaTags = currentRoute.data[ROUTE_META_TAGS_KEY] ?? [];
|
|
38
|
-
for (const metaTag of metaTags) {
|
|
39
|
-
metaTagMap[getMetaTagSelector(metaTag)] = metaTag;
|
|
40
|
-
}
|
|
41
|
-
currentRoute = currentRoute.firstChild;
|
|
42
|
-
}
|
|
43
|
-
return metaTagMap;
|
|
44
|
-
}
|
|
45
|
-
function getMetaTagSelector(metaTag) {
|
|
46
|
-
if (metaTag.name) {
|
|
47
|
-
return `${NAME_KEY}="${metaTag.name}"`;
|
|
48
|
-
}
|
|
49
|
-
if (metaTag.property) {
|
|
50
|
-
return `${PROPERTY_KEY}="${metaTag.property}"`;
|
|
51
|
-
}
|
|
52
|
-
if (metaTag.httpEquiv) {
|
|
53
|
-
return `${HTTP_EQUIV_SELECTOR_KEY}="${metaTag.httpEquiv}"`;
|
|
54
|
-
}
|
|
55
|
-
if (metaTag.itemprop) {
|
|
56
|
-
return `${ITEMPROP_KEY}="${metaTag.itemprop}"`;
|
|
57
|
-
}
|
|
58
|
-
return CHARSET_KEY;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const ANALOG_META_KEY = Symbol('@analogjs/router Analog Route Metadata Key');
|
|
62
|
-
/**
|
|
63
|
-
* This variable reference is replaced with a glob of all route endpoints.
|
|
64
|
-
*/
|
|
65
|
-
let ANALOG_PAGE_ENDPOINTS = {};
|
|
66
|
-
|
|
67
|
-
function injectRouteEndpointURL(route) {
|
|
68
|
-
const routeConfig = route.routeConfig;
|
|
69
|
-
const apiPrefix = injectAPIPrefix();
|
|
70
|
-
const baseUrl = injectBaseURL();
|
|
71
|
-
const { queryParams, fragment: hash, params, parent } = route;
|
|
72
|
-
const segment = parent?.url.map((segment) => segment.path).join('/') || '';
|
|
73
|
-
const url = new URL('', import.meta.env['VITE_ANALOG_PUBLIC_BASE_URL'] ||
|
|
74
|
-
baseUrl ||
|
|
75
|
-
(typeof window !== 'undefined' && window.location.origin
|
|
76
|
-
? window.location.origin
|
|
77
|
-
: ''));
|
|
78
|
-
url.pathname = `${url.pathname.endsWith('/') ? url.pathname : url.pathname + '/'}${apiPrefix}/_analog${routeConfig[ANALOG_META_KEY].endpoint}`;
|
|
79
|
-
url.search = `${new URLSearchParams(queryParams).toString()}`;
|
|
80
|
-
url.hash = hash ?? '';
|
|
81
|
-
Object.keys(params).forEach((param) => {
|
|
82
|
-
url.pathname = url.pathname.replace(`[${param}]`, params[param]);
|
|
83
|
-
});
|
|
84
|
-
url.pathname = url.pathname.replace('**', segment);
|
|
85
|
-
return url;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function toRouteConfig(routeMeta) {
|
|
89
|
-
if (routeMeta && isRedirectRouteMeta(routeMeta)) {
|
|
90
|
-
return routeMeta;
|
|
91
|
-
}
|
|
92
|
-
let { meta, ...routeConfig } = routeMeta ?? {};
|
|
93
|
-
if (Array.isArray(meta)) {
|
|
94
|
-
routeConfig.data = { ...routeConfig.data, [ROUTE_META_TAGS_KEY]: meta };
|
|
95
|
-
}
|
|
96
|
-
else if (typeof meta === 'function') {
|
|
97
|
-
routeConfig.resolve = {
|
|
98
|
-
...routeConfig.resolve,
|
|
99
|
-
[ROUTE_META_TAGS_KEY]: meta,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
if (!routeConfig) {
|
|
103
|
-
routeConfig = {};
|
|
104
|
-
}
|
|
105
|
-
routeConfig.runGuardsAndResolvers =
|
|
106
|
-
routeConfig.runGuardsAndResolvers ?? 'paramsOrQueryParamsChange';
|
|
107
|
-
routeConfig.resolve = {
|
|
108
|
-
...routeConfig.resolve,
|
|
109
|
-
load: async (route) => {
|
|
110
|
-
const routeConfig = route.routeConfig;
|
|
111
|
-
if (ANALOG_PAGE_ENDPOINTS[routeConfig[ANALOG_META_KEY].endpointKey]) {
|
|
112
|
-
const http = inject(HttpClient);
|
|
113
|
-
const url = injectRouteEndpointURL(route);
|
|
114
|
-
if (!!import.meta.env['VITE_ANALOG_PUBLIC_BASE_URL'] &&
|
|
115
|
-
globalThis.$fetch) {
|
|
116
|
-
return globalThis.$fetch(url.pathname);
|
|
117
|
-
}
|
|
118
|
-
return firstValueFrom(http.get(`${url.href}`));
|
|
119
|
-
}
|
|
120
|
-
return {};
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
return routeConfig;
|
|
124
|
-
}
|
|
125
|
-
function isRedirectRouteMeta(routeMeta) {
|
|
126
|
-
return !!routeMeta.redirectTo;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// The Zone is currently enabled by default, so we wouldn't need this check.
|
|
130
|
-
// However, leaving this open space will be useful if zone.js becomes optional
|
|
131
|
-
// in the future. This means we won't have to modify the current code, and it will
|
|
132
|
-
// continue to work seamlessly.
|
|
133
|
-
const isNgZoneEnabled = typeof Zone !== 'undefined' && !!Zone.root;
|
|
134
|
-
function toMarkdownModule(markdownFileFactory) {
|
|
135
|
-
return async () => {
|
|
136
|
-
const createLoader = () => Promise.all([import('@analogjs/content'), markdownFileFactory()]);
|
|
137
|
-
const [{ parseRawContentFile, MarkdownRouteComponent, ContentRenderer }, markdownFile,] = await (isNgZoneEnabled
|
|
138
|
-
? // We are not able to use `runOutsideAngular` because we are not inside
|
|
139
|
-
// an injection context to retrieve the `NgZone` instance.
|
|
140
|
-
// The `Zone.root.run` is required when the code is running in the
|
|
141
|
-
// browser since asynchronous tasks being scheduled in the current context
|
|
142
|
-
// are a reason for unnecessary change detection cycles.
|
|
143
|
-
Zone.root.run(createLoader)
|
|
144
|
-
: createLoader());
|
|
145
|
-
const { content, attributes } = parseRawContentFile(markdownFile);
|
|
146
|
-
const { title, meta } = attributes;
|
|
147
|
-
return {
|
|
148
|
-
default: MarkdownRouteComponent,
|
|
149
|
-
routeMeta: {
|
|
150
|
-
data: { _analogContent: content },
|
|
151
|
-
title,
|
|
152
|
-
meta,
|
|
153
|
-
resolve: {
|
|
154
|
-
renderedAnalogContent: async () => {
|
|
155
|
-
const contentRenderer = inject(ContentRenderer);
|
|
156
|
-
const rendered = await contentRenderer.render(content);
|
|
157
|
-
return typeof rendered === 'string'
|
|
158
|
-
? rendered
|
|
159
|
-
: rendered.content;
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
};
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const ENDPOINT_EXTENSION = '.server.ts';
|
|
168
|
-
const APP_DIR = 'src/app';
|
|
169
|
-
|
|
1
|
+
import { a as updateMetaTagsOnRouteChange, i as injectRouteEndpointURL, n as createRoutes, r as routes, t as injectDebugRoutes } from "./routes.mjs";
|
|
2
|
+
import { ActivatedRoute, ROUTES, Router, provideRouter } from "@angular/router";
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import { ChangeDetectionStrategy, Component, Directive, ENVIRONMENT_INITIALIZER, Injector, PLATFORM_ID, TransferState, effect, inject, input, makeEnvironmentProviders, makeStateKey, output, signal } from "@angular/core";
|
|
5
|
+
import { HttpClient, HttpHeaders, HttpRequest, HttpResponse, ɵHTTP_ROOT_INTERCEPTOR_FNS } from "@angular/common/http";
|
|
6
|
+
import { catchError, from, map, of, throwError } from "rxjs";
|
|
7
|
+
import { API_PREFIX, injectAPIPrefix, injectBaseURL, injectInternalServerFetch, injectRequest } from "@analogjs/router/tokens";
|
|
8
|
+
import { DomSanitizer } from "@angular/platform-browser";
|
|
9
|
+
import { isPlatformServer } from "@angular/common";
|
|
10
|
+
//#region packages/router/src/lib/define-route.ts
|
|
170
11
|
/**
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
...acc,
|
|
201
|
-
[level]: {
|
|
202
|
-
...acc[level],
|
|
203
|
-
[rawPath]: {
|
|
204
|
-
filename,
|
|
205
|
-
rawSegment,
|
|
206
|
-
ancestorRawSegments,
|
|
207
|
-
segment: toSegment(rawSegment),
|
|
208
|
-
level,
|
|
209
|
-
children: [],
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
};
|
|
213
|
-
}, {});
|
|
214
|
-
const allLevels = Object.keys(rawRoutesByLevelMap).map(Number);
|
|
215
|
-
const maxLevel = Math.max(...allLevels);
|
|
216
|
-
// add each raw route to its parent's children array
|
|
217
|
-
for (let level = maxLevel; level > 0; level--) {
|
|
218
|
-
const rawRoutesMap = rawRoutesByLevelMap[level];
|
|
219
|
-
const rawPaths = Object.keys(rawRoutesMap);
|
|
220
|
-
for (const rawPath of rawPaths) {
|
|
221
|
-
const rawRoute = rawRoutesMap[rawPath];
|
|
222
|
-
const parentRawPath = rawRoute.ancestorRawSegments.join('/');
|
|
223
|
-
const parentRawSegmentIndex = rawRoute.ancestorRawSegments.length - 1;
|
|
224
|
-
const parentRawSegment = rawRoute.ancestorRawSegments[parentRawSegmentIndex];
|
|
225
|
-
// create the parent level and/or raw route if it does not exist
|
|
226
|
-
// parent route won't exist for nested routes that don't have a layout route
|
|
227
|
-
rawRoutesByLevelMap[level - 1] ||= {};
|
|
228
|
-
rawRoutesByLevelMap[level - 1][parentRawPath] ||= {
|
|
229
|
-
filename: null,
|
|
230
|
-
rawSegment: parentRawSegment,
|
|
231
|
-
ancestorRawSegments: rawRoute.ancestorRawSegments.slice(0, parentRawSegmentIndex),
|
|
232
|
-
segment: toSegment(parentRawSegment),
|
|
233
|
-
level: level - 1,
|
|
234
|
-
children: [],
|
|
235
|
-
};
|
|
236
|
-
rawRoutesByLevelMap[level - 1][parentRawPath].children.push(rawRoute);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
// only take raw routes from the root level
|
|
240
|
-
// since they already contain nested routes as their children
|
|
241
|
-
const rootRawRoutesMap = rawRoutesByLevelMap[0];
|
|
242
|
-
const rawRoutes = Object.keys(rootRawRoutesMap).map((segment) => rootRawRoutesMap[segment]);
|
|
243
|
-
sortRawRoutes(rawRoutes);
|
|
244
|
-
return toRoutes(rawRoutes, files, debug);
|
|
245
|
-
}
|
|
246
|
-
function toRawPath(filename) {
|
|
247
|
-
return (filename
|
|
248
|
-
.replace(
|
|
249
|
-
// convert to relative path and remove file extension
|
|
250
|
-
/^(?:[a-zA-Z]:[\\/])?(.*?)[\\/](?:routes|pages)[\\/]|(?:[\\/](?:app[\\/](?:routes|pages)|src[\\/]content)[\\/])|(\.page\.(js|ts|analog|ag)$)|(\.(ts|md|analog|ag)$)/g, '')
|
|
251
|
-
// [[...slug]] => placeholder (named empty) which is stripped by toSegment
|
|
252
|
-
.replace(/\[\[\.\.\.([^\]]+)\]\]/g, '(opt-$1)')
|
|
253
|
-
.replace(/\[\.{3}.+\]/, '**') // [...not-found] => **
|
|
254
|
-
.replace(/\[([^\]]+)\]/g, ':$1')); // [id] => :id
|
|
255
|
-
}
|
|
256
|
-
function toSegment(rawSegment) {
|
|
257
|
-
return rawSegment
|
|
258
|
-
.replace(/index|\(.*?\)/g, '') // replace named empty segments
|
|
259
|
-
.replace(/\.|\/+/g, '/') // replace dots with slashes and remove redundant slashes
|
|
260
|
-
.replace(/^\/+|\/+$/g, ''); // remove trailing slashes
|
|
261
|
-
}
|
|
262
|
-
function createOptionalCatchAllMatcher(paramName) {
|
|
263
|
-
return (segments) => {
|
|
264
|
-
if (segments.length === 0) {
|
|
265
|
-
return null;
|
|
266
|
-
}
|
|
267
|
-
const joined = segments.map((s) => s.path).join('/');
|
|
268
|
-
return {
|
|
269
|
-
consumed: segments,
|
|
270
|
-
posParams: { [paramName]: new UrlSegment(joined, {}) },
|
|
271
|
-
};
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
function toRoutes(rawRoutes, files, debug = false) {
|
|
275
|
-
const routes = [];
|
|
276
|
-
for (const rawRoute of rawRoutes) {
|
|
277
|
-
const children = rawRoute.children.length > 0
|
|
278
|
-
? toRoutes(rawRoute.children, files, debug)
|
|
279
|
-
: undefined;
|
|
280
|
-
let module = undefined;
|
|
281
|
-
let analogMeta = undefined;
|
|
282
|
-
if (rawRoute.filename) {
|
|
283
|
-
const isMarkdownFile = rawRoute.filename.endsWith('.md');
|
|
284
|
-
if (!debug) {
|
|
285
|
-
module = isMarkdownFile
|
|
286
|
-
? toMarkdownModule(files[rawRoute.filename])
|
|
287
|
-
: files[rawRoute.filename];
|
|
288
|
-
}
|
|
289
|
-
const endpointKey = rawRoute.filename.replace(/\.page\.(ts|analog|ag)$/, ENDPOINT_EXTENSION);
|
|
290
|
-
// get endpoint path
|
|
291
|
-
const rawEndpoint = rawRoute.filename
|
|
292
|
-
.replace(/\.page\.(ts|analog|ag)$/, '')
|
|
293
|
-
.replace(/\[\[\.\.\..+\]\]/, '**')
|
|
294
|
-
.replace(/\[\.{3}.+\]/, '**') // [...not-found] => **
|
|
295
|
-
.replace(/^(.*?)\/pages/, '/pages');
|
|
296
|
-
// replace periods, remove (index) paths
|
|
297
|
-
const endpoint = (rawEndpoint || '')
|
|
298
|
-
.replace(/\./g, '/')
|
|
299
|
-
.replace(/\/\((.*?)\)$/, '/-$1-');
|
|
300
|
-
analogMeta = {
|
|
301
|
-
endpoint,
|
|
302
|
-
endpointKey,
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
// Detect Next.js-style optional catch-all at this node: [[...param]]
|
|
306
|
-
const optCatchAllMatch = rawRoute.filename?.match(/\[\[\.\.\.([^\]]+)\]\]/);
|
|
307
|
-
const optCatchAllParam = optCatchAllMatch ? optCatchAllMatch[1] : null;
|
|
308
|
-
const route = module
|
|
309
|
-
? {
|
|
310
|
-
path: rawRoute.segment,
|
|
311
|
-
loadChildren: () => module().then((m) => {
|
|
312
|
-
if (import.meta.env.DEV) {
|
|
313
|
-
const hasModuleDefault = !!m.default;
|
|
314
|
-
const hasRedirect = !!m.routeMeta?.redirectTo;
|
|
315
|
-
if (!hasModuleDefault && !hasRedirect) {
|
|
316
|
-
console.warn(`[Analog] Missing default export at ${rawRoute.filename}`);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
const baseChild = {
|
|
320
|
-
path: '',
|
|
321
|
-
component: m.default,
|
|
322
|
-
...toRouteConfig(m.routeMeta),
|
|
323
|
-
children,
|
|
324
|
-
[ANALOG_META_KEY]: analogMeta,
|
|
325
|
-
};
|
|
326
|
-
// Base route first so static matches win, then optional catch-all matcher
|
|
327
|
-
return [
|
|
328
|
-
{
|
|
329
|
-
...baseChild,
|
|
330
|
-
},
|
|
331
|
-
...(optCatchAllParam
|
|
332
|
-
? [
|
|
333
|
-
{
|
|
334
|
-
matcher: createOptionalCatchAllMatcher(optCatchAllParam),
|
|
335
|
-
component: m.default,
|
|
336
|
-
...toRouteConfig(m.routeMeta),
|
|
337
|
-
[ANALOG_META_KEY]: analogMeta,
|
|
338
|
-
},
|
|
339
|
-
]
|
|
340
|
-
: []),
|
|
341
|
-
];
|
|
342
|
-
}),
|
|
343
|
-
}
|
|
344
|
-
: {
|
|
345
|
-
path: rawRoute.segment,
|
|
346
|
-
...(debug
|
|
347
|
-
? {
|
|
348
|
-
filename: rawRoute.filename ? rawRoute.filename : undefined,
|
|
349
|
-
isLayout: children && children.length > 0 ? true : false,
|
|
350
|
-
}
|
|
351
|
-
: {}),
|
|
352
|
-
children,
|
|
353
|
-
};
|
|
354
|
-
routes.push(route);
|
|
355
|
-
}
|
|
356
|
-
return routes;
|
|
357
|
-
}
|
|
358
|
-
function sortRawRoutes(rawRoutes) {
|
|
359
|
-
rawRoutes.sort((a, b) => {
|
|
360
|
-
let segmentA = deprioritizeSegment(a.segment);
|
|
361
|
-
let segmentB = deprioritizeSegment(b.segment);
|
|
362
|
-
// prioritize routes with fewer children
|
|
363
|
-
if (a.children.length > b.children.length) {
|
|
364
|
-
segmentA = `~${segmentA}`;
|
|
365
|
-
}
|
|
366
|
-
else if (a.children.length < b.children.length) {
|
|
367
|
-
segmentB = `~${segmentB}`;
|
|
368
|
-
}
|
|
369
|
-
return segmentA > segmentB ? 1 : -1;
|
|
370
|
-
});
|
|
371
|
-
for (const rawRoute of rawRoutes) {
|
|
372
|
-
sortRawRoutes(rawRoute.children);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
function deprioritizeSegment(segment) {
|
|
376
|
-
// deprioritize param and wildcard segments
|
|
377
|
-
return segment.replace(':', '~~').replace('**', '~~~~');
|
|
378
|
-
}
|
|
379
|
-
const routes = createRoutes({
|
|
380
|
-
...ANALOG_ROUTE_FILES,
|
|
381
|
-
...ANALOG_CONTENT_ROUTE_FILES,
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* @deprecated Use `RouteMeta` type instead.
|
|
386
|
-
* For more info see: https://github.com/analogjs/analog/issues/223
|
|
387
|
-
*
|
|
388
|
-
* Defines additional route config metadata. This
|
|
389
|
-
* object is merged into the route config with
|
|
390
|
-
* the predefined file-based route.
|
|
391
|
-
*
|
|
392
|
-
* @usageNotes
|
|
393
|
-
*
|
|
394
|
-
* ```
|
|
395
|
-
* import { Component } from '@angular/core';
|
|
396
|
-
* import { defineRouteMeta } from '@analogjs/router';
|
|
397
|
-
*
|
|
398
|
-
* export const routeMeta = defineRouteMeta({
|
|
399
|
-
* title: 'Welcome'
|
|
400
|
-
* });
|
|
401
|
-
*
|
|
402
|
-
* @Component({
|
|
403
|
-
* template: `Home`,
|
|
404
|
-
* standalone: true,
|
|
405
|
-
* })
|
|
406
|
-
* export default class HomeComponent {}
|
|
407
|
-
* ```
|
|
408
|
-
*
|
|
409
|
-
* @param route
|
|
410
|
-
* @returns
|
|
411
|
-
*/
|
|
412
|
-
const defineRouteMeta = (route) => {
|
|
413
|
-
return route;
|
|
12
|
+
* @deprecated Use `RouteMeta` type instead.
|
|
13
|
+
* For more info see: https://github.com/analogjs/analog/issues/223
|
|
14
|
+
*
|
|
15
|
+
* Defines additional route config metadata. This
|
|
16
|
+
* object is merged into the route config with
|
|
17
|
+
* the predefined file-based route.
|
|
18
|
+
*
|
|
19
|
+
* @usageNotes
|
|
20
|
+
*
|
|
21
|
+
* ```
|
|
22
|
+
* import { Component } from '@angular/core';
|
|
23
|
+
* import { defineRouteMeta } from '@analogjs/router';
|
|
24
|
+
*
|
|
25
|
+
* export const routeMeta = defineRouteMeta({
|
|
26
|
+
* title: 'Welcome'
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* @Component({
|
|
30
|
+
* template: `Home`,
|
|
31
|
+
* standalone: true,
|
|
32
|
+
* })
|
|
33
|
+
* export default class HomeComponent {}
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @param route
|
|
37
|
+
* @returns
|
|
38
|
+
*/
|
|
39
|
+
var defineRouteMeta = (route) => {
|
|
40
|
+
return route;
|
|
414
41
|
};
|
|
415
42
|
/**
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
43
|
+
* Returns the instance of Angular Router
|
|
44
|
+
*
|
|
45
|
+
* @returns The router
|
|
46
|
+
*/
|
|
47
|
+
var injectRouter = () => {
|
|
48
|
+
return inject(Router);
|
|
422
49
|
};
|
|
423
50
|
/**
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
51
|
+
* Returns the instance of the Activate Route for the component
|
|
52
|
+
*
|
|
53
|
+
* @returns The activated route
|
|
54
|
+
*/
|
|
55
|
+
var injectActivatedRoute = () => {
|
|
56
|
+
return inject(ActivatedRoute);
|
|
430
57
|
};
|
|
431
|
-
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region packages/router/src/lib/cookie-interceptor.ts
|
|
432
60
|
function cookieInterceptor(req, next, location = inject(PLATFORM_ID), serverRequest = injectRequest()) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
});
|
|
440
|
-
return next(cookiedRequest);
|
|
441
|
-
}
|
|
442
|
-
else {
|
|
443
|
-
return next(req);
|
|
444
|
-
}
|
|
61
|
+
if (isPlatformServer(location) && req.url.includes("/_analog/")) {
|
|
62
|
+
let headers = new HttpHeaders();
|
|
63
|
+
const cookies = serverRequest?.headers.cookie;
|
|
64
|
+
headers = headers.set("cookie", cookies ?? "");
|
|
65
|
+
return next(req.clone({ headers }));
|
|
66
|
+
} else return next(req);
|
|
445
67
|
}
|
|
446
|
-
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region packages/router/src/lib/provide-file-router.ts
|
|
447
70
|
/**
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
71
|
+
* Sets up providers for the Angular router, and registers
|
|
72
|
+
* file-based routes. Additional features can be provided
|
|
73
|
+
* to further configure the behavior of the router.
|
|
74
|
+
*
|
|
75
|
+
* @param features
|
|
76
|
+
* @returns Providers and features to configure the router with routes
|
|
77
|
+
*/
|
|
455
78
|
function provideFileRouter(...features) {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
},
|
|
479
|
-
]);
|
|
79
|
+
const extraRoutesFeature = features.filter((feat) => feat.ɵkind >= 100);
|
|
80
|
+
const routerFeatures = features.filter((feat) => feat.ɵkind < 100);
|
|
81
|
+
return makeEnvironmentProviders([
|
|
82
|
+
extraRoutesFeature.map((erf) => erf.ɵproviders),
|
|
83
|
+
provideRouter(routes, ...routerFeatures),
|
|
84
|
+
{
|
|
85
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
86
|
+
multi: true,
|
|
87
|
+
useValue: () => updateMetaTagsOnRouteChange()
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
provide: ɵHTTP_ROOT_INTERCEPTOR_FNS,
|
|
91
|
+
multi: true,
|
|
92
|
+
useValue: cookieInterceptor
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
provide: API_PREFIX,
|
|
96
|
+
useFactory() {
|
|
97
|
+
return typeof ANALOG_API_PREFIX !== "undefined" ? ANALOG_API_PREFIX : "api";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
]);
|
|
480
101
|
}
|
|
481
102
|
/**
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
103
|
+
* Provides extra custom routes in addition to the routes
|
|
104
|
+
* discovered from the filesystem-based routing. These routes are
|
|
105
|
+
* inserted before the filesystem-based routes, and take priority in
|
|
106
|
+
* route matching.
|
|
107
|
+
*/
|
|
487
108
|
function withExtraRoutes(routes) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
109
|
+
return {
|
|
110
|
+
ɵkind: 100,
|
|
111
|
+
ɵproviders: [{
|
|
112
|
+
provide: ROUTES,
|
|
113
|
+
useValue: routes,
|
|
114
|
+
multi: true
|
|
115
|
+
}]
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region packages/router/src/lib/inject-load.ts
|
|
120
|
+
function isResponse(value) {
|
|
121
|
+
return typeof value === "object" && value instanceof Response;
|
|
492
122
|
}
|
|
493
|
-
|
|
494
123
|
function injectLoad(options) {
|
|
495
|
-
|
|
496
|
-
const route = injector.get(ActivatedRoute);
|
|
497
|
-
return route.data.pipe(map((data) => data['load']));
|
|
124
|
+
return (options?.injector ?? inject(Injector)).get(ActivatedRoute).data.pipe(map((data) => data["load"]));
|
|
498
125
|
}
|
|
499
|
-
|
|
126
|
+
function injectLoadData(options) {
|
|
127
|
+
return injectLoad(options).pipe(map((result) => {
|
|
128
|
+
if (isResponse(result)) throw new Error("Expected page load data but received a response.");
|
|
129
|
+
return result;
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region packages/router/src/lib/get-load-resolver.ts
|
|
500
134
|
/**
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
135
|
+
* Get server load resolver data for the route
|
|
136
|
+
*
|
|
137
|
+
* @param route Provides the route to get server load resolver
|
|
138
|
+
* @returns Returns server load resolver data for the route
|
|
139
|
+
*/
|
|
506
140
|
async function getLoadResolver(route) {
|
|
507
|
-
|
|
141
|
+
return route.routeConfig?.resolve?.["load"]?.(route);
|
|
508
142
|
}
|
|
509
|
-
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region packages/router/src/lib/cache-key.ts
|
|
510
145
|
function sortAndConcatParams(params) {
|
|
511
|
-
|
|
512
|
-
.sort()
|
|
513
|
-
.map((k) => `${k}=${params.getAll(k)}`)
|
|
514
|
-
.join('&');
|
|
146
|
+
return [...params.keys()].sort().map((k) => `${k}=${params.getAll(k)}`).join("&");
|
|
515
147
|
}
|
|
516
148
|
function makeCacheKey(request, mappedRequestUrl) {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
responseType,
|
|
530
|
-
mappedRequestUrl,
|
|
531
|
-
serializedBody,
|
|
532
|
-
encodedParams,
|
|
533
|
-
].join('|');
|
|
534
|
-
const hash = generateHash(key);
|
|
535
|
-
return makeStateKey(hash);
|
|
149
|
+
const { params, method, responseType } = request;
|
|
150
|
+
const encodedParams = sortAndConcatParams(params);
|
|
151
|
+
let serializedBody = request.serializeBody();
|
|
152
|
+
if (serializedBody instanceof URLSearchParams) serializedBody = sortAndConcatParams(serializedBody);
|
|
153
|
+
else if (typeof serializedBody !== "string") serializedBody = "";
|
|
154
|
+
return makeStateKey(generateHash([
|
|
155
|
+
method,
|
|
156
|
+
responseType,
|
|
157
|
+
mappedRequestUrl,
|
|
158
|
+
serializedBody,
|
|
159
|
+
encodedParams
|
|
160
|
+
].join("|")));
|
|
536
161
|
}
|
|
537
162
|
function generateHash(str) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
163
|
+
let hash = 0;
|
|
164
|
+
for (let i = 0, len = str.length; i < len; i++) {
|
|
165
|
+
const chr = str.charCodeAt(i);
|
|
166
|
+
hash = (hash << 5) - hash + chr;
|
|
167
|
+
hash |= 0;
|
|
168
|
+
}
|
|
169
|
+
return `${hash}`;
|
|
545
170
|
}
|
|
546
|
-
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region packages/router/src/lib/request-context.ts
|
|
547
173
|
/**
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
174
|
+
* Interceptor that is server-aware when making HttpClient requests.
|
|
175
|
+
* Server-side requests use the full URL
|
|
176
|
+
* Prerendering uses the internal Nitro $fetch function, along with state transfer
|
|
177
|
+
* Client-side requests use the window.location.origin
|
|
178
|
+
*
|
|
179
|
+
* @param req HttpRequest<unknown>
|
|
180
|
+
* @param next HttpHandlerFn
|
|
181
|
+
* @returns
|
|
182
|
+
*/
|
|
557
183
|
function requestContextInterceptor(req, next) {
|
|
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
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
const storeKey = makeStateKey(`analog_${cacheKey}`);
|
|
608
|
-
const cacheRestoreResponse = transferState.get(storeKey, null);
|
|
609
|
-
if (cacheRestoreResponse) {
|
|
610
|
-
transferState.remove(storeKey);
|
|
611
|
-
return of(new HttpResponse(cacheRestoreResponse));
|
|
612
|
-
}
|
|
613
|
-
return next(req.clone({
|
|
614
|
-
url: requestUrl,
|
|
615
|
-
}));
|
|
616
|
-
}
|
|
617
|
-
// on the server
|
|
618
|
-
if (baseUrl && (req.url.startsWith('/') || req.url.startsWith(baseUrl))) {
|
|
619
|
-
const requestUrl = req.url.startsWith(baseUrl) && !req.url.startsWith('/')
|
|
620
|
-
? req.url
|
|
621
|
-
: `${baseUrl}${req.url}`;
|
|
622
|
-
return next(req.clone({
|
|
623
|
-
url: requestUrl,
|
|
624
|
-
}));
|
|
625
|
-
}
|
|
626
|
-
return next(req);
|
|
184
|
+
const apiPrefix = injectAPIPrefix();
|
|
185
|
+
const baseUrl = injectBaseURL();
|
|
186
|
+
const transferState = inject(TransferState);
|
|
187
|
+
const nitroGlobal = globalThis;
|
|
188
|
+
const serverFetch = injectInternalServerFetch() ?? nitroGlobal.$fetch;
|
|
189
|
+
if (serverFetch && baseUrl && (req.url.startsWith("/") || req.url.startsWith(baseUrl) || req.url.startsWith(`/${apiPrefix}`))) {
|
|
190
|
+
const requestUrl = new URL(req.url, baseUrl);
|
|
191
|
+
const storeKey = makeStateKey(`analog_${makeCacheKey(req, new URL(requestUrl).pathname)}`);
|
|
192
|
+
const fetchUrl = requestUrl.pathname;
|
|
193
|
+
const responseType = req.responseType === "arraybuffer" ? "arrayBuffer" : req.responseType;
|
|
194
|
+
return from(serverFetch.raw(fetchUrl, {
|
|
195
|
+
method: req.method,
|
|
196
|
+
body: req.body ? req.body : void 0,
|
|
197
|
+
params: requestUrl.searchParams,
|
|
198
|
+
responseType,
|
|
199
|
+
headers: req.headers.keys().reduce((hdrs, current) => {
|
|
200
|
+
return {
|
|
201
|
+
...hdrs,
|
|
202
|
+
[current]: req.headers.get(current)
|
|
203
|
+
};
|
|
204
|
+
}, {})
|
|
205
|
+
}).then((res) => {
|
|
206
|
+
const cacheResponse = {
|
|
207
|
+
body: res._data,
|
|
208
|
+
headers: new HttpHeaders(res.headers),
|
|
209
|
+
status: res.status ?? 200,
|
|
210
|
+
statusText: res.statusText ?? "OK",
|
|
211
|
+
url: fetchUrl
|
|
212
|
+
};
|
|
213
|
+
const transferResponse = new HttpResponse(cacheResponse);
|
|
214
|
+
transferState.set(storeKey, cacheResponse);
|
|
215
|
+
return transferResponse;
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
if (req.url.startsWith("/") || req.url.includes("/_analog/")) {
|
|
219
|
+
const requestUrl = req.url.includes("/_analog/") ? req.url : `${window.location.origin}${req.url}`;
|
|
220
|
+
const storeKey = makeStateKey(`analog_${makeCacheKey(req, new URL(requestUrl).pathname)}`);
|
|
221
|
+
const cacheRestoreResponse = transferState.get(storeKey, null);
|
|
222
|
+
if (cacheRestoreResponse) {
|
|
223
|
+
transferState.remove(storeKey);
|
|
224
|
+
return of(new HttpResponse(cacheRestoreResponse));
|
|
225
|
+
}
|
|
226
|
+
return next(req.clone({ url: requestUrl }));
|
|
227
|
+
}
|
|
228
|
+
if (baseUrl && (req.url.startsWith("/") || req.url.startsWith(baseUrl))) {
|
|
229
|
+
const requestUrl = req.url.startsWith(baseUrl) && !req.url.startsWith("/") ? req.url : `${baseUrl}${req.url}`;
|
|
230
|
+
return next(req.clone({ url: requestUrl }));
|
|
231
|
+
}
|
|
232
|
+
return next(req);
|
|
627
233
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region packages/router/src/lib/form-action.directive.ts
|
|
236
|
+
var FormAction = class FormAction {
|
|
237
|
+
constructor() {
|
|
238
|
+
this.action = input("", ...[]);
|
|
239
|
+
this.onSuccess = output();
|
|
240
|
+
this.onError = output();
|
|
241
|
+
this.state = output();
|
|
242
|
+
this.router = inject(Router);
|
|
243
|
+
this.route = inject(ActivatedRoute);
|
|
244
|
+
this.currentState = signal("idle", ...[]);
|
|
245
|
+
/** Cached during construction (injection context) so inject() works. */
|
|
246
|
+
this._endpointUrl = this.route ? injectRouteEndpointURL(this.route.snapshot) : void 0;
|
|
247
|
+
}
|
|
248
|
+
submitted($event) {
|
|
249
|
+
$event.preventDefault();
|
|
250
|
+
const form = $event.target;
|
|
251
|
+
this._emitState("submitting");
|
|
252
|
+
const body = new FormData(form);
|
|
253
|
+
if (form.method.toUpperCase() === "GET") this._handleGet(body, this._getGetPath(form));
|
|
254
|
+
else this._handlePost(body, this._getPostPath(form), form.method);
|
|
255
|
+
}
|
|
256
|
+
_handleGet(body, path) {
|
|
257
|
+
const url = new URL(path, window.location.href);
|
|
258
|
+
const params = new URLSearchParams(url.search);
|
|
259
|
+
body.forEach((value, key) => {
|
|
260
|
+
params.append(key, value instanceof File ? value.name : value);
|
|
261
|
+
});
|
|
262
|
+
url.search = params.toString();
|
|
263
|
+
this._emitState("navigate");
|
|
264
|
+
this._navigateTo(url);
|
|
265
|
+
}
|
|
266
|
+
_handlePost(body, path, method) {
|
|
267
|
+
fetch(path, {
|
|
268
|
+
method,
|
|
269
|
+
body
|
|
270
|
+
}).then((res) => {
|
|
271
|
+
if (res.ok) if (res.redirected) {
|
|
272
|
+
this._emitState("redirect");
|
|
273
|
+
this._navigateTo(new URL(res.url, window.location.href));
|
|
274
|
+
} else if (this._isJSON(res.headers.get("Content-type"))) res.json().then((result) => {
|
|
275
|
+
this.onSuccess.emit(result);
|
|
276
|
+
this._emitState("success");
|
|
277
|
+
});
|
|
278
|
+
else res.text().then((result) => {
|
|
279
|
+
this.onSuccess.emit(result);
|
|
280
|
+
this._emitState("success");
|
|
281
|
+
});
|
|
282
|
+
else if (res.headers.get("X-Analog-Errors")) res.json().then((errors) => {
|
|
283
|
+
this.onError.emit(errors);
|
|
284
|
+
this._emitState("error");
|
|
285
|
+
});
|
|
286
|
+
else this._emitState("error");
|
|
287
|
+
}).catch((_) => {
|
|
288
|
+
this._emitState("error");
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
_getExplicitAction(form) {
|
|
292
|
+
return this.action().trim() || form.getAttribute("action")?.trim() || void 0;
|
|
293
|
+
}
|
|
294
|
+
_getGetPath(form) {
|
|
295
|
+
return this._getExplicitAction(form) ?? this.router.url;
|
|
296
|
+
}
|
|
297
|
+
_getPostPath(form) {
|
|
298
|
+
const explicitAction = this._getExplicitAction(form);
|
|
299
|
+
if (explicitAction) return new URL(explicitAction, window.location.href).toString();
|
|
300
|
+
if (this._endpointUrl) return this._endpointUrl.pathname;
|
|
301
|
+
return `/api/_analog/pages${window.location.pathname}`;
|
|
302
|
+
}
|
|
303
|
+
_emitState(state) {
|
|
304
|
+
this.currentState.set(state);
|
|
305
|
+
this.state.emit(state);
|
|
306
|
+
}
|
|
307
|
+
_navigateTo(url) {
|
|
308
|
+
if (url.origin === window.location.origin) {
|
|
309
|
+
this.router.navigateByUrl(`${url.pathname}${url.search}${url.hash}`, { onSameUrlNavigation: "reload" });
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
window.location.assign(url.toString());
|
|
313
|
+
}
|
|
314
|
+
_isJSON(contentType) {
|
|
315
|
+
return (contentType ? contentType.split(";") : [])[0] === "application/json";
|
|
316
|
+
}
|
|
317
|
+
static {
|
|
318
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
319
|
+
minVersion: "12.0.0",
|
|
320
|
+
version: "21.1.1",
|
|
321
|
+
ngImport: i0,
|
|
322
|
+
type: FormAction,
|
|
323
|
+
deps: [],
|
|
324
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
static {
|
|
328
|
+
this.ɵdir = i0.ɵɵngDeclareDirective({
|
|
329
|
+
minVersion: "17.1.0",
|
|
330
|
+
version: "21.1.1",
|
|
331
|
+
type: FormAction,
|
|
332
|
+
isStandalone: true,
|
|
333
|
+
selector: "form[action],form[method]",
|
|
334
|
+
inputs: { action: {
|
|
335
|
+
classPropertyName: "action",
|
|
336
|
+
publicName: "action",
|
|
337
|
+
isSignal: true,
|
|
338
|
+
isRequired: false,
|
|
339
|
+
transformFunction: null
|
|
340
|
+
} },
|
|
341
|
+
outputs: {
|
|
342
|
+
onSuccess: "onSuccess",
|
|
343
|
+
onError: "onError",
|
|
344
|
+
state: "state"
|
|
345
|
+
},
|
|
346
|
+
host: {
|
|
347
|
+
listeners: { "submit": "submitted($event)" },
|
|
348
|
+
properties: {
|
|
349
|
+
"attr.data-state": "currentState()",
|
|
350
|
+
"attr.aria-busy": "currentState() === \"submitting\" ? \"true\" : null"
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
ngImport: i0
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
358
|
+
minVersion: "12.0.0",
|
|
359
|
+
version: "21.1.1",
|
|
360
|
+
ngImport: i0,
|
|
361
|
+
type: FormAction,
|
|
362
|
+
decorators: [{
|
|
363
|
+
type: Directive,
|
|
364
|
+
args: [{
|
|
365
|
+
selector: "form[action],form[method]",
|
|
366
|
+
host: {
|
|
367
|
+
"(submit)": `submitted($event)`,
|
|
368
|
+
"[attr.data-state]": "currentState()",
|
|
369
|
+
"[attr.aria-busy]": "currentState() === \"submitting\" ? \"true\" : null"
|
|
370
|
+
},
|
|
371
|
+
standalone: true
|
|
372
|
+
}]
|
|
373
|
+
}],
|
|
374
|
+
propDecorators: {
|
|
375
|
+
action: [{
|
|
376
|
+
type: i0.Input,
|
|
377
|
+
args: [{
|
|
378
|
+
isSignal: true,
|
|
379
|
+
alias: "action",
|
|
380
|
+
required: false
|
|
381
|
+
}]
|
|
382
|
+
}],
|
|
383
|
+
onSuccess: [{
|
|
384
|
+
type: i0.Output,
|
|
385
|
+
args: ["onSuccess"]
|
|
386
|
+
}],
|
|
387
|
+
onError: [{
|
|
388
|
+
type: i0.Output,
|
|
389
|
+
args: ["onError"]
|
|
390
|
+
}],
|
|
391
|
+
state: [{
|
|
392
|
+
type: i0.Output,
|
|
393
|
+
args: ["state"]
|
|
394
|
+
}]
|
|
395
|
+
}
|
|
735
396
|
});
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region packages/router/src/lib/debug/index.ts
|
|
740
399
|
/**
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
400
|
+
* Provides routes that provide additional
|
|
401
|
+
* pages for displaying and debugging
|
|
402
|
+
* routes.
|
|
403
|
+
*/
|
|
745
404
|
function withDebugRoutes() {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
405
|
+
return {
|
|
406
|
+
ɵkind: 101,
|
|
407
|
+
ɵproviders: [{
|
|
408
|
+
provide: ROUTES,
|
|
409
|
+
useValue: [{
|
|
410
|
+
path: "__analog/routes",
|
|
411
|
+
loadComponent: () => import("./debug.page.mjs")
|
|
412
|
+
}],
|
|
413
|
+
multi: true
|
|
414
|
+
}]
|
|
415
|
+
};
|
|
756
416
|
}
|
|
757
|
-
|
|
417
|
+
//#endregion
|
|
418
|
+
//#region packages/router/src/lib/server.component.ts
|
|
758
419
|
/**
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
class ServerOnly {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
420
|
+
* @description
|
|
421
|
+
* Component that defines the bridge between the client and server-only
|
|
422
|
+
* components. The component passes the component ID and props to the server
|
|
423
|
+
* and retrieves the rendered HTML and outputs from the server-only component.
|
|
424
|
+
*
|
|
425
|
+
* Status: experimental
|
|
426
|
+
*/
|
|
427
|
+
var ServerOnly = class ServerOnly {
|
|
428
|
+
constructor() {
|
|
429
|
+
this.component = input.required(...[]);
|
|
430
|
+
this.props = input(...[]);
|
|
431
|
+
this.outputs = output();
|
|
432
|
+
this.http = inject(HttpClient);
|
|
433
|
+
this.sanitizer = inject(DomSanitizer);
|
|
434
|
+
this.content = signal("", ...[]);
|
|
435
|
+
this.route = inject(ActivatedRoute, { optional: true });
|
|
436
|
+
this.baseURL = injectBaseURL();
|
|
437
|
+
this.transferState = inject(TransferState);
|
|
438
|
+
effect(() => {
|
|
439
|
+
const routeComponentId = this.route?.snapshot.data["component"];
|
|
440
|
+
const props = this.props() || {};
|
|
441
|
+
const componentId = routeComponentId || this.component();
|
|
442
|
+
const headers = new HttpHeaders(new Headers({
|
|
443
|
+
"Content-type": "application/json",
|
|
444
|
+
"X-Analog-Component": componentId
|
|
445
|
+
}));
|
|
446
|
+
const componentUrl = this.getComponentUrl(componentId);
|
|
447
|
+
const httpRequest = new HttpRequest("POST", componentUrl, props, { headers });
|
|
448
|
+
const storeKey = makeStateKey(makeCacheKey(httpRequest, new URL(componentUrl).pathname));
|
|
449
|
+
const componentState = this.transferState.get(storeKey, null);
|
|
450
|
+
if (componentState) {
|
|
451
|
+
this.updateContent(componentState);
|
|
452
|
+
this.transferState.remove(storeKey);
|
|
453
|
+
} else this.http.request(httpRequest).pipe(map((response) => {
|
|
454
|
+
if (response instanceof HttpResponse) return response.body;
|
|
455
|
+
return throwError(() => ({}));
|
|
456
|
+
}), catchError((error) => {
|
|
457
|
+
console.log(error);
|
|
458
|
+
return of({
|
|
459
|
+
html: "",
|
|
460
|
+
outputs: {}
|
|
461
|
+
});
|
|
462
|
+
})).subscribe((content) => this.updateContent(content));
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
updateContent(content) {
|
|
466
|
+
this.content.set(this.sanitizer.bypassSecurityTrustHtml(content.html));
|
|
467
|
+
this.outputs.emit(content.outputs);
|
|
468
|
+
}
|
|
469
|
+
getComponentUrl(componentId) {
|
|
470
|
+
let baseURL = this.baseURL;
|
|
471
|
+
if (!baseURL && typeof window !== "undefined") baseURL = window.location.origin;
|
|
472
|
+
return `${baseURL}/_analog/components/${componentId}`;
|
|
473
|
+
}
|
|
474
|
+
static {
|
|
475
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
476
|
+
minVersion: "12.0.0",
|
|
477
|
+
version: "21.1.1",
|
|
478
|
+
ngImport: i0,
|
|
479
|
+
type: ServerOnly,
|
|
480
|
+
deps: [],
|
|
481
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
static {
|
|
485
|
+
this.ɵcmp = i0.ɵɵngDeclareComponent({
|
|
486
|
+
minVersion: "17.1.0",
|
|
487
|
+
version: "21.1.1",
|
|
488
|
+
type: ServerOnly,
|
|
489
|
+
isStandalone: true,
|
|
490
|
+
selector: "server-only,ServerOnly,Server",
|
|
491
|
+
inputs: {
|
|
492
|
+
component: {
|
|
493
|
+
classPropertyName: "component",
|
|
494
|
+
publicName: "component",
|
|
495
|
+
isSignal: true,
|
|
496
|
+
isRequired: true,
|
|
497
|
+
transformFunction: null
|
|
498
|
+
},
|
|
499
|
+
props: {
|
|
500
|
+
classPropertyName: "props",
|
|
501
|
+
publicName: "props",
|
|
502
|
+
isSignal: true,
|
|
503
|
+
isRequired: false,
|
|
504
|
+
transformFunction: null
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
outputs: { outputs: "outputs" },
|
|
508
|
+
ngImport: i0,
|
|
509
|
+
template: ` <div [innerHTML]="content()"></div> `,
|
|
510
|
+
isInline: true,
|
|
511
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
516
|
+
minVersion: "12.0.0",
|
|
517
|
+
version: "21.1.1",
|
|
518
|
+
ngImport: i0,
|
|
519
|
+
type: ServerOnly,
|
|
520
|
+
decorators: [{
|
|
521
|
+
type: Component,
|
|
522
|
+
args: [{
|
|
523
|
+
selector: "server-only,ServerOnly,Server",
|
|
524
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
525
|
+
template: ` <div [innerHTML]="content()"></div> `
|
|
526
|
+
}]
|
|
527
|
+
}],
|
|
528
|
+
ctorParameters: () => [],
|
|
529
|
+
propDecorators: {
|
|
530
|
+
component: [{
|
|
531
|
+
type: i0.Input,
|
|
532
|
+
args: [{
|
|
533
|
+
isSignal: true,
|
|
534
|
+
alias: "component",
|
|
535
|
+
required: true
|
|
536
|
+
}]
|
|
537
|
+
}],
|
|
538
|
+
props: [{
|
|
539
|
+
type: i0.Input,
|
|
540
|
+
args: [{
|
|
541
|
+
isSignal: true,
|
|
542
|
+
alias: "props",
|
|
543
|
+
required: false
|
|
544
|
+
}]
|
|
545
|
+
}],
|
|
546
|
+
outputs: [{
|
|
547
|
+
type: i0.Output,
|
|
548
|
+
args: ["outputs"]
|
|
549
|
+
}]
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
//#endregion
|
|
553
|
+
export { FormAction, ServerOnly, createRoutes, defineRouteMeta, getLoadResolver, injectActivatedRoute, injectDebugRoutes, injectLoad, injectLoadData, injectRouteEndpointURL, injectRouter, provideFileRouter, requestContextInterceptor, routes, withDebugRoutes, withExtraRoutes };
|
|
844
554
|
|
|
845
|
-
|
|
846
|
-
//# sourceMappingURL=analogjs-router.mjs.map
|
|
555
|
+
//# sourceMappingURL=analogjs-router.mjs.map
|