@analogjs/router 3.0.0-alpha.6 → 3.0.0-alpha.60

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 (78) hide show
  1. package/content/package.json +4 -0
  2. package/fesm2022/analogjs-router-content.mjs +63 -0
  3. package/fesm2022/analogjs-router-content.mjs.map +1 -0
  4. package/fesm2022/analogjs-router-i18n.mjs +156 -0
  5. package/fesm2022/analogjs-router-i18n.mjs.map +1 -0
  6. package/fesm2022/analogjs-router-server-actions.mjs +2 -27
  7. package/fesm2022/analogjs-router-server.mjs +60 -3
  8. package/fesm2022/analogjs-router-server.mjs.map +1 -0
  9. package/fesm2022/analogjs-router-tanstack-query-server.mjs +71 -0
  10. package/fesm2022/analogjs-router-tanstack-query-server.mjs.map +1 -0
  11. package/fesm2022/analogjs-router-tanstack-query.mjs +39 -0
  12. package/fesm2022/analogjs-router-tanstack-query.mjs.map +1 -0
  13. package/fesm2022/analogjs-router-tokens.mjs +7 -2
  14. package/fesm2022/analogjs-router-tokens.mjs.map +1 -0
  15. package/fesm2022/analogjs-router.mjs +559 -61
  16. package/fesm2022/analogjs-router.mjs.map +1 -0
  17. package/fesm2022/debug.page.mjs +53 -31
  18. package/fesm2022/debug.page.mjs.map +1 -0
  19. package/fesm2022/provide-analog-query.mjs +72 -0
  20. package/fesm2022/provide-analog-query.mjs.map +1 -0
  21. package/fesm2022/route-files.mjs +361 -0
  22. package/fesm2022/route-files.mjs.map +1 -0
  23. package/fesm2022/routes.mjs +5 -278
  24. package/fesm2022/routes.mjs.map +1 -0
  25. package/fesm2022/src.mjs +335 -0
  26. package/fesm2022/src.mjs.map +1 -0
  27. package/i18n/package.json +4 -0
  28. package/package.json +77 -26
  29. package/tanstack-query/package.json +4 -0
  30. package/tanstack-query/server/package.json +4 -0
  31. package/types/content/src/index.d.ts +4 -0
  32. package/types/content/src/lib/debug/routes.d.ts +10 -0
  33. package/types/{src → content/src}/lib/markdown-helpers.d.ts +1 -1
  34. package/types/content/src/lib/routes.d.ts +8 -0
  35. package/types/content/src/lib/with-content-routes.d.ts +2 -0
  36. package/types/i18n/src/index.d.ts +1 -0
  37. package/types/i18n/src/provide-i18n.d.ts +92 -0
  38. package/types/server/actions/src/define-action.d.ts +54 -0
  39. package/types/server/actions/src/define-api-route.d.ts +57 -0
  40. package/types/server/actions/src/define-page-load.d.ts +55 -0
  41. package/types/server/actions/src/define-server-route.d.ts +68 -0
  42. package/types/server/actions/src/index.d.ts +9 -1
  43. package/types/server/actions/src/parse-request-data.d.ts +9 -0
  44. package/types/server/actions/src/validate.d.ts +8 -0
  45. package/types/server/src/provide-server-context.d.ts +15 -1
  46. package/types/server/src/render.d.ts +1 -1
  47. package/types/server/src/server-component-render.d.ts +1 -1
  48. package/types/src/index.d.ts +16 -5
  49. package/types/src/lib/cache-key.d.ts +1 -1
  50. package/types/src/lib/cookie-interceptor.d.ts +1 -1
  51. package/types/src/lib/debug/debug.page.d.ts +4 -2
  52. package/types/src/lib/define-route.d.ts +6 -1
  53. package/types/src/lib/endpoints.d.ts +1 -1
  54. package/types/src/lib/experimental.d.ts +140 -0
  55. package/types/src/lib/form-action.directive.d.ts +12 -5
  56. package/types/src/lib/inject-load.d.ts +5 -2
  57. package/types/src/lib/inject-navigate.d.ts +23 -0
  58. package/types/src/lib/inject-route-context.d.ts +32 -0
  59. package/types/src/lib/inject-typed-params.d.ts +63 -0
  60. package/types/src/lib/json-ld.d.ts +32 -0
  61. package/types/src/lib/meta-tags.d.ts +3 -1
  62. package/types/src/lib/models.d.ts +3 -0
  63. package/types/src/lib/provide-file-router-base.d.ts +4 -0
  64. package/types/src/lib/provide-file-router.d.ts +2 -8
  65. package/types/src/lib/route-builder.d.ts +5 -0
  66. package/types/src/lib/route-files.d.ts +18 -0
  67. package/types/src/lib/route-path.d.ts +124 -0
  68. package/types/src/lib/route-types.d.ts +2 -1
  69. package/types/src/lib/routes.d.ts +2 -10
  70. package/types/src/lib/validation-errors.d.ts +7 -0
  71. package/types/tanstack-query/server/src/define-page-load-queries.d.ts +66 -0
  72. package/types/tanstack-query/server/src/index.d.ts +3 -0
  73. package/types/tanstack-query/src/constants.d.ts +9 -0
  74. package/types/tanstack-query/src/index.d.ts +2 -0
  75. package/types/tanstack-query/src/provide-analog-query.d.ts +4 -0
  76. package/types/tanstack-query/src/provide-server-analog-query.d.ts +2 -0
  77. package/types/tanstack-query/src/server-query.d.ts +16 -0
  78. package/types/tokens/src/index.d.ts +2 -0
@@ -1,301 +1,28 @@
1
- import { NavigationEnd, Router, UrlSegment } from "@angular/router";
1
+ import { n as ANALOG_ROUTE_FILES, r as createRoutes$1 } from "./route-files.mjs";
2
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
3
  //#region packages/router/src/lib/routes.ts
141
4
  /**
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
5
  * A function used to parse list of files and create configuration of routes.
151
6
  *
152
7
  * @param files
153
8
  * @returns Array of routes
154
9
  */
155
10
  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");
11
+ return createRoutes$1(files, (_filename, fileLoader) => fileLoader, debug);
208
12
  }
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
- });
13
+ var routes = createRoutes(ANALOG_ROUTE_FILES);
284
14
  //#endregion
285
15
  //#region packages/router/src/lib/debug/routes.ts
286
16
  var DEBUG_ROUTES = new InjectionToken("@analogjs/router debug routes", {
287
17
  providedIn: "root",
288
18
  factory() {
289
- return createRoutes({
290
- ...ANALOG_ROUTE_FILES,
291
- ...ANALOG_CONTENT_ROUTE_FILES
292
- }, true);
19
+ return createRoutes(ANALOG_ROUTE_FILES, true);
293
20
  }
294
21
  });
295
22
  function injectDebugRoutes() {
296
23
  return inject(DEBUG_ROUTES);
297
24
  }
298
25
  //#endregion
299
- export { updateMetaTagsOnRouteChange as a, injectRouteEndpointURL as i, createRoutes as n, routes as r, injectDebugRoutes as t };
26
+ export { createRoutes as n, routes as r, injectDebugRoutes as t };
300
27
 
301
28
  //# sourceMappingURL=routes.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.mjs","names":[],"sources":["../../src/lib/routes.ts","../../src/lib/debug/routes.ts"],"sourcesContent":["import type { Route } from '@angular/router';\n\nimport type { RouteExport } from './models';\nimport { createRoutes as createBaseRoutes } from './route-builder';\nimport { ANALOG_ROUTE_FILES, type Files } from './route-files';\n\n/**\n * A function used to parse list of files and create configuration of routes.\n *\n * @param files\n * @returns Array of routes\n */\nexport function createRoutes(files: Files, debug = false): Route[] {\n return createBaseRoutes(\n files,\n (_filename, fileLoader) => fileLoader as () => Promise<RouteExport>,\n debug,\n );\n}\n\nexport { ANALOG_ROUTE_FILES } from './route-files';\n\nexport const routes: Route[] = createRoutes(ANALOG_ROUTE_FILES);\n","import { inject, InjectionToken } from '@angular/core';\nimport { Route } from '@angular/router';\n\nimport { ANALOG_ROUTE_FILES, createRoutes } from '../routes';\n\nexport const DEBUG_ROUTES: InjectionToken<(Route & DebugRoute)[]> =\n new InjectionToken<(Route & DebugRoute)[]>('@analogjs/router debug routes', {\n providedIn: 'root',\n factory() {\n const debugRoutes = createRoutes(ANALOG_ROUTE_FILES, true);\n\n return debugRoutes as (Route & DebugRoute)[];\n },\n });\n\nexport type DebugRoute = {\n path: string;\n filename: string;\n isLayout: boolean;\n children?: DebugRoute[];\n};\n\nexport function injectDebugRoutes(): (Route & DebugRoute)[] {\n return inject(DEBUG_ROUTES);\n}\n"],"mappings":";;;;;;;;;AAYA,SAAgB,aAAa,OAAc,QAAQ,OAAgB;AACjE,QAAO,eACL,QACC,WAAW,eAAe,YAC3B,MACD;;AAKH,IAAa,SAAkB,aAAa,mBAAmB;;;ACjB/D,IAAa,eACX,IAAI,eAAuC,iCAAiC;CAC1E,YAAY;CACZ,UAAU;AAGR,SAFoB,aAAa,oBAAyB,KAAA;;CAI7D,CAAC;AASJ,SAAgB,oBAA4C;AAC1D,QAAO,OAAO,aAAa"}
@@ -0,0 +1,335 @@
1
+ import { getRequestURL, readBody, readFormData, toRequest } from "nitro/h3";
2
+ //#region packages/router/server/actions/src/actions.ts
3
+ function fail(status, errors) {
4
+ return new Response(JSON.stringify(errors), {
5
+ status,
6
+ headers: { "X-Analog-Errors": "true" }
7
+ });
8
+ }
9
+ function json(data, config) {
10
+ return new Response(JSON.stringify(data), {
11
+ headers: { "Content-Type": "application/json; charset=utf-8" },
12
+ ...config
13
+ });
14
+ }
15
+ function redirect(url, config = 302) {
16
+ if (typeof config === "number") return new Response(null, {
17
+ status: config,
18
+ headers: { Location: `${url}` }
19
+ });
20
+ return new Response(null, {
21
+ headers: { Location: `${url}` },
22
+ ...config
23
+ });
24
+ }
25
+ //#endregion
26
+ //#region packages/router/server/actions/src/parse-request-data.ts
27
+ function appendEntry(target, key, value) {
28
+ const existingValue = target[key];
29
+ if (existingValue === void 0) {
30
+ target[key] = value;
31
+ return;
32
+ }
33
+ if (Array.isArray(existingValue)) {
34
+ existingValue.push(value);
35
+ return;
36
+ }
37
+ target[key] = [existingValue, value];
38
+ }
39
+ function getRequest(event) {
40
+ const maybeRequest = event.request;
41
+ if (maybeRequest) return maybeRequest;
42
+ return toRequest(event);
43
+ }
44
+ function getContentType(event) {
45
+ return getRequest(event).headers.get("content-type") ?? event.headers.get("content-type") ?? event.headers.get("Content-Type") ?? "";
46
+ }
47
+ function isJsonContentType(contentType) {
48
+ const mimeType = contentType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
49
+ return mimeType === "application/json" || mimeType.endsWith("+json");
50
+ }
51
+ function isFormContentType(contentType) {
52
+ return contentType.includes("multipart/form-data") || contentType.includes("application/x-www-form-urlencoded");
53
+ }
54
+ function parseSearchParams(searchParams) {
55
+ const result = {};
56
+ searchParams.forEach((value, key) => {
57
+ appendEntry(result, key, value);
58
+ });
59
+ return result;
60
+ }
61
+ function parseFormData(formData) {
62
+ const result = {};
63
+ formData.forEach((value, key) => {
64
+ appendEntry(result, key, value);
65
+ });
66
+ return result;
67
+ }
68
+ async function parseRequestData(event) {
69
+ const request = getRequest(event);
70
+ const httpEvent = event;
71
+ const h3Event = event;
72
+ const method = event.method.toUpperCase();
73
+ if (method === "GET" || method === "HEAD") return parseSearchParams(new URL(request.url, "http://localhost").searchParams);
74
+ const contentType = getContentType(event);
75
+ if (isJsonContentType(contentType)) try {
76
+ return await readBody(httpEvent) ?? {};
77
+ } catch {
78
+ try {
79
+ return await request.json();
80
+ } catch {
81
+ return {};
82
+ }
83
+ }
84
+ if (isFormContentType(contentType)) try {
85
+ return parseFormData(await readFormData(h3Event));
86
+ } catch {
87
+ if (typeof request.formData === "function") return parseFormData(await request.formData());
88
+ return {};
89
+ }
90
+ try {
91
+ return await readBody(httpEvent) ?? {};
92
+ } catch {
93
+ try {
94
+ return await request.json();
95
+ } catch {
96
+ return {};
97
+ }
98
+ }
99
+ }
100
+ //#endregion
101
+ //#region packages/router/server/actions/src/validate.ts
102
+ /**
103
+ * Validates unknown input against a Standard Schema.
104
+ *
105
+ * Handles both sync and async `validate` implementations — the Standard
106
+ * Schema spec allows either return shape.
107
+ */
108
+ async function validateWithSchema(schema, data) {
109
+ return schema["~standard"].validate(data);
110
+ }
111
+ //#endregion
112
+ //#region packages/router/server/actions/src/define-action.ts
113
+ /**
114
+ * Creates a server action handler with Standard Schema input validation.
115
+ *
116
+ * Parses the request body (JSON or FormData) and validates it against the
117
+ * provided schema before invoking the handler. On validation failure,
118
+ * returns `fail(422, issues)` with `StandardSchemaV1.Issue[]`.
119
+ * Repeated form fields are preserved as arrays instead of being collapsed
120
+ * to the last value.
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * import { defineAction, json } from '@analogjs/router/server/actions';
125
+ * import * as v from 'valibot';
126
+ *
127
+ * const Schema = v.object({
128
+ * email: v.pipe(v.string(), v.email()),
129
+ * });
130
+ *
131
+ * export const action = defineAction({
132
+ * schema: Schema,
133
+ * handler: async ({ data }) => {
134
+ * // data is typed as { email: string }
135
+ * return json({ ok: true });
136
+ * },
137
+ * });
138
+ * ```
139
+ */
140
+ function defineAction(options) {
141
+ function getParams(params) {
142
+ return params ?? {};
143
+ }
144
+ return async (ctx) => {
145
+ const rawParams = getParams(ctx.params);
146
+ if (options.params) {
147
+ const paramsResult = await validateWithSchema(options.params, rawParams);
148
+ if (paramsResult.issues) return fail(422, paramsResult.issues);
149
+ return handleValidatedRequest(ctx, options, paramsResult.value);
150
+ }
151
+ return handleValidatedRequest(ctx, options, rawParams);
152
+ };
153
+ }
154
+ async function handleValidatedRequest(ctx, options, params) {
155
+ const body = await parseRequestData(ctx.event);
156
+ let data = body;
157
+ if (options.schema) {
158
+ const result = await validateWithSchema(options.schema, body);
159
+ if (result.issues) return fail(422, result.issues);
160
+ data = result.value;
161
+ }
162
+ return options.handler({
163
+ data,
164
+ params,
165
+ req: ctx.req,
166
+ res: ctx.res,
167
+ fetch: ctx.fetch,
168
+ event: ctx.event
169
+ });
170
+ }
171
+ //#endregion
172
+ //#region packages/router/server/actions/src/define-server-route.ts
173
+ function isDevEnvironment() {
174
+ return typeof process !== "undefined" && (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test");
175
+ }
176
+ function warnOnOutputIssues(issues) {
177
+ console.warn(`[analog] Server route output validation failed:\n` + issues.map((i) => {
178
+ const path = i.path ? ` at "${i.path.map((p) => typeof p === "object" ? p.key : p).join(".")}"` : "";
179
+ return ` - ${i.message}${path}`;
180
+ }).join("\n"));
181
+ }
182
+ function getRequestUrl(event) {
183
+ try {
184
+ return getRequestURL(event).href;
185
+ } catch {
186
+ return event.request.url;
187
+ }
188
+ }
189
+ /**
190
+ * Creates an h3-compatible event handler with Standard Schema validation.
191
+ *
192
+ * - `input` schema validates the request body (POST/PUT/PATCH) or query
193
+ * params (GET). Returns 422 with `StandardSchemaV1.Issue[]` on failure.
194
+ * - `output` schema validates the response in development only (stripped
195
+ * in production for zero overhead). Logs a warning on mismatch.
196
+ * - Plain return values are serialized with `json(...)`; raw `Response`
197
+ * objects are returned unchanged.
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * import { defineServerRoute } from '@analogjs/router/server/actions';
202
+ * import * as v from 'valibot';
203
+ *
204
+ * const Input = v.object({
205
+ * name: v.pipe(v.string(), v.minLength(1)),
206
+ * email: v.pipe(v.string(), v.email()),
207
+ * });
208
+ * const Output = v.object({
209
+ * id: v.string(),
210
+ * name: v.string(),
211
+ * });
212
+ *
213
+ * export default defineServerRoute({
214
+ * input: Input,
215
+ * output: Output,
216
+ * handler: async ({ data }) => {
217
+ * const user = await db.users.create(data);
218
+ * return user;
219
+ * },
220
+ * });
221
+ * ```
222
+ */
223
+ function defineServerRoute(options) {
224
+ return (async (event) => {
225
+ const method = event.method.toUpperCase();
226
+ let data;
227
+ let query;
228
+ let body;
229
+ let params = event.context?.params ?? {};
230
+ if (options.params) {
231
+ const paramsResult = await validateWithSchema(options.params, params);
232
+ if (paramsResult.issues) return fail(422, paramsResult.issues);
233
+ params = paramsResult.value;
234
+ }
235
+ if (options.input) {
236
+ data = await parseRequestData(event);
237
+ const inputResult = await validateWithSchema(options.input, data);
238
+ if (inputResult.issues) return fail(422, inputResult.issues);
239
+ data = inputResult.value;
240
+ } else {
241
+ if (options.query) {
242
+ const url = new URL(getRequestUrl(event), "http://localhost");
243
+ const queryResult = await validateWithSchema(options.query, parseSearchParams(url.searchParams));
244
+ if (queryResult.issues) return fail(422, queryResult.issues);
245
+ query = queryResult.value;
246
+ }
247
+ if (options.body && method !== "GET" && method !== "HEAD") {
248
+ body = await parseRequestData(event);
249
+ const bodyResult = await validateWithSchema(options.body, body);
250
+ if (bodyResult.issues) return fail(422, bodyResult.issues);
251
+ body = bodyResult.value;
252
+ }
253
+ if (method === "GET" || method === "HEAD") data = query;
254
+ else if (body !== void 0) data = body;
255
+ else data = query;
256
+ }
257
+ const result = await options.handler({
258
+ data,
259
+ query,
260
+ body,
261
+ params,
262
+ event
263
+ });
264
+ if (result instanceof Response) return result;
265
+ if (options.output && isDevEnvironment()) {
266
+ const outputResult = await validateWithSchema(options.output, result);
267
+ if (outputResult.issues) warnOnOutputIssues(outputResult.issues);
268
+ }
269
+ return json(result);
270
+ });
271
+ }
272
+ //#endregion
273
+ //#region packages/router/server/actions/src/define-page-load.ts
274
+ /**
275
+ * Creates a typed page server load function with optional
276
+ * Standard Schema validation for route params and query.
277
+ *
278
+ * Follows the same validation patterns as `defineAction` and
279
+ * `defineServerRoute`: validates before invoking the handler,
280
+ * returns `fail(422, issues)` on validation failure.
281
+ *
282
+ * @example
283
+ * ```typescript
284
+ * // src/app/pages/users/[id].server.ts
285
+ * import { definePageLoad } from '@analogjs/router/server/actions';
286
+ * import * as v from 'valibot';
287
+ *
288
+ * export const routeParamsSchema = v.object({
289
+ * id: v.pipe(v.string(), v.regex(/^\d+$/)),
290
+ * });
291
+ *
292
+ * export const load = definePageLoad({
293
+ * params: routeParamsSchema,
294
+ * handler: async ({ params, fetch }) => {
295
+ * // params.id is typed as string (validated)
296
+ * const user = await fetch(`/api/users/${params.id}`);
297
+ * return user;
298
+ * },
299
+ * });
300
+ * ```
301
+ */
302
+ function definePageLoad(options) {
303
+ return async (ctx) => {
304
+ let params = ctx.params ?? {};
305
+ let requestUrl;
306
+ try {
307
+ requestUrl = getRequestURL(ctx.event).href;
308
+ } catch {
309
+ requestUrl = ctx.event.request.url;
310
+ }
311
+ let query = parseSearchParams(new URL(requestUrl, "http://localhost").searchParams);
312
+ if (options.params) {
313
+ const result = await validateWithSchema(options.params, params);
314
+ if (result.issues) return fail(422, result.issues);
315
+ params = result.value;
316
+ }
317
+ if (options.query) {
318
+ const result = await validateWithSchema(options.query, query);
319
+ if (result.issues) return fail(422, result.issues);
320
+ query = result.value;
321
+ }
322
+ return options.handler({
323
+ params,
324
+ query,
325
+ req: ctx.req,
326
+ res: ctx.res,
327
+ fetch: ctx.fetch,
328
+ event: ctx.event
329
+ });
330
+ };
331
+ }
332
+ //#endregion
333
+ export { fail as a, validateWithSchema as i, defineServerRoute as n, json as o, defineAction as r, redirect as s, definePageLoad as t };
334
+
335
+ //# sourceMappingURL=src.mjs.map