@analogjs/router 3.0.0-alpha.63 → 3.0.0-alpha.65
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.mjs +684 -112
- package/fesm2022/analogjs-router-server.mjs.map +1 -1
- package/fesm2022/analogjs-router.mjs +133 -131
- package/fesm2022/analogjs-router.mjs.map +1 -1
- package/fesm2022/route-files.mjs.map +1 -1
- package/package.json +4 -4
- package/types/server/src/defer-reconcile-runtime.d.ts +23 -0
- package/types/server/src/index.d.ts +9 -2
- package/types/server/src/render-stream.d.ts +40 -0
- package/types/server/src/render.d.ts +2 -2
- package/types/server/src/server-fn/app-injector.d.ts +30 -0
- package/types/server/src/server-fn/dispatch.d.ts +56 -0
- package/types/server/src/server-fn/event-handler.d.ts +22 -0
- package/types/server/src/server-fn/interceptors.d.ts +30 -0
- package/types/server/src/server-fn/node-context.d.ts +14 -0
- package/types/server/src/server-fn/registry.d.ts +7 -0
- package/types/server/src/server-fn/same-origin.d.ts +46 -0
- package/types/server/src/server-fn/server-fn.d.ts +21 -0
- package/types/server/src/server-fn/ssr-dispatcher.d.ts +17 -0
- package/types/server/src/utils/reset-component-def-tviews.d.ts +14 -0
- package/types/server/src/utils/stream-html.d.ts +13 -0
- package/types/server/src/utils/stream-request.d.ts +24 -0
- package/types/src/index.d.ts +4 -1
- package/types/src/lib/server-fn/dispatcher.d.ts +23 -0
- package/types/src/lib/server-fn/inject-server-fn.d.ts +44 -0
- package/types/src/lib/server-fn/server-fn-ref.d.ts +24 -0
- package/types/src/lib/server-fn/types.d.ts +55 -0
- package/types/server/src/server-component-render.d.ts +0 -4
- package/types/server/src/tokens.d.ts +0 -7
- package/types/src/lib/server.component.d.ts +0 -33
|
@@ -7,6 +7,6 @@ import type { ServerContext } from '../../tokens/src/index.js';
|
|
|
7
7
|
* @param rootComponent
|
|
8
8
|
* @param config
|
|
9
9
|
* @param platformProviders
|
|
10
|
-
* @returns Promise<string
|
|
10
|
+
* @returns Promise<string>
|
|
11
11
|
*/
|
|
12
|
-
export declare function render(rootComponent: Type<unknown>, config: ApplicationConfig, platformProviders?: Provider[]): (url: string, document: string, serverContext: ServerContext) => Promise<string
|
|
12
|
+
export declare function render(rootComponent: Type<unknown>, config: ApplicationConfig, platformProviders?: Provider[]): (url: string, document: string, serverContext: ServerContext) => Promise<string>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ApplicationConfig, Injector, type StaticProvider } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* Builds the parent injector the server-function dispatch endpoint runs handlers
|
|
4
|
+
* against, over HTTP.
|
|
5
|
+
*
|
|
6
|
+
* A plain `Injector.create({ providers })` resolves explicitly-listed providers
|
|
7
|
+
* but not tree-shakeable `providedIn: 'root'` services — those attach to a
|
|
8
|
+
* *bootstrapped* application's root injector, which `Injector.create` is not.
|
|
9
|
+
* So the in-process SSR leg (whose parent is the app's own bootstrapped
|
|
10
|
+
* injector) resolved `root` services while the HTTP leg did not — the same
|
|
11
|
+
* handler could work while rendering and fail when called from the browser.
|
|
12
|
+
*
|
|
13
|
+
* Bootstrapping a real application on the server platform closes that gap: the
|
|
14
|
+
* returned `appRef.injector` is a root environment injector, so both listed
|
|
15
|
+
* providers and `providedIn: 'root'` services resolve, matching SSR.
|
|
16
|
+
*
|
|
17
|
+
* The generated endpoint passes the app's own server `ApplicationConfig` (the
|
|
18
|
+
* one `main.server.ts` renders with), so a handler sees exactly the DI the app
|
|
19
|
+
* configured — services, tokens, and interceptors alike — with no second
|
|
20
|
+
* provider list to keep in sync. No root component is bootstrapped
|
|
21
|
+
* (`createApplication`, not `bootstrapApplication`), so nothing renders, no
|
|
22
|
+
* change detection runs, and the router registers but never navigates. It is a
|
|
23
|
+
* DI container with the app's providers, built once and reused for the process,
|
|
24
|
+
* with only `REQUEST`/`RESPONSE` rebuilt per call in the child.
|
|
25
|
+
*
|
|
26
|
+
* A bare provider array is also accepted (direct callers and tests without an
|
|
27
|
+
* app config); it is wrapped with `provideServerRendering` so the server tokens
|
|
28
|
+
* resolve the same way.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createServerFnAppInjector(configOrProviders?: ApplicationConfig | StaticProvider[]): Promise<Injector>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Injector, type StaticProvider } from '@angular/core';
|
|
2
|
+
import type { H3Event } from 'nitro/h3';
|
|
3
|
+
export interface DispatchResult {
|
|
4
|
+
status: number;
|
|
5
|
+
body: unknown;
|
|
6
|
+
/**
|
|
7
|
+
* Headers from a returned `Response` (`fail`/`redirect`): Location, … The
|
|
8
|
+
* value is an array when the header legitimately repeats, which is why
|
|
9
|
+
* `Set-Cookie` is read separately below — collapsing several cookies into one
|
|
10
|
+
* comma-joined value corrupts them.
|
|
11
|
+
*/
|
|
12
|
+
headers?: Record<string, string | string[]>;
|
|
13
|
+
}
|
|
14
|
+
export interface DispatchServerFnOptions {
|
|
15
|
+
/**
|
|
16
|
+
* The app's environment injector. The per-request injector is created as its
|
|
17
|
+
* child, so handlers resolve app services (and `providedIn: 'root'` services,
|
|
18
|
+
* when this is the app's bootstrapped injector) and registered interceptors
|
|
19
|
+
* without re-listing them per request.
|
|
20
|
+
*/
|
|
21
|
+
parent?: Injector;
|
|
22
|
+
/** Extra per-request providers, for direct callers without an app injector. */
|
|
23
|
+
providers?: StaticProvider[];
|
|
24
|
+
/** Request HTTP method; enforced against the function's configured method. */
|
|
25
|
+
method?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Origins permitted beyond same-origin, merged with any registered through DI
|
|
28
|
+
* (`provideServerFns(withAllowedOrigins([...]))`). The transport is
|
|
29
|
+
* same-origin by default (cross-origin browser calls are rejected with 403);
|
|
30
|
+
* `'*'` disables the check entirely. Only consulted for HTTP-transport calls
|
|
31
|
+
* (those that pass `method`).
|
|
32
|
+
*/
|
|
33
|
+
allowedOrigins?: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Server-side dispatch for a server function call.
|
|
37
|
+
*
|
|
38
|
+
* 1. reject cross-origin browser calls (403), unless allow-listed — HTTP
|
|
39
|
+
* transport only (in-process callers omit `method` and are exempt)
|
|
40
|
+
* 2. look up the function by id
|
|
41
|
+
* 3. enforce the configured HTTP method (405 on mismatch)
|
|
42
|
+
* 4. require a JSON body on input-bearing calls (415 otherwise)
|
|
43
|
+
* 5. validate `input` against the Standard-Schema (4xx on failure)
|
|
44
|
+
* 6. build a per-request injector (REQUEST/RESPONSE + app providers)
|
|
45
|
+
* 7. run the interceptor chain, then the handler, re-entering
|
|
46
|
+
* `runInInjectionContext` at every hop so `inject()` works even after an
|
|
47
|
+
* interceptor `await`s before calling `next`
|
|
48
|
+
* 8. a `Response` returned by an interceptor/handler (`fail`/`redirect`)
|
|
49
|
+
* short-circuits with its status AND headers
|
|
50
|
+
*
|
|
51
|
+
* `options.method` is the request's HTTP method; when provided it is enforced
|
|
52
|
+
* against the function's configured method AND it turns on the same-origin
|
|
53
|
+
* guard. Transports (the generated Nitro handler) always pass it; trusted
|
|
54
|
+
* in-process callers may omit it, which also exempts them from the origin guard.
|
|
55
|
+
*/
|
|
56
|
+
export declare function dispatchServerFn(id: string, rawInput: unknown, event: Pick<H3Event, 'node'>, options?: DispatchServerFnOptions): Promise<DispatchResult>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Injector } from '@angular/core';
|
|
2
|
+
import { type EventHandler, type H3Event } from 'nitro/h3';
|
|
3
|
+
/**
|
|
4
|
+
* The h3 request/response layer for the server-function dispatch route.
|
|
5
|
+
*
|
|
6
|
+
* `createServerFnAppInjector` bootstraps the parent injector once; this wraps
|
|
7
|
+
* that in the `/_analog/fn/:id` handler the Nitro build registers. Kept as a
|
|
8
|
+
* runtime function (rather than inlined into the generated module) so the
|
|
9
|
+
* transport behaviour — body decoding, the malformed-body contract, and header
|
|
10
|
+
* propagation — is unit-tested directly instead of by matching generated source.
|
|
11
|
+
*
|
|
12
|
+
* `appInjector` may be a promise: the generated module bootstraps the app at
|
|
13
|
+
* import time and passes the pending injector, which is awaited on first request
|
|
14
|
+
* and resolved instantly thereafter.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createServerFnEventHandler(appInjector: Injector | Promise<Injector>): EventHandler;
|
|
17
|
+
/**
|
|
18
|
+
* Decode a server-function request, dispatch it, and write the result to the
|
|
19
|
+
* h3 response. Same-origin, method, content-type, validation, and interceptors
|
|
20
|
+
* are enforced inside `dispatchServerFn`; this owns only the h3 I/O around it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function handleServerFnRequest(event: H3Event, appInjector: Injector | Promise<Injector>): Promise<unknown>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { InjectionToken, type Provider } from '@angular/core';
|
|
2
|
+
import type { ServerFnContext } from '@analogjs/router';
|
|
3
|
+
/** Context threaded through the interceptor chain and handed to the handler. */
|
|
4
|
+
export interface ServerFnInterceptorContext {
|
|
5
|
+
readonly input: unknown;
|
|
6
|
+
readonly context: ServerFnContext;
|
|
7
|
+
/** Return a new context with additional typed fields merged in. */
|
|
8
|
+
with(patch: Partial<ServerFnContext> & Record<string, unknown>): ServerFnInterceptorContext;
|
|
9
|
+
}
|
|
10
|
+
export type ServerFnNext = (ctx: ServerFnInterceptorContext) => Promise<unknown>;
|
|
11
|
+
/** Functional interceptor, modeled on `HttpInterceptorFn`. */
|
|
12
|
+
export type ServerFnInterceptorFn = (ctx: ServerFnInterceptorContext, next: ServerFnNext) => Promise<unknown> | unknown;
|
|
13
|
+
export declare const SERVER_FN_INTERCEPTORS: InjectionToken<ServerFnInterceptorFn[]>;
|
|
14
|
+
export interface ServerFnsFeature {
|
|
15
|
+
providers: Provider[];
|
|
16
|
+
}
|
|
17
|
+
/** `withServerFnInterceptors([...])` — registers the chain (DI, ordered). */
|
|
18
|
+
export declare function withServerFnInterceptors(interceptors: ServerFnInterceptorFn[]): ServerFnsFeature;
|
|
19
|
+
/** `provideServerFns(withServerFnInterceptors(...))` — mirrors provideHttpClient. */
|
|
20
|
+
export declare function provideServerFns(...features: ServerFnsFeature[]): Provider[];
|
|
21
|
+
/**
|
|
22
|
+
* Run the interceptor chain, then the handler, threading the context.
|
|
23
|
+
*
|
|
24
|
+
* `runInCtx` re-establishes the DI injection context around each interceptor
|
|
25
|
+
* and the handler individually. This is what keeps `inject()` working in a
|
|
26
|
+
* handler even when an upstream interceptor `await`s before calling `next`
|
|
27
|
+
* (which would otherwise resume outside Angular's synchronous injection
|
|
28
|
+
* context). It defaults to a pass-through for non-DI callers/tests.
|
|
29
|
+
*/
|
|
30
|
+
export declare function runInterceptors(interceptors: ServerFnInterceptorFn[], input: unknown, handler: (input: unknown, context: ServerFnContext) => Promise<unknown> | unknown, runInCtx?: <T>(fn: () => T) => T): Promise<unknown>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { H3Event } from 'nitro/h3';
|
|
3
|
+
type NodeContext = NonNullable<H3Event['node']>;
|
|
4
|
+
type NodeRuntimeContext = NodeContext & {
|
|
5
|
+
req: IncomingMessage;
|
|
6
|
+
res: ServerResponse;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Dispatch provides the Node request and response through `REQUEST` and
|
|
10
|
+
* `RESPONSE`, which are typed as Node primitives, so server functions only run
|
|
11
|
+
* on a Node runtime. h3 leaves `node` undefined on other runtimes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function assertNodeContext(event: Pick<H3Event, 'node'>): NodeRuntimeContext;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ServerFnDef } from '@analogjs/router';
|
|
2
|
+
/**
|
|
3
|
+
* Server-side registry of server functions, keyed by id. A `.server.ts` module
|
|
4
|
+
* populates it as a side effect of `serverFn(...)` running at import time; the
|
|
5
|
+
* Nitro dispatch route imports those modules to fill it, then looks up by id.
|
|
6
|
+
*/
|
|
7
|
+
export declare const serverFnRegistry: Map<string, ServerFnDef>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Same-origin enforcement for the server-function HTTP transport.
|
|
3
|
+
*
|
|
4
|
+
* Server functions are same-origin RPC: a client proxy only ever calls the
|
|
5
|
+
* relative `/_analog/fn/:id` URL of its own app. A cross-origin page must not be
|
|
6
|
+
* able to invoke them against a logged-in user (a CSRF-shaped attack), so the
|
|
7
|
+
* transport rejects browser requests whose origin is not the app's own — out of
|
|
8
|
+
* the box, with no per-app configuration.
|
|
9
|
+
*
|
|
10
|
+
* The signals used (`Sec-Fetch-Site`, `Origin`) are added by the browser and
|
|
11
|
+
* cannot be forged by a cross-origin page's `fetch`. Non-browser callers (curl,
|
|
12
|
+
* server-to-server, SSR in-process) send neither, so they are unaffected: the
|
|
13
|
+
* guard blocks the cross-origin browser attack it is meant to, and nothing else.
|
|
14
|
+
*/
|
|
15
|
+
import { InjectionToken } from '@angular/core';
|
|
16
|
+
import type { ServerFnsFeature } from './interceptors';
|
|
17
|
+
/** Node/h3 header bag shape (`IncomingHttpHeaders`). */
|
|
18
|
+
export type HeaderBag = Record<string, string | string[] | undefined>;
|
|
19
|
+
/**
|
|
20
|
+
* Origins permitted beyond the app's own, registered through DI:
|
|
21
|
+
* `provideServerFns(withAllowedOrigins([...]))`. Empty by default — the
|
|
22
|
+
* transport is same-origin unless an app opts out explicitly.
|
|
23
|
+
*/
|
|
24
|
+
export declare const SERVER_FN_ALLOWED_ORIGINS: InjectionToken<string[]>;
|
|
25
|
+
/**
|
|
26
|
+
* `withAllowedOrigins([...])` — permit cross-origin browser calls from the
|
|
27
|
+
* listed origins, or pass `'*'` to disable the same-origin guard entirely.
|
|
28
|
+
* Server functions are frequently cookie-authenticated, so this is an explicit
|
|
29
|
+
* opt-out of CSRF protection: allow-list the exact origins you control.
|
|
30
|
+
*/
|
|
31
|
+
export declare function withAllowedOrigins(origins: string[]): ServerFnsFeature;
|
|
32
|
+
/**
|
|
33
|
+
* Whether an HTTP request to a server function may proceed.
|
|
34
|
+
*
|
|
35
|
+
* Allowed when the request is same-origin, carries no browser-origin signal at
|
|
36
|
+
* all (a non-browser client, or a same-origin GET that omits `Origin`), or its
|
|
37
|
+
* `Origin` is listed in `allowedOrigins`. Passing `'*'` in `allowedOrigins`
|
|
38
|
+
* disables the check — the explicit opt-in to cross-origin access.
|
|
39
|
+
*
|
|
40
|
+
* `Sec-Fetch-Site` is the authoritative signal when present: `same-origin` and
|
|
41
|
+
* `none` (a direct navigation, not a cross-site fetch) pass; `same-site` and
|
|
42
|
+
* `cross-site` require an explicit `allowedOrigins` entry. When the header is
|
|
43
|
+
* absent (older browsers, some proxies) the `Origin` host is compared to the
|
|
44
|
+
* request host as a fallback.
|
|
45
|
+
*/
|
|
46
|
+
export declare function isServerFnOriginAllowed(headers: HeaderBag, allowedOrigins?: readonly string[]): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ServerFn, ServerFnConfig, ServerFnHandler, StandardSchemaV1 } from '@analogjs/router';
|
|
2
|
+
/**
|
|
3
|
+
* Define a server function. Authored in a `*.server.ts` module.
|
|
4
|
+
*
|
|
5
|
+
* Three call shapes, chosen for ergonomics — they all normalize to the same
|
|
6
|
+
* `(config, handler)` form and the build transform derives the route id for each:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* serverFn(() => inject(Svc).list()); // input-less GET
|
|
10
|
+
* serverFn(schema, (input) => …); // schema ⇒ POST + input
|
|
11
|
+
* serverFn({ method: 'POST' }, () => …); // explicit config
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* On the server the function self-registers and its handler runs via
|
|
15
|
+
* `dispatchServerFn`. On the client the build transform replaces the body with a
|
|
16
|
+
* proxy that calls `/_analog/fn/<id>`; the reference still carries
|
|
17
|
+
* `id`/`url`/`method` so `injectServerFn`/`ServerFnClient` can dispatch.
|
|
18
|
+
*/
|
|
19
|
+
export declare function serverFn<Out>(handler: ServerFnHandler<void, Out>): ServerFn<void, Out>;
|
|
20
|
+
export declare function serverFn<In, Out>(input: StandardSchemaV1<In>, handler: ServerFnHandler<In, Out>): ServerFn<In, Out>;
|
|
21
|
+
export declare function serverFn<In, Out>(config: ServerFnConfig<In>, handler: ServerFnHandler<In, Out>): ServerFn<In, Out>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ServerRequest, ServerResponse } from '@analogjs/router/tokens';
|
|
2
|
+
import type { ServerFnDispatcher } from '@analogjs/router';
|
|
3
|
+
/**
|
|
4
|
+
* The in-process transport used during SSR. `ServerFnClient` picks this up from
|
|
5
|
+
* DI and calls the handler directly instead of issuing an HTTP request back
|
|
6
|
+
* into the app — the render and the handler already share a process and a
|
|
7
|
+
* request, so the round-trip only adds latency (and would need an absolute URL).
|
|
8
|
+
*
|
|
9
|
+
* `method` is deliberately not passed to `dispatchServerFn`: this is a trusted
|
|
10
|
+
* in-process caller, so the HTTP-transport-only checks (same-origin, method
|
|
11
|
+
* enforcement, content type) do not apply. Validation and the interceptor chain
|
|
12
|
+
* still run, so an SSR call behaves like a browser call in every other respect.
|
|
13
|
+
*
|
|
14
|
+
* A non-2xx result is thrown as an `HttpErrorResponse` so the failure surfaces
|
|
15
|
+
* on `resource.error()` exactly as it does in the browser.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createServerFnDispatcher(req: ServerRequest, res: ServerResponse): ServerFnDispatcher;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nulls `def.tView` on every component definition that Angular has
|
|
3
|
+
* compiled in this process. Angular caches the result of `consts()` on
|
|
4
|
+
* `def.tView` — that factory is where `$localize` tagged templates are
|
|
5
|
+
* evaluated — so without this reset the first rendered locale would be
|
|
6
|
+
* frozen into the cache for the process lifetime.
|
|
7
|
+
*
|
|
8
|
+
* The set on `globalThis.__ngComponentDefs` is populated by a Vite
|
|
9
|
+
* transform in `@analogjs/platform` that patches `@angular/core`'s
|
|
10
|
+
* `getComponentId()` to mirror every compiled component definition to
|
|
11
|
+
* a global Set, bypassing the `ngServerMode` guard that normally
|
|
12
|
+
* prevents registration on the server.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resetComponentDefTViews(): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure string helpers for slicing a fully rendered SSR document into the parts
|
|
3
|
+
* the streaming renderer flushes: the shell up to `<body>`, the authoritative
|
|
4
|
+
* `<body>` inner HTML for the tail, and the authoritative `<head>` inner HTML
|
|
5
|
+
* for the finalize-time head reconcile. Extracted from `render-stream` so they
|
|
6
|
+
* can be unit tested without driving the platform.
|
|
7
|
+
*/
|
|
8
|
+
/** Byte offset just after the opening `<body>` tag, or 0 if none. */
|
|
9
|
+
export declare function afterBodyOpen(html: string): number;
|
|
10
|
+
/** Inner HTML of `<body>` from a fully rendered document string. */
|
|
11
|
+
export declare function bodyInner(html: string): string;
|
|
12
|
+
/** Inner HTML of `<head>` from a fully rendered document string. */
|
|
13
|
+
export declare function headInner(html: string): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ServerContext } from '@analogjs/router/tokens';
|
|
2
|
+
/**
|
|
3
|
+
* Per-request decisions about whether the streaming renderer should fall back
|
|
4
|
+
* to a buffered render. Extracted from `render-stream` so they can be unit
|
|
5
|
+
* tested without driving the platform.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* User agents that receive a fully buffered render (with a resolved `<head>`)
|
|
9
|
+
* instead of the streamed shell. Streaming flushes the head before the app has
|
|
10
|
+
* set a dynamic title/meta and reconciles it via a finalize script; a crawler
|
|
11
|
+
* that does not run that script would index the shell's static head. Mirrors
|
|
12
|
+
* Nuxt's bot bypass — streaming targets interactive clients, bots get the
|
|
13
|
+
* buffered path whose head is byte-identical to the classic `render()`.
|
|
14
|
+
*/
|
|
15
|
+
export declare const SSR_BOT_RE: RegExp;
|
|
16
|
+
export declare function isLikelyBot(serverContext: ServerContext): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Whether streaming is disabled for this request by a `streaming: false` route
|
|
19
|
+
* rule. The platform plugin translates that rule into an `x-analog-no-streaming`
|
|
20
|
+
* response header (mirroring how `ssr: false` becomes `x-analog-no-ssr`); when
|
|
21
|
+
* present, `renderStream` produces the buffered `render()` output for this
|
|
22
|
+
* route instead of streaming.
|
|
23
|
+
*/
|
|
24
|
+
export declare function streamingDisabledByRoute(serverContext: ServerContext): boolean;
|
package/types/src/index.d.ts
CHANGED
|
@@ -14,10 +14,13 @@ export { FormAction } from './lib/form-action.directive';
|
|
|
14
14
|
export type { FormActionState } from './lib/form-action.directive';
|
|
15
15
|
export { injectDebugRoutes } from './lib/debug/routes';
|
|
16
16
|
export { withDebugRoutes } from './lib/debug';
|
|
17
|
-
export { ServerOnly } from './lib/server.component';
|
|
18
17
|
export type { AnalogJsonLdDocument } from './lib/json-ld';
|
|
19
18
|
export { issuesToFieldErrors, issuesToFormErrors, issuePathToFieldName, } from './lib/validation-errors';
|
|
20
19
|
export type { ValidationFieldErrors } from './lib/validation-errors';
|
|
20
|
+
export { injectServerFn, injectServerFnMutation, provideServerFnClient, ServerFnClient, } from './lib/server-fn/inject-server-fn';
|
|
21
|
+
export { createServerFnRef, type ServerFnRefConfig, } from './lib/server-fn/server-fn-ref';
|
|
22
|
+
export { SERVER_FN_DISPATCHER, type ServerFnDispatcher, } from './lib/server-fn/dispatcher';
|
|
23
|
+
export type { ServerFn, ServerFnConfig, ServerFnContext, ServerFnDef, ServerFnHandler, ServerFnMethod, StandardSchemaV1, } from './lib/server-fn/types';
|
|
21
24
|
export type { AnalogRouteTable, AnalogRoutePath, RoutePathOptions, RoutePathArgs, RoutePathOptionsBase, RouteParamsOutput, RouteQueryOutput, RouteLinkResult, } from './lib/route-path';
|
|
22
25
|
export { routePath } from './lib/route-path';
|
|
23
26
|
export { injectNavigate } from './lib/inject-navigate';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { InjectionToken, type Injector } from '@angular/core';
|
|
2
|
+
import type { ServerFn } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* In-process transport for a server function call.
|
|
5
|
+
*
|
|
6
|
+
* Provided on the server by `provideServerContext`, so during SSR a server
|
|
7
|
+
* function runs in the same process — and the same request injector — as the
|
|
8
|
+
* render instead of making an HTTP request back into the app. Absent in the
|
|
9
|
+
* browser, where `ServerFnClient` falls back to `HttpClient`.
|
|
10
|
+
*
|
|
11
|
+
* `injector` is the **app environment injector** — `ServerFnClient` is
|
|
12
|
+
* `providedIn: 'root'`, and SSR bootstraps a fresh application per request, so
|
|
13
|
+
* its injector is both per-request and the right scope for a handler to resolve
|
|
14
|
+
* from. It is passed rather than captured from the token because
|
|
15
|
+
* `provideServerContext` is applied as *platform* providers, which sit above
|
|
16
|
+
* the app's `providedIn: 'root'` services.
|
|
17
|
+
*
|
|
18
|
+
* Deliberately not a component's node injector: a handler resolves app-level
|
|
19
|
+
* services, and making that depend on which component happened to call it would
|
|
20
|
+
* be surprising and unportable.
|
|
21
|
+
*/
|
|
22
|
+
export type ServerFnDispatcher = <In, Out>(fn: ServerFn<In, Out>, input: In, injector: Injector) => Promise<Out>;
|
|
23
|
+
export declare const SERVER_FN_DISPATCHER: InjectionToken<ServerFnDispatcher>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type ResourceRef, type StateKey } from '@angular/core';
|
|
2
|
+
import type { ServerFn } from './types';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* Client transport for server functions. In the browser it goes through Angular
|
|
6
|
+
* `HttpClient`, so client `HttpInterceptorFn`s apply. During SSR the dispatcher
|
|
7
|
+
* token is provided, and the call short-circuits the HTTP round-trip: the
|
|
8
|
+
* handler runs in-process in the current request injector. Lives in the client
|
|
9
|
+
* entry (client-safe).
|
|
10
|
+
*/
|
|
11
|
+
export declare class ServerFnClient {
|
|
12
|
+
private readonly http;
|
|
13
|
+
private readonly transferState;
|
|
14
|
+
private readonly injector;
|
|
15
|
+
private readonly dispatcher;
|
|
16
|
+
/** True while rendering on the server (the in-process dispatcher is provided). */
|
|
17
|
+
get isServer(): boolean;
|
|
18
|
+
call<In, Out>(fn: ServerFn<In, Out>, input: In): Promise<Out>;
|
|
19
|
+
/** Key a read's value for TransferState hydration (fn id + input). */
|
|
20
|
+
stateKey<Out>(fn: ServerFn<unknown, Out>, input: unknown): StateKey<Out>;
|
|
21
|
+
readSeed<Out>(fn: ServerFn<unknown, Out>, input: unknown): Out | undefined;
|
|
22
|
+
writeSeed<Out>(fn: ServerFn<unknown, Out>, input: unknown, value: Out): void;
|
|
23
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ServerFnClient, never>;
|
|
24
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<ServerFnClient>;
|
|
25
|
+
}
|
|
26
|
+
/** No-op provider hook; ServerFnClient is `providedIn: 'root'`. */
|
|
27
|
+
export declare function provideServerFnClient(): readonly [];
|
|
28
|
+
/**
|
|
29
|
+
* Reactive read of a server function as an Angular `resource()`.
|
|
30
|
+
*
|
|
31
|
+
* `args` is optional: omit it for an input-less read (the resource loads once);
|
|
32
|
+
* provide it for an input-bearing read (returning `undefined` from `args` leaves
|
|
33
|
+
* the resource idle until inputs are ready, the standard resource pattern). For
|
|
34
|
+
* imperative calls (mutations, event handlers) use `injectServerFnMutation`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function injectServerFn<Out>(fn: ServerFn<void, Out>): ResourceRef<Out | undefined>;
|
|
37
|
+
export declare function injectServerFn<In, Out>(fn: ServerFn<In, Out>, args: () => In | undefined): ResourceRef<Out | undefined>;
|
|
38
|
+
/**
|
|
39
|
+
* Imperative binding of a server function: returns a callable that dispatches
|
|
40
|
+
* the call through `HttpClient` (so client interceptors apply) and resolves the
|
|
41
|
+
* result. Use for mutations and event-driven calls; use `injectServerFn` for
|
|
42
|
+
* reactive reads.
|
|
43
|
+
*/
|
|
44
|
+
export declare function injectServerFnMutation<In, Out>(fn: ServerFn<In, Out>): (input: In) => Promise<Out>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ServerFn, ServerFnMethod } from './types';
|
|
2
|
+
export interface ServerFnRefConfig {
|
|
3
|
+
id?: string;
|
|
4
|
+
method?: ServerFnMethod;
|
|
5
|
+
/**
|
|
6
|
+
* Only its presence matters here: when `method` is omitted, a config with an
|
|
7
|
+
* `input` schema defaults to `POST`, otherwise `GET`. The schema itself is
|
|
8
|
+
* never used to build the ref — validation happens server-side.
|
|
9
|
+
*/
|
|
10
|
+
input?: unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Builds a server-function reference: the client-safe `{ __serverFn, id, url,
|
|
14
|
+
* method }` metadata that `injectServerFn`/`ServerFnClient` dispatch through.
|
|
15
|
+
*
|
|
16
|
+
* Shared by both sides so they produce identical refs: the server `serverFn`
|
|
17
|
+
* wraps this with registration + the handler, and the client build's scrub
|
|
18
|
+
* transform emits a call to this factory in place of the server module so the
|
|
19
|
+
* browser bundle carries only the ref, never the handler or its server imports.
|
|
20
|
+
*
|
|
21
|
+
* The returned value is callable-typed but throws if invoked directly — it is
|
|
22
|
+
* always dispatched via `injectServerFn`/`ServerFnClient`, never called.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createServerFnRef<In, Out>(config: ServerFnRefConfig): ServerFn<In, Out>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Minimal Standard Schema shape (valibot/zod/arktype conform). */
|
|
2
|
+
export interface StandardSchemaV1<In = unknown> {
|
|
3
|
+
readonly '~standard': {
|
|
4
|
+
readonly version: 1;
|
|
5
|
+
readonly vendor: string;
|
|
6
|
+
validate: (value: unknown) => {
|
|
7
|
+
value: In;
|
|
8
|
+
issues?: undefined;
|
|
9
|
+
} | {
|
|
10
|
+
issues: ReadonlyArray<{
|
|
11
|
+
message: string;
|
|
12
|
+
}>;
|
|
13
|
+
} | Promise<{
|
|
14
|
+
value: In;
|
|
15
|
+
issues?: undefined;
|
|
16
|
+
} | {
|
|
17
|
+
issues: ReadonlyArray<{
|
|
18
|
+
message: string;
|
|
19
|
+
}>;
|
|
20
|
+
}>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export type ServerFnMethod = 'GET' | 'POST';
|
|
24
|
+
export interface ServerFnConfig<In> {
|
|
25
|
+
/**
|
|
26
|
+
* Opaque route id `/_analog/fn/<id>`. **Build-injected, not author-supplied:**
|
|
27
|
+
* the Analog transform derives `hash(fileId + exportName)` and stamps it into
|
|
28
|
+
* both the server registration and the client proxy, so authors never choose a
|
|
29
|
+
* route. Absent at runtime means the transform did not run — `serverFn` throws.
|
|
30
|
+
*/
|
|
31
|
+
id?: string;
|
|
32
|
+
/** Defaults to 'POST' when `input` is present, otherwise 'GET'. */
|
|
33
|
+
method?: ServerFnMethod;
|
|
34
|
+
input?: StandardSchemaV1<In>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Interceptor-accumulated context, delivered to the handler as its second
|
|
38
|
+
* argument. Apps extend this by declaration merging.
|
|
39
|
+
*/
|
|
40
|
+
export interface ServerFnContext {
|
|
41
|
+
}
|
|
42
|
+
/** A server function reference: callable type on both sides; real impl only on the server. */
|
|
43
|
+
export type ServerFn<In, Out> = ((input: In) => Promise<Out>) & {
|
|
44
|
+
readonly __serverFn: true;
|
|
45
|
+
readonly id: string;
|
|
46
|
+
readonly url: string;
|
|
47
|
+
readonly method: ServerFnMethod;
|
|
48
|
+
};
|
|
49
|
+
export type ServerFnHandler<In, Out> = (input: In, context: ServerFnContext) => Promise<Out> | Out;
|
|
50
|
+
export interface ServerFnDef {
|
|
51
|
+
id: string;
|
|
52
|
+
method: ServerFnMethod;
|
|
53
|
+
config: ServerFnConfig<unknown>;
|
|
54
|
+
handler: ServerFnHandler<unknown, unknown>;
|
|
55
|
+
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { ApplicationConfig } from '@angular/core';
|
|
2
|
-
import type { ServerContext } from '../../tokens/src/index.js';
|
|
3
|
-
export declare function serverComponentRequest(serverContext: ServerContext): string | undefined;
|
|
4
|
-
export declare function renderServerComponent(url: string, serverContext: ServerContext, config?: ApplicationConfig): Promise<Response>;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { InjectionToken, Provider } from '@angular/core';
|
|
2
|
-
export declare const STATIC_PROPS: InjectionToken<Record<string, any>>;
|
|
3
|
-
export declare function provideStaticProps<T = Record<string, any>>(props: T): Provider;
|
|
4
|
-
export declare function injectStaticProps(): Record<string, any>;
|
|
5
|
-
export declare function injectStaticOutputs<T>(): {
|
|
6
|
-
set(data: T): void;
|
|
7
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { InputSignal, OutputEmitterRef, WritableSignal } from '@angular/core';
|
|
2
|
-
import { SafeHtml } from '@angular/platform-browser';
|
|
3
|
-
import * as i0 from "@angular/core";
|
|
4
|
-
type ServerProps = Record<string, any>;
|
|
5
|
-
type ServerOutputs = Record<string, any>;
|
|
6
|
-
/**
|
|
7
|
-
* @description
|
|
8
|
-
* Component that defines the bridge between the client and server-only
|
|
9
|
-
* components. The component passes the component ID and props to the server
|
|
10
|
-
* and retrieves the rendered HTML and outputs from the server-only component.
|
|
11
|
-
*
|
|
12
|
-
* Status: experimental
|
|
13
|
-
*/
|
|
14
|
-
export declare class ServerOnly {
|
|
15
|
-
component: InputSignal<string>;
|
|
16
|
-
props: InputSignal<ServerProps | undefined>;
|
|
17
|
-
outputs: OutputEmitterRef<ServerOutputs>;
|
|
18
|
-
private http;
|
|
19
|
-
private sanitizer;
|
|
20
|
-
protected content: WritableSignal<SafeHtml>;
|
|
21
|
-
private route;
|
|
22
|
-
private baseURL;
|
|
23
|
-
private transferState;
|
|
24
|
-
constructor();
|
|
25
|
-
updateContent(content: {
|
|
26
|
-
html: string;
|
|
27
|
-
outputs: ServerOutputs;
|
|
28
|
-
}): void;
|
|
29
|
-
getComponentUrl(componentId: string): string;
|
|
30
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<ServerOnly, never>;
|
|
31
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<ServerOnly, "server-only,ServerOnly,Server", never, { "component": { "alias": "component"; "required": true; "isSignal": true; }; "props": { "alias": "props"; "required": false; "isSignal": true; }; }, { "outputs": "outputs"; }, never, never, true, never>;
|
|
32
|
-
}
|
|
33
|
-
export {};
|