@thomasfosterau/effect-sveltekit 0.4.0 → 0.4.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.
- package/README.md +509 -30
- package/dist/Remote.d.ts +57 -0
- package/dist/Remote.js +16 -0
- package/dist/Remote.js.map +1 -1
- package/dist/adapter/handler.d.ts +1 -0
- package/dist/adapter/handler.js.map +1 -1
- package/dist/adapter/index.d.ts +1 -0
- package/dist/adapter/index.js.map +1 -1
- package/dist/adapter/serve.d.ts +1 -0
- package/dist/adapter/serve.js.map +1 -1
- package/dist/internal/remote-run.d.ts +1 -0
- package/dist/internal/remote-run.js.map +1 -1
- package/dist/realtime.d.ts +2 -0
- package/dist/realtime.js.map +1 -1
- package/dist/remote-functions.d.ts +14 -0
- package/dist/remote-functions.js +43 -0
- package/dist/remote-functions.js.map +1 -1
- package/dist/vite.d.ts +2 -0
- package/dist/vite.js.map +1 -1
- package/package.json +3 -2
package/dist/Remote.js
CHANGED
|
@@ -28,6 +28,9 @@ function callCommand(command, input) {
|
|
|
28
28
|
* Newer SvelteKit versions cache remote query instances, so a command that
|
|
29
29
|
* changes data those queries depend on must list them here (or call
|
|
30
30
|
* `query.refresh()` afterwards) for their cached values to update.
|
|
31
|
+
*
|
|
32
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
33
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
31
34
|
*/
|
|
32
35
|
function callCommandWithUpdates(command, input, ...queries) {
|
|
33
36
|
return fromRemotePromise(() => command(input).updates(...queries));
|
|
@@ -36,9 +39,22 @@ function callPrerender(prerender, input) {
|
|
|
36
39
|
if (arguments.length === 1) return fromRemotePromise(() => prerender(void 0));
|
|
37
40
|
return fromRemotePromise(() => prerender(input));
|
|
38
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Runs an enhanced remote form's `submit()` as an Effect.
|
|
44
|
+
*
|
|
45
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
46
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
47
|
+
*/
|
|
39
48
|
function submitForm(submit) {
|
|
40
49
|
return Effect.asVoid(fromRemotePromise(() => submit()));
|
|
41
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Runs an enhanced remote form's `submit()` and refreshes the given queries in
|
|
53
|
+
* the same round trip (SvelteKit's single-flight mutations).
|
|
54
|
+
*
|
|
55
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
56
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
57
|
+
*/
|
|
42
58
|
function submitFormWithUpdates(submit, ...queries) {
|
|
43
59
|
return Effect.asVoid(fromRemotePromise(() => submit().updates(...queries)));
|
|
44
60
|
}
|
package/dist/Remote.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Remote.js","names":[],"sources":["../src/Remote.ts"],"sourcesContent":["import type {\n RemoteCommand,\n RemoteLiveQuery,\n RemotePrerenderFunction,\n RemoteQuery,\n RemoteQueryFunction,\n RemoteQueryOverride,\n} from \"@sveltejs/kit\";\nimport { Effect, Stream } from \"effect\";\n\nfunction fromRemotePromise<A>(thunk: () => PromiseLike<A>): Effect.Effect<A, unknown, never> {\n return Effect.promise(() => Promise.resolve(thunk()));\n}\n\nexport function callQuery<Output>(\n query: RemoteQueryFunction<void, Output>,\n): Effect.Effect<Output, unknown, never>;\nexport function callQuery<Input, Output>(\n query: RemoteQueryFunction<Input, Output>,\n input: Input,\n): Effect.Effect<Output, unknown, never>;\nexport function callQuery<Input, Output>(\n query: RemoteQueryFunction<Input, Output>,\n input?: Input,\n): Effect.Effect<Output, unknown, never> {\n if (arguments.length === 1) {\n return fromRemotePromise(() =>\n (query as unknown as RemoteQueryFunction<void, Output>)(undefined),\n );\n }\n\n return fromRemotePromise(() => query(input as Input));\n}\n\nexport function callCommand<Output>(\n command: RemoteCommand<void, Output>,\n): Effect.Effect<Output, unknown, never>;\nexport function callCommand<Input, Output>(\n command: RemoteCommand<Input, Output>,\n input: Input,\n): Effect.Effect<Output, unknown, never>;\nexport function callCommand<Input, Output>(\n command: RemoteCommand<Input, Output>,\n input?: Input,\n): Effect.Effect<Output, unknown, never> {\n if (arguments.length === 1) {\n return fromRemotePromise(() => (command as unknown as RemoteCommand<void, Output>)(undefined));\n }\n\n return fromRemotePromise(() => command(input as Input));\n}\n\n/**\n * Calls a remote command and refreshes the given queries in the same round\n * trip (SvelteKit's single-flight mutations).\n *\n * Newer SvelteKit versions cache remote query instances, so a command that\n * changes data those queries depend on must list them here (or call\n * `query.refresh()` afterwards) for their cached values to update.\n */\nexport function callCommandWithUpdates<Input, Output>(\n command: RemoteCommand<Input, Output>,\n input: Input,\n ...queries: Array<RemoteQuery<any> | RemoteQueryOverride>\n): Effect.Effect<Output, unknown, never> {\n return fromRemotePromise(() => command(input).updates(...queries));\n}\n\nexport function callPrerender<Output>(\n prerender: RemotePrerenderFunction<void, Output>,\n): Effect.Effect<Output, unknown, never>;\nexport function callPrerender<Input, Output>(\n prerender: RemotePrerenderFunction<Input, Output>,\n input: Input,\n): Effect.Effect<Output, unknown, never>;\nexport function callPrerender<Input, Output>(\n prerender: RemotePrerenderFunction<Input, Output>,\n input?: Input,\n): Effect.Effect<Output, unknown, never> {\n if (arguments.length === 1) {\n return fromRemotePromise(() =>\n (prerender as unknown as RemotePrerenderFunction<void, Output>)(undefined),\n );\n }\n\n return fromRemotePromise(() => prerender(input as Input));\n}\n\n/**\n * The shape of an enhanced remote form `submit` function. The promise result\n * is left open (SvelteKit returns `Promise<void>` in older versions and\n * `Promise<boolean>` in newer ones).\n */\nexport type RemoteSubmit = () => PromiseLike<unknown> & {\n updates: (...queries: Array<RemoteQuery<any> | RemoteQueryOverride>) => PromiseLike<unknown>;\n};\n\nexport function submitForm(\n submit: () => PromiseLike<unknown>,\n): Effect.Effect<void, unknown, never> {\n return Effect.asVoid(fromRemotePromise(() => submit()));\n}\n\nexport function submitFormWithUpdates(\n submit: RemoteSubmit,\n ...queries: Array<RemoteQuery<any> | RemoteQueryOverride>\n): Effect.Effect<void, unknown, never> {\n return Effect.asVoid(fromRemotePromise(() => submit().updates(...queries)));\n}\n\n/**\n * Consumes a SvelteKit live query (`query.live(...)`) as an Effect `Stream`.\n *\n * A `RemoteLiveQuery<T>` is an `AsyncIterable<T>` that yields every update\n * pushed from the server; this turns it into a `Stream` so live data can drive\n * Effect pipelines (and, via `useStream`, Svelte runes).\n *\n * @example\n * ```ts\n * import { Remote } from '@thomasfosterau/effect-sveltekit';\n * import { messages } from './chat.remote';\n * import { useStream } from '@thomasfosterau/effect-svelte';\n *\n * const feed = useStream(Remote.liveQueryStream(messages()));\n * ```\n */\nexport function liveQueryStream<Output>(\n query: RemoteLiveQuery<Output>,\n): Stream.Stream<Output, unknown> {\n return Stream.fromAsyncIterable(query, (error) => error);\n}\n"],"mappings":";;;;;;;;;;;;AAUA,SAAS,kBAAqB,OAA+D;CAC3F,OAAO,OAAO,cAAc,QAAQ,QAAQ,MAAM,CAAC,CAAC;AACtD;
|
|
1
|
+
{"version":3,"file":"Remote.js","names":[],"sources":["../src/Remote.ts"],"sourcesContent":["import type {\n RemoteCommand,\n RemoteLiveQuery,\n RemotePrerenderFunction,\n RemoteQuery,\n RemoteQueryFunction,\n RemoteQueryOverride,\n} from \"@sveltejs/kit\";\nimport { Effect, Stream } from \"effect\";\n\nfunction fromRemotePromise<A>(thunk: () => PromiseLike<A>): Effect.Effect<A, unknown, never> {\n return Effect.promise(() => Promise.resolve(thunk()));\n}\n\n/**\n * Calls a SvelteKit remote `query` as an Effect that succeeds with its output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callQuery<Output>(\n query: RemoteQueryFunction<void, Output>,\n): Effect.Effect<Output, unknown, never>;\n/**\n * Calls a SvelteKit remote `query` with `input` as an Effect that succeeds\n * with its output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callQuery<Input, Output>(\n query: RemoteQueryFunction<Input, Output>,\n input: Input,\n): Effect.Effect<Output, unknown, never>;\nexport function callQuery<Input, Output>(\n query: RemoteQueryFunction<Input, Output>,\n input?: Input,\n): Effect.Effect<Output, unknown, never> {\n if (arguments.length === 1) {\n return fromRemotePromise(() =>\n (query as unknown as RemoteQueryFunction<void, Output>)(undefined),\n );\n }\n\n return fromRemotePromise(() => query(input as Input));\n}\n\n/**\n * Calls a SvelteKit remote `command` as an Effect that succeeds with its\n * output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callCommand<Output>(\n command: RemoteCommand<void, Output>,\n): Effect.Effect<Output, unknown, never>;\n/**\n * Calls a SvelteKit remote `command` with `input` as an Effect that succeeds\n * with its output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callCommand<Input, Output>(\n command: RemoteCommand<Input, Output>,\n input: Input,\n): Effect.Effect<Output, unknown, never>;\nexport function callCommand<Input, Output>(\n command: RemoteCommand<Input, Output>,\n input?: Input,\n): Effect.Effect<Output, unknown, never> {\n if (arguments.length === 1) {\n return fromRemotePromise(() => (command as unknown as RemoteCommand<void, Output>)(undefined));\n }\n\n return fromRemotePromise(() => command(input as Input));\n}\n\n/**\n * Calls a remote command and refreshes the given queries in the same round\n * trip (SvelteKit's single-flight mutations).\n *\n * Newer SvelteKit versions cache remote query instances, so a command that\n * changes data those queries depend on must list them here (or call\n * `query.refresh()` afterwards) for their cached values to update.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callCommandWithUpdates<Input, Output>(\n command: RemoteCommand<Input, Output>,\n input: Input,\n ...queries: Array<RemoteQuery<any> | RemoteQueryOverride>\n): Effect.Effect<Output, unknown, never> {\n return fromRemotePromise(() => command(input).updates(...queries));\n}\n\n/**\n * Calls a SvelteKit remote `prerender` function as an Effect that succeeds\n * with its output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callPrerender<Output>(\n prerender: RemotePrerenderFunction<void, Output>,\n): Effect.Effect<Output, unknown, never>;\n/**\n * Calls a SvelteKit remote `prerender` function with `input` as an Effect\n * that succeeds with its output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callPrerender<Input, Output>(\n prerender: RemotePrerenderFunction<Input, Output>,\n input: Input,\n): Effect.Effect<Output, unknown, never>;\nexport function callPrerender<Input, Output>(\n prerender: RemotePrerenderFunction<Input, Output>,\n input?: Input,\n): Effect.Effect<Output, unknown, never> {\n if (arguments.length === 1) {\n return fromRemotePromise(() =>\n (prerender as unknown as RemotePrerenderFunction<void, Output>)(undefined),\n );\n }\n\n return fromRemotePromise(() => prerender(input as Input));\n}\n\n/**\n * The shape of an enhanced remote form `submit` function. The promise result\n * is left open (SvelteKit returns `Promise<void>` in older versions and\n * `Promise<boolean>` in newer ones).\n */\nexport type RemoteSubmit = () => PromiseLike<unknown> & {\n updates: (...queries: Array<RemoteQuery<any> | RemoteQueryOverride>) => PromiseLike<unknown>;\n};\n\n/**\n * Runs an enhanced remote form's `submit()` as an Effect.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function submitForm(\n submit: () => PromiseLike<unknown>,\n): Effect.Effect<void, unknown, never> {\n return Effect.asVoid(fromRemotePromise(() => submit()));\n}\n\n/**\n * Runs an enhanced remote form's `submit()` and refreshes the given queries in\n * the same round trip (SvelteKit's single-flight mutations).\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function submitFormWithUpdates(\n submit: RemoteSubmit,\n ...queries: Array<RemoteQuery<any> | RemoteQueryOverride>\n): Effect.Effect<void, unknown, never> {\n return Effect.asVoid(fromRemotePromise(() => submit().updates(...queries)));\n}\n\n/**\n * Consumes a SvelteKit live query (`query.live(...)`) as an Effect `Stream`.\n *\n * A `RemoteLiveQuery<T>` is an `AsyncIterable<T>` that yields every update\n * pushed from the server; this turns it into a `Stream` so live data can drive\n * Effect pipelines (and, via `useStream`, Svelte runes).\n *\n * @example\n * ```ts\n * import { Remote } from '@thomasfosterau/effect-sveltekit';\n * import { messages } from './chat.remote';\n * import { useStream } from '@thomasfosterau/effect-svelte';\n *\n * const feed = useStream(Remote.liveQueryStream(messages()));\n * ```\n */\nexport function liveQueryStream<Output>(\n query: RemoteLiveQuery<Output>,\n): Stream.Stream<Output, unknown> {\n return Stream.fromAsyncIterable(query, (error) => error);\n}\n"],"mappings":";;;;;;;;;;;;AAUA,SAAS,kBAAqB,OAA+D;CAC3F,OAAO,OAAO,cAAc,QAAQ,QAAQ,MAAM,CAAC,CAAC;AACtD;AAsBA,SAAgB,UACd,OACA,OACuC;CACvC,IAAI,UAAU,WAAW,GACvB,OAAO,wBACJ,MAAuD,KAAA,CAAS,CACnE;CAGF,OAAO,wBAAwB,MAAM,KAAc,CAAC;AACtD;AAuBA,SAAgB,YACd,SACA,OACuC;CACvC,IAAI,UAAU,WAAW,GACvB,OAAO,wBAAyB,QAAmD,KAAA,CAAS,CAAC;CAG/F,OAAO,wBAAwB,QAAQ,KAAc,CAAC;AACxD;;;;;;;;;;;;AAaA,SAAgB,uBACd,SACA,OACA,GAAG,SACoC;CACvC,OAAO,wBAAwB,QAAQ,KAAK,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAC;AACnE;AAuBA,SAAgB,cACd,WACA,OACuC;CACvC,IAAI,UAAU,WAAW,GACvB,OAAO,wBACJ,UAA+D,KAAA,CAAS,CAC3E;CAGF,OAAO,wBAAwB,UAAU,KAAc,CAAC;AAC1D;;;;;;;AAiBA,SAAgB,WACd,QACqC;CACrC,OAAO,OAAO,OAAO,wBAAwB,OAAO,CAAC,CAAC;AACxD;;;;;;;;AASA,SAAgB,sBACd,QACA,GAAG,SACkC;CACrC,OAAO,OAAO,OAAO,wBAAwB,OAAO,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC;AAC5E;;;;;;;;;;;;;;;;;AAkBA,SAAgB,gBACd,OACgC;CAChC,OAAO,OAAO,kBAAkB,QAAQ,UAAU,KAAK;AACzD"}
|
|
@@ -17,6 +17,7 @@ type SvelteKitHttpAppServices = FileSystemAssetServices;
|
|
|
17
17
|
* is the asset source's services (defaults to the file-system services).
|
|
18
18
|
*/
|
|
19
19
|
type SvelteKitHttpApp<R = SvelteKitHttpAppServices> = Effect.Effect<HttpServerResponse.HttpServerResponse, never, R | HttpServerRequest.HttpServerRequest>;
|
|
20
|
+
/** The SvelteKit build output {@link makeHandler} serves. */
|
|
20
21
|
interface SvelteKitHandlerOptions {
|
|
21
22
|
/** The SvelteKit `Server` class exported by the build output (`./server/index.js`). */
|
|
22
23
|
readonly Server: typeof Server;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.js","names":[],"sources":["../../src/adapter/handler.ts"],"sourcesContent":["/**\n * Runtime handler for the Effect SvelteKit adapter.\n *\n * `makeHandler` turns a built SvelteKit app (the `Server` class and manifest\n * emitted by `vite build`) into an Effect HTTP application that serves\n * static client assets, prerendered pages, and server-side rendered routes from\n * disk — the Node default.\n *\n * {@link makeHandlerWith} is the platform-neutral core: it takes an injected\n * {@link AssetSource} (where assets come from) and a `read` capability (how to\n * read server assets), so the same SvelteKit `HttpApp` can run on a Node server\n * or on Cloudflare Workers (assets from `env.ASSETS`).\n *\n * The returned app is an ordinary Effect value: it can be served with the\n * generated `build/index.js` entry (see `./serve.js`), mounted inside any\n * existing `effect/http` HTTP server, or converted to a `fetch` handler\n * (see {@link toWebHandler}).\n */\n\nimport type { SSRManifest, Server as SvelteKitServer } from \"@sveltejs/kit\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as HttpMethod from \"effect/http/HttpMethod\";\nimport * as HttpEffect from \"effect/http/HttpEffect\";\nimport * as HttpServerRequest from \"effect/http/HttpServerRequest\";\nimport * as HttpServerResponse from \"effect/http/HttpServerResponse\";\nimport * as Path from \"effect/Path\";\nimport { Config, Context, Effect, Layer, Option, Stream } from \"effect\";\nimport { AssetSource, type FileSystemAssetServices } from \"./assets.js\";\n\nexport { AssetSource, type FileSystemAssetServices } from \"./assets.js\";\n\n/**\n * The services required by the SvelteKit HTTP application when serving assets\n * from the file system (the Node default). All of them are provided by\n * `NodeHttpServer.layer` from `@effect/platform-node`.\n */\nexport type SvelteKitHttpAppServices = FileSystemAssetServices;\n\n/**\n * The SvelteKit application as a composable Effect HTTP handler: an effect that\n * produces an {@link HttpServerResponse.HttpServerResponse} for the current\n * request. The `HttpServerRequest` is supplied per request by the server. `R`\n * is the asset source's services (defaults to the file-system services).\n */\nexport type SvelteKitHttpApp<R = SvelteKitHttpAppServices> = Effect.Effect<\n HttpServerResponse.HttpServerResponse,\n never,\n R | HttpServerRequest.HttpServerRequest\n>;\n\nexport interface SvelteKitHandlerOptions {\n /** The SvelteKit `Server` class exported by the build output (`./server/index.js`). */\n readonly Server: typeof SvelteKitServer;\n /** The SSR manifest exported by the build output (`./manifest.js`). */\n readonly manifest: SSRManifest;\n /** The set of prerendered paths exported by the build output (`./manifest.js`). */\n readonly prerendered: ReadonlySet<string>;\n /** The configured `paths.base`, exported by the build output (`./manifest.js`). */\n readonly base: string;\n /** Absolute path of the build output directory (contains `client/`, `server/`, `prerendered/`). */\n readonly dir: string;\n /** Environment variables passed to the SvelteKit server (e.g. `process.env`). */\n readonly env?: Record<string, string | undefined>;\n /** Prefix for the runtime configuration environment variables (ORIGIN, ...). */\n readonly envPrefix?: string;\n}\n\n/** Options for the platform-neutral SSR core, {@link makeHandlerWith}. */\nexport interface SvelteKitNeutralHandlerOptions<RAssets> {\n /** The SvelteKit `Server` class exported by the build output. */\n readonly Server: typeof SvelteKitServer;\n /** The SSR manifest exported by the build output. */\n readonly manifest: SSRManifest;\n /** Environment variables passed to the SvelteKit server. */\n readonly env?: Record<string, string | undefined> | undefined;\n /** Prefix for the runtime configuration environment variables (ORIGIN, ...). */\n readonly envPrefix?: string | undefined;\n /** Where static client assets and prerendered pages come from. */\n readonly assets: AssetSource<RAssets>;\n /**\n * How to read a server asset (backs `read()` from `$app/server`). On Node\n * this streams from `<dir>/server`; on Workers, back it with the platform's\n * own mechanism.\n */\n readonly read: (file: string) => ReadableStream<Uint8Array>;\n}\n\n/** Runtime configuration, read from the environment (compatible with adapter-node). */\nconst handlerConfig = (prefix: string) =>\n Config.all({\n origin: Config.option(Config.String(`${prefix}ORIGIN`)),\n protocolHeader: Config.option(Config.String(`${prefix}PROTOCOL_HEADER`)),\n hostHeader: Config.option(Config.String(`${prefix}HOST_HEADER`)),\n portHeader: Config.option(Config.String(`${prefix}PORT_HEADER`)),\n addressHeader: Config.option(Config.String(`${prefix}ADDRESS_HEADER`)),\n xffDepth: Config.Int(`${prefix}XFF_DEPTH`).pipe(Config.withDefault(1)),\n });\n\ntype HandlerConfig = Effect.Success<ReturnType<typeof handlerConfig>>;\n\n/** Compute the request origin from configuration and headers (adapter-node semantics). */\nconst originOf = (request: HttpServerRequest.HttpServerRequest, config: HandlerConfig): string => {\n if (Option.isSome(config.origin)) return config.origin.value;\n\n const headers = request.headers;\n const protocolHeader = Option.getOrUndefined(config.protocolHeader)?.toLowerCase();\n const hostHeader = Option.getOrUndefined(config.hostHeader)?.toLowerCase() ?? \"host\";\n const portHeader = Option.getOrUndefined(config.portHeader)?.toLowerCase();\n\n const protocol = (protocolHeader && headers[protocolHeader]) || \"http\";\n const host = headers[hostHeader] ?? \"localhost\";\n const port = portHeader ? headers[portHeader] : undefined;\n\n return port ? `${protocol}://${host.split(\":\")[0]}:${port}` : `${protocol}://${host}`;\n};\n\n/** Resolve the client address, honouring ADDRESS_HEADER / XFF_DEPTH if configured. */\nconst clientAddressOf = (\n request: HttpServerRequest.HttpServerRequest,\n config: HandlerConfig,\n): string => {\n const addressHeader = Option.getOrUndefined(config.addressHeader)?.toLowerCase();\n if (addressHeader) {\n const value = request.headers[addressHeader];\n if (value) {\n if (addressHeader === \"x-forwarded-for\") {\n const addresses = value.split(\",\").map((address) => address.trim());\n if (config.xffDepth < 1 || config.xffDepth > addresses.length) {\n return addresses[0] ?? \"\";\n }\n return addresses[addresses.length - config.xffDepth]!;\n }\n return value;\n }\n }\n return Option.getOrElse(request.remoteAddress, () => \"\");\n};\n\n/** Build a web `Request` for SvelteKit from an `HttpServerRequest`. */\nconst toWebRequest = (\n request: HttpServerRequest.HttpServerRequest,\n url: URL,\n context: Context.Context<never>,\n): Request => {\n // `Headers` is a redactable record-like value; copy the enumerable string\n // entries into a plain record for the web `Request` constructor.\n const init: RequestInit & { duplex?: \"half\" } = {\n method: request.method,\n headers: { ...request.headers },\n };\n if (HttpMethod.hasBody(request.method)) {\n init.body = Stream.toReadableStreamWith(request.stream, context);\n init.duplex = \"half\";\n }\n return new Request(url, init);\n};\n\n/**\n * Creates the SvelteKit HTTP handler.\n *\n * The returned effect initialises the SvelteKit server (running your app's\n * server `init` hook) and yields an HTTP handler that:\n *\n * 1. serves static client assets from `<dir>/client` (with immutable cache\n * headers for hashed build assets),\n * 2. serves prerendered pages from `<dir>/prerendered`,\n * 3. falls back to server-side rendering via the SvelteKit server.\n *\n * @example\n * ```ts\n * // Mounting SvelteKit inside an existing effect/http server:\n * import { handler } from \"./build/handler.js\";\n * import * as HttpServer from \"effect/http/HttpServer\";\n * import { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\";\n * import { Effect, Layer } from \"effect\";\n * import { createServer } from \"node:http\";\n *\n * const serverLayer = Layer.unwrap(\n * Effect.map(handler, (app) => HttpServer.serve(app))\n * ).pipe(Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })));\n *\n * NodeRuntime.runMain(Layer.launch(serverLayer));\n * ```\n */\nexport const makeHandler = (\n options: SvelteKitHandlerOptions,\n): Effect.Effect<SvelteKitHttpApp, never, SvelteKitHttpAppServices> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const context = yield* Effect.context<never>();\n\n const serverDir = path.join(options.dir, \"server\");\n\n return yield* makeHandlerWith<SvelteKitHttpAppServices>({\n Server: options.Server,\n manifest: options.manifest,\n env: options.env,\n envPrefix: options.envPrefix,\n // `read` backs the `read()` helper from `$app/server`, streaming server\n // assets from the build output.\n read: (file) => Stream.toReadableStreamWith(fs.stream(path.join(serverDir, file)), context),\n assets: AssetSource.fileSystem({\n dir: options.dir,\n appPath: options.manifest.appPath,\n prerendered: options.prerendered,\n }),\n });\n });\n\n/**\n * The platform-neutral SSR core. Like {@link makeHandler}, but the asset source\n * and the server-asset `read` capability are injected, so the same SvelteKit\n * `HttpApp` can run on Node (file-system assets) or on Cloudflare Workers\n * (`AssetSource.cloudflareAssets(env.ASSETS)`).\n *\n * The returned app:\n *\n * 1. falls through to the injected {@link AssetSource} for static client assets\n * and prerendered pages (GET/HEAD), then\n * 2. server-side renders via the SvelteKit server.\n *\n * @example\n * ```ts\n * // Workers: a fetch handler with assets from env.ASSETS\n * import { makeHandlerWith, AssetSource, toWebHandler } from \"...adapter/handler\";\n * import { Server } from \"./server/index.js\";\n * import { manifest } from \"./manifest.js\";\n *\n * export default {\n * fetch: (request, env, ctx) =>\n * toWebHandler(\n * makeHandlerWith({\n * Server, manifest, env,\n * assets: AssetSource.cloudflareAssets(env.ASSETS),\n * read: () => { throw new Error(\"read() not supported on this deployment\"); },\n * }),\n * )(request),\n * };\n * ```\n */\nexport const makeHandlerWith = <RAssets = never>(\n options: SvelteKitNeutralHandlerOptions<RAssets>,\n): Effect.Effect<SvelteKitHttpApp<RAssets>, never, never> =>\n Effect.gen(function* () {\n const config = yield* handlerConfig(options.envPrefix ?? \"\").pipe(Effect.orDie);\n\n // Initialise the SvelteKit server. `read` backs `read()` from `$app/server`.\n const server = new options.Server(options.manifest);\n yield* Effect.promise(() =>\n server.init({\n env: (options.env ?? {}) as Record<string, string>,\n read: options.read,\n }),\n );\n\n // The HTTP handler itself, evaluated once per request.\n return Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const requestContext = yield* Effect.context<never>();\n\n const origin = originOf(request, config);\n const url = new URL(request.url, origin);\n\n let pathname: string;\n try {\n pathname = decodeURIComponent(url.pathname);\n } catch {\n return HttpServerResponse.empty({ status: 400 });\n }\n\n // 1. Static client assets / prerendered pages, via the injected source.\n // Only GET/HEAD reach assets; build a body-less request for the source.\n if (request.method === \"GET\" || request.method === \"HEAD\") {\n const assetRequest = new Request(url, {\n method: request.method,\n headers: { ...request.headers },\n });\n const asset = yield* options.assets.serveAsset(assetRequest, pathname);\n if (Option.isSome(asset)) return asset.value;\n }\n\n // 2. Server-side rendering.\n const webRequest = toWebRequest(request, url, requestContext);\n const response = yield* Effect.promise(() =>\n server.respond(webRequest, {\n getClientAddress: () => clientAddressOf(request, config),\n }),\n );\n return HttpServerResponse.fromWeb(response);\n });\n });\n\n/**\n * Convert a SvelteKit `HttpApp` to a Web `fetch` handler, providing the asset\n * source's services with `layer`. Use this for a Workers `fetch` export or any\n * runtime that speaks the Web platform.\n *\n * @example\n * ```ts\n * const fetchHandler = toWebHandler(\n * makeHandlerWith({ ..., assets: AssetSource.cloudflareAssets(env.ASSETS) }),\n * Layer.empty,\n * );\n * ```\n */\nexport const toWebHandler = <RAssets, E>(\n app: Effect.Effect<SvelteKitHttpApp<RAssets>, never, never>,\n layer: Layer.Layer<RAssets, E>,\n): ((request: Request) => Promise<Response>) => {\n // Build the app (server.init) and the per-request web handler once, lazily on\n // the first request, then dispatch each request through it.\n let built: Promise<(request: Request) => Promise<Response>> | undefined;\n const build = (): Promise<(request: Request) => Promise<Response>> =>\n (built ??= Effect.runPromise(app).then(\n (inner) =>\n HttpEffect.toWebHandlerLayer(inner, layer).handler as (\n request: Request,\n ) => Promise<Response>,\n ));\n return async (request) => (await build())(request);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"handler.js","names":[],"sources":["../../src/adapter/handler.ts"],"sourcesContent":["/**\n * Runtime handler for the Effect SvelteKit adapter.\n *\n * `makeHandler` turns a built SvelteKit app (the `Server` class and manifest\n * emitted by `vite build`) into an Effect HTTP application that serves\n * static client assets, prerendered pages, and server-side rendered routes from\n * disk — the Node default.\n *\n * {@link makeHandlerWith} is the platform-neutral core: it takes an injected\n * {@link AssetSource} (where assets come from) and a `read` capability (how to\n * read server assets), so the same SvelteKit `HttpApp` can run on a Node server\n * or on Cloudflare Workers (assets from `env.ASSETS`).\n *\n * The returned app is an ordinary Effect value: it can be served with the\n * generated `build/index.js` entry (see `./serve.js`), mounted inside any\n * existing `effect/http` HTTP server, or converted to a `fetch` handler\n * (see {@link toWebHandler}).\n */\n\nimport type { SSRManifest, Server as SvelteKitServer } from \"@sveltejs/kit\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as HttpMethod from \"effect/http/HttpMethod\";\nimport * as HttpEffect from \"effect/http/HttpEffect\";\nimport * as HttpServerRequest from \"effect/http/HttpServerRequest\";\nimport * as HttpServerResponse from \"effect/http/HttpServerResponse\";\nimport * as Path from \"effect/Path\";\nimport { Config, Context, Effect, Layer, Option, Stream } from \"effect\";\nimport { AssetSource, type FileSystemAssetServices } from \"./assets.js\";\n\nexport { AssetSource, type FileSystemAssetServices } from \"./assets.js\";\n\n/**\n * The services required by the SvelteKit HTTP application when serving assets\n * from the file system (the Node default). All of them are provided by\n * `NodeHttpServer.layer` from `@effect/platform-node`.\n */\nexport type SvelteKitHttpAppServices = FileSystemAssetServices;\n\n/**\n * The SvelteKit application as a composable Effect HTTP handler: an effect that\n * produces an {@link HttpServerResponse.HttpServerResponse} for the current\n * request. The `HttpServerRequest` is supplied per request by the server. `R`\n * is the asset source's services (defaults to the file-system services).\n */\nexport type SvelteKitHttpApp<R = SvelteKitHttpAppServices> = Effect.Effect<\n HttpServerResponse.HttpServerResponse,\n never,\n R | HttpServerRequest.HttpServerRequest\n>;\n\n/** The SvelteKit build output {@link makeHandler} serves. */\nexport interface SvelteKitHandlerOptions {\n /** The SvelteKit `Server` class exported by the build output (`./server/index.js`). */\n readonly Server: typeof SvelteKitServer;\n /** The SSR manifest exported by the build output (`./manifest.js`). */\n readonly manifest: SSRManifest;\n /** The set of prerendered paths exported by the build output (`./manifest.js`). */\n readonly prerendered: ReadonlySet<string>;\n /** The configured `paths.base`, exported by the build output (`./manifest.js`). */\n readonly base: string;\n /** Absolute path of the build output directory (contains `client/`, `server/`, `prerendered/`). */\n readonly dir: string;\n /** Environment variables passed to the SvelteKit server (e.g. `process.env`). */\n readonly env?: Record<string, string | undefined>;\n /** Prefix for the runtime configuration environment variables (ORIGIN, ...). */\n readonly envPrefix?: string;\n}\n\n/** Options for the platform-neutral SSR core, {@link makeHandlerWith}. */\nexport interface SvelteKitNeutralHandlerOptions<RAssets> {\n /** The SvelteKit `Server` class exported by the build output. */\n readonly Server: typeof SvelteKitServer;\n /** The SSR manifest exported by the build output. */\n readonly manifest: SSRManifest;\n /** Environment variables passed to the SvelteKit server. */\n readonly env?: Record<string, string | undefined> | undefined;\n /** Prefix for the runtime configuration environment variables (ORIGIN, ...). */\n readonly envPrefix?: string | undefined;\n /** Where static client assets and prerendered pages come from. */\n readonly assets: AssetSource<RAssets>;\n /**\n * How to read a server asset (backs `read()` from `$app/server`). On Node\n * this streams from `<dir>/server`; on Workers, back it with the platform's\n * own mechanism.\n */\n readonly read: (file: string) => ReadableStream<Uint8Array>;\n}\n\n/** Runtime configuration, read from the environment (compatible with adapter-node). */\nconst handlerConfig = (prefix: string) =>\n Config.all({\n origin: Config.option(Config.String(`${prefix}ORIGIN`)),\n protocolHeader: Config.option(Config.String(`${prefix}PROTOCOL_HEADER`)),\n hostHeader: Config.option(Config.String(`${prefix}HOST_HEADER`)),\n portHeader: Config.option(Config.String(`${prefix}PORT_HEADER`)),\n addressHeader: Config.option(Config.String(`${prefix}ADDRESS_HEADER`)),\n xffDepth: Config.Int(`${prefix}XFF_DEPTH`).pipe(Config.withDefault(1)),\n });\n\ntype HandlerConfig = Effect.Success<ReturnType<typeof handlerConfig>>;\n\n/** Compute the request origin from configuration and headers (adapter-node semantics). */\nconst originOf = (request: HttpServerRequest.HttpServerRequest, config: HandlerConfig): string => {\n if (Option.isSome(config.origin)) return config.origin.value;\n\n const headers = request.headers;\n const protocolHeader = Option.getOrUndefined(config.protocolHeader)?.toLowerCase();\n const hostHeader = Option.getOrUndefined(config.hostHeader)?.toLowerCase() ?? \"host\";\n const portHeader = Option.getOrUndefined(config.portHeader)?.toLowerCase();\n\n const protocol = (protocolHeader && headers[protocolHeader]) || \"http\";\n const host = headers[hostHeader] ?? \"localhost\";\n const port = portHeader ? headers[portHeader] : undefined;\n\n return port ? `${protocol}://${host.split(\":\")[0]}:${port}` : `${protocol}://${host}`;\n};\n\n/** Resolve the client address, honouring ADDRESS_HEADER / XFF_DEPTH if configured. */\nconst clientAddressOf = (\n request: HttpServerRequest.HttpServerRequest,\n config: HandlerConfig,\n): string => {\n const addressHeader = Option.getOrUndefined(config.addressHeader)?.toLowerCase();\n if (addressHeader) {\n const value = request.headers[addressHeader];\n if (value) {\n if (addressHeader === \"x-forwarded-for\") {\n const addresses = value.split(\",\").map((address) => address.trim());\n if (config.xffDepth < 1 || config.xffDepth > addresses.length) {\n return addresses[0] ?? \"\";\n }\n return addresses[addresses.length - config.xffDepth]!;\n }\n return value;\n }\n }\n return Option.getOrElse(request.remoteAddress, () => \"\");\n};\n\n/** Build a web `Request` for SvelteKit from an `HttpServerRequest`. */\nconst toWebRequest = (\n request: HttpServerRequest.HttpServerRequest,\n url: URL,\n context: Context.Context<never>,\n): Request => {\n // `Headers` is a redactable record-like value; copy the enumerable string\n // entries into a plain record for the web `Request` constructor.\n const init: RequestInit & { duplex?: \"half\" } = {\n method: request.method,\n headers: { ...request.headers },\n };\n if (HttpMethod.hasBody(request.method)) {\n init.body = Stream.toReadableStreamWith(request.stream, context);\n init.duplex = \"half\";\n }\n return new Request(url, init);\n};\n\n/**\n * Creates the SvelteKit HTTP handler.\n *\n * The returned effect initialises the SvelteKit server (running your app's\n * server `init` hook) and yields an HTTP handler that:\n *\n * 1. serves static client assets from `<dir>/client` (with immutable cache\n * headers for hashed build assets),\n * 2. serves prerendered pages from `<dir>/prerendered`,\n * 3. falls back to server-side rendering via the SvelteKit server.\n *\n * @example\n * ```ts\n * // Mounting SvelteKit inside an existing effect/http server:\n * import { handler } from \"./build/handler.js\";\n * import * as HttpServer from \"effect/http/HttpServer\";\n * import { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\";\n * import { Effect, Layer } from \"effect\";\n * import { createServer } from \"node:http\";\n *\n * const serverLayer = Layer.unwrap(\n * Effect.map(handler, (app) => HttpServer.serve(app))\n * ).pipe(Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })));\n *\n * NodeRuntime.runMain(Layer.launch(serverLayer));\n * ```\n */\nexport const makeHandler = (\n options: SvelteKitHandlerOptions,\n): Effect.Effect<SvelteKitHttpApp, never, SvelteKitHttpAppServices> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const context = yield* Effect.context<never>();\n\n const serverDir = path.join(options.dir, \"server\");\n\n return yield* makeHandlerWith<SvelteKitHttpAppServices>({\n Server: options.Server,\n manifest: options.manifest,\n env: options.env,\n envPrefix: options.envPrefix,\n // `read` backs the `read()` helper from `$app/server`, streaming server\n // assets from the build output.\n read: (file) => Stream.toReadableStreamWith(fs.stream(path.join(serverDir, file)), context),\n assets: AssetSource.fileSystem({\n dir: options.dir,\n appPath: options.manifest.appPath,\n prerendered: options.prerendered,\n }),\n });\n });\n\n/**\n * The platform-neutral SSR core. Like {@link makeHandler}, but the asset source\n * and the server-asset `read` capability are injected, so the same SvelteKit\n * `HttpApp` can run on Node (file-system assets) or on Cloudflare Workers\n * (`AssetSource.cloudflareAssets(env.ASSETS)`).\n *\n * The returned app:\n *\n * 1. falls through to the injected {@link AssetSource} for static client assets\n * and prerendered pages (GET/HEAD), then\n * 2. server-side renders via the SvelteKit server.\n *\n * @example\n * ```ts\n * // Workers: a fetch handler with assets from env.ASSETS\n * import { makeHandlerWith, AssetSource, toWebHandler } from \"...adapter/handler\";\n * import { Server } from \"./server/index.js\";\n * import { manifest } from \"./manifest.js\";\n *\n * export default {\n * fetch: (request, env, ctx) =>\n * toWebHandler(\n * makeHandlerWith({\n * Server, manifest, env,\n * assets: AssetSource.cloudflareAssets(env.ASSETS),\n * read: () => { throw new Error(\"read() not supported on this deployment\"); },\n * }),\n * )(request),\n * };\n * ```\n */\nexport const makeHandlerWith = <RAssets = never>(\n options: SvelteKitNeutralHandlerOptions<RAssets>,\n): Effect.Effect<SvelteKitHttpApp<RAssets>, never, never> =>\n Effect.gen(function* () {\n const config = yield* handlerConfig(options.envPrefix ?? \"\").pipe(Effect.orDie);\n\n // Initialise the SvelteKit server. `read` backs `read()` from `$app/server`.\n const server = new options.Server(options.manifest);\n yield* Effect.promise(() =>\n server.init({\n env: (options.env ?? {}) as Record<string, string>,\n read: options.read,\n }),\n );\n\n // The HTTP handler itself, evaluated once per request.\n return Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const requestContext = yield* Effect.context<never>();\n\n const origin = originOf(request, config);\n const url = new URL(request.url, origin);\n\n let pathname: string;\n try {\n pathname = decodeURIComponent(url.pathname);\n } catch {\n return HttpServerResponse.empty({ status: 400 });\n }\n\n // 1. Static client assets / prerendered pages, via the injected source.\n // Only GET/HEAD reach assets; build a body-less request for the source.\n if (request.method === \"GET\" || request.method === \"HEAD\") {\n const assetRequest = new Request(url, {\n method: request.method,\n headers: { ...request.headers },\n });\n const asset = yield* options.assets.serveAsset(assetRequest, pathname);\n if (Option.isSome(asset)) return asset.value;\n }\n\n // 2. Server-side rendering.\n const webRequest = toWebRequest(request, url, requestContext);\n const response = yield* Effect.promise(() =>\n server.respond(webRequest, {\n getClientAddress: () => clientAddressOf(request, config),\n }),\n );\n return HttpServerResponse.fromWeb(response);\n });\n });\n\n/**\n * Convert a SvelteKit `HttpApp` to a Web `fetch` handler, providing the asset\n * source's services with `layer`. Use this for a Workers `fetch` export or any\n * runtime that speaks the Web platform.\n *\n * @example\n * ```ts\n * const fetchHandler = toWebHandler(\n * makeHandlerWith({ ..., assets: AssetSource.cloudflareAssets(env.ASSETS) }),\n * Layer.empty,\n * );\n * ```\n */\nexport const toWebHandler = <RAssets, E>(\n app: Effect.Effect<SvelteKitHttpApp<RAssets>, never, never>,\n layer: Layer.Layer<RAssets, E>,\n): ((request: Request) => Promise<Response>) => {\n // Build the app (server.init) and the per-request web handler once, lazily on\n // the first request, then dispatch each request through it.\n let built: Promise<(request: Request) => Promise<Response>> | undefined;\n const build = (): Promise<(request: Request) => Promise<Response>> =>\n (built ??= Effect.runPromise(app).then(\n (inner) =>\n HttpEffect.toWebHandlerLayer(inner, layer).handler as (\n request: Request,\n ) => Promise<Response>,\n ));\n return async (request) => (await build())(request);\n};\n"],"mappings":"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"}
|
package/dist/adapter/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/adapter/index.ts"],"sourcesContent":["/**\n * Effect deployment adapter for SvelteKit.\n *\n * Like `@sveltejs/adapter-node`, but the production server is an\n * `effect/http` HTTP server: requests are handled by an Effect HTTP\n * handler, configuration is read through `Config`, and startup/shutdown is\n * managed by Effect layers and scopes (SIGINT/SIGTERM trigger a graceful\n * shutdown that runs all finalizers).\n *\n * ```js\n * // svelte.config.js\n * import adapter from \"@thomasfosterau/effect-sveltekit/adapter\";\n *\n * const config = {\n * kit: { adapter: adapter() },\n * };\n *\n * export default config;\n * ```\n *\n * The build output (`build/` by default) contains:\n *\n * - `index.js` — runs the production server: `node build`\n * - `handler.js` — exports the SvelteKit app as a composable Effect HTTP\n * handler, for mounting inside an existing `effect/http` server\n * - `client/`, `server/`, `prerendered/`, `manifest.js` — the built app\n *\n * Unlike `adapter-node`, the server output is not bundled: the deployment\n * needs `node_modules` with the app's production dependencies installed\n * (including `@thomasfosterau/effect-sveltekit`, `effect`, and\n * `@effect/platform-node`).\n */\n\nimport { writeFileSync } from \"node:fs\";\nimport type { Adapter } from \"@sveltejs/kit\";\n\nexport interface EffectAdapterOptions {\n /**\n * The directory to write the production build to.\n * @default \"build\"\n */\n readonly out?: string;\n /**\n * Precompress static assets and prerendered pages with gzip and brotli.\n * @default true\n */\n readonly precompress?: boolean;\n /**\n * Prefix for the environment variables the production server reads\n * (`PORT`, `HOST`, `ORIGIN`, `PROTOCOL_HEADER`, `HOST_HEADER`,\n * `PORT_HEADER`, `ADDRESS_HEADER`, `XFF_DEPTH`).\n * @default \"\"\n */\n readonly envPrefix?: string;\n /**\n * The deployment target for the generated entry:\n *\n * - `\"node\"` (default) — a production Node HTTP server (`node build`) on\n * `@effect/platform-node`, serving assets from disk.\n * - `\"fetch\"` — a Web `fetch` handler suitable for Cloudflare Workers\n * (`build/index.js` exports `default { fetch }`), serving assets from the\n * `env.ASSETS` binding via `AssetSource.cloudflareAssets`.\n *\n * Both targets also write `build/handler.js` (the SvelteKit `HttpApp`), so you\n * can compose your own entry for any `effect/http` runtime.\n *\n * @default \"node\"\n */\n readonly target?: \"node\" | \"fetch\";\n}\n\nconst PACKAGE = \"@thomasfosterau/effect-sveltekit\";\n\n/** Template for the generated `<out>/handler.js`. */\nconst handlerTemplate = (envPrefix: string): string => `\\\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport process from \"node:process\";\nimport { makeHandler } from \"${PACKAGE}/adapter/handler\";\nimport { Server } from \"./server/index.js\";\nimport { manifest, prerendered, base } from \"./manifest.js\";\n\nconst dir = dirname(fileURLToPath(import.meta.url));\n\n/**\n * The SvelteKit application as a composable Effect HTTP handler.\n *\n * Serve it with the generated index.js, or mount it inside any\n * \\`effect/http\\` HTTP server.\n */\nexport const handler = makeHandler({\n Server,\n manifest,\n prerendered,\n base,\n dir,\n env: process.env,\n envPrefix: ${JSON.stringify(envPrefix)},\n});\n`;\n\n/** Template for the generated Node `<out>/index.js`. */\nconst nodeEntryTemplate = (envPrefix: string): string => `\\\nimport { serve } from \"${PACKAGE}/adapter/serve\";\nimport { handler } from \"./handler.js\";\n\nserve(handler, { envPrefix: ${JSON.stringify(envPrefix)} });\n`;\n\n/**\n * Template for the generated Cloudflare Workers `<out>/index.js`.\n *\n * Exports a module worker (`default { fetch }`). Static client assets and\n * prerendered pages are served from the `ASSETS` binding; everything else is\n * server-side rendered by the SvelteKit `HttpApp`.\n */\nconst fetchEntryTemplate = (envPrefix: string): string => `\\\nimport { Layer } from \"effect\";\nimport { makeHandlerWith, AssetSource, toWebHandler } from \"${PACKAGE}/adapter/handler\";\nimport { Server } from \"./server/index.js\";\nimport { manifest } from \"./manifest.js\";\n\nlet fetchHandler;\n\nexport default {\n /** @param {Request} request @param {Record<string, any>} env @param {any} ctx */\n fetch(request, env, ctx) {\n fetchHandler ??= toWebHandler(\n makeHandlerWith({\n Server,\n manifest,\n env,\n envPrefix: ${JSON.stringify(envPrefix)},\n // Static assets come from the Workers ASSETS binding (configure it to\n // serve the uploaded \\`client/\\` + \\`prerendered/\\` output).\n assets: AssetSource.cloudflareAssets(env.ASSETS),\n // \\`read()\\` from \\`$app/server\\` is not backed by a file system on workerd.\n read: (file) => {\n throw new Error(\\`read() is not supported on this deployment (\\${file})\\`);\n },\n }),\n Layer.empty,\n );\n return fetchHandler(request);\n },\n};\n`;\n\n/**\n * Creates the Effect SvelteKit adapter.\n */\nexport function effectAdapter(options: EffectAdapterOptions = {}): Adapter {\n const { out = \"build\", precompress = true, envPrefix = \"\", target = \"node\" } = options;\n\n return {\n name: `${PACKAGE}/adapter`,\n\n async adapt(builder) {\n const base = builder.config.kit.paths.base;\n\n builder.rimraf(out);\n builder.mkdirp(out);\n\n builder.log.minor(\"Copying assets\");\n builder.writeClient(`${out}/client${base}`);\n builder.writePrerendered(`${out}/prerendered${base}`);\n\n if (precompress) {\n builder.log.minor(\"Compressing assets\");\n await Promise.all([\n builder.compress(`${out}/client`),\n builder.compress(`${out}/prerendered`),\n ]);\n }\n\n builder.log.minor(\"Building server\");\n builder.writeServer(`${out}/server`);\n\n writeFileSync(\n `${out}/manifest.js`,\n [\n `export const manifest = ${builder.generateManifest({ relativePath: \"./server\" })};`,\n `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});`,\n `export const base = ${JSON.stringify(base)};`,\n ].join(\"\\n\\n\"),\n );\n\n builder.log.minor(\"Writing server entry\");\n writeFileSync(`${out}/handler.js`, handlerTemplate(envPrefix));\n writeFileSync(\n `${out}/index.js`,\n target === \"fetch\" ? fetchEntryTemplate(envPrefix) : nodeEntryTemplate(envPrefix),\n );\n },\n\n supports: {\n // On Node, `read()` from `$app/server` is backed by the platform\n // FileSystem. On the `fetch` (Workers) target it has no file system, so\n // routes using `read()` are unsupported there.\n read: () => target !== \"fetch\",\n },\n };\n}\n\nexport default effectAdapter;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/adapter/index.ts"],"sourcesContent":["/**\n * Effect deployment adapter for SvelteKit.\n *\n * Like `@sveltejs/adapter-node`, but the production server is an\n * `effect/http` HTTP server: requests are handled by an Effect HTTP\n * handler, configuration is read through `Config`, and startup/shutdown is\n * managed by Effect layers and scopes (SIGINT/SIGTERM trigger a graceful\n * shutdown that runs all finalizers).\n *\n * ```js\n * // svelte.config.js\n * import adapter from \"@thomasfosterau/effect-sveltekit/adapter\";\n *\n * const config = {\n * kit: { adapter: adapter() },\n * };\n *\n * export default config;\n * ```\n *\n * The build output (`build/` by default) contains:\n *\n * - `index.js` — runs the production server: `node build`\n * - `handler.js` — exports the SvelteKit app as a composable Effect HTTP\n * handler, for mounting inside an existing `effect/http` server\n * - `client/`, `server/`, `prerendered/`, `manifest.js` — the built app\n *\n * Unlike `adapter-node`, the server output is not bundled: the deployment\n * needs `node_modules` with the app's production dependencies installed\n * (including `@thomasfosterau/effect-sveltekit`, `effect`, and\n * `@effect/platform-node`).\n */\n\nimport { writeFileSync } from \"node:fs\";\nimport type { Adapter } from \"@sveltejs/kit\";\n\n/** Options for {@link effectAdapter}. */\nexport interface EffectAdapterOptions {\n /**\n * The directory to write the production build to.\n * @default \"build\"\n */\n readonly out?: string;\n /**\n * Precompress static assets and prerendered pages with gzip and brotli.\n * @default true\n */\n readonly precompress?: boolean;\n /**\n * Prefix for the environment variables the production server reads\n * (`PORT`, `HOST`, `ORIGIN`, `PROTOCOL_HEADER`, `HOST_HEADER`,\n * `PORT_HEADER`, `ADDRESS_HEADER`, `XFF_DEPTH`).\n * @default \"\"\n */\n readonly envPrefix?: string;\n /**\n * The deployment target for the generated entry:\n *\n * - `\"node\"` (default) — a production Node HTTP server (`node build`) on\n * `@effect/platform-node`, serving assets from disk.\n * - `\"fetch\"` — a Web `fetch` handler suitable for Cloudflare Workers\n * (`build/index.js` exports `default { fetch }`), serving assets from the\n * `env.ASSETS` binding via `AssetSource.cloudflareAssets`.\n *\n * Both targets also write `build/handler.js` (the SvelteKit `HttpApp`), so you\n * can compose your own entry for any `effect/http` runtime.\n *\n * @default \"node\"\n */\n readonly target?: \"node\" | \"fetch\";\n}\n\nconst PACKAGE = \"@thomasfosterau/effect-sveltekit\";\n\n/** Template for the generated `<out>/handler.js`. */\nconst handlerTemplate = (envPrefix: string): string => `\\\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport process from \"node:process\";\nimport { makeHandler } from \"${PACKAGE}/adapter/handler\";\nimport { Server } from \"./server/index.js\";\nimport { manifest, prerendered, base } from \"./manifest.js\";\n\nconst dir = dirname(fileURLToPath(import.meta.url));\n\n/**\n * The SvelteKit application as a composable Effect HTTP handler.\n *\n * Serve it with the generated index.js, or mount it inside any\n * \\`effect/http\\` HTTP server.\n */\nexport const handler = makeHandler({\n Server,\n manifest,\n prerendered,\n base,\n dir,\n env: process.env,\n envPrefix: ${JSON.stringify(envPrefix)},\n});\n`;\n\n/** Template for the generated Node `<out>/index.js`. */\nconst nodeEntryTemplate = (envPrefix: string): string => `\\\nimport { serve } from \"${PACKAGE}/adapter/serve\";\nimport { handler } from \"./handler.js\";\n\nserve(handler, { envPrefix: ${JSON.stringify(envPrefix)} });\n`;\n\n/**\n * Template for the generated Cloudflare Workers `<out>/index.js`.\n *\n * Exports a module worker (`default { fetch }`). Static client assets and\n * prerendered pages are served from the `ASSETS` binding; everything else is\n * server-side rendered by the SvelteKit `HttpApp`.\n */\nconst fetchEntryTemplate = (envPrefix: string): string => `\\\nimport { Layer } from \"effect\";\nimport { makeHandlerWith, AssetSource, toWebHandler } from \"${PACKAGE}/adapter/handler\";\nimport { Server } from \"./server/index.js\";\nimport { manifest } from \"./manifest.js\";\n\nlet fetchHandler;\n\nexport default {\n /** @param {Request} request @param {Record<string, any>} env @param {any} ctx */\n fetch(request, env, ctx) {\n fetchHandler ??= toWebHandler(\n makeHandlerWith({\n Server,\n manifest,\n env,\n envPrefix: ${JSON.stringify(envPrefix)},\n // Static assets come from the Workers ASSETS binding (configure it to\n // serve the uploaded \\`client/\\` + \\`prerendered/\\` output).\n assets: AssetSource.cloudflareAssets(env.ASSETS),\n // \\`read()\\` from \\`$app/server\\` is not backed by a file system on workerd.\n read: (file) => {\n throw new Error(\\`read() is not supported on this deployment (\\${file})\\`);\n },\n }),\n Layer.empty,\n );\n return fetchHandler(request);\n },\n};\n`;\n\n/**\n * Creates the Effect SvelteKit adapter.\n */\nexport function effectAdapter(options: EffectAdapterOptions = {}): Adapter {\n const { out = \"build\", precompress = true, envPrefix = \"\", target = \"node\" } = options;\n\n return {\n name: `${PACKAGE}/adapter`,\n\n async adapt(builder) {\n const base = builder.config.kit.paths.base;\n\n builder.rimraf(out);\n builder.mkdirp(out);\n\n builder.log.minor(\"Copying assets\");\n builder.writeClient(`${out}/client${base}`);\n builder.writePrerendered(`${out}/prerendered${base}`);\n\n if (precompress) {\n builder.log.minor(\"Compressing assets\");\n await Promise.all([\n builder.compress(`${out}/client`),\n builder.compress(`${out}/prerendered`),\n ]);\n }\n\n builder.log.minor(\"Building server\");\n builder.writeServer(`${out}/server`);\n\n writeFileSync(\n `${out}/manifest.js`,\n [\n `export const manifest = ${builder.generateManifest({ relativePath: \"./server\" })};`,\n `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});`,\n `export const base = ${JSON.stringify(base)};`,\n ].join(\"\\n\\n\"),\n );\n\n builder.log.minor(\"Writing server entry\");\n writeFileSync(`${out}/handler.js`, handlerTemplate(envPrefix));\n writeFileSync(\n `${out}/index.js`,\n target === \"fetch\" ? fetchEntryTemplate(envPrefix) : nodeEntryTemplate(envPrefix),\n );\n },\n\n supports: {\n // On Node, `read()` from `$app/server` is backed by the platform\n // FileSystem. On the `fetch` (Workers) target it has no file system, so\n // routes using `read()` are unsupported there.\n read: () => target !== \"fetch\",\n },\n };\n}\n\nexport default effectAdapter;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEA,MAAM,UAAU;;AAGhB,MAAM,mBAAmB,cAA8B;;;;+BAIxB,QAAQ;;;;;;;;;;;;;;;;;;;eAmBxB,KAAK,UAAU,SAAS,EAAE;;;;AAKzC,MAAM,qBAAqB,cAA8B;yBAChC,QAAQ;;;8BAGH,KAAK,UAAU,SAAS,EAAE;;;;;;;;;AAUxD,MAAM,sBAAsB,cAA8B;;8DAEI,QAAQ;;;;;;;;;;;;;;qBAcjD,KAAK,UAAU,SAAS,EAAE;;;;;;;;;;;;;;;;;;AAmB/C,SAAgB,cAAc,UAAgC,CAAC,GAAY;CACzE,MAAM,EAAE,MAAM,SAAS,cAAc,MAAM,YAAY,IAAI,SAAS,WAAW;CAE/E,OAAO;EACL,MAAM,GAAG,QAAQ;EAEjB,MAAM,MAAM,SAAS;GACnB,MAAM,OAAO,QAAQ,OAAO,IAAI,MAAM;GAEtC,QAAQ,OAAO,GAAG;GAClB,QAAQ,OAAO,GAAG;GAElB,QAAQ,IAAI,MAAM,gBAAgB;GAClC,QAAQ,YAAY,GAAG,IAAI,SAAS,MAAM;GAC1C,QAAQ,iBAAiB,GAAG,IAAI,cAAc,MAAM;GAEpD,IAAI,aAAa;IACf,QAAQ,IAAI,MAAM,oBAAoB;IACtC,MAAM,QAAQ,IAAI,CAChB,QAAQ,SAAS,GAAG,IAAI,QAAQ,GAChC,QAAQ,SAAS,GAAG,IAAI,aAAa,CACvC,CAAC;GACH;GAEA,QAAQ,IAAI,MAAM,iBAAiB;GACnC,QAAQ,YAAY,GAAG,IAAI,QAAQ;GAEnC,cACE,GAAG,IAAI,eACP;IACE,2BAA2B,QAAQ,iBAAiB,EAAE,cAAc,WAAW,CAAC,EAAE;IAClF,sCAAsC,KAAK,UAAU,QAAQ,YAAY,KAAK,EAAE;IAChF,uBAAuB,KAAK,UAAU,IAAI,EAAE;GAC9C,CAAC,CAAC,KAAK,MAAM,CACf;GAEA,QAAQ,IAAI,MAAM,sBAAsB;GACxC,cAAc,GAAG,IAAI,cAAc,gBAAgB,SAAS,CAAC;GAC7D,cACE,GAAG,IAAI,YACP,WAAW,UAAU,mBAAmB,SAAS,IAAI,kBAAkB,SAAS,CAClF;EACF;EAEA,UAAU,EAIR,YAAY,WAAW,QACzB;CACF;AACF"}
|
package/dist/adapter/serve.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { SvelteKitHttpApp } from "./handler.js";
|
|
|
2
2
|
import { Effect, Layer } from "effect";
|
|
3
3
|
import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
|
|
4
4
|
//#region src/adapter/serve.d.ts
|
|
5
|
+
/** Options for {@link serve} and {@link layer}. */
|
|
5
6
|
interface ServeOptions {
|
|
6
7
|
/** Prefix for the configuration environment variables (PORT, HOST, ...). */
|
|
7
8
|
readonly envPrefix?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.js","names":[],"sources":["../../src/adapter/serve.ts"],"sourcesContent":["/**\n * Production server entry for the Effect SvelteKit adapter.\n *\n * The adapter's generated `build/index.js` calls {@link serve} with the\n * handler from `build/handler.js`. The server runs on\n * `@effect/platform-node`'s `NodeHttpServer`, with configuration read from\n * the environment and graceful shutdown handled by Effect scopes.\n *\n * For full control (custom layers, tracing, additional routes), skip the\n * generated entry and compose {@link layer} — or the handler itself — into\n * your own Effect application.\n */\n\nimport { createServer } from \"node:http\";\nimport * as HttpMiddleware from \"effect/http/HttpMiddleware\";\nimport * as HttpServer from \"effect/http/HttpServer\";\nimport * as NodeHttpServer from \"@effect/platform-node/NodeHttpServer\";\nimport * as NodeRuntime from \"@effect/platform-node/NodeRuntime\";\nimport { Config, Effect, Layer } from \"effect\";\nimport type { SvelteKitHttpApp } from \"./handler.js\";\n\nexport interface ServeOptions {\n /** Prefix for the configuration environment variables (PORT, HOST, ...). */\n readonly envPrefix?: string;\n /**\n * Additional layer merged into the server context, e.g. tracing or logging\n * layers.\n */\n readonly layer?: Layer.Layer<never, never>;\n /**\n * Disable the default request logger middleware.\n */\n readonly disableLogger?: boolean;\n}\n\n/**\n * Builds the full server layer for a SvelteKit HttpApp: HTTP listener\n * (`PORT` / `HOST` environment variables), request logging, and the\n * platform services the handler needs.\n *\n * Launch it with `Layer.launch`, or merge it into a larger application.\n */\nexport const layer = (\n handler: Effect.Effect<SvelteKitHttpApp, never, NodeHttpServerServices>,\n options: ServeOptions = {},\n): Layer.Layer<never, unknown> => {\n const prefix = options.envPrefix ?? \"\";\n\n const serveLayer = Layer.unwrap(\n Effect.map(handler, (app) =>\n options.disableLogger ? HttpServer.serve(app) : HttpServer.serve(app, HttpMiddleware.logger),\n ),\n );\n\n const listenLayer = NodeHttpServer.layerConfig(createServer, {\n port: Config.Int(`${prefix}PORT`).pipe(Config.withDefault(3000)),\n host: Config.String(`${prefix}HOST`).pipe(Config.withDefault(\"0.0.0.0\")),\n });\n\n const withListener = serveLayer.pipe(HttpServer.withLogAddress, Layer.provide(listenLayer));\n\n return options.layer ? Layer.merge(withListener, options.layer) : withListener;\n};\n\n/** The services provided to the handler effect by `NodeHttpServer.layerConfig`. */\ntype NodeHttpServerServices = Layer.Success<ReturnType<typeof NodeHttpServer.layerConfig>>;\n\n/**\n * Starts the production server and runs it until the process receives\n * SIGINT/SIGTERM, at which point the server (and every Effect resource) is\n * shut down gracefully.\n *\n * @example\n * ```ts\n * // build/index.js (generated by the adapter)\n * import { handler } from \"./handler.js\";\n * import { serve } from \"@thomasfosterau/effect-sveltekit/adapter/serve\";\n *\n * serve(handler);\n * ```\n */\nexport const serve = (\n handler: Effect.Effect<SvelteKitHttpApp, never, NodeHttpServerServices>,\n options: ServeOptions = {},\n): void => {\n NodeRuntime.runMain(Layer.launch(layer(handler, options)));\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"serve.js","names":[],"sources":["../../src/adapter/serve.ts"],"sourcesContent":["/**\n * Production server entry for the Effect SvelteKit adapter.\n *\n * The adapter's generated `build/index.js` calls {@link serve} with the\n * handler from `build/handler.js`. The server runs on\n * `@effect/platform-node`'s `NodeHttpServer`, with configuration read from\n * the environment and graceful shutdown handled by Effect scopes.\n *\n * For full control (custom layers, tracing, additional routes), skip the\n * generated entry and compose {@link layer} — or the handler itself — into\n * your own Effect application.\n */\n\nimport { createServer } from \"node:http\";\nimport * as HttpMiddleware from \"effect/http/HttpMiddleware\";\nimport * as HttpServer from \"effect/http/HttpServer\";\nimport * as NodeHttpServer from \"@effect/platform-node/NodeHttpServer\";\nimport * as NodeRuntime from \"@effect/platform-node/NodeRuntime\";\nimport { Config, Effect, Layer } from \"effect\";\nimport type { SvelteKitHttpApp } from \"./handler.js\";\n\n/** Options for {@link serve} and {@link layer}. */\nexport interface ServeOptions {\n /** Prefix for the configuration environment variables (PORT, HOST, ...). */\n readonly envPrefix?: string;\n /**\n * Additional layer merged into the server context, e.g. tracing or logging\n * layers.\n */\n readonly layer?: Layer.Layer<never, never>;\n /**\n * Disable the default request logger middleware.\n */\n readonly disableLogger?: boolean;\n}\n\n/**\n * Builds the full server layer for a SvelteKit HttpApp: HTTP listener\n * (`PORT` / `HOST` environment variables), request logging, and the\n * platform services the handler needs.\n *\n * Launch it with `Layer.launch`, or merge it into a larger application.\n */\nexport const layer = (\n handler: Effect.Effect<SvelteKitHttpApp, never, NodeHttpServerServices>,\n options: ServeOptions = {},\n): Layer.Layer<never, unknown> => {\n const prefix = options.envPrefix ?? \"\";\n\n const serveLayer = Layer.unwrap(\n Effect.map(handler, (app) =>\n options.disableLogger ? HttpServer.serve(app) : HttpServer.serve(app, HttpMiddleware.logger),\n ),\n );\n\n const listenLayer = NodeHttpServer.layerConfig(createServer, {\n port: Config.Int(`${prefix}PORT`).pipe(Config.withDefault(3000)),\n host: Config.String(`${prefix}HOST`).pipe(Config.withDefault(\"0.0.0.0\")),\n });\n\n const withListener = serveLayer.pipe(HttpServer.withLogAddress, Layer.provide(listenLayer));\n\n return options.layer ? Layer.merge(withListener, options.layer) : withListener;\n};\n\n/** The services provided to the handler effect by `NodeHttpServer.layerConfig`. */\ntype NodeHttpServerServices = Layer.Success<ReturnType<typeof NodeHttpServer.layerConfig>>;\n\n/**\n * Starts the production server and runs it until the process receives\n * SIGINT/SIGTERM, at which point the server (and every Effect resource) is\n * shut down gracefully.\n *\n * @example\n * ```ts\n * // build/index.js (generated by the adapter)\n * import { handler } from \"./handler.js\";\n * import { serve } from \"@thomasfosterau/effect-sveltekit/adapter/serve\";\n *\n * serve(handler);\n * ```\n */\nexport const serve = (\n handler: Effect.Effect<SvelteKitHttpApp, never, NodeHttpServerServices>,\n options: ServeOptions = {},\n): void => {\n NodeRuntime.runMain(Layer.launch(layer(handler, options)));\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,SACX,SACA,UAAwB,CAAC,MACO;CAChC,MAAM,SAAS,QAAQ,aAAa;CAEpC,MAAM,aAAa,MAAM,OACvB,OAAO,IAAI,UAAU,QACnB,QAAQ,gBAAgB,WAAW,MAAM,GAAG,IAAI,WAAW,MAAM,KAAK,eAAe,MAAM,CAC7F,CACF;CAEA,MAAM,cAAc,eAAe,YAAY,cAAc;EAC3D,MAAM,OAAO,IAAI,GAAG,OAAO,KAAK,CAAC,CAAC,KAAK,OAAO,YAAY,GAAI,CAAC;EAC/D,MAAM,OAAO,OAAO,GAAG,OAAO,KAAK,CAAC,CAAC,KAAK,OAAO,YAAY,SAAS,CAAC;CACzE,CAAC;CAED,MAAM,eAAe,WAAW,KAAK,WAAW,gBAAgB,MAAM,QAAQ,WAAW,CAAC;CAE1F,OAAO,QAAQ,QAAQ,MAAM,MAAM,cAAc,QAAQ,KAAK,IAAI;AACpE;;;;;;;;;;;;;;;AAmBA,MAAa,SACX,SACA,UAAwB,CAAC,MAChB;CACT,YAAY,QAAQ,MAAM,OAAO,MAAM,SAAS,OAAO,CAAC,CAAC;AAC3D"}
|
|
@@ -19,6 +19,7 @@ type FormRunner = <A>(effect: Effect.Effect<A, unknown, any>, ctx: {
|
|
|
19
19
|
readonly issue: InvalidField<any>;
|
|
20
20
|
readonly data: unknown;
|
|
21
21
|
}) => Promise<A>;
|
|
22
|
+
/** Runners that execute a remote-function handler's Effect on a request runtime, as `runRemote` returns them. */
|
|
22
23
|
interface RunHelpers {
|
|
23
24
|
readonly run: Runner;
|
|
24
25
|
readonly runForm: FormRunner;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-run.js","names":[],"sources":["../../src/internal/remote-run.ts"],"sourcesContent":["/**\n * Run/settle helpers for the remote-function wrappers, factored out so they can\n * be unit-tested without importing `$app/server` (a SvelteKit virtual module\n * unavailable outside an app build). The `$app/server`-dependent resolvers live\n * in `remote-functions.ts`; this module only deals in `Exit`s.\n */\n\nimport type { InvalidField } from \"@sveltejs/kit\";\nimport { Cause, type Effect, Exit } from \"effect\";\nimport type { ErrorTranslator } from \"../request-runtime.js\";\nimport { SvelteKit } from \"../kit.js\";\n\n/** The default translator: Kit control-flow errors through, anything else a 500. */\nexport const defaultTranslate: ErrorTranslator = SvelteKit.translate;\n\n/**\n * Maps a remote *form* handler's failure into SvelteKit's field issues, given\n * the form's parsed `data` and the `issue` helper. It may call `invalid()` so a\n * structured/nested validation error drives field-level invalidation instead of\n * a generic failure. Receives the squashed handler failure.\n */\nexport type FormErrorMapper = (\n error: unknown,\n ctx: { readonly issue: InvalidField<any>; readonly data: unknown },\n) => never;\n\n/** Runs a remote handler's effect and settles it (value or thrown via translator). */\nexport type Runner = <A>(effect: Effect.Effect<A, unknown, any>) => Promise<A>;\n\n/** Like {@link Runner}, but with the form's `issue` / `data` for error interop. */\nexport type FormRunner = <A>(\n effect: Effect.Effect<A, unknown, any>,\n ctx: { readonly issue: InvalidField<any>; readonly data: unknown },\n) => Promise<A>;\n\nexport interface RunHelpers {\n readonly run: Runner;\n readonly runForm: FormRunner;\n}\n\n/** How to obtain an `Exit` for the current request, plus optional teardown. */\nexport type ExitResolver = () => {\n readonly runExit: <A>(effect: Effect.Effect<A, unknown, any>) => Promise<Exit.Exit<A, unknown>>;\n readonly dispose?: () => void;\n};\n\n/** Build the `run` / `runForm` helpers from an exit resolver and a translator. */\nexport const makeRunHelpers = (\n resolve: ExitResolver,\n translate: ErrorTranslator,\n formError?: FormErrorMapper,\n): RunHelpers => {\n const run: Runner = (effect) => {\n const { runExit, dispose } = resolve();\n return runExit(effect).then((exit) => {\n try {\n return Exit.isSuccess(exit) ? exit.value : translate(exit.cause);\n } finally {\n dispose?.();\n }\n });\n };\n const runForm: FormRunner = (effect, ctx) => {\n const { runExit, dispose } = resolve();\n return runExit(effect).then((exit) => {\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n return formError ? formError(Cause.squash(exit.cause), ctx) : translate(exit.cause);\n } finally {\n dispose?.();\n }\n });\n };\n return { run, runForm };\n};\n"],"mappings":";;;;AAaA,MAAa,mBAAoC,UAAU;;
|
|
1
|
+
{"version":3,"file":"remote-run.js","names":[],"sources":["../../src/internal/remote-run.ts"],"sourcesContent":["/**\n * Run/settle helpers for the remote-function wrappers, factored out so they can\n * be unit-tested without importing `$app/server` (a SvelteKit virtual module\n * unavailable outside an app build). The `$app/server`-dependent resolvers live\n * in `remote-functions.ts`; this module only deals in `Exit`s.\n */\n\nimport type { InvalidField } from \"@sveltejs/kit\";\nimport { Cause, type Effect, Exit } from \"effect\";\nimport type { ErrorTranslator } from \"../request-runtime.js\";\nimport { SvelteKit } from \"../kit.js\";\n\n/** The default translator: Kit control-flow errors through, anything else a 500. */\nexport const defaultTranslate: ErrorTranslator = SvelteKit.translate;\n\n/**\n * Maps a remote *form* handler's failure into SvelteKit's field issues, given\n * the form's parsed `data` and the `issue` helper. It may call `invalid()` so a\n * structured/nested validation error drives field-level invalidation instead of\n * a generic failure. Receives the squashed handler failure.\n */\nexport type FormErrorMapper = (\n error: unknown,\n ctx: { readonly issue: InvalidField<any>; readonly data: unknown },\n) => never;\n\n/** Runs a remote handler's effect and settles it (value or thrown via translator). */\nexport type Runner = <A>(effect: Effect.Effect<A, unknown, any>) => Promise<A>;\n\n/** Like {@link Runner}, but with the form's `issue` / `data` for error interop. */\nexport type FormRunner = <A>(\n effect: Effect.Effect<A, unknown, any>,\n ctx: { readonly issue: InvalidField<any>; readonly data: unknown },\n) => Promise<A>;\n\n/** Runners that execute a remote-function handler's Effect on a request runtime, as `runRemote` returns them. */\nexport interface RunHelpers {\n readonly run: Runner;\n readonly runForm: FormRunner;\n}\n\n/** How to obtain an `Exit` for the current request, plus optional teardown. */\nexport type ExitResolver = () => {\n readonly runExit: <A>(effect: Effect.Effect<A, unknown, any>) => Promise<Exit.Exit<A, unknown>>;\n readonly dispose?: () => void;\n};\n\n/** Build the `run` / `runForm` helpers from an exit resolver and a translator. */\nexport const makeRunHelpers = (\n resolve: ExitResolver,\n translate: ErrorTranslator,\n formError?: FormErrorMapper,\n): RunHelpers => {\n const run: Runner = (effect) => {\n const { runExit, dispose } = resolve();\n return runExit(effect).then((exit) => {\n try {\n return Exit.isSuccess(exit) ? exit.value : translate(exit.cause);\n } finally {\n dispose?.();\n }\n });\n };\n const runForm: FormRunner = (effect, ctx) => {\n const { runExit, dispose } = resolve();\n return runExit(effect).then((exit) => {\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n return formError ? formError(Cause.squash(exit.cause), ctx) : translate(exit.cause);\n } finally {\n dispose?.();\n }\n });\n };\n return { run, runForm };\n};\n"],"mappings":";;;;AAaA,MAAa,mBAAoC,UAAU;;AAmC3D,MAAa,kBACX,SACA,WACA,cACe;CACf,MAAM,OAAe,WAAW;EAC9B,MAAM,EAAE,SAAS,YAAY,QAAQ;EACrC,OAAO,QAAQ,MAAM,CAAC,CAAC,MAAM,SAAS;GACpC,IAAI;IACF,OAAO,KAAK,UAAU,IAAI,IAAI,KAAK,QAAQ,UAAU,KAAK,KAAK;GACjE,UAAU;IACR,UAAU;GACZ;EACF,CAAC;CACH;CACA,MAAM,WAAuB,QAAQ,QAAQ;EAC3C,MAAM,EAAE,SAAS,YAAY,QAAQ;EACrC,OAAO,QAAQ,MAAM,CAAC,CAAC,MAAM,SAAS;GACpC,IAAI;IACF,IAAI,KAAK,UAAU,IAAI,GAAG,OAAO,KAAK;IACtC,OAAO,YAAY,UAAU,MAAM,OAAO,KAAK,KAAK,GAAG,GAAG,IAAI,UAAU,KAAK,KAAK;GACpF,UAAU;IACR,UAAU;GACZ;EACF,CAAC;CACH;CACA,OAAO;EAAE;EAAK;CAAQ;AACxB"}
|
package/dist/realtime.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ declare class EventSourceError extends EventSourceError_base<{
|
|
|
22
22
|
type EventSourceConstructor = new (url: string, eventSourceInitDict?: {
|
|
23
23
|
withCredentials?: boolean;
|
|
24
24
|
}) => EventSource;
|
|
25
|
+
/** Options for {@link fromEventSource}. */
|
|
25
26
|
interface FromEventSourceOptions {
|
|
26
27
|
/** Send credentials (cookies) with the request. Defaults to `false`. */
|
|
27
28
|
readonly withCredentials?: boolean;
|
|
@@ -67,6 +68,7 @@ declare class WebSocketError extends WebSocketError_base<{
|
|
|
67
68
|
}> {}
|
|
68
69
|
/** The subset of the `WebSocket` constructor this module relies on. */
|
|
69
70
|
type WebSocketConstructor = new (url: string, protocols?: string | ReadonlyArray<string>) => WebSocket;
|
|
71
|
+
/** Options for {@link fromWebSocket}. */
|
|
70
72
|
interface FromWebSocketOptions {
|
|
71
73
|
/** Sub-protocols passed to the `WebSocket` constructor. */
|
|
72
74
|
readonly protocols?: string | ReadonlyArray<string>;
|
package/dist/realtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime.js","names":[],"sources":["../src/realtime.ts"],"sourcesContent":["/**\n * Client-side realtime bridges: consume a Server-Sent-Events endpoint or a\n * WebSocket as an Effect `Stream`. The counterpart to `Endpoint.fromStream`\n * (server) on the `@thomasfosterau/effect-sveltekit/client` `Realtime` namespace.\n *\n * These use the browser globals `EventSource` / `WebSocket`; both can be\n * overridden (e.g. a Node polyfill, or a fake in tests) via the `implementation`\n * option. The source is opened when the stream is run and closed when its scope\n * closes, so a `Stream.take` / component teardown tears down the connection.\n */\n\nimport { Cause, Data, Effect, Queue, Stream } from \"effect\";\n\n// ---------------------------------------------------------------------------\n// EventSource\n// ---------------------------------------------------------------------------\n\n/** A decoded Server-Sent Event. */\nexport interface SseMessage {\n /** The event name (`\"message\"` for unnamed events). */\n readonly event: string;\n /** The event payload (the joined `data:` lines). */\n readonly data: string;\n /** The last event id seen, or `\"\"` if none. */\n readonly lastEventId: string;\n}\n\n/** Raised when an `EventSource` connection fails fatally (its `readyState` is `CLOSED`). */\nexport class EventSourceError extends Data.TaggedError(\"EventSourceError\")<{\n readonly url: string;\n}> {}\n\n/** The subset of the `EventSource` constructor this module relies on. */\nexport type EventSourceConstructor = new (\n url: string,\n eventSourceInitDict?: { withCredentials?: boolean },\n) => EventSource;\n\nexport interface FromEventSourceOptions {\n /** Send credentials (cookies) with the request. Defaults to `false`. */\n readonly withCredentials?: boolean;\n /**\n * Named events to listen for in addition to the default unnamed `message`\n * event. An `Endpoint.fromStream({ event: \"tick\" })` server needs\n * `events: [\"tick\"]` here.\n */\n readonly events?: ReadonlyArray<string>;\n /** Buffer size for the backing queue. Defaults to 16. */\n readonly bufferSize?: number;\n /** Backpressure strategy when the buffer is full. Defaults to `\"sliding\"`. */\n readonly strategy?: \"sliding\" | \"dropping\" | \"suspend\";\n /** Override the `EventSource` constructor (Node polyfill, tests). */\n readonly implementation?: EventSourceConstructor;\n}\n\n/**\n * Consume a Server-Sent-Events endpoint as a `Stream` of {@link SseMessage}.\n *\n * The browser's `EventSource` reconnects automatically after transient drops,\n * so the stream stays live across reconnects; it fails with\n * {@link EventSourceError} only when the connection closes fatally.\n *\n * @example\n * ```ts\n * import { Realtime } from \"@thomasfosterau/effect-sveltekit/client\";\n * import { Effect, Stream } from \"effect\";\n *\n * const program = Realtime.fromEventSource(\"/events\", { events: [\"tick\"] }).pipe(\n * Stream.runForEach((msg) => Effect.log(`${msg.event}: ${msg.data}`)),\n * );\n * ```\n */\nexport const fromEventSource = (\n url: string,\n options?: FromEventSourceOptions,\n): Stream.Stream<SseMessage, EventSourceError> => {\n const Impl = options?.implementation ?? (globalThis.EventSource as EventSourceConstructor);\n return Stream.callback<SseMessage, EventSourceError>(\n (queue) =>\n Effect.sync(() => {\n const source =\n options?.withCredentials === undefined\n ? new Impl(url)\n : new Impl(url, { withCredentials: options.withCredentials });\n\n const onMessage = (e: MessageEvent): void => {\n Queue.offerUnsafe(queue, { event: e.type, data: e.data, lastEventId: e.lastEventId });\n };\n\n source.addEventListener(\"message\", onMessage);\n for (const name of options?.events ?? []) source.addEventListener(name, onMessage);\n\n source.addEventListener(\"error\", () => {\n // EventSource reconnects on its own unless it has closed for good.\n if (source.readyState === 2 /* CLOSED */) {\n Queue.failCauseUnsafe(queue, Cause.fail(new EventSourceError({ url })));\n }\n });\n\n return Effect.sync(() => source.close());\n }),\n { bufferSize: options?.bufferSize ?? 16, strategy: options?.strategy ?? \"sliding\" },\n );\n};\n\n// ---------------------------------------------------------------------------\n// WebSocket\n// ---------------------------------------------------------------------------\n\n/** Raised when a `WebSocket` errors or closes uncleanly. */\nexport class WebSocketError extends Data.TaggedError(\"WebSocketError\")<{\n readonly url: string;\n readonly code?: number;\n readonly reason?: string;\n}> {}\n\n/** The subset of the `WebSocket` constructor this module relies on. */\nexport type WebSocketConstructor = new (\n url: string,\n protocols?: string | ReadonlyArray<string>,\n) => WebSocket;\n\nexport interface FromWebSocketOptions {\n /** Sub-protocols passed to the `WebSocket` constructor. */\n readonly protocols?: string | ReadonlyArray<string>;\n /** `arraybuffer` or `blob` for binary frames. Defaults to the platform default. */\n readonly binaryType?: BinaryType;\n /** Buffer size for the backing queue. Defaults to 16. */\n readonly bufferSize?: number;\n /** Backpressure strategy when the buffer is full. Defaults to `\"suspend\"`. */\n readonly strategy?: \"sliding\" | \"dropping\" | \"suspend\";\n /** Override the `WebSocket` constructor (Node polyfill, tests). */\n readonly implementation?: WebSocketConstructor;\n}\n\n/**\n * Consume a `WebSocket` as a `Stream` of its message payloads\n * (`string | ArrayBuffer | Blob`, depending on `binaryType`).\n *\n * The stream ends when the socket closes cleanly and fails with\n * {@link WebSocketError} on an error or unclean close. The socket is closed when\n * the stream's scope closes.\n *\n * @example\n * ```ts\n * import { Realtime } from \"@thomasfosterau/effect-sveltekit/client\";\n * import { Effect, Stream } from \"effect\";\n *\n * const program = Realtime.fromWebSocket(\"wss://example.com/ws\").pipe(\n * Stream.runForEach((data) => Effect.log(String(data))),\n * );\n * ```\n */\nexport const fromWebSocket = (\n url: string,\n options?: FromWebSocketOptions,\n): Stream.Stream<string | ArrayBuffer | Blob, WebSocketError> => {\n const Impl = options?.implementation ?? (globalThis.WebSocket as WebSocketConstructor);\n return Stream.callback<string | ArrayBuffer | Blob, WebSocketError>(\n (queue) =>\n Effect.sync(() => {\n const socket = new Impl(url, options?.protocols);\n if (options?.binaryType !== undefined) socket.binaryType = options.binaryType;\n\n socket.addEventListener(\"message\", (e: MessageEvent) => {\n Queue.offerUnsafe(queue, e.data);\n });\n\n socket.addEventListener(\"error\", () => {\n Queue.failCauseUnsafe(queue, Cause.fail(new WebSocketError({ url })));\n });\n\n socket.addEventListener(\"close\", (e: CloseEvent) => {\n if (e.wasClean) {\n Queue.endUnsafe(queue);\n } else {\n Queue.failCauseUnsafe(\n queue,\n Cause.fail(new WebSocketError({ url, code: e.code, reason: e.reason })),\n );\n }\n });\n\n return Effect.sync(() => {\n // Only close a socket that is still connecting/open.\n if (socket.readyState === 0 || socket.readyState === 1) socket.close();\n });\n }),\n { bufferSize: options?.bufferSize ?? 16, strategy: options?.strategy ?? \"suspend\" },\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4BA,IAAa,mBAAb,cAAsC,KAAK,YAAY,kBAAkB,CAAC,CAEvE,CAAC;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"realtime.js","names":[],"sources":["../src/realtime.ts"],"sourcesContent":["/**\n * Client-side realtime bridges: consume a Server-Sent-Events endpoint or a\n * WebSocket as an Effect `Stream`. The counterpart to `Endpoint.fromStream`\n * (server) on the `@thomasfosterau/effect-sveltekit/client` `Realtime` namespace.\n *\n * These use the browser globals `EventSource` / `WebSocket`; both can be\n * overridden (e.g. a Node polyfill, or a fake in tests) via the `implementation`\n * option. The source is opened when the stream is run and closed when its scope\n * closes, so a `Stream.take` / component teardown tears down the connection.\n */\n\nimport { Cause, Data, Effect, Queue, Stream } from \"effect\";\n\n// ---------------------------------------------------------------------------\n// EventSource\n// ---------------------------------------------------------------------------\n\n/** A decoded Server-Sent Event. */\nexport interface SseMessage {\n /** The event name (`\"message\"` for unnamed events). */\n readonly event: string;\n /** The event payload (the joined `data:` lines). */\n readonly data: string;\n /** The last event id seen, or `\"\"` if none. */\n readonly lastEventId: string;\n}\n\n/** Raised when an `EventSource` connection fails fatally (its `readyState` is `CLOSED`). */\nexport class EventSourceError extends Data.TaggedError(\"EventSourceError\")<{\n readonly url: string;\n}> {}\n\n/** The subset of the `EventSource` constructor this module relies on. */\nexport type EventSourceConstructor = new (\n url: string,\n eventSourceInitDict?: { withCredentials?: boolean },\n) => EventSource;\n\n/** Options for {@link fromEventSource}. */\nexport interface FromEventSourceOptions {\n /** Send credentials (cookies) with the request. Defaults to `false`. */\n readonly withCredentials?: boolean;\n /**\n * Named events to listen for in addition to the default unnamed `message`\n * event. An `Endpoint.fromStream({ event: \"tick\" })` server needs\n * `events: [\"tick\"]` here.\n */\n readonly events?: ReadonlyArray<string>;\n /** Buffer size for the backing queue. Defaults to 16. */\n readonly bufferSize?: number;\n /** Backpressure strategy when the buffer is full. Defaults to `\"sliding\"`. */\n readonly strategy?: \"sliding\" | \"dropping\" | \"suspend\";\n /** Override the `EventSource` constructor (Node polyfill, tests). */\n readonly implementation?: EventSourceConstructor;\n}\n\n/**\n * Consume a Server-Sent-Events endpoint as a `Stream` of {@link SseMessage}.\n *\n * The browser's `EventSource` reconnects automatically after transient drops,\n * so the stream stays live across reconnects; it fails with\n * {@link EventSourceError} only when the connection closes fatally.\n *\n * @example\n * ```ts\n * import { Realtime } from \"@thomasfosterau/effect-sveltekit/client\";\n * import { Effect, Stream } from \"effect\";\n *\n * const program = Realtime.fromEventSource(\"/events\", { events: [\"tick\"] }).pipe(\n * Stream.runForEach((msg) => Effect.log(`${msg.event}: ${msg.data}`)),\n * );\n * ```\n */\nexport const fromEventSource = (\n url: string,\n options?: FromEventSourceOptions,\n): Stream.Stream<SseMessage, EventSourceError> => {\n const Impl = options?.implementation ?? (globalThis.EventSource as EventSourceConstructor);\n return Stream.callback<SseMessage, EventSourceError>(\n (queue) =>\n Effect.sync(() => {\n const source =\n options?.withCredentials === undefined\n ? new Impl(url)\n : new Impl(url, { withCredentials: options.withCredentials });\n\n const onMessage = (e: MessageEvent): void => {\n Queue.offerUnsafe(queue, { event: e.type, data: e.data, lastEventId: e.lastEventId });\n };\n\n source.addEventListener(\"message\", onMessage);\n for (const name of options?.events ?? []) source.addEventListener(name, onMessage);\n\n source.addEventListener(\"error\", () => {\n // EventSource reconnects on its own unless it has closed for good.\n if (source.readyState === 2 /* CLOSED */) {\n Queue.failCauseUnsafe(queue, Cause.fail(new EventSourceError({ url })));\n }\n });\n\n return Effect.sync(() => source.close());\n }),\n { bufferSize: options?.bufferSize ?? 16, strategy: options?.strategy ?? \"sliding\" },\n );\n};\n\n// ---------------------------------------------------------------------------\n// WebSocket\n// ---------------------------------------------------------------------------\n\n/** Raised when a `WebSocket` errors or closes uncleanly. */\nexport class WebSocketError extends Data.TaggedError(\"WebSocketError\")<{\n readonly url: string;\n readonly code?: number;\n readonly reason?: string;\n}> {}\n\n/** The subset of the `WebSocket` constructor this module relies on. */\nexport type WebSocketConstructor = new (\n url: string,\n protocols?: string | ReadonlyArray<string>,\n) => WebSocket;\n\n/** Options for {@link fromWebSocket}. */\nexport interface FromWebSocketOptions {\n /** Sub-protocols passed to the `WebSocket` constructor. */\n readonly protocols?: string | ReadonlyArray<string>;\n /** `arraybuffer` or `blob` for binary frames. Defaults to the platform default. */\n readonly binaryType?: BinaryType;\n /** Buffer size for the backing queue. Defaults to 16. */\n readonly bufferSize?: number;\n /** Backpressure strategy when the buffer is full. Defaults to `\"suspend\"`. */\n readonly strategy?: \"sliding\" | \"dropping\" | \"suspend\";\n /** Override the `WebSocket` constructor (Node polyfill, tests). */\n readonly implementation?: WebSocketConstructor;\n}\n\n/**\n * Consume a `WebSocket` as a `Stream` of its message payloads\n * (`string | ArrayBuffer | Blob`, depending on `binaryType`).\n *\n * The stream ends when the socket closes cleanly and fails with\n * {@link WebSocketError} on an error or unclean close. The socket is closed when\n * the stream's scope closes.\n *\n * @example\n * ```ts\n * import { Realtime } from \"@thomasfosterau/effect-sveltekit/client\";\n * import { Effect, Stream } from \"effect\";\n *\n * const program = Realtime.fromWebSocket(\"wss://example.com/ws\").pipe(\n * Stream.runForEach((data) => Effect.log(String(data))),\n * );\n * ```\n */\nexport const fromWebSocket = (\n url: string,\n options?: FromWebSocketOptions,\n): Stream.Stream<string | ArrayBuffer | Blob, WebSocketError> => {\n const Impl = options?.implementation ?? (globalThis.WebSocket as WebSocketConstructor);\n return Stream.callback<string | ArrayBuffer | Blob, WebSocketError>(\n (queue) =>\n Effect.sync(() => {\n const socket = new Impl(url, options?.protocols);\n if (options?.binaryType !== undefined) socket.binaryType = options.binaryType;\n\n socket.addEventListener(\"message\", (e: MessageEvent) => {\n Queue.offerUnsafe(queue, e.data);\n });\n\n socket.addEventListener(\"error\", () => {\n Queue.failCauseUnsafe(queue, Cause.fail(new WebSocketError({ url })));\n });\n\n socket.addEventListener(\"close\", (e: CloseEvent) => {\n if (e.wasClean) {\n Queue.endUnsafe(queue);\n } else {\n Queue.failCauseUnsafe(\n queue,\n Cause.fail(new WebSocketError({ url, code: e.code, reason: e.reason })),\n );\n }\n });\n\n return Effect.sync(() => {\n // Only close a socket that is still connecting/open.\n if (socket.readyState === 0 || socket.readyState === 1) socket.close();\n });\n }),\n { bufferSize: options?.bufferSize ?? 16, strategy: options?.strategy ?? \"suspend\" },\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4BA,IAAa,mBAAb,cAAsC,KAAK,YAAY,kBAAkB,CAAC,CAEvE,CAAC;;;;;;;;;;;;;;;;;;AA2CJ,MAAa,mBACX,KACA,YACgD;CAChD,MAAM,OAAO,SAAS,kBAAmB,WAAW;CACpD,OAAO,OAAO,UACX,UACC,OAAO,WAAW;EAChB,MAAM,SACJ,SAAS,oBAAoB,KAAA,IACzB,IAAI,KAAK,GAAG,IACZ,IAAI,KAAK,KAAK,EAAE,iBAAiB,QAAQ,gBAAgB,CAAC;EAEhE,MAAM,aAAa,MAA0B;GAC3C,MAAM,YAAY,OAAO;IAAE,OAAO,EAAE;IAAM,MAAM,EAAE;IAAM,aAAa,EAAE;GAAY,CAAC;EACtF;EAEA,OAAO,iBAAiB,WAAW,SAAS;EAC5C,KAAK,MAAM,QAAQ,SAAS,UAAU,CAAC,GAAG,OAAO,iBAAiB,MAAM,SAAS;EAEjF,OAAO,iBAAiB,eAAe;GAErC,IAAI,OAAO,eAAe,GACxB,MAAM,gBAAgB,OAAO,MAAM,KAAK,IAAI,iBAAiB,EAAE,IAAI,CAAC,CAAC,CAAC;EAE1E,CAAC;EAED,OAAO,OAAO,WAAW,OAAO,MAAM,CAAC;CACzC,CAAC,GACH;EAAE,YAAY,SAAS,cAAc;EAAI,UAAU,SAAS,YAAY;CAAU,CACpF;AACF;;AAOA,IAAa,iBAAb,cAAoC,KAAK,YAAY,gBAAgB,CAAC,CAInE,CAAC;;;;;;;;;;;;;;;;;;;AAwCJ,MAAa,iBACX,KACA,YAC+D;CAC/D,MAAM,OAAO,SAAS,kBAAmB,WAAW;CACpD,OAAO,OAAO,UACX,UACC,OAAO,WAAW;EAChB,MAAM,SAAS,IAAI,KAAK,KAAK,SAAS,SAAS;EAC/C,IAAI,SAAS,eAAe,KAAA,GAAW,OAAO,aAAa,QAAQ;EAEnE,OAAO,iBAAiB,YAAY,MAAoB;GACtD,MAAM,YAAY,OAAO,EAAE,IAAI;EACjC,CAAC;EAED,OAAO,iBAAiB,eAAe;GACrC,MAAM,gBAAgB,OAAO,MAAM,KAAK,IAAI,eAAe,EAAE,IAAI,CAAC,CAAC,CAAC;EACtE,CAAC;EAED,OAAO,iBAAiB,UAAU,MAAkB;GAClD,IAAI,EAAE,UACJ,MAAM,UAAU,KAAK;QAErB,MAAM,gBACJ,OACA,MAAM,KAAK,IAAI,eAAe;IAAE;IAAK,MAAM,EAAE;IAAM,QAAQ,EAAE;GAAO,CAAC,CAAC,CACxE;EAEJ,CAAC;EAED,OAAO,OAAO,WAAW;GAEvB,IAAI,OAAO,eAAe,KAAK,OAAO,eAAe,GAAG,OAAO,MAAM;EACvE,CAAC;CACH,CAAC,GACH;EAAE,YAAY,SAAS,cAAc;EAAI,UAAU,SAAS,YAAY;CAAU,CACpF;AACF"}
|
|
@@ -14,28 +14,33 @@ interface QueryFn<Env> {
|
|
|
14
14
|
<Sch extends StandardSchemaV1, Output>(schema: Sch, fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;
|
|
15
15
|
<S extends Schema.Top, Output>(schema: S, fn: (arg: S["Type"]) => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<S["Encoded"], Output>;
|
|
16
16
|
}
|
|
17
|
+
/** The public shape of `query.batch`, parameterised by its handler env. */
|
|
17
18
|
interface QueryBatchFn<Env> {
|
|
18
19
|
<Input, Output>(validate: "unchecked", fn: (args: Input[]) => Effect.Effect<(arg: Input, idx: number) => Output, unknown, Env>): RemoteQueryFunction<Input, Output>;
|
|
19
20
|
<Sch extends StandardSchemaV1, Output>(schema: Sch, fn: (args: StandardSchemaV1.InferOutput<Sch>[]) => Effect.Effect<(arg: StandardSchemaV1.InferOutput<Sch>, idx: number) => Output, unknown, Env>): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;
|
|
20
21
|
<S extends Schema.Top, Output>(schema: S, fn: (args: S["Type"][]) => Effect.Effect<(arg: S["Type"], idx: number) => Output, unknown, Env>): RemoteQueryFunction<S["Encoded"], Output>;
|
|
21
22
|
}
|
|
23
|
+
/** The public shape of `query.live`, parameterised by its handler env. */
|
|
22
24
|
interface QueryLiveFn<Env> {
|
|
23
25
|
<Input, Output>(validate: "unchecked", fn: (arg: Input) => Effect.Effect<AsyncIterable<Output>, unknown, Env>): RemoteQueryFunction<Input, Output>;
|
|
24
26
|
<Sch extends StandardSchemaV1, Output>(schema: Sch, fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<AsyncIterable<Output>, unknown, Env>): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;
|
|
25
27
|
<S extends Schema.Top, Output>(schema: S, fn: (arg: S["Type"]) => Effect.Effect<AsyncIterable<Output>, unknown, Env>): RemoteQueryFunction<S["Encoded"], Output>;
|
|
26
28
|
}
|
|
29
|
+
/** The public shape of the `form` helper, parameterised by its handler env. */
|
|
27
30
|
interface FormFn<Env> {
|
|
28
31
|
<Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteForm<void, Output>;
|
|
29
32
|
<Input, Output>(validate: "unchecked", fn: (data: Input, issue: InvalidField<Input & RemoteFormInput>) => Effect.Effect<Output, unknown, Env>): RemoteForm<Input & RemoteFormInput, Output>;
|
|
30
33
|
<Sch extends StandardSchemaV1, Output>(validate: Sch, fn: (data: StandardSchemaV1.InferOutput<Sch>, issue: InvalidField<StandardSchemaV1.InferInput<Sch> & RemoteFormInput>) => Effect.Effect<Output, unknown, Env>): RemoteForm<StandardSchemaV1.InferInput<Sch> & RemoteFormInput, Output>;
|
|
31
34
|
<S extends Schema.Top, Output>(validate: S, fn: (data: S["Type"], issue: InvalidField<S["Encoded"] & RemoteFormInput>) => Effect.Effect<Output, unknown, Env>): RemoteForm<S["Encoded"] & RemoteFormInput, Output>;
|
|
32
35
|
}
|
|
36
|
+
/** The public shape of the `command` helper, parameterised by its handler env. */
|
|
33
37
|
interface CommandFn<Env> {
|
|
34
38
|
<Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteCommand<void, Output>;
|
|
35
39
|
<Input, Output>(validate: "unchecked", fn: (arg: Input) => Effect.Effect<Output, unknown, Env>): RemoteCommand<Input, Output>;
|
|
36
40
|
<Sch extends StandardSchemaV1, Output>(validate: Sch, fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>): RemoteCommand<StandardSchemaV1.InferInput<Sch>, Output>;
|
|
37
41
|
<S extends Schema.Top, Output>(validate: S, fn: (arg: S["Type"]) => Effect.Effect<Output, unknown, Env>): RemoteCommand<S["Encoded"], Output>;
|
|
38
42
|
}
|
|
43
|
+
/** The public shape of the `prerender` helper, parameterised by its handler env. */
|
|
39
44
|
interface PrerenderFn<Env> {
|
|
40
45
|
<Output>(fn: () => Effect.Effect<Output, unknown, Env>, options?: {
|
|
41
46
|
inputs?: () => Promise<void[]> | void[];
|
|
@@ -54,6 +59,10 @@ interface PrerenderFn<Env> {
|
|
|
54
59
|
dynamic?: boolean;
|
|
55
60
|
}): RemotePrerenderFunction<S["Encoded"], Output>;
|
|
56
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* SvelteKit's `requested` from `$app/server`, passed through. Throws when the
|
|
64
|
+
* installed SvelteKit version does not provide it.
|
|
65
|
+
*/
|
|
57
66
|
declare function requested<Result = unknown>(...args: unknown[]): Result;
|
|
58
67
|
/** A `query` with its `batch` / `live` helpers attached. */
|
|
59
68
|
type QueryNamespace<Env> = QueryFn<Env> & {
|
|
@@ -106,6 +115,11 @@ declare function withRuntime<R>(provider: RequestRuntimeProvider<R, any>, opts?:
|
|
|
106
115
|
translateError?: ErrorTranslator;
|
|
107
116
|
formError?: FormErrorMapper;
|
|
108
117
|
}): RemoteFunctions<R>;
|
|
118
|
+
/**
|
|
119
|
+
* The remote-function helpers (`query`, `form`, `command`, `prerender`,
|
|
120
|
+
* `requested`) bound to the default per-request runtime; `withRuntime` binds
|
|
121
|
+
* them to your own services.
|
|
122
|
+
*/
|
|
109
123
|
declare const RemoteFunction: {
|
|
110
124
|
query: QueryNamespace<SvelteKitEffectEnv>;
|
|
111
125
|
form: FormFn<SvelteKitEffectEnv>;
|
package/dist/remote-functions.js
CHANGED
|
@@ -74,6 +74,10 @@ const makePrerender = (run) => ((arg1, arg2, arg3) => {
|
|
|
74
74
|
if (typeof arg2 !== "function") throw new Error("prerender requires a handler function when a schema is provided");
|
|
75
75
|
return AppServer.prerender(toStandardValidator(arg1), (arg) => run(arg2(arg)), arg3);
|
|
76
76
|
});
|
|
77
|
+
/**
|
|
78
|
+
* SvelteKit's `requested` from `$app/server`, passed through. Throws when the
|
|
79
|
+
* installed SvelteKit version does not provide it.
|
|
80
|
+
*/
|
|
77
81
|
function requested(...args) {
|
|
78
82
|
return getRequested()(...args);
|
|
79
83
|
}
|
|
@@ -81,11 +85,45 @@ const makeQueryNamespace = (run) => Object.assign(makeQuery(run), {
|
|
|
81
85
|
batch: makeQueryBatch(run),
|
|
82
86
|
live: makeQueryLive(run)
|
|
83
87
|
});
|
|
88
|
+
/**
|
|
89
|
+
* SvelteKit's remote `query` with an Effect handler. Accepts `"unchecked"`, a
|
|
90
|
+
* Standard Schema or a bare Effect `Schema` as the validator; `query.batch`
|
|
91
|
+
* and `query.live` are attached.
|
|
92
|
+
*
|
|
93
|
+
* Handlers run on the default per-request runtime (`HttpClient` plus
|
|
94
|
+
* `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /
|
|
95
|
+
* `redirect()` becomes a 500. Use {@link withRuntime} to run on your own
|
|
96
|
+
* services.
|
|
97
|
+
*/
|
|
84
98
|
const query = makeQueryNamespace(defaultHelpers.run);
|
|
85
99
|
query.batch;
|
|
86
100
|
query.live;
|
|
101
|
+
/**
|
|
102
|
+
* SvelteKit's remote `form` with an Effect handler.
|
|
103
|
+
*
|
|
104
|
+
* Handlers run on the default per-request runtime (`HttpClient` plus
|
|
105
|
+
* `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /
|
|
106
|
+
* `redirect()` becomes a 500. Use {@link withRuntime} to run on your own
|
|
107
|
+
* services.
|
|
108
|
+
*/
|
|
87
109
|
const form = makeForm(defaultHelpers);
|
|
110
|
+
/**
|
|
111
|
+
* SvelteKit's remote `command` with an Effect handler.
|
|
112
|
+
*
|
|
113
|
+
* Handlers run on the default per-request runtime (`HttpClient` plus
|
|
114
|
+
* `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /
|
|
115
|
+
* `redirect()` becomes a 500. Use {@link withRuntime} to run on your own
|
|
116
|
+
* services.
|
|
117
|
+
*/
|
|
88
118
|
const command = makeCommand(defaultHelpers.run);
|
|
119
|
+
/**
|
|
120
|
+
* SvelteKit's remote `prerender` with an Effect handler.
|
|
121
|
+
*
|
|
122
|
+
* Handlers run on the default per-request runtime (`HttpClient` plus
|
|
123
|
+
* `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /
|
|
124
|
+
* `redirect()` becomes a 500. Use {@link withRuntime} to run on your own
|
|
125
|
+
* services.
|
|
126
|
+
*/
|
|
89
127
|
const prerender = makePrerender(defaultHelpers.run);
|
|
90
128
|
/**
|
|
91
129
|
* Bind the remote-function helpers to a {@link RequestRuntimeProvider} (and an
|
|
@@ -131,6 +169,11 @@ function withRuntime(provider, opts) {
|
|
|
131
169
|
prerender: makePrerender(helpers.run)
|
|
132
170
|
};
|
|
133
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* The remote-function helpers (`query`, `form`, `command`, `prerender`,
|
|
174
|
+
* `requested`) bound to the default per-request runtime; `withRuntime` binds
|
|
175
|
+
* them to your own services.
|
|
176
|
+
*/
|
|
134
177
|
const RemoteFunction = {
|
|
135
178
|
query,
|
|
136
179
|
form,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-functions.js","names":["isBareHandlerValue"],"sources":["../src/remote-functions.ts"],"sourcesContent":["// NOTE: This module builds the `query` / `form` / `command` / `prerender`\n// *factories* that consumers call inside their own `*.remote.ts` files — it is\n// NOT itself a SvelteKit remote module, and its exports (e.g. `RemoteFunction`)\n// are deliberately not remote functions. Its filename must therefore never match\n// SvelteKit's remote-module matcher `/[/.]remote(\\.[^/]+)+$/`\n// (kit `src/exports/vite/utils.js`), or kit's build-time validation will reject\n// the built file with \"all exports from this file must be remote functions\".\n// That is why this is `remote-functions.ts` and never `remote.server.ts` /\n// `*.remote.ts`. See `tests/remote-module-naming.test.ts` for the guard.\nimport * as AppServer from \"$app/server\";\nimport type {\n RemoteCommand,\n RemoteForm,\n RemoteFormInput,\n RemotePrerenderFunction,\n RemoteQueryFunction,\n InvalidField,\n} from \"@sveltejs/kit\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport * as HttpClient from \"effect/http/HttpClient\";\nimport { Effect, type Schema } from \"effect\";\nimport { SvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nimport {\n isBareHandler as isBareHandlerValue,\n toStandardValidator,\n} from \"./internal/remote-validators.js\";\nimport {\n defaultTranslate,\n makeRunHelpers,\n type FormErrorMapper,\n type RunHelpers,\n type Runner,\n} from \"./internal/remote-run.js\";\nimport type { ErrorTranslator, RequestRuntimeProvider } from \"./request-runtime.js\";\n\nexport type { FormErrorMapper } from \"./internal/remote-run.js\";\n\ntype SvelteKitEffectEnv = HttpClient.HttpClient | SvelteKitRequestEvent;\n\n/** The default helpers: a fresh SvelteKit per-request runtime, passthrough translation. */\nconst defaultHelpers: RunHelpers = makeRunHelpers(() => {\n const event = AppServer.getRequestEvent();\n return { runExit: (effect) => SvelteKitRuntime.forRequest(event).runPromiseExit(effect) };\n}, defaultTranslate);\n\n/** Build helpers from a {@link RequestRuntimeProvider}, translator, and form-error mapper. */\nconst helpersFromProvider = (\n provider: RequestRuntimeProvider<any, any>,\n translate: ErrorTranslator,\n formError?: FormErrorMapper,\n): RunHelpers =>\n makeRunHelpers(\n () => {\n const event = AppServer.getRequestEvent();\n return {\n runExit: (effect) => provider.run(event).runPromiseExit(effect),\n dispose: () => provider.disposeAfterResponse?.(event),\n };\n },\n translate,\n formError,\n );\n\nfunction unavailable(name: string): never {\n throw new Error(\n `${name} is not available in the installed SvelteKit version. Update @sveltejs/kit to use this API.`,\n );\n}\n\ntype EffectHandler<Env> = (arg: unknown) => Effect.Effect<unknown, unknown, Env>;\ntype EffectFormHandler<Env> = (\n data: unknown,\n issue: unknown,\n) => Effect.Effect<unknown, unknown, Env>;\ntype EffectVoidHandler<Env> = () => Effect.Effect<unknown, unknown, Env>;\n\n/** True when the argument is the no-validation handler overload (typed narrowing). */\nconst isBareHandler = <Env>(value: unknown): value is EffectVoidHandler<Env> =>\n isBareHandlerValue(value);\n\n/** The public shape of the `query` helper, parameterised by its handler env. */\nexport interface QueryFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQuery = <Env>(run: Runner): QueryFn<Env> =>\n ((arg1: unknown, arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, Env>): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.query(() => run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"query requires a handler function when a schema is provided\");\n }\n\n return AppServer.query(toStandardValidator(arg1) as \"unchecked\", (arg: unknown) =>\n run(arg2(arg)),\n );\n }) as QueryFn<Env>;\n\nexport interface QueryBatchFn<Env> {\n <Input, Output>(\n validate: \"unchecked\",\n fn: (args: Input[]) => Effect.Effect<(arg: Input, idx: number) => Output, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (\n args: StandardSchemaV1.InferOutput<Sch>[],\n ) => Effect.Effect<\n (arg: StandardSchemaV1.InferOutput<Sch>, idx: number) => Output,\n unknown,\n Env\n >,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (args: S[\"Type\"][]) => Effect.Effect<(arg: S[\"Type\"], idx: number) => Output, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQueryBatch = <Env>(run: Runner): QueryBatchFn<Env> =>\n ((\n validate: unknown,\n fn: (args: unknown[]) => Effect.Effect<(arg: unknown, idx: number) => unknown, unknown, Env>,\n ): any =>\n AppServer.query.batch(toStandardValidator(validate) as \"unchecked\", (args: unknown[]) =>\n run(fn(args)),\n )) as QueryBatchFn<Env>;\n\ntype QueryLiveFactory = (\n validate: unknown,\n fn: (arg: unknown) => Promise<AsyncIterable<unknown>> | AsyncIterable<unknown>,\n) => unknown;\n\ntype QueryWithCompat = typeof AppServer.query & {\n live?: QueryLiveFactory;\n};\n\ntype AppServerWithCompat = typeof AppServer & {\n requested?: (...args: unknown[]) => unknown;\n};\n\nconst appServerCompat = AppServer as AppServerWithCompat;\nconst queryCompat = AppServer.query as QueryWithCompat;\n\nfunction getQueryLive(): QueryLiveFactory {\n const live = queryCompat.live;\n\n if (typeof live !== \"function\") {\n unavailable(\"query.live\");\n }\n\n return live;\n}\n\nfunction getRequested(): (...args: unknown[]) => unknown {\n const requestedFn = appServerCompat.requested;\n\n if (typeof requestedFn !== \"function\") {\n unavailable(\"requested\");\n }\n\n return requestedFn;\n}\n\nexport interface QueryLiveFn<Env> {\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (\n arg: StandardSchemaV1.InferOutput<Sch>,\n ) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQueryLive = <Env>(run: Runner): QueryLiveFn<Env> =>\n ((\n validate: unknown,\n fn: (arg: unknown) => Effect.Effect<AsyncIterable<unknown>, unknown, Env>,\n ): any => {\n const live = getQueryLive();\n return live(toStandardValidator(validate), (arg) => run(fn(arg)));\n }) as QueryLiveFn<Env>;\n\nexport interface FormFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteForm<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (\n data: Input,\n issue: InvalidField<Input & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<Input & RemoteFormInput, Output>;\n <Sch extends StandardSchemaV1, Output>(\n validate: Sch,\n fn: (\n data: StandardSchemaV1.InferOutput<Sch>,\n issue: InvalidField<StandardSchemaV1.InferInput<Sch> & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<StandardSchemaV1.InferInput<Sch> & RemoteFormInput, Output>;\n <S extends Schema.Top, Output>(\n validate: S,\n fn: (\n data: S[\"Type\"],\n issue: InvalidField<S[\"Encoded\"] & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<S[\"Encoded\"] & RemoteFormInput, Output>;\n}\n\nconst makeForm = <Env>(helpers: RunHelpers): FormFn<Env> =>\n ((arg1: unknown, arg2?: unknown): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.form(() => helpers.run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"form requires a handler function when a schema is provided\");\n }\n\n return AppServer.form(\n toStandardValidator(arg1) as \"unchecked\",\n (data: unknown, issue: unknown) =>\n helpers.runForm((arg2 as EffectFormHandler<Env>)(data, issue), {\n issue: issue as InvalidField<any>,\n data,\n }),\n );\n }) as FormFn<Env>;\n\nexport interface CommandFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteCommand<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n validate: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n validate: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<S[\"Encoded\"], Output>;\n}\n\nconst makeCommand = <Env>(run: Runner): CommandFn<Env> =>\n ((arg1: unknown, arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, Env>): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.command(() => run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"command requires a handler function when a schema is provided\");\n }\n\n return AppServer.command(toStandardValidator(arg1) as \"unchecked\", (arg: unknown) =>\n run(arg2(arg)),\n );\n }) as CommandFn<Env>;\n\nexport interface PrerenderFn<Env> {\n <Output>(\n fn: () => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<void[]> | void[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<Input[]> | Input[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () =>\n | Promise<StandardSchemaV1.InferInput<Sch>[]>\n | StandardSchemaV1.InferInput<Sch>[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<S[\"Encoded\"][]> | S[\"Encoded\"][];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<S[\"Encoded\"], Output>;\n}\n\nconst makePrerender = <Env>(run: Runner): PrerenderFn<Env> =>\n ((\n arg1: unknown,\n arg2?:\n | ((arg: unknown) => Effect.Effect<unknown, unknown, Env>)\n | {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n arg3?: {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n ): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.prerender(\n () => run(handler()),\n arg2 as { inputs?: () => Promise<void[]> | void[]; dynamic?: boolean } | undefined,\n );\n }\n\n if (typeof arg2 !== \"function\") {\n throw new Error(\"prerender requires a handler function when a schema is provided\");\n }\n\n return AppServer.prerender(\n toStandardValidator(arg1) as \"unchecked\",\n (arg: unknown) => run((arg2 as EffectHandler<Env>)(arg)),\n arg3,\n );\n }) as PrerenderFn<Env>;\n\nexport function requested<Result = unknown>(...args: unknown[]): Result {\n const requestedFn = getRequested();\n return requestedFn(...args) as Result;\n}\n\n/** A `query` with its `batch` / `live` helpers attached. */\nexport type QueryNamespace<Env> = QueryFn<Env> & {\n batch: QueryBatchFn<Env>;\n live: QueryLiveFn<Env>;\n};\n\nconst makeQueryNamespace = <Env>(run: Runner): QueryNamespace<Env> =>\n Object.assign(makeQuery<Env>(run), {\n batch: makeQueryBatch<Env>(run),\n live: makeQueryLive<Env>(run),\n }) as QueryNamespace<Env>;\n\n// ── Standalone exports (default SvelteKit per-request runtime) ──────────────\n\nexport const query: QueryNamespace<SvelteKitEffectEnv> = makeQueryNamespace(defaultHelpers.run);\nexport const queryBatch: QueryBatchFn<SvelteKitEffectEnv> = query.batch;\nexport const queryLive: QueryLiveFn<SvelteKitEffectEnv> = query.live;\nexport const form: FormFn<SvelteKitEffectEnv> = makeForm(defaultHelpers);\nexport const command: CommandFn<SvelteKitEffectEnv> = makeCommand(defaultHelpers.run);\nexport const prerender: PrerenderFn<SvelteKitEffectEnv> = makePrerender(defaultHelpers.run);\n\n/** The set of remote-function helpers bound to a particular runner. */\nexport interface RemoteFunctions<Env> {\n readonly query: QueryNamespace<Env>;\n readonly form: FormFn<Env>;\n readonly command: CommandFn<Env>;\n readonly prerender: PrerenderFn<Env>;\n}\n\n/**\n * Bind the remote-function helpers to a {@link RequestRuntimeProvider} (and an\n * optional boundary error translator), so their handlers run on the provider's\n * services `Env` — your own application catalogue — instead of the fixed\n * {@link SvelteKitServices}. The Standard-Schema / `\"unchecked\"` validation\n * argument and the native return types (`RemoteQueryFunction`, `RemoteForm`,\n * ...) are exactly as the standalone helpers.\n *\n * For remote forms, the failure path runs `opts.formError` when provided —\n * mapping a structured/nested error shape into SvelteKit's field issues (via the\n * `issue` helper / `invalid()`) so a domain validation failure drives\n * field-level invalidation rather than a generic failure. Otherwise the form\n * falls back to `translateError`.\n *\n * @example\n * ```ts\n * const { query, form, command } = RemoteFunction.withRuntime(\n * RequestRuntime.fromLocals((event) => event.locals.runtime),\n * {\n * translateError: toSvelteKit,\n * formError: (error, { issue }) => {\n * if (error instanceof ValidationFailure) issue.field(error.field, error.message);\n * invalid();\n * },\n * },\n * );\n *\n * export const items = query(() =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return yield* db.list();\n * }),\n * );\n * ```\n */\nexport function withRuntime<R>(\n provider: RequestRuntimeProvider<R, any>,\n opts?: { translateError?: ErrorTranslator; formError?: FormErrorMapper },\n): RemoteFunctions<R> {\n const helpers = helpersFromProvider(\n provider,\n opts?.translateError ?? defaultTranslate,\n opts?.formError,\n );\n return {\n query: makeQueryNamespace<R>(helpers.run),\n form: makeForm<R>(helpers),\n command: makeCommand<R>(helpers.run),\n prerender: makePrerender<R>(helpers.run),\n };\n}\n\nexport const RemoteFunction = {\n query,\n form,\n command,\n prerender,\n requested,\n withRuntime,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"remote-functions.js","names":["isBareHandlerValue"],"sources":["../src/remote-functions.ts"],"sourcesContent":["// NOTE: This module builds the `query` / `form` / `command` / `prerender`\n// *factories* that consumers call inside their own `*.remote.ts` files — it is\n// NOT itself a SvelteKit remote module, and its exports (e.g. `RemoteFunction`)\n// are deliberately not remote functions. Its filename must therefore never match\n// SvelteKit's remote-module matcher `/[/.]remote(\\.[^/]+)+$/`\n// (kit `src/exports/vite/utils.js`), or kit's build-time validation will reject\n// the built file with \"all exports from this file must be remote functions\".\n// That is why this is `remote-functions.ts` and never `remote.server.ts` /\n// `*.remote.ts`. See `tests/remote-module-naming.test.ts` for the guard.\nimport * as AppServer from \"$app/server\";\nimport type {\n RemoteCommand,\n RemoteForm,\n RemoteFormInput,\n RemotePrerenderFunction,\n RemoteQueryFunction,\n InvalidField,\n} from \"@sveltejs/kit\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport * as HttpClient from \"effect/http/HttpClient\";\nimport { Effect, type Schema } from \"effect\";\nimport { SvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nimport {\n isBareHandler as isBareHandlerValue,\n toStandardValidator,\n} from \"./internal/remote-validators.js\";\nimport {\n defaultTranslate,\n makeRunHelpers,\n type FormErrorMapper,\n type RunHelpers,\n type Runner,\n} from \"./internal/remote-run.js\";\nimport type { ErrorTranslator, RequestRuntimeProvider } from \"./request-runtime.js\";\n\nexport type { FormErrorMapper } from \"./internal/remote-run.js\";\n\ntype SvelteKitEffectEnv = HttpClient.HttpClient | SvelteKitRequestEvent;\n\n/** The default helpers: a fresh SvelteKit per-request runtime, passthrough translation. */\nconst defaultHelpers: RunHelpers = makeRunHelpers(() => {\n const event = AppServer.getRequestEvent();\n return { runExit: (effect) => SvelteKitRuntime.forRequest(event).runPromiseExit(effect) };\n}, defaultTranslate);\n\n/** Build helpers from a {@link RequestRuntimeProvider}, translator, and form-error mapper. */\nconst helpersFromProvider = (\n provider: RequestRuntimeProvider<any, any>,\n translate: ErrorTranslator,\n formError?: FormErrorMapper,\n): RunHelpers =>\n makeRunHelpers(\n () => {\n const event = AppServer.getRequestEvent();\n return {\n runExit: (effect) => provider.run(event).runPromiseExit(effect),\n dispose: () => provider.disposeAfterResponse?.(event),\n };\n },\n translate,\n formError,\n );\n\nfunction unavailable(name: string): never {\n throw new Error(\n `${name} is not available in the installed SvelteKit version. Update @sveltejs/kit to use this API.`,\n );\n}\n\ntype EffectHandler<Env> = (arg: unknown) => Effect.Effect<unknown, unknown, Env>;\ntype EffectFormHandler<Env> = (\n data: unknown,\n issue: unknown,\n) => Effect.Effect<unknown, unknown, Env>;\ntype EffectVoidHandler<Env> = () => Effect.Effect<unknown, unknown, Env>;\n\n/** True when the argument is the no-validation handler overload (typed narrowing). */\nconst isBareHandler = <Env>(value: unknown): value is EffectVoidHandler<Env> =>\n isBareHandlerValue(value);\n\n/** The public shape of the `query` helper, parameterised by its handler env. */\nexport interface QueryFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQuery = <Env>(run: Runner): QueryFn<Env> =>\n ((arg1: unknown, arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, Env>): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.query(() => run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"query requires a handler function when a schema is provided\");\n }\n\n return AppServer.query(toStandardValidator(arg1) as \"unchecked\", (arg: unknown) =>\n run(arg2(arg)),\n );\n }) as QueryFn<Env>;\n\n/** The public shape of `query.batch`, parameterised by its handler env. */\nexport interface QueryBatchFn<Env> {\n <Input, Output>(\n validate: \"unchecked\",\n fn: (args: Input[]) => Effect.Effect<(arg: Input, idx: number) => Output, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (\n args: StandardSchemaV1.InferOutput<Sch>[],\n ) => Effect.Effect<\n (arg: StandardSchemaV1.InferOutput<Sch>, idx: number) => Output,\n unknown,\n Env\n >,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (args: S[\"Type\"][]) => Effect.Effect<(arg: S[\"Type\"], idx: number) => Output, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQueryBatch = <Env>(run: Runner): QueryBatchFn<Env> =>\n ((\n validate: unknown,\n fn: (args: unknown[]) => Effect.Effect<(arg: unknown, idx: number) => unknown, unknown, Env>,\n ): any =>\n AppServer.query.batch(toStandardValidator(validate) as \"unchecked\", (args: unknown[]) =>\n run(fn(args)),\n )) as QueryBatchFn<Env>;\n\ntype QueryLiveFactory = (\n validate: unknown,\n fn: (arg: unknown) => Promise<AsyncIterable<unknown>> | AsyncIterable<unknown>,\n) => unknown;\n\ntype QueryWithCompat = typeof AppServer.query & {\n live?: QueryLiveFactory;\n};\n\ntype AppServerWithCompat = typeof AppServer & {\n requested?: (...args: unknown[]) => unknown;\n};\n\nconst appServerCompat = AppServer as AppServerWithCompat;\nconst queryCompat = AppServer.query as QueryWithCompat;\n\nfunction getQueryLive(): QueryLiveFactory {\n const live = queryCompat.live;\n\n if (typeof live !== \"function\") {\n unavailable(\"query.live\");\n }\n\n return live;\n}\n\nfunction getRequested(): (...args: unknown[]) => unknown {\n const requestedFn = appServerCompat.requested;\n\n if (typeof requestedFn !== \"function\") {\n unavailable(\"requested\");\n }\n\n return requestedFn;\n}\n\n/** The public shape of `query.live`, parameterised by its handler env. */\nexport interface QueryLiveFn<Env> {\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (\n arg: StandardSchemaV1.InferOutput<Sch>,\n ) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQueryLive = <Env>(run: Runner): QueryLiveFn<Env> =>\n ((\n validate: unknown,\n fn: (arg: unknown) => Effect.Effect<AsyncIterable<unknown>, unknown, Env>,\n ): any => {\n const live = getQueryLive();\n return live(toStandardValidator(validate), (arg) => run(fn(arg)));\n }) as QueryLiveFn<Env>;\n\n/** The public shape of the `form` helper, parameterised by its handler env. */\nexport interface FormFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteForm<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (\n data: Input,\n issue: InvalidField<Input & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<Input & RemoteFormInput, Output>;\n <Sch extends StandardSchemaV1, Output>(\n validate: Sch,\n fn: (\n data: StandardSchemaV1.InferOutput<Sch>,\n issue: InvalidField<StandardSchemaV1.InferInput<Sch> & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<StandardSchemaV1.InferInput<Sch> & RemoteFormInput, Output>;\n <S extends Schema.Top, Output>(\n validate: S,\n fn: (\n data: S[\"Type\"],\n issue: InvalidField<S[\"Encoded\"] & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<S[\"Encoded\"] & RemoteFormInput, Output>;\n}\n\nconst makeForm = <Env>(helpers: RunHelpers): FormFn<Env> =>\n ((arg1: unknown, arg2?: unknown): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.form(() => helpers.run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"form requires a handler function when a schema is provided\");\n }\n\n return AppServer.form(\n toStandardValidator(arg1) as \"unchecked\",\n (data: unknown, issue: unknown) =>\n helpers.runForm((arg2 as EffectFormHandler<Env>)(data, issue), {\n issue: issue as InvalidField<any>,\n data,\n }),\n );\n }) as FormFn<Env>;\n\n/** The public shape of the `command` helper, parameterised by its handler env. */\nexport interface CommandFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteCommand<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n validate: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n validate: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<S[\"Encoded\"], Output>;\n}\n\nconst makeCommand = <Env>(run: Runner): CommandFn<Env> =>\n ((arg1: unknown, arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, Env>): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.command(() => run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"command requires a handler function when a schema is provided\");\n }\n\n return AppServer.command(toStandardValidator(arg1) as \"unchecked\", (arg: unknown) =>\n run(arg2(arg)),\n );\n }) as CommandFn<Env>;\n\n/** The public shape of the `prerender` helper, parameterised by its handler env. */\nexport interface PrerenderFn<Env> {\n <Output>(\n fn: () => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<void[]> | void[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<Input[]> | Input[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () =>\n | Promise<StandardSchemaV1.InferInput<Sch>[]>\n | StandardSchemaV1.InferInput<Sch>[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<S[\"Encoded\"][]> | S[\"Encoded\"][];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<S[\"Encoded\"], Output>;\n}\n\nconst makePrerender = <Env>(run: Runner): PrerenderFn<Env> =>\n ((\n arg1: unknown,\n arg2?:\n | ((arg: unknown) => Effect.Effect<unknown, unknown, Env>)\n | {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n arg3?: {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n ): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.prerender(\n () => run(handler()),\n arg2 as { inputs?: () => Promise<void[]> | void[]; dynamic?: boolean } | undefined,\n );\n }\n\n if (typeof arg2 !== \"function\") {\n throw new Error(\"prerender requires a handler function when a schema is provided\");\n }\n\n return AppServer.prerender(\n toStandardValidator(arg1) as \"unchecked\",\n (arg: unknown) => run((arg2 as EffectHandler<Env>)(arg)),\n arg3,\n );\n }) as PrerenderFn<Env>;\n\n/**\n * SvelteKit's `requested` from `$app/server`, passed through. Throws when the\n * installed SvelteKit version does not provide it.\n */\nexport function requested<Result = unknown>(...args: unknown[]): Result {\n const requestedFn = getRequested();\n return requestedFn(...args) as Result;\n}\n\n/** A `query` with its `batch` / `live` helpers attached. */\nexport type QueryNamespace<Env> = QueryFn<Env> & {\n batch: QueryBatchFn<Env>;\n live: QueryLiveFn<Env>;\n};\n\nconst makeQueryNamespace = <Env>(run: Runner): QueryNamespace<Env> =>\n Object.assign(makeQuery<Env>(run), {\n batch: makeQueryBatch<Env>(run),\n live: makeQueryLive<Env>(run),\n }) as QueryNamespace<Env>;\n\n// ── Standalone exports (default SvelteKit per-request runtime) ──────────────\n\n/**\n * SvelteKit's remote `query` with an Effect handler. Accepts `\"unchecked\"`, a\n * Standard Schema or a bare Effect `Schema` as the validator; `query.batch`\n * and `query.live` are attached.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const query: QueryNamespace<SvelteKitEffectEnv> = makeQueryNamespace(defaultHelpers.run);\n/**\n * SvelteKit's remote `query.batch` with an Effect handler (the same function\n * as `query.batch`).\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const queryBatch: QueryBatchFn<SvelteKitEffectEnv> = query.batch;\n/**\n * SvelteKit's remote `query.live` with an Effect handler that returns an\n * `AsyncIterable` (the same function as `query.live`).\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const queryLive: QueryLiveFn<SvelteKitEffectEnv> = query.live;\n/**\n * SvelteKit's remote `form` with an Effect handler.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const form: FormFn<SvelteKitEffectEnv> = makeForm(defaultHelpers);\n/**\n * SvelteKit's remote `command` with an Effect handler.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const command: CommandFn<SvelteKitEffectEnv> = makeCommand(defaultHelpers.run);\n/**\n * SvelteKit's remote `prerender` with an Effect handler.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const prerender: PrerenderFn<SvelteKitEffectEnv> = makePrerender(defaultHelpers.run);\n\n/** The set of remote-function helpers bound to a particular runner. */\nexport interface RemoteFunctions<Env> {\n readonly query: QueryNamespace<Env>;\n readonly form: FormFn<Env>;\n readonly command: CommandFn<Env>;\n readonly prerender: PrerenderFn<Env>;\n}\n\n/**\n * Bind the remote-function helpers to a {@link RequestRuntimeProvider} (and an\n * optional boundary error translator), so their handlers run on the provider's\n * services `Env` — your own application catalogue — instead of the fixed\n * {@link SvelteKitServices}. The Standard-Schema / `\"unchecked\"` validation\n * argument and the native return types (`RemoteQueryFunction`, `RemoteForm`,\n * ...) are exactly as the standalone helpers.\n *\n * For remote forms, the failure path runs `opts.formError` when provided —\n * mapping a structured/nested error shape into SvelteKit's field issues (via the\n * `issue` helper / `invalid()`) so a domain validation failure drives\n * field-level invalidation rather than a generic failure. Otherwise the form\n * falls back to `translateError`.\n *\n * @example\n * ```ts\n * const { query, form, command } = RemoteFunction.withRuntime(\n * RequestRuntime.fromLocals((event) => event.locals.runtime),\n * {\n * translateError: toSvelteKit,\n * formError: (error, { issue }) => {\n * if (error instanceof ValidationFailure) issue.field(error.field, error.message);\n * invalid();\n * },\n * },\n * );\n *\n * export const items = query(() =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return yield* db.list();\n * }),\n * );\n * ```\n */\nexport function withRuntime<R>(\n provider: RequestRuntimeProvider<R, any>,\n opts?: { translateError?: ErrorTranslator; formError?: FormErrorMapper },\n): RemoteFunctions<R> {\n const helpers = helpersFromProvider(\n provider,\n opts?.translateError ?? defaultTranslate,\n opts?.formError,\n );\n return {\n query: makeQueryNamespace<R>(helpers.run),\n form: makeForm<R>(helpers),\n command: makeCommand<R>(helpers.run),\n prerender: makePrerender<R>(helpers.run),\n };\n}\n\n/**\n * The remote-function helpers (`query`, `form`, `command`, `prerender`,\n * `requested`) bound to the default per-request runtime; `withRuntime` binds\n * them to your own services.\n */\nexport const RemoteFunction = {\n query,\n form,\n command,\n prerender,\n requested,\n withRuntime,\n};\n"],"mappings":"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"}
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Plugin } from "vite";
|
|
2
2
|
//#region src/vite.d.ts
|
|
3
|
+
/** The SvelteKit route and hook file kinds `effectKit()` can rewrite. */
|
|
3
4
|
type EffectKitFileKind = "pageServer" | "page" | "layoutServer" | "layout" | "server" | "hooks" | "hooksServer" | "hooksClient";
|
|
5
|
+
/** Options for {@link effectKit}. */
|
|
4
6
|
interface EffectKitPluginOptions {
|
|
5
7
|
/**
|
|
6
8
|
* Restrict transformation to a subset of SvelteKit file kinds.
|