@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.
Files changed (60) hide show
  1. package/fesm2022/analogjs-router-server-actions.mjs +327 -31
  2. package/fesm2022/analogjs-router-server.mjs +155 -176
  3. package/fesm2022/analogjs-router-tanstack-query.mjs +58 -0
  4. package/fesm2022/analogjs-router-tokens.mjs +17 -16
  5. package/fesm2022/analogjs-router.mjs +521 -812
  6. package/fesm2022/debug.page.mjs +121 -0
  7. package/fesm2022/routes.mjs +301 -0
  8. package/package.json +44 -26
  9. package/server/actions/package.json +4 -0
  10. package/server/package.json +4 -0
  11. package/tokens/package.json +4 -0
  12. package/types/server/actions/src/actions.d.ts +13 -0
  13. package/types/server/actions/src/define-action.d.ts +54 -0
  14. package/types/server/actions/src/define-page-load.d.ts +55 -0
  15. package/types/server/actions/src/define-server-route.d.ts +68 -0
  16. package/types/server/actions/src/index.d.ts +9 -0
  17. package/types/server/actions/src/parse-request-data.d.ts +9 -0
  18. package/types/server/actions/src/validate.d.ts +8 -0
  19. package/types/server/src/index.d.ts +4 -0
  20. package/types/server/src/provide-server-context.d.ts +11 -0
  21. package/types/server/src/render.d.ts +12 -0
  22. package/types/server/src/server-component-render.d.ts +4 -0
  23. package/types/server/src/tokens.d.ts +7 -0
  24. package/types/src/index.d.ts +16 -0
  25. package/types/src/lib/cache-key.d.ts +3 -0
  26. package/types/src/lib/constants.d.ts +2 -0
  27. package/types/src/lib/cookie-interceptor.d.ts +4 -0
  28. package/types/src/lib/debug/debug.page.d.ts +18 -0
  29. package/types/src/lib/debug/index.d.ts +10 -0
  30. package/types/src/lib/debug/routes.d.ts +10 -0
  31. package/types/src/lib/define-route.d.ts +46 -0
  32. package/types/src/lib/endpoints.d.ts +5 -0
  33. package/types/src/lib/form-action.directive.d.ts +25 -0
  34. package/types/src/lib/get-load-resolver.d.ts +8 -0
  35. package/types/src/lib/inject-load.d.ts +9 -0
  36. package/types/src/lib/inject-route-endpoint-url.d.ts +2 -0
  37. package/types/src/lib/markdown-helpers.d.ts +2 -0
  38. package/types/src/lib/meta-tags.d.ts +33 -0
  39. package/types/src/lib/models.d.ts +29 -0
  40. package/types/src/lib/provide-file-router.d.ts +18 -0
  41. package/types/src/lib/request-context.d.ts +13 -0
  42. package/types/src/lib/route-config.d.ts +2 -0
  43. package/types/src/lib/route-types.d.ts +12 -0
  44. package/types/src/lib/routes.d.ts +19 -0
  45. package/types/src/lib/server.component.d.ts +33 -0
  46. package/types/tanstack-query/src/index.d.ts +2 -0
  47. package/types/tanstack-query/src/provide-analog-query.d.ts +4 -0
  48. package/types/tanstack-query/src/provide-server-analog-query.d.ts +2 -0
  49. package/types/tanstack-query/src/server-query.d.ts +16 -0
  50. package/types/tokens/src/index.d.ts +23 -0
  51. package/fesm2022/analogjs-router-debug.page-jzggTA45.mjs +0 -91
  52. package/fesm2022/analogjs-router-debug.page-jzggTA45.mjs.map +0 -1
  53. package/fesm2022/analogjs-router-server-actions.mjs.map +0 -1
  54. package/fesm2022/analogjs-router-server.mjs.map +0 -1
  55. package/fesm2022/analogjs-router-tokens.mjs.map +0 -1
  56. package/fesm2022/analogjs-router.mjs.map +0 -1
  57. package/types/analogjs-router-server-actions.d.ts +0 -16
  58. package/types/analogjs-router-server.d.ts +0 -28
  59. package/types/analogjs-router-tokens.d.ts +0 -22
  60. package/types/analogjs-router.d.ts +0 -268
@@ -0,0 +1,121 @@
1
+ import { t as injectDebugRoutes } from "./routes.mjs";
2
+ import * as i0 from "@angular/core";
3
+ import { Component } from "@angular/core";
4
+ //#region packages/router/src/lib/debug/debug.page.ts
5
+ var DebugRoutesComponent = class DebugRoutesComponent {
6
+ constructor() {
7
+ this.collectedRoutes = [];
8
+ this.debugRoutes = injectDebugRoutes();
9
+ }
10
+ ngOnInit() {
11
+ this.traverseRoutes(this.debugRoutes);
12
+ }
13
+ traverseRoutes(routes, parent) {
14
+ routes.forEach((route) => {
15
+ this.collectedRoutes.push({
16
+ path: route.isLayout ? `${parent ? `/${parent}` : ""}${route.path ? `/${route.path}` : ""}` : `${parent ? `/${parent}` : ""}${route.path ? `/${route.path}` : "/"}`,
17
+ filename: route.filename,
18
+ file: route.filename?.replace(/(^.*)pages\//, "") || "",
19
+ isLayout: route.isLayout
20
+ });
21
+ if (route.children) {
22
+ const fullParentPath = [parent, route.path].filter((s) => !!s).join("/");
23
+ this.traverseRoutes(route.children, fullParentPath);
24
+ }
25
+ });
26
+ }
27
+ static {
28
+ this.ɵfac = i0.ɵɵngDeclareFactory({
29
+ minVersion: "12.0.0",
30
+ version: "21.1.1",
31
+ ngImport: i0,
32
+ type: DebugRoutesComponent,
33
+ deps: [],
34
+ target: i0.ɵɵFactoryTarget.Component
35
+ });
36
+ }
37
+ static {
38
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
39
+ minVersion: "17.0.0",
40
+ version: "21.1.1",
41
+ type: DebugRoutesComponent,
42
+ isStandalone: true,
43
+ selector: "analogjs-debug-routes-page",
44
+ ngImport: i0,
45
+ template: `
46
+ <h2>Routes</h2>
47
+
48
+ <div class="table-container">
49
+ <div class="table-header">
50
+ <div class="header-cell">Route Path</div>
51
+ <div class="header-cell">File</div>
52
+ <div class="header-cell">Type</div>
53
+ </div>
54
+ <div class="table-body">
55
+ @for (
56
+ collectedRoute of collectedRoutes;
57
+ track collectedRoute.filename
58
+ ) {
59
+ <div class="table-row">
60
+ <div class="table-cell">{{ collectedRoute.path }}</div>
61
+ <div class="table-cell" [title]="collectedRoute.filename">
62
+ {{ collectedRoute.file }}
63
+ </div>
64
+ <div class="table-cell">
65
+ {{ collectedRoute.isLayout ? 'Layout' : 'Page' }}
66
+ </div>
67
+ </div>
68
+ }
69
+ </div>
70
+ </div>
71
+ `,
72
+ isInline: true,
73
+ styles: ["\n :host {\n width: 100%;\n }\n\n .table-container {\n width: 100%;\n max-width: 900px;\n margin: 0 auto;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n overflow: hidden;\n }\n\n .table-header {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n background: gray;\n border-bottom: 2px solid #e5e7eb;\n }\n\n .header-cell {\n padding: 16px 24px;\n font-weight: 600;\n text-transform: uppercase;\n font-size: 14px;\n letter-spacing: 0.05em;\n color: white;\n }\n\n .table-body {\n display: flex;\n flex-direction: column;\n }\n\n .table-row {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n border-bottom: 1px solid #e5e7eb;\n transition: background-color 0.2s ease;\n }\n\n .table-row:last-child {\n border-bottom: none;\n }\n\n .table-row:hover {\n background-color: #f9fafb;\n }\n\n .table-cell {\n padding: 16px 24px;\n font-size: 16px;\n color: #4b5563;\n }\n\n @media (max-width: 640px) {\n .table-container {\n border-radius: 0;\n margin: 0;\n }\n\n .header-cell,\n .table-cell {\n padding: 12px 16px;\n }\n }\n "]
74
+ });
75
+ }
76
+ };
77
+ i0.ɵɵngDeclareClassMetadata({
78
+ minVersion: "12.0.0",
79
+ version: "21.1.1",
80
+ ngImport: i0,
81
+ type: DebugRoutesComponent,
82
+ decorators: [{
83
+ type: Component,
84
+ args: [{
85
+ selector: "analogjs-debug-routes-page",
86
+ standalone: true,
87
+ template: `
88
+ <h2>Routes</h2>
89
+
90
+ <div class="table-container">
91
+ <div class="table-header">
92
+ <div class="header-cell">Route Path</div>
93
+ <div class="header-cell">File</div>
94
+ <div class="header-cell">Type</div>
95
+ </div>
96
+ <div class="table-body">
97
+ @for (
98
+ collectedRoute of collectedRoutes;
99
+ track collectedRoute.filename
100
+ ) {
101
+ <div class="table-row">
102
+ <div class="table-cell">{{ collectedRoute.path }}</div>
103
+ <div class="table-cell" [title]="collectedRoute.filename">
104
+ {{ collectedRoute.file }}
105
+ </div>
106
+ <div class="table-cell">
107
+ {{ collectedRoute.isLayout ? 'Layout' : 'Page' }}
108
+ </div>
109
+ </div>
110
+ }
111
+ </div>
112
+ </div>
113
+ `,
114
+ styles: ["\n :host {\n width: 100%;\n }\n\n .table-container {\n width: 100%;\n max-width: 900px;\n margin: 0 auto;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n overflow: hidden;\n }\n\n .table-header {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n background: gray;\n border-bottom: 2px solid #e5e7eb;\n }\n\n .header-cell {\n padding: 16px 24px;\n font-weight: 600;\n text-transform: uppercase;\n font-size: 14px;\n letter-spacing: 0.05em;\n color: white;\n }\n\n .table-body {\n display: flex;\n flex-direction: column;\n }\n\n .table-row {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n border-bottom: 1px solid #e5e7eb;\n transition: background-color 0.2s ease;\n }\n\n .table-row:last-child {\n border-bottom: none;\n }\n\n .table-row:hover {\n background-color: #f9fafb;\n }\n\n .table-cell {\n padding: 16px 24px;\n font-size: 16px;\n color: #4b5563;\n }\n\n @media (max-width: 640px) {\n .table-container {\n border-radius: 0;\n margin: 0;\n }\n\n .header-cell,\n .table-cell {\n padding: 12px 16px;\n }\n }\n "]
115
+ }]
116
+ }]
117
+ });
118
+ //#endregion
119
+ export { DebugRoutesComponent as default };
120
+
121
+ //# sourceMappingURL=debug.page.mjs.map
@@ -0,0 +1,301 @@
1
+ import { NavigationEnd, Router, UrlSegment } from "@angular/router";
2
+ import { InjectionToken, inject } from "@angular/core";
3
+ import { HttpClient } from "@angular/common/http";
4
+ import { firstValueFrom } from "rxjs";
5
+ import { injectAPIPrefix, injectBaseURL, injectInternalServerFetch } from "@analogjs/router/tokens";
6
+ import { Meta } from "@angular/platform-browser";
7
+ import { filter } from "rxjs/operators";
8
+ //#region packages/router/src/lib/meta-tags.ts
9
+ var ROUTE_META_TAGS_KEY = Symbol("@analogjs/router Route Meta Tags Key");
10
+ var CHARSET_KEY = "charset";
11
+ var HTTP_EQUIV_SELECTOR_KEY = "http-equiv";
12
+ var NAME_KEY = "name";
13
+ var PROPERTY_KEY = "property";
14
+ var ITEMPROP_KEY = "itemprop";
15
+ function updateMetaTagsOnRouteChange() {
16
+ const router = inject(Router);
17
+ const metaService = inject(Meta);
18
+ router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => {
19
+ const metaTagMap = getMetaTagMap(router.routerState.snapshot.root);
20
+ for (const metaTagSelector in metaTagMap) {
21
+ const metaTag = metaTagMap[metaTagSelector];
22
+ metaService.updateTag(metaTag, metaTagSelector);
23
+ }
24
+ });
25
+ }
26
+ function getMetaTagMap(route) {
27
+ const metaTagMap = {};
28
+ let currentRoute = route;
29
+ while (currentRoute) {
30
+ const metaTags = currentRoute.data[ROUTE_META_TAGS_KEY] ?? [];
31
+ for (const metaTag of metaTags) metaTagMap[getMetaTagSelector(metaTag)] = metaTag;
32
+ currentRoute = currentRoute.firstChild;
33
+ }
34
+ return metaTagMap;
35
+ }
36
+ function getMetaTagSelector(metaTag) {
37
+ if (metaTag.name) return `${NAME_KEY}="${metaTag.name}"`;
38
+ if (metaTag.property) return `${PROPERTY_KEY}="${metaTag.property}"`;
39
+ if (metaTag.httpEquiv) return `${HTTP_EQUIV_SELECTOR_KEY}="${metaTag.httpEquiv}"`;
40
+ if (metaTag.itemprop) return `${ITEMPROP_KEY}="${metaTag.itemprop}"`;
41
+ return CHARSET_KEY;
42
+ }
43
+ //#endregion
44
+ //#region packages/router/src/lib/endpoints.ts
45
+ var ANALOG_META_KEY = Symbol("@analogjs/router Analog Route Metadata Key");
46
+ /**
47
+ * This variable reference is replaced with a glob of all route endpoints.
48
+ */
49
+ var ANALOG_PAGE_ENDPOINTS = {};
50
+ //#endregion
51
+ //#region packages/router/src/lib/inject-route-endpoint-url.ts
52
+ function injectRouteEndpointURL(route) {
53
+ const routeConfig = route.routeConfig;
54
+ const apiPrefix = injectAPIPrefix();
55
+ const baseUrl = injectBaseURL();
56
+ const { queryParams, fragment: hash, params, parent } = route;
57
+ const segment = parent?.url.map((segment) => segment.path).join("/") || "";
58
+ const url = new URL("", {
59
+ "BASE_URL": "/",
60
+ "DEV": false,
61
+ "MODE": "production",
62
+ "PROD": true,
63
+ "SSR": false
64
+ }["VITE_ANALOG_PUBLIC_BASE_URL"] || baseUrl || (typeof window !== "undefined" && window.location.origin ? window.location.origin : ""));
65
+ url.pathname = `${url.pathname.endsWith("/") ? url.pathname : url.pathname + "/"}${apiPrefix}/_analog${routeConfig[ANALOG_META_KEY].endpoint}`;
66
+ url.search = `${new URLSearchParams(queryParams).toString()}`;
67
+ url.hash = hash ?? "";
68
+ Object.keys(params).forEach((param) => {
69
+ url.pathname = url.pathname.replace(`[${param}]`, params[param]);
70
+ });
71
+ url.pathname = url.pathname.replace("**", segment);
72
+ return url;
73
+ }
74
+ //#endregion
75
+ //#region packages/router/src/lib/route-config.ts
76
+ function toRouteConfig(routeMeta) {
77
+ if (routeMeta && isRedirectRouteMeta(routeMeta)) return routeMeta;
78
+ const { meta, ...routeConfig } = routeMeta ?? {};
79
+ if (Array.isArray(meta)) routeConfig.data = {
80
+ ...routeConfig.data,
81
+ [ROUTE_META_TAGS_KEY]: meta
82
+ };
83
+ else if (typeof meta === "function") routeConfig.resolve = {
84
+ ...routeConfig.resolve,
85
+ [ROUTE_META_TAGS_KEY]: meta
86
+ };
87
+ routeConfig.runGuardsAndResolvers = routeConfig.runGuardsAndResolvers ?? "paramsOrQueryParamsChange";
88
+ routeConfig.resolve = {
89
+ ...routeConfig.resolve,
90
+ load: async (route) => {
91
+ if (ANALOG_PAGE_ENDPOINTS[route.routeConfig[ANALOG_META_KEY].endpointKey]) {
92
+ const http = inject(HttpClient);
93
+ const url = injectRouteEndpointURL(route);
94
+ const internalFetch = injectInternalServerFetch();
95
+ if (internalFetch) return internalFetch(url.pathname);
96
+ if (!!{
97
+ "BASE_URL": "/",
98
+ "DEV": false,
99
+ "MODE": "production",
100
+ "PROD": true,
101
+ "SSR": false
102
+ }["VITE_ANALOG_PUBLIC_BASE_URL"] && globalThis.$fetch) return globalThis.$fetch(url.pathname);
103
+ return firstValueFrom(http.get(`${url.href}`));
104
+ }
105
+ return {};
106
+ }
107
+ };
108
+ return routeConfig;
109
+ }
110
+ function isRedirectRouteMeta(routeMeta) {
111
+ return !!routeMeta.redirectTo;
112
+ }
113
+ //#endregion
114
+ //#region packages/router/src/lib/markdown-helpers.ts
115
+ var isNgZoneEnabled = typeof Zone !== "undefined" && !!Zone.root;
116
+ function toMarkdownModule(markdownFileFactory) {
117
+ return async () => {
118
+ const createLoader = () => Promise.all([import("@analogjs/content"), markdownFileFactory()]);
119
+ const [{ parseRawContentFile, MarkdownRouteComponent, ContentRenderer }, markdownFile] = await (isNgZoneEnabled ? Zone.root.run(createLoader) : createLoader());
120
+ const { content, attributes } = parseRawContentFile(markdownFile);
121
+ const { title, meta } = attributes;
122
+ return {
123
+ default: MarkdownRouteComponent,
124
+ routeMeta: {
125
+ data: { _analogContent: content },
126
+ title,
127
+ meta,
128
+ resolve: { renderedAnalogContent: async () => {
129
+ const rendered = await inject(ContentRenderer).render(content);
130
+ return typeof rendered === "string" ? rendered : rendered.content;
131
+ } }
132
+ }
133
+ };
134
+ };
135
+ }
136
+ //#endregion
137
+ //#region packages/router/src/lib/constants.ts
138
+ var ENDPOINT_EXTENSION = ".server.ts";
139
+ //#endregion
140
+ //#region packages/router/src/lib/routes.ts
141
+ /**
142
+ * This variable reference is replaced with a glob of all page routes.
143
+ */
144
+ var ANALOG_ROUTE_FILES = {};
145
+ /**
146
+ * This variable reference is replaced with a glob of all content routes.
147
+ */
148
+ var ANALOG_CONTENT_ROUTE_FILES = {};
149
+ /**
150
+ * A function used to parse list of files and create configuration of routes.
151
+ *
152
+ * @param files
153
+ * @returns Array of routes
154
+ */
155
+ function createRoutes(files, debug = false) {
156
+ const filenames = Object.keys(files);
157
+ if (filenames.length === 0) return [];
158
+ const rawRoutesByLevelMap = filenames.reduce((acc, filename) => {
159
+ const rawPath = toRawPath(filename);
160
+ const rawSegments = rawPath.split("/");
161
+ const level = rawSegments.length - 1;
162
+ const rawSegment = rawSegments[level];
163
+ const ancestorRawSegments = rawSegments.slice(0, level);
164
+ return {
165
+ ...acc,
166
+ [level]: {
167
+ ...acc[level],
168
+ [rawPath]: {
169
+ filename,
170
+ rawSegment,
171
+ ancestorRawSegments,
172
+ segment: toSegment(rawSegment),
173
+ level,
174
+ children: []
175
+ }
176
+ }
177
+ };
178
+ }, {});
179
+ const allLevels = Object.keys(rawRoutesByLevelMap).map(Number);
180
+ const maxLevel = Math.max(...allLevels);
181
+ for (let level = maxLevel; level > 0; level--) {
182
+ const rawRoutesMap = rawRoutesByLevelMap[level];
183
+ const rawPaths = Object.keys(rawRoutesMap);
184
+ for (const rawPath of rawPaths) {
185
+ const rawRoute = rawRoutesMap[rawPath];
186
+ const parentRawPath = rawRoute.ancestorRawSegments.join("/");
187
+ const parentRawSegmentIndex = rawRoute.ancestorRawSegments.length - 1;
188
+ const parentRawSegment = rawRoute.ancestorRawSegments[parentRawSegmentIndex];
189
+ rawRoutesByLevelMap[level - 1] ||= {};
190
+ rawRoutesByLevelMap[level - 1][parentRawPath] ||= {
191
+ filename: null,
192
+ rawSegment: parentRawSegment,
193
+ ancestorRawSegments: rawRoute.ancestorRawSegments.slice(0, parentRawSegmentIndex),
194
+ segment: toSegment(parentRawSegment),
195
+ level: level - 1,
196
+ children: []
197
+ };
198
+ rawRoutesByLevelMap[level - 1][parentRawPath].children.push(rawRoute);
199
+ }
200
+ }
201
+ const rootRawRoutesMap = rawRoutesByLevelMap[0];
202
+ const rawRoutes = Object.keys(rootRawRoutesMap).map((segment) => rootRawRoutesMap[segment]);
203
+ sortRawRoutes(rawRoutes);
204
+ return toRoutes(rawRoutes, files, debug);
205
+ }
206
+ function toRawPath(filename) {
207
+ return filename.replace(/^(?:[a-zA-Z]:[\\/])?(.*?)[\\/](?:routes|pages)[\\/]|(?:[\\/](?:app[\\/](?:routes|pages)|src[\\/]content)[\\/])|(\.page\.(js|ts|analog|ag)$)|(\.(ts|md|analog|ag)$)/g, "").replace(/\[\[\.\.\.([^\]]+)\]\]/g, "(opt-$1)").replace(/\[\.{3}.+\]/, "**").replace(/\[([^\]]+)\]/g, ":$1");
208
+ }
209
+ function toSegment(rawSegment) {
210
+ return rawSegment.replace(/index|\(.*?\)/g, "").replace(/\.|\/+/g, "/").replace(/^\/+|\/+$/g, "");
211
+ }
212
+ function createOptionalCatchAllMatcher(paramName) {
213
+ return (segments) => {
214
+ if (segments.length === 0) return null;
215
+ const joined = segments.map((s) => s.path).join("/");
216
+ return {
217
+ consumed: segments,
218
+ posParams: { [paramName]: new UrlSegment(joined, {}) }
219
+ };
220
+ };
221
+ }
222
+ function toRoutes(rawRoutes, files, debug = false) {
223
+ const routes = [];
224
+ for (const rawRoute of rawRoutes) {
225
+ const children = rawRoute.children.length > 0 ? toRoutes(rawRoute.children, files, debug) : void 0;
226
+ let module = void 0;
227
+ let analogMeta = void 0;
228
+ if (rawRoute.filename) {
229
+ const isMarkdownFile = rawRoute.filename.endsWith(".md");
230
+ if (!debug) module = isMarkdownFile ? toMarkdownModule(files[rawRoute.filename]) : files[rawRoute.filename];
231
+ const endpointKey = rawRoute.filename.replace(/\.page\.(ts|analog|ag)$/, ENDPOINT_EXTENSION);
232
+ analogMeta = {
233
+ endpoint: (rawRoute.filename.replace(/\.page\.(ts|analog|ag)$/, "").replace(/\[\[\.\.\..+\]\]/, "**").replace(/\[\.{3}.+\]/, "**").replace(/^(.*?)\/pages/, "/pages") || "").replace(/\./g, "/").replace(/\/\((.*?)\)$/, "/-$1-"),
234
+ endpointKey
235
+ };
236
+ }
237
+ const optCatchAllMatch = rawRoute.filename?.match(/\[\[\.\.\.([^\]]+)\]\]/);
238
+ const optCatchAllParam = optCatchAllMatch ? optCatchAllMatch[1] : null;
239
+ const route = module ? {
240
+ path: rawRoute.segment,
241
+ loadChildren: () => module().then((m) => {
242
+ return [{
243
+ path: "",
244
+ component: m.default,
245
+ ...toRouteConfig(m.routeMeta),
246
+ children,
247
+ [ANALOG_META_KEY]: analogMeta
248
+ }, ...optCatchAllParam ? [{
249
+ matcher: createOptionalCatchAllMatcher(optCatchAllParam),
250
+ component: m.default,
251
+ ...toRouteConfig(m.routeMeta),
252
+ [ANALOG_META_KEY]: analogMeta
253
+ }] : []];
254
+ })
255
+ } : {
256
+ path: rawRoute.segment,
257
+ ...debug ? {
258
+ filename: rawRoute.filename ? rawRoute.filename : void 0,
259
+ isLayout: children && children.length > 0 ? true : false
260
+ } : {},
261
+ children
262
+ };
263
+ routes.push(route);
264
+ }
265
+ return routes;
266
+ }
267
+ function sortRawRoutes(rawRoutes) {
268
+ rawRoutes.sort((a, b) => {
269
+ let segmentA = deprioritizeSegment(a.segment);
270
+ let segmentB = deprioritizeSegment(b.segment);
271
+ if (a.children.length > b.children.length) segmentA = `~${segmentA}`;
272
+ else if (a.children.length < b.children.length) segmentB = `~${segmentB}`;
273
+ return segmentA > segmentB ? 1 : -1;
274
+ });
275
+ for (const rawRoute of rawRoutes) sortRawRoutes(rawRoute.children);
276
+ }
277
+ function deprioritizeSegment(segment) {
278
+ return segment.replace(":", "~~").replace("**", "~~~~");
279
+ }
280
+ var routes = createRoutes({
281
+ ...ANALOG_ROUTE_FILES,
282
+ ...ANALOG_CONTENT_ROUTE_FILES
283
+ });
284
+ //#endregion
285
+ //#region packages/router/src/lib/debug/routes.ts
286
+ var DEBUG_ROUTES = new InjectionToken("@analogjs/router debug routes", {
287
+ providedIn: "root",
288
+ factory() {
289
+ return createRoutes({
290
+ ...ANALOG_ROUTE_FILES,
291
+ ...ANALOG_CONTENT_ROUTE_FILES
292
+ }, true);
293
+ }
294
+ });
295
+ function injectDebugRoutes() {
296
+ return inject(DEBUG_ROUTES);
297
+ }
298
+ //#endregion
299
+ export { updateMetaTagsOnRouteChange as a, injectRouteEndpointURL as i, createRoutes as n, routes as r, injectDebugRoutes as t };
300
+
301
+ //# sourceMappingURL=routes.mjs.map
package/package.json CHANGED
@@ -1,9 +1,34 @@
1
1
  {
2
2
  "name": "@analogjs/router",
3
- "version": "2.4.0-beta.9",
3
+ "version": "3.0.0-alpha.10",
4
4
  "description": "Filesystem-based routing for Angular",
5
5
  "type": "module",
6
6
  "author": "Brandon Roberts <robertsbt@gmail.com>",
7
+ "exports": {
8
+ "./package.json": {
9
+ "default": "./package.json"
10
+ },
11
+ ".": {
12
+ "types": "./types/src/index.d.ts",
13
+ "import": "./fesm2022/analogjs-router.mjs",
14
+ "default": "./fesm2022/analogjs-router.mjs"
15
+ },
16
+ "./server": {
17
+ "types": "./types/server/src/index.d.ts",
18
+ "import": "./fesm2022/analogjs-router-server.mjs",
19
+ "default": "./fesm2022/analogjs-router-server.mjs"
20
+ },
21
+ "./server/actions": {
22
+ "types": "./types/server/actions/src/index.d.ts",
23
+ "import": "./fesm2022/analogjs-router-server-actions.mjs",
24
+ "default": "./fesm2022/analogjs-router-server-actions.mjs"
25
+ },
26
+ "./tokens": {
27
+ "types": "./types/tokens/src/index.d.ts",
28
+ "import": "./fesm2022/analogjs-router-tokens.mjs",
29
+ "default": "./fesm2022/analogjs-router-tokens.mjs"
30
+ }
31
+ },
7
32
  "keywords": [
8
33
  "angular",
9
34
  "router",
@@ -24,13 +49,27 @@
24
49
  "url": "https://github.com/sponsors/brandonroberts"
25
50
  },
26
51
  "peerDependencies": {
27
- "@analogjs/content": "^2.4.0-beta.9",
52
+ "@analogjs/content": "^3.0.0-alpha.10",
28
53
  "@angular/core": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
29
- "@angular/router": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0"
54
+ "@angular/platform-server": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
55
+ "@angular/router": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
56
+ "@tanstack/angular-query-experimental": ">=5.95.0"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "@angular/platform-server": {
60
+ "optional": true
61
+ },
62
+ "@tanstack/angular-query-experimental": {
63
+ "optional": true
64
+ }
30
65
  },
31
66
  "dependencies": {
67
+ "@standard-schema/spec": "^1.1.0",
32
68
  "tslib": "^2.0.0"
33
69
  },
70
+ "devDependencies": {
71
+ "@analogjs/vite-plugin-angular": "^3.0.0-alpha.10"
72
+ },
34
73
  "ng-update": {
35
74
  "packageGroup": [
36
75
  "@analogjs/platform",
@@ -48,27 +87,6 @@
48
87
  "provenance": true
49
88
  },
50
89
  "module": "fesm2022/analogjs-router.mjs",
51
- "typings": "types/analogjs-router.d.ts",
52
- "exports": {
53
- "./package.json": {
54
- "default": "./package.json"
55
- },
56
- ".": {
57
- "types": "./types/analogjs-router.d.ts",
58
- "default": "./fesm2022/analogjs-router.mjs"
59
- },
60
- "./server": {
61
- "types": "./types/analogjs-router-server.d.ts",
62
- "default": "./fesm2022/analogjs-router-server.mjs"
63
- },
64
- "./server/actions": {
65
- "types": "./types/analogjs-router-server-actions.d.ts",
66
- "default": "./fesm2022/analogjs-router-server-actions.mjs"
67
- },
68
- "./tokens": {
69
- "types": "./types/analogjs-router-tokens.d.ts",
70
- "default": "./fesm2022/analogjs-router-tokens.mjs"
71
- }
72
- },
90
+ "typings": "types/src/index.d.ts",
73
91
  "sideEffects": false
74
- }
92
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "module": "../../fesm2022/analogjs-router-server-actions.mjs",
3
+ "typings": "../../types/server/actions/src/index.d.ts"
4
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "module": "../fesm2022/analogjs-router-server.mjs",
3
+ "typings": "../types/server/src/index.d.ts"
4
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "module": "../fesm2022/analogjs-router-tokens.mjs",
3
+ "typings": "../types/tokens/src/index.d.ts"
4
+ }
@@ -0,0 +1,13 @@
1
+ import type { H3Event, H3EventContext } from 'nitro/h3';
2
+ import type { $Fetch } from 'nitro/types';
3
+ import type { NodeContext } from '../../../src/lib/route-types.js';
4
+ export type PageServerAction = {
5
+ params: H3EventContext['params'];
6
+ req: NodeContext['req'];
7
+ res: NonNullable<NodeContext['res']>;
8
+ fetch: $Fetch;
9
+ event: H3Event;
10
+ };
11
+ export declare function fail<T = object>(status: number, errors: T): Response;
12
+ export declare function json<T = object>(data: T, config?: ResponseInit): Response;
13
+ export declare function redirect(url: string, config?: number | ResponseInit): Response;
@@ -0,0 +1,54 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import type { H3Event, H3EventContext } from 'nitro/h3';
3
+ import type { $Fetch } from 'nitro/types';
4
+ type NodeContext = NonNullable<H3Event['node']>;
5
+ type OptionalSchema = StandardSchemaV1 | undefined;
6
+ type InferSchema<TSchema extends OptionalSchema, TFallback> = TSchema extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<TSchema> : TFallback;
7
+ export interface DefineActionContext<TSchema extends OptionalSchema = undefined, TParamsSchema extends OptionalSchema = undefined> {
8
+ data: InferSchema<TSchema, Record<string, unknown>>;
9
+ params: InferSchema<TParamsSchema, H3EventContext['params']>;
10
+ req: NodeContext['req'];
11
+ res: NonNullable<NodeContext['res']>;
12
+ fetch: $Fetch;
13
+ event: H3Event;
14
+ }
15
+ export interface DefineActionOptions<TSchema extends OptionalSchema = undefined, TParamsSchema extends OptionalSchema = undefined> {
16
+ schema?: TSchema;
17
+ params?: TParamsSchema;
18
+ handler: (context: DefineActionContext<TSchema, TParamsSchema>) => Promise<Response> | Response;
19
+ }
20
+ /**
21
+ * Creates a server action handler with Standard Schema input validation.
22
+ *
23
+ * Parses the request body (JSON or FormData) and validates it against the
24
+ * provided schema before invoking the handler. On validation failure,
25
+ * returns `fail(422, issues)` with `StandardSchemaV1.Issue[]`.
26
+ * Repeated form fields are preserved as arrays instead of being collapsed
27
+ * to the last value.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * import { defineAction, json } from '@analogjs/router/server/actions';
32
+ * import * as v from 'valibot';
33
+ *
34
+ * const Schema = v.object({
35
+ * email: v.pipe(v.string(), v.email()),
36
+ * });
37
+ *
38
+ * export const action = defineAction({
39
+ * schema: Schema,
40
+ * handler: async ({ data }) => {
41
+ * // data is typed as { email: string }
42
+ * return json({ ok: true });
43
+ * },
44
+ * });
45
+ * ```
46
+ */
47
+ export declare function defineAction<TSchema extends OptionalSchema = undefined, TParamsSchema extends OptionalSchema = undefined>(options: DefineActionOptions<TSchema, TParamsSchema>): (ctx: {
48
+ params: H3EventContext["params"];
49
+ req: NodeContext["req"];
50
+ res: NonNullable<NodeContext["res"]>;
51
+ fetch: $Fetch;
52
+ event: H3Event;
53
+ }) => Promise<Response>;
54
+ export {};
@@ -0,0 +1,55 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import type { H3Event, H3EventContext } from 'nitro/h3';
3
+ import type { $Fetch } from 'nitro/types';
4
+ type NodeContext = NonNullable<H3Event['node']>;
5
+ type OptionalSchema = StandardSchemaV1 | undefined;
6
+ type InferSchema<TSchema extends OptionalSchema, TFallback> = TSchema extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<TSchema> : TFallback;
7
+ export interface PageLoadContext<TParamsSchema extends OptionalSchema = undefined, TQuerySchema extends OptionalSchema = undefined> {
8
+ params: InferSchema<TParamsSchema, H3EventContext['params']>;
9
+ query: InferSchema<TQuerySchema, Record<string, string | string[] | undefined>>;
10
+ req: NodeContext['req'];
11
+ res: NonNullable<NodeContext['res']>;
12
+ fetch: $Fetch;
13
+ event: H3Event;
14
+ }
15
+ export interface DefinePageLoadOptions<TParamsSchema extends OptionalSchema = undefined, TQuerySchema extends OptionalSchema = undefined, TResult = unknown> {
16
+ params?: TParamsSchema;
17
+ query?: TQuerySchema;
18
+ handler: (context: PageLoadContext<TParamsSchema, TQuerySchema>) => Promise<TResult> | TResult;
19
+ }
20
+ /**
21
+ * Creates a typed page server load function with optional
22
+ * Standard Schema validation for route params and query.
23
+ *
24
+ * Follows the same validation patterns as `defineAction` and
25
+ * `defineServerRoute`: validates before invoking the handler,
26
+ * returns `fail(422, issues)` on validation failure.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // src/app/pages/users/[id].server.ts
31
+ * import { definePageLoad } from '@analogjs/router/server/actions';
32
+ * import * as v from 'valibot';
33
+ *
34
+ * export const routeParamsSchema = v.object({
35
+ * id: v.pipe(v.string(), v.regex(/^\d+$/)),
36
+ * });
37
+ *
38
+ * export const load = definePageLoad({
39
+ * params: routeParamsSchema,
40
+ * handler: async ({ params, fetch }) => {
41
+ * // params.id is typed as string (validated)
42
+ * const user = await fetch(`/api/users/${params.id}`);
43
+ * return user;
44
+ * },
45
+ * });
46
+ * ```
47
+ */
48
+ export declare function definePageLoad<TParamsSchema extends OptionalSchema = undefined, TQuerySchema extends OptionalSchema = undefined, TResult = unknown>(options: DefinePageLoadOptions<TParamsSchema, TQuerySchema, TResult>): (ctx: {
49
+ params: H3EventContext['params'];
50
+ req: NodeContext['req'];
51
+ res: NonNullable<NodeContext['res']>;
52
+ fetch: $Fetch;
53
+ event: H3Event;
54
+ }) => Promise<TResult | Response>;
55
+ export {};