@analogjs/router 2.6.4-beta.4 → 2.7.0-beta.1

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.
@@ -1,13 +1,315 @@
1
- import { ɵresetCompiledComponents as _resetCompiledComponents, InjectionToken, assertInInjectionContext, inject, TransferState, makeStateKey, reflectComponentType, APP_ID, ɵConsole as _Console, enableProdMode } from '@angular/core';
2
- import { ɵSERVER_CONTEXT as _SERVER_CONTEXT, provideServerRendering, renderApplication } from '@angular/platform-server';
1
+ import { InjectionToken, Injector, runInInjectionContext, ɵresetCompiledComponents as _resetCompiledComponents, assertInInjectionContext, inject, TransferState, makeStateKey, reflectComponentType, APP_ID, ɵConsole as _Console, enableProdMode } from '@angular/core';
2
+ import { ɵSERVER_CONTEXT as _SERVER_CONTEXT, provideServerRendering, renderApplication, platformServer } from '@angular/platform-server';
3
3
  import { REQUEST, RESPONSE, BASE_URL, LOCALE } from '@analogjs/router/tokens';
4
- import { bootstrapApplication } from '@angular/platform-browser';
5
- import { getHeader, createEvent, readBody } from 'h3';
4
+ import { SERVER_FN_DISPATCHER, createServerFnRef } from '@analogjs/router';
5
+ import { HttpErrorResponse } from '@angular/common/http';
6
+ import { bootstrapApplication, createApplication } from '@angular/platform-browser';
7
+ import { getHeader, createEvent, readBody, eventHandler, getRouterParam } from 'h3';
8
+
9
+ /**
10
+ * Server-side registry of server functions, keyed by id. A `.server.ts` module
11
+ * populates it as a side effect of `serverFn(...)` running at import time; the
12
+ * Nitro dispatch route imports those modules to fill it, then looks up by id.
13
+ */
14
+ const serverFnRegistry = new Map();
15
+
16
+ const SERVER_FN_INTERCEPTORS = new InjectionToken('SERVER_FN_INTERCEPTORS');
17
+ /** `withServerFnInterceptors([...])` — registers the chain (DI, ordered). */
18
+ function withServerFnInterceptors(interceptors) {
19
+ return {
20
+ providers: interceptors.map((fn) => ({
21
+ provide: SERVER_FN_INTERCEPTORS,
22
+ useValue: fn,
23
+ multi: true,
24
+ })),
25
+ };
26
+ }
27
+ /** `provideServerFns(withServerFnInterceptors(...))` — mirrors provideHttpClient. */
28
+ function provideServerFns(...features) {
29
+ return features.flatMap((f) => f.providers);
30
+ }
31
+ function makeCtx(input, context) {
32
+ return {
33
+ input,
34
+ context,
35
+ with(patch) {
36
+ return makeCtx(input, { ...context, ...patch });
37
+ },
38
+ };
39
+ }
40
+ /**
41
+ * Run the interceptor chain, then the handler, threading the context.
42
+ *
43
+ * `runInCtx` re-establishes the DI injection context around each interceptor
44
+ * and the handler individually. This is what keeps `inject()` working in a
45
+ * handler even when an upstream interceptor `await`s before calling `next`
46
+ * (which would otherwise resume outside Angular's synchronous injection
47
+ * context). It defaults to a pass-through for non-DI callers/tests.
48
+ */
49
+ async function runInterceptors(interceptors, input, handler, runInCtx = (fn) => fn()) {
50
+ let i = -1;
51
+ const dispatch = async (ctx) => {
52
+ i += 1;
53
+ if (i < interceptors.length) {
54
+ return runInCtx(() => interceptors[i](ctx, dispatch));
55
+ }
56
+ return runInCtx(() => handler(ctx.input, ctx.context));
57
+ };
58
+ return dispatch(makeCtx(input, {}));
59
+ }
60
+
61
+ /**
62
+ * Same-origin enforcement for the server-function HTTP transport.
63
+ *
64
+ * Server functions are same-origin RPC: a client proxy only ever calls the
65
+ * relative `/_analog/fn/:id` URL of its own app. A cross-origin page must not be
66
+ * able to invoke them against a logged-in user (a CSRF-shaped attack), so the
67
+ * transport rejects browser requests whose origin is not the app's own — out of
68
+ * the box, with no per-app configuration.
69
+ *
70
+ * The signals used (`Sec-Fetch-Site`, `Origin`) are added by the browser and
71
+ * cannot be forged by a cross-origin page's `fetch`. Non-browser callers (curl,
72
+ * server-to-server, SSR in-process) send neither, so they are unaffected: the
73
+ * guard blocks the cross-origin browser attack it is meant to, and nothing else.
74
+ */
75
+ /**
76
+ * Origins permitted beyond the app's own, registered through DI:
77
+ * `provideServerFns(withAllowedOrigins([...]))`. Empty by default — the
78
+ * transport is same-origin unless an app opts out explicitly.
79
+ */
80
+ const SERVER_FN_ALLOWED_ORIGINS = new InjectionToken('SERVER_FN_ALLOWED_ORIGINS');
81
+ /**
82
+ * `withAllowedOrigins([...])` — permit cross-origin browser calls from the
83
+ * listed origins, or pass `'*'` to disable the same-origin guard entirely.
84
+ * Server functions are frequently cookie-authenticated, so this is an explicit
85
+ * opt-out of CSRF protection: allow-list the exact origins you control.
86
+ */
87
+ function withAllowedOrigins(origins) {
88
+ return {
89
+ providers: origins.map((origin) => ({
90
+ provide: SERVER_FN_ALLOWED_ORIGINS,
91
+ useValue: origin,
92
+ multi: true,
93
+ })),
94
+ };
95
+ }
96
+ function firstHeader(value) {
97
+ return Array.isArray(value) ? value[0] : value;
98
+ }
99
+ /**
100
+ * Whether an HTTP request to a server function may proceed.
101
+ *
102
+ * Allowed when the request is same-origin, carries no browser-origin signal at
103
+ * all (a non-browser client, or a same-origin GET that omits `Origin`), or its
104
+ * `Origin` is listed in `allowedOrigins`. Passing `'*'` in `allowedOrigins`
105
+ * disables the check — the explicit opt-in to cross-origin access.
106
+ *
107
+ * `Sec-Fetch-Site` is the authoritative signal when present: `same-origin` and
108
+ * `none` (a direct navigation, not a cross-site fetch) pass; `same-site` and
109
+ * `cross-site` require an explicit `allowedOrigins` entry. When the header is
110
+ * absent (older browsers, some proxies) the `Origin` host is compared to the
111
+ * request host as a fallback.
112
+ */
113
+ function isServerFnOriginAllowed(headers, allowedOrigins = []) {
114
+ if (allowedOrigins.includes('*')) {
115
+ return true;
116
+ }
117
+ const origin = firstHeader(headers['origin']);
118
+ const originAllowlisted = origin !== undefined && allowedOrigins.includes(origin);
119
+ const site = firstHeader(headers['sec-fetch-site']);
120
+ if (site) {
121
+ if (site === 'same-origin' || site === 'none') {
122
+ return true;
123
+ }
124
+ // same-site / cross-site: only when the origin is explicitly permitted.
125
+ return originAllowlisted;
126
+ }
127
+ // No `Sec-Fetch-Site`: fall back to comparing the `Origin` host to the host.
128
+ if (!origin) {
129
+ // Non-browser client, or a same-origin GET with no `Origin` — not the
130
+ // cross-origin browser request this guard exists to reject.
131
+ return true;
132
+ }
133
+ if (originAllowlisted) {
134
+ return true;
135
+ }
136
+ const host = firstHeader(headers['x-forwarded-host']) ?? firstHeader(headers['host']);
137
+ if (!host) {
138
+ return false;
139
+ }
140
+ try {
141
+ return new URL(origin).host === host;
142
+ }
143
+ catch {
144
+ return false;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Server-side dispatch for a server function call.
150
+ *
151
+ * 1. reject cross-origin browser calls (403), unless allow-listed — HTTP
152
+ * transport only (in-process callers omit `method` and are exempt)
153
+ * 2. look up the function by id
154
+ * 3. enforce the configured HTTP method (405 on mismatch)
155
+ * 4. require a JSON body on input-bearing calls (415 otherwise)
156
+ * 5. validate `input` against the Standard-Schema (4xx on failure)
157
+ * 6. build a per-request injector (REQUEST/RESPONSE + app providers)
158
+ * 7. run the interceptor chain, then the handler, re-entering
159
+ * `runInInjectionContext` at every hop so `inject()` works even after an
160
+ * interceptor `await`s before calling `next`
161
+ * 8. a `Response` returned by an interceptor/handler (`fail`/`redirect`)
162
+ * short-circuits with its status AND headers
163
+ *
164
+ * `options.method` is the request's HTTP method; when provided it is enforced
165
+ * against the function's configured method AND it turns on the same-origin
166
+ * guard. Transports (the generated Nitro handler) always pass it; trusted
167
+ * in-process callers may omit it, which also exempts them from the origin guard.
168
+ */
169
+ async function dispatchServerFn(id, rawInput, event, options = {}) {
170
+ const { parent, providers = [], method, allowedOrigins = [] } = options;
171
+ const headers = (event.node.req.headers ?? {});
172
+ // Same-origin guard runs first — before we even confirm the function exists —
173
+ // so a cross-origin page cannot probe which ids are registered. Gated on
174
+ // `method` so only HTTP-transport calls are checked; in-process callers omit
175
+ // it. The signals (`Origin`/`Sec-Fetch-Site`) are browser-set and unforgeable.
176
+ if (method) {
177
+ const allowed = [
178
+ ...allowedOrigins,
179
+ ...(parent?.get(SERVER_FN_ALLOWED_ORIGINS, []) ?? []),
180
+ ];
181
+ if (!isServerFnOriginAllowed(headers, allowed)) {
182
+ return {
183
+ status: 403,
184
+ body: { message: 'Cross-origin server function call rejected' },
185
+ };
186
+ }
187
+ }
188
+ const def = serverFnRegistry.get(id);
189
+ if (!def) {
190
+ return { status: 404, body: { message: `Unknown server function: ${id}` } };
191
+ }
192
+ // Enforce the transport method: a GET-only read must not be POSTable, and an
193
+ // input-bearing POST must not be reachable via GET.
194
+ if (method && method.toUpperCase() !== def.method) {
195
+ return {
196
+ status: 405,
197
+ body: { message: `Method ${method} not allowed for ${id}` },
198
+ headers: { Allow: def.method },
199
+ };
200
+ }
201
+ // Input travels as a JSON body. Reject anything else before decoding, so a
202
+ // form post from a cross-origin page (which cannot set a JSON content type
203
+ // without a CORS preflight) never reaches a handler. HTTP transport only.
204
+ if (method && def.method === 'POST' && !isJsonContentType(headers)) {
205
+ return {
206
+ status: 415,
207
+ body: { message: 'Server functions accept an application/json body' },
208
+ };
209
+ }
210
+ let input = rawInput;
211
+ if (def.config.input) {
212
+ const result = await def.config.input['~standard'].validate(rawInput);
213
+ if ('issues' in result && result.issues) {
214
+ return { status: 400, body: { errors: result.issues } };
215
+ }
216
+ input = result.value;
217
+ }
218
+ // Child of the app injector: only the request tokens are per-request; app
219
+ // services + interceptors resolve up the parent chain. All four are provided
220
+ // here so a handler resolves them the same way it would inside a component
221
+ // during SSR, whether it was reached over HTTP or in-process.
222
+ const req = event.node.req;
223
+ const locale = detectLocale(req);
224
+ const injector = Injector.create({
225
+ parent,
226
+ providers: [
227
+ { provide: REQUEST, useValue: event.node.req },
228
+ { provide: RESPONSE, useValue: event.node.res },
229
+ { provide: BASE_URL, useValue: getBaseUrl(req) },
230
+ ...(locale ? [{ provide: LOCALE, useValue: locale }] : []),
231
+ ...providers,
232
+ ],
233
+ });
234
+ // Re-enter the injection context at each hop rather than wrapping the whole
235
+ // chain once: an interceptor that awaits before `next` would otherwise run the
236
+ // handler outside the context and break `inject()`.
237
+ const runInCtx = (fn) => runInInjectionContext(injector, fn);
238
+ const interceptors = injector.get(SERVER_FN_INTERCEPTORS, []);
239
+ const outcome = await runInterceptors(interceptors, input, def.handler, runInCtx);
240
+ if (outcome instanceof Response) {
241
+ const text = await outcome.text();
242
+ const body = text ? safeJson(text) : null;
243
+ const headers = {};
244
+ outcome.headers.forEach((value, key) => {
245
+ if (key.toLowerCase() !== 'set-cookie') {
246
+ headers[key] = value;
247
+ }
248
+ });
249
+ // `Headers.forEach` yields cookies comma-joined into a single value, which
250
+ // is not a valid way to send more than one; `getSetCookie` keeps them apart.
251
+ const setCookie = outcome.headers.getSetCookie?.() ?? [];
252
+ if (setCookie.length) {
253
+ headers['set-cookie'] = setCookie;
254
+ }
255
+ return {
256
+ status: outcome.status,
257
+ body,
258
+ headers: Object.keys(headers).length ? headers : undefined,
259
+ };
260
+ }
261
+ return { status: 200, body: outcome };
262
+ }
263
+ function isJsonContentType(headers) {
264
+ const contentType = headers['content-type'];
265
+ const value = Array.isArray(contentType) ? contentType[0] : contentType;
266
+ if (!value) {
267
+ return false;
268
+ }
269
+ const mediaType = value.split(';')[0].trim().toLowerCase();
270
+ return mediaType === 'application/json' || mediaType.endsWith('+json');
271
+ }
272
+ function safeJson(text) {
273
+ try {
274
+ return JSON.parse(text);
275
+ }
276
+ catch {
277
+ return text;
278
+ }
279
+ }
280
+
281
+ /**
282
+ * The in-process transport used during SSR. `ServerFnClient` picks this up from
283
+ * DI and calls the handler directly instead of issuing an HTTP request back
284
+ * into the app — the render and the handler already share a process and a
285
+ * request, so the round-trip only adds latency (and would need an absolute URL).
286
+ *
287
+ * `method` is deliberately not passed to `dispatchServerFn`: this is a trusted
288
+ * in-process caller, so the HTTP-transport-only checks (same-origin, method
289
+ * enforcement, content type) do not apply. Validation and the interceptor chain
290
+ * still run, so an SSR call behaves like a browser call in every other respect.
291
+ *
292
+ * A non-2xx result is thrown as an `HttpErrorResponse` so the failure surfaces
293
+ * on `resource.error()` exactly as it does in the browser.
294
+ */
295
+ function createServerFnDispatcher(req, res) {
296
+ const event = { node: { req, res } };
297
+ return async (fn, input, injector) => {
298
+ const { status, body } = await dispatchServerFn(fn.id, input, event, {
299
+ parent: injector,
300
+ });
301
+ if (status < 200 || status > 299) {
302
+ throw new HttpErrorResponse({ status, error: body, url: fn.url });
303
+ }
304
+ return body;
305
+ };
306
+ }
6
307
 
7
308
  function provideServerContext({ req, res, }) {
8
309
  const baseUrl = getBaseUrl(req);
9
310
  const locale = detectLocale(req);
10
- if (import.meta.env.DEV) {
311
+ // Optional chaining: a Nitro-bundled caller has no `import.meta.env` at all.
312
+ if (import.meta.env?.DEV) {
11
313
  _resetCompiledComponents();
12
314
  }
13
315
  return [
@@ -15,6 +317,11 @@ function provideServerContext({ req, res, }) {
15
317
  { provide: REQUEST, useValue: req },
16
318
  { provide: RESPONSE, useValue: res },
17
319
  { provide: BASE_URL, useValue: baseUrl },
320
+ // Server functions called while rendering run in-process, in this injector.
321
+ {
322
+ provide: SERVER_FN_DISPATCHER,
323
+ useValue: createServerFnDispatcher(req, res),
324
+ },
18
325
  ...(locale ? [{ provide: LOCALE, useValue: locale }] : []),
19
326
  ];
20
327
  }
@@ -67,7 +374,10 @@ function parseAcceptLanguage(header) {
67
374
  }
68
375
  function getBaseUrl(req) {
69
376
  const protocol = getRequestProtocol(req);
70
- const { originalUrl, headers } = req;
377
+ const { headers } = req;
378
+ // Node's `IncomingMessage` has no `originalUrl`, and a server function
379
+ // endpoint is reached with a plain request, so fall back before dereferencing.
380
+ const originalUrl = req.originalUrl || req.url || '/';
71
381
  const parsedUrl = new URL('', `${protocol}://${headers.host}${originalUrl.endsWith('/')
72
382
  ? originalUrl.substring(0, originalUrl.length - 1)
73
383
  : originalUrl}`);
@@ -115,9 +425,16 @@ function serverComponentRequest(serverContext) {
115
425
  }
116
426
  return serverComponentId;
117
427
  }
118
- const components = import.meta.glob([
119
- '/src/server/components/**/*.{ts,analog,ag}',
120
- ]);
428
+ // Deferred so the module can be imported outside a Vite context (e.g. by the
429
+ // Nitro server-function dispatch path, or tooling) without eagerly evaluating
430
+ // the Vite-only `import.meta.glob` macro at load time. Vite still statically
431
+ // rewrites the macro; it is just invoked lazily on first server-component render.
432
+ let _components;
433
+ function serverComponents() {
434
+ return (_components ??= import.meta.glob([
435
+ '/src/server/components/**/*.{ts,analog,ag}',
436
+ ]));
437
+ }
121
438
  async function renderServerComponent(url, serverContext, config) {
122
439
  const componentReqId = serverComponentRequest(serverContext);
123
440
  const { componentLoader, componentId } = getComponentLoader(componentReqId);
@@ -181,6 +498,7 @@ function getComponentLoader(componentReqId) {
181
498
  let _componentId = `/src/server/components/${componentReqId.toLowerCase()}`;
182
499
  let componentLoader = undefined;
183
500
  let componentId = _componentId;
501
+ const components = serverComponents();
184
502
  if (components[`${_componentId}.ts`]) {
185
503
  componentId = `${_componentId}.ts`;
186
504
  componentLoader = components[componentId];
@@ -208,7 +526,9 @@ function retrieveTransferredState(html, appId) {
208
526
  }
209
527
  }
210
528
 
211
- if (import.meta.env.PROD) {
529
+ // Optional chaining: the server-function dispatch endpoint imports this entry
530
+ // from a Nitro bundle, where `import.meta.env` is not defined at all.
531
+ if (import.meta.env?.PROD) {
212
532
  enableProdMode();
213
533
  }
214
534
  /**
@@ -262,9 +582,138 @@ function render(rootComponent, config, platformProviders = []) {
262
582
  };
263
583
  }
264
584
 
585
+ function serverFn(arg1, arg2) {
586
+ const { config, handler } = normalizeArgs(arg1, arg2);
587
+ // GET is reserved for input-less reads; an input schema requires POST (the
588
+ // input travels in the body, not the query). Build transforms reject this too.
589
+ if (config.method === 'GET' && config.input) {
590
+ throw new Error('[analog] a serverFn with `input` must use POST; GET is reserved for input-less reads.');
591
+ }
592
+ // `createServerFnRef` throws if the build-derived id is missing, so `ref.id`
593
+ // is the authoritative route key here.
594
+ const ref = createServerFnRef(config);
595
+ serverFnRegistry.set(ref.id, {
596
+ id: ref.id,
597
+ method: ref.method,
598
+ config,
599
+ handler,
600
+ });
601
+ return ref;
602
+ }
603
+ function normalizeArgs(arg1, arg2) {
604
+ // serverFn(handler) — input-less GET.
605
+ if (typeof arg1 === 'function') {
606
+ return {
607
+ config: {},
608
+ handler: arg1,
609
+ };
610
+ }
611
+ // serverFn(schema, handler) — a Standard Schema ⇒ POST + input.
612
+ if (isStandardSchema(arg1)) {
613
+ return {
614
+ config: { input: arg1 },
615
+ handler: arg2,
616
+ };
617
+ }
618
+ // serverFn(config, handler) — explicit config object.
619
+ return {
620
+ config: arg1 ?? {},
621
+ handler: arg2,
622
+ };
623
+ }
624
+ function isStandardSchema(value) {
625
+ return typeof value === 'object' && value !== null && '~standard' in value;
626
+ }
627
+
628
+ /**
629
+ * Builds the parent injector the server-function dispatch endpoint runs handlers
630
+ * against, over HTTP.
631
+ *
632
+ * A plain `Injector.create({ providers })` resolves explicitly-listed providers
633
+ * but not tree-shakeable `providedIn: 'root'` services — those attach to a
634
+ * *bootstrapped* application's root injector, which `Injector.create` is not.
635
+ * So the in-process SSR leg (whose parent is the app's own bootstrapped
636
+ * injector) resolved `root` services while the HTTP leg did not — the same
637
+ * handler could work while rendering and fail when called from the browser.
638
+ *
639
+ * Bootstrapping a real application on the server platform closes that gap: the
640
+ * returned `appRef.injector` is a root environment injector, so both listed
641
+ * providers and `providedIn: 'root'` services resolve, matching SSR.
642
+ *
643
+ * The generated endpoint passes the app's own server `ApplicationConfig` (the
644
+ * one `main.server.ts` renders with), so a handler sees exactly the DI the app
645
+ * configured — services, tokens, and interceptors alike — with no second
646
+ * provider list to keep in sync. No root component is bootstrapped
647
+ * (`createApplication`, not `bootstrapApplication`), so nothing renders, no
648
+ * change detection runs, and the router registers but never navigates. It is a
649
+ * DI container with the app's providers, built once and reused for the process,
650
+ * with only `REQUEST`/`RESPONSE` rebuilt per call in the child.
651
+ *
652
+ * A bare provider array is also accepted (direct callers and tests without an
653
+ * app config); it is wrapped with `provideServerRendering` so the server tokens
654
+ * resolve the same way.
655
+ */
656
+ async function createServerFnAppInjector(configOrProviders = []) {
657
+ const config = Array.isArray(configOrProviders)
658
+ ? { providers: [provideServerRendering(), ...configOrProviders] }
659
+ : configOrProviders;
660
+ const appRef = await createApplication(config, {
661
+ platformRef: platformServer(),
662
+ });
663
+ return appRef.injector;
664
+ }
665
+
666
+ /**
667
+ * The h3 request/response layer for the server-function dispatch route.
668
+ *
669
+ * `createServerFnAppInjector` bootstraps the parent injector once; this wraps
670
+ * that in the `/_analog/fn/:id` handler the Nitro build registers. Kept as a
671
+ * runtime function (rather than inlined into the generated module) so the
672
+ * transport behaviour — body decoding, the malformed-body contract, and header
673
+ * propagation — is unit-tested directly instead of by matching generated source.
674
+ *
675
+ * `appInjector` may be a promise: the generated module bootstraps the app at
676
+ * import time and passes the pending injector, which is awaited on first request
677
+ * and resolved instantly thereafter.
678
+ */
679
+ function createServerFnEventHandler(appInjector) {
680
+ return eventHandler((event) => handleServerFnRequest(event, appInjector));
681
+ }
682
+ /**
683
+ * Decode a server-function request, dispatch it, and write the result to the
684
+ * h3 response. Same-origin, method, content-type, validation, and interceptors
685
+ * are enforced inside `dispatchServerFn`; this owns only the h3 I/O around it.
686
+ */
687
+ async function handleServerFnRequest(event, appInjector) {
688
+ const id = getRouterParam(event, 'id') ?? '';
689
+ // h3 parses the body before dispatch gets a say, and its parse error is an
690
+ // HTML/500-shaped response rather than the JSON contract callers expect.
691
+ let input;
692
+ if (event.method !== 'GET') {
693
+ try {
694
+ input = await readBody(event);
695
+ }
696
+ catch {
697
+ event.node.res.statusCode = 400;
698
+ return { message: 'Malformed request body' };
699
+ }
700
+ }
701
+ const { status, body, headers } = await dispatchServerFn(id, input, event, {
702
+ parent: await appInjector,
703
+ method: event.method,
704
+ });
705
+ event.node.res.statusCode = status;
706
+ if (headers) {
707
+ for (const [key, value] of Object.entries(headers)) {
708
+ event.node.res.setHeader(key, value);
709
+ }
710
+ }
711
+ return body;
712
+ }
713
+
265
714
  /**
266
715
  * Generated bundle index. Do not edit.
267
716
  */
268
717
 
269
- export { injectStaticOutputs, injectStaticProps, provideServerContext, render, renderServerComponent, serverComponentRequest };
718
+ export { SERVER_FN_ALLOWED_ORIGINS, SERVER_FN_INTERCEPTORS, createServerFnAppInjector, createServerFnEventHandler, dispatchServerFn, handleServerFnRequest, injectStaticOutputs, injectStaticProps, isServerFnOriginAllowed, provideServerContext, provideServerFns, render, renderServerComponent, runInterceptors, serverComponentRequest, serverFn, serverFnRegistry, withAllowedOrigins, withServerFnInterceptors };
270
719
  //# sourceMappingURL=analogjs-router-server.mjs.map