@thomasfosterau/effect-sveltekit 0.1.0 → 0.1.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/dist/app-runtime.d.ts +6 -5
- package/dist/app-runtime.js.map +1 -1
- package/dist/internal/route-params.d.ts +23 -0
- package/dist/remote.server.d.ts +3 -5
- package/dist/remote.server.js.map +1 -1
- package/dist/request-runtime.d.ts +5 -4
- package/dist/request-runtime.js.map +1 -1
- package/dist/server.d.ts +5 -4
- package/dist/server.js.map +1 -1
- package/dist/universal.d.ts +2 -1
- package/dist/universal.js.map +1 -1
- package/package.json +1 -1
package/dist/app-runtime.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SvelteRouteParams } from "./internal/route-params.js";
|
|
1
2
|
import { SvelteKitRequestEvent } from "./runtime.js";
|
|
2
3
|
import { ErrorTranslator } from "./request-runtime.js";
|
|
3
4
|
import { Effect, Exit, Fiber, Layer, ManagedRuntime } from "effect";
|
|
@@ -59,15 +60,15 @@ interface ServerRuntime<R = never, E = never> {
|
|
|
59
60
|
/**
|
|
60
61
|
* Wraps an Effect-based function as a SvelteKit server `load`.
|
|
61
62
|
*/
|
|
62
|
-
readonly serverLoad: <Params extends
|
|
63
|
+
readonly serverLoad: <Params extends SvelteRouteParams = SvelteRouteParams, ParentData extends Record<string, any> = any, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>) => ServerLoad<Params, ParentData, OutputData, RouteId>;
|
|
63
64
|
/**
|
|
64
65
|
* Wraps an Effect-based function as a SvelteKit form action.
|
|
65
66
|
*/
|
|
66
|
-
readonly action: <Params extends
|
|
67
|
+
readonly action: <Params extends SvelteRouteParams = SvelteRouteParams, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>) => Action<Params, OutputData, RouteId>;
|
|
67
68
|
/**
|
|
68
69
|
* Wraps an object of Effect-based form actions.
|
|
69
70
|
*/
|
|
70
|
-
readonly actions: <Params extends
|
|
71
|
+
readonly actions: <Params extends SvelteRouteParams = SvelteRouteParams, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(actionsMap: Record<string, (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>>) => Actions<Params, OutputData, RouteId>;
|
|
71
72
|
/**
|
|
72
73
|
* Wraps an Effect-based function as a SvelteKit `handle` hook.
|
|
73
74
|
*/
|
|
@@ -114,7 +115,7 @@ interface ServerRuntime<R = never, E = never> {
|
|
|
114
115
|
* Wraps an Effect-based function as a SvelteKit `+server.ts` endpoint
|
|
115
116
|
* handler (`GET`, `POST`, ...).
|
|
116
117
|
*/
|
|
117
|
-
readonly endpoint: <Params extends
|
|
118
|
+
readonly endpoint: <Params extends SvelteRouteParams = SvelteRouteParams, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, R | SvelteKitServices>) => (event: RequestEvent<Params, RouteId>) => Promise<Response>;
|
|
118
119
|
/**
|
|
119
120
|
* Runs an effect for a specific request, providing both the application
|
|
120
121
|
* layer and the per-request services. Failures (other than SvelteKit
|
|
@@ -185,7 +186,7 @@ interface UniversalRuntime<R = never, E = never> {
|
|
|
185
186
|
* The effect gets `HttpClient` (backed by the load event's `fetch`) on both
|
|
186
187
|
* client and server, plus the services of the application layer.
|
|
187
188
|
*/
|
|
188
|
-
readonly load: <Params extends
|
|
189
|
+
readonly load: <Params extends SvelteRouteParams = SvelteRouteParams, InputData extends Record<string, unknown> | null = any, ParentData extends Record<string, unknown> = any, OutputData extends Record<string, unknown> | void = any, RouteId extends string | null = any>(fn: (event: Parameters<Load<Params, InputData, ParentData, OutputData, RouteId>>[0]) => Effect.Effect<OutputData, unknown, R | HttpClient.HttpClient>) => Load<Params, InputData, ParentData, OutputData, RouteId>;
|
|
189
190
|
/**
|
|
190
191
|
* Runs an effect with `HttpClient` backed by the given fetch implementation
|
|
191
192
|
* plus the application layer.
|
package/dist/app-runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-runtime.js","names":[],"sources":["../src/app-runtime.ts"],"sourcesContent":["/**\n * App-wide Effect runtimes for SvelteKit.\n *\n * `makeServerRuntime` / `makeUniversalRuntime` let an application define its\n * Effect services (database clients, API wrappers, config, ...) as a single\n * `Layer`, built once per server process and shared by every request, instead\n * of being rebuilt for each `load` / `action` / endpoint invocation.\n *\n * Per-request services — the {@link SvelteKitRequestEvent} and an `HttpClient`\n * backed by the event's `fetch` — are provided on top of the shared runtime\n * for each call, so SvelteKit's fetch behaviours (cookie forwarding, request\n * deduplication, relative URLs) keep working as expected.\n */\n\nimport type {\n Action,\n Actions,\n Handle,\n HandleFetch,\n HandleServerError,\n Load,\n RequestEvent,\n ServerInit,\n ServerLoad,\n} from \"@sveltejs/kit\";\nimport { Context, Effect, type Fiber, Exit, Layer, ManagedRuntime, Scope } from \"effect\";\nimport * as FetchHttpClient from \"effect/unstable/http/FetchHttpClient\";\nimport type * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport { SvelteKitRequestEvent } from \"./runtime.js\";\nimport { causeToThrowable } from \"./internal/error.js\";\nimport type { ErrorTranslator } from \"./request-runtime.js\";\n\n/**\n * The services provided to every wrapped effect on the server, in addition to\n * the application's own layer.\n */\nexport type SvelteKitServices = HttpClient.HttpClient | SvelteKitRequestEvent;\n\n/**\n * A per-request Effect runtime: the application layer's services plus the\n * per-request {@link SvelteKitServices} (an `HttpClient` backed by the request's\n * `fetch`, and the {@link SvelteKitRequestEvent}), with the request services\n * built once for the request and shared.\n *\n * A {@link ServerRuntime}'s `handle` hook creates one per request and stores it\n * on `event.locals` (`App.Locals`). Every other wrapper for the same runtime\n * (`serverLoad`, `action`, `endpoint`, ...) then runs on it, so a request's\n * effects share one `HttpClient` and one request scope. Retrieve it from an\n * event with `ServerRuntime.requestRuntime` to run your own effects.\n */\nexport interface RequestRuntime<R = never> {\n /** Run an effect for this request, returning its `Exit`. */\n readonly runPromiseExit: <A>(\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<Exit.Exit<A, unknown>>;\n /** Run an effect for this request, throwing on failure (passing SvelteKit control flow through). */\n readonly runPromise: <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Promise<A>;\n /** Fork an effect for this request, returning the running fiber. */\n readonly runFork: <A>(\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Fiber.Fiber<A, unknown>;\n}\n\n/**\n * Symbol under which a {@link ServerRuntime}'s `handle` stores the per-request\n * {@link RequestRuntime} on `event.locals`. `Symbol.for` so the key is stable\n * across module instances.\n */\nconst RequestRuntimeKey: unique symbol = Symbol.for(\n \"@thomasfosterau/effect-sveltekit/requestRuntime\",\n);\n\n/**\n * The locals entry: the per-request runtime tagged with the `ServerRuntime`\n * that created it, so a different runtime's wrappers ignore it (and fall back\n * to building request services themselves) rather than running on the wrong\n * application layer.\n */\ninterface RequestRuntimeEntry {\n readonly owner: object;\n readonly runtime: RequestRuntime<any>;\n}\n\n/** Read the per-request runtime entry an event carries, if any. */\nconst requestRuntimeEntry = (event: RequestEvent): RequestRuntimeEntry | undefined =>\n (event.locals as Record<PropertyKey, unknown> | undefined)?.[RequestRuntimeKey] as\n | RequestRuntimeEntry\n | undefined;\n\n/** Layer with the per-request services for a server-side request. */\nconst requestLayer = (event: RequestEvent): Layer.Layer<SvelteKitServices> =>\n Layer.merge(\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, event.fetch))),\n Layer.succeed(SvelteKitRequestEvent, event),\n );\n\n/** Layer providing `HttpClient` backed by an arbitrary fetch implementation. */\nconst fetchLayer = (fetchFn: typeof globalThis.fetch): Layer.Layer<HttpClient.HttpClient> =>\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)));\n\n/** Build a settle that returns a success value or throws via the translator. */\nconst makeSettle =\n (translate: ErrorTranslator) =>\n <A>(exit: Exit.Exit<A, unknown>): A =>\n Exit.match(exit, {\n onFailure: (cause) => translate(cause),\n onSuccess: (value) => value,\n });\n\n/** The default settle: SvelteKit control flow through, anything else a 500. */\nconst settle = makeSettle((cause) => {\n throw causeToThrowable(cause);\n});\n\n/**\n * Options for {@link makeServerRuntime}.\n */\nexport interface ServerRuntimeOptions {\n /**\n * Translate a failed `Cause` into SvelteKit control flow at the boundary of\n * every wrapper (`serverLoad`, `action`, `actions`, `endpoint`, `handle`,\n * `handleFetch`, `handleError`, `init`). It may throw `redirect()` /\n * `error()` so a domain error catalogue surfaces as SvelteKit control flow\n * instead of a generic 500. Defaults to the package passthrough: SvelteKit's\n * own `redirect()` / `error()` are rethrown, anything else becomes an\n * `EffectCauseError` (a 500).\n */\n readonly translateError?: ErrorTranslator;\n}\n\n/**\n * A server-side Effect runtime bound to an application layer.\n *\n * Produced by {@link makeServerRuntime}. The wrapper methods mirror the\n * standalone helpers in `@thomasfosterau/effect-sveltekit/server`, but\n * effects additionally have access to the services `R` of the application\n * layer.\n */\nexport interface ServerRuntime<R = never, E = never> {\n /** The underlying managed runtime for the application layer. */\n readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `load`.\n */\n readonly serverLoad: <\n Params extends Record<string, string> = any,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>,\n ) => ServerLoad<Params, ParentData, OutputData, RouteId>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit form action.\n */\n readonly action: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>,\n ) => Action<Params, OutputData, RouteId>;\n\n /**\n * Wraps an object of Effect-based form actions.\n */\n readonly actions: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n actionsMap: Record<\n string,\n (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>\n >,\n ) => Actions<Params, OutputData, RouteId>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit `handle` hook.\n */\n readonly handle: (\n fn: (args: Parameters<Handle>[0]) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => Handle;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `handleFetch` hook,\n * which intercepts `event.fetch` calls made on the server. The effect runs\n * with the per-request services on top of the application layer and produces\n * the `Response` to use.\n */\n readonly handleFetch: (\n fn: (\n args: Parameters<HandleFetch>[0],\n ) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => HandleFetch;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `handleError` hook.\n *\n * The effect receives the error and the request event, and produces the\n * shape that SvelteKit exposes to the client (your `App.Error`, or nothing).\n * It runs with the per-request services on top of the application layer.\n */\n readonly handleError: (\n fn: (\n args: Parameters<HandleServerError>[0],\n ) => Effect.Effect<Awaited<ReturnType<HandleServerError>>, unknown, R | SvelteKitServices>,\n ) => HandleServerError;\n\n /**\n * Wraps an Effect as a SvelteKit server `init` hook, run once before the\n * server responds to its first request. There is no request event, so the\n * effect only has access to the application layer's services.\n */\n readonly init: (effect: Effect.Effect<void, unknown, R>) => ServerInit;\n\n /**\n * The per-request {@link RequestRuntime} this runtime's `handle` stored on the\n * event, or `undefined` when `handle` was not used for the request. Use it to\n * run your own effects on the same per-request runtime (shared `HttpClient`\n * and request scope) that `serverLoad` / `action` / `endpoint` use.\n *\n * @example\n * ```ts\n * // anywhere with the request event, e.g. another `handle` in the sequence\n * const rt = runtime.requestRuntime(event);\n * await rt?.runPromise(Effect.gen(function* () {\n * const user = yield* Auth.currentUser;\n * event.locals.user = user;\n * }));\n * ```\n */\n readonly requestRuntime: (event: RequestEvent) => RequestRuntime<R> | undefined;\n\n /**\n * Wraps an Effect-based function as a SvelteKit `+server.ts` endpoint\n * handler (`GET`, `POST`, ...).\n */\n readonly endpoint: <\n Params extends Record<string, string> = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => (event: RequestEvent<Params, RouteId>) => Promise<Response>;\n\n /**\n * Runs an effect for a specific request, providing both the application\n * layer and the per-request services. Failures (other than SvelteKit\n * control flow) are thrown as `EffectCauseError`.\n */\n readonly runPromise: <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<A>;\n\n /**\n * Runs an effect for a specific request and returns the Exit, without\n * translating failures into thrown errors.\n */\n readonly runPromiseExit: <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<Exit.Exit<A, unknown>>;\n\n /**\n * Disposes the underlying managed runtime, running the layer's finalizers.\n * Call this on server shutdown or in tests.\n */\n readonly dispose: () => Promise<void>;\n}\n\n/**\n * Creates a {@link ServerRuntime} from an application layer.\n *\n * The layer is built lazily on first use and shared by all requests for the\n * lifetime of the server process. Define it once in a server-only module and\n * import its wrappers from your routes:\n *\n * @example\n * ```ts\n * // src/lib/server/runtime.ts\n * import { makeServerRuntime } from '@thomasfosterau/effect-sveltekit/server';\n * import { Layer } from 'effect';\n * import { Database } from './database';\n *\n * export const runtime = makeServerRuntime(Database.layer);\n * ```\n *\n * ```ts\n * // src/routes/items/+page.server.ts\n * import { runtime } from '$lib/server/runtime';\n * import { Effect } from 'effect';\n * import { Database } from '$lib/server/database';\n *\n * export const load = runtime.serverLoad((event) =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return { items: yield* db.listItems() };\n * })\n * );\n * ```\n *\n * Note that the application layer is built outside of any request, so its\n * services cannot depend on the per-request `SvelteKitRequestEvent` or\n * `HttpClient`. Request-specific data should be passed to service methods as\n * arguments instead.\n */\nexport const makeServerRuntime = <R = never, E = never>(\n layer?: Layer.Layer<R, E>,\n options?: ServerRuntimeOptions,\n): ServerRuntime<R, E> => {\n const runtime = ManagedRuntime.make((layer ?? Layer.empty) as Layer.Layer<R, E>);\n\n // The boundary translator for this runtime's wrappers (defaults to the\n // package passthrough: SvelteKit control flow through, anything else a 500).\n const settleRequest = options?.translateError ? makeSettle(options.translateError) : settle;\n\n // Identity token tagging the per-request runtimes this runtime creates, so a\n // different runtime's wrappers don't run on the wrong application layer.\n const owner = {};\n\n /** A {@link ScopedRequestRuntime} that provides the prebuilt request services. */\n const makeRequestRuntime = (\n requestServices: Context.Context<SvelteKitServices>,\n ): RequestRuntime<R> => {\n const provide = <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) =>\n Effect.provideContext(effect, requestServices) as Effect.Effect<A, unknown, R>;\n return {\n runPromiseExit: (effect) =>\n runtime.runPromiseExit(provide(effect)) as Promise<Exit.Exit<any, unknown>>,\n runPromise: (effect) => runtime.runPromiseExit(provide(effect)).then(settleRequest),\n runFork: (effect) => runtime.runFork(provide(effect)) as Fiber.Fiber<any, unknown>,\n };\n };\n\n /** The per-request runtime for this runtime carried by the event, if any. */\n const requestRuntimeOf = (event: RequestEvent): RequestRuntime<R> | undefined => {\n const entry = requestRuntimeEntry(event);\n return entry?.owner === owner ? (entry.runtime as RequestRuntime<R>) : undefined;\n };\n\n const runExit = <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ): Promise<Exit.Exit<A, unknown>> => {\n // Reuse the per-request runtime `handle` installed, if any; otherwise build\n // the per-request services for this single call.\n const requestRuntime = requestRuntimeOf(event);\n if (requestRuntime !== undefined) return requestRuntime.runPromiseExit(effect);\n const provided = Effect.provide(effect, requestLayer(event)) as Effect.Effect<A, unknown, R>;\n return runtime.runPromiseExit(provided) as Promise<Exit.Exit<A, unknown>>;\n };\n\n const run = <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ): Promise<A> => runExit(event, effect).then(settleRequest);\n\n const action: ServerRuntime<R, E>[\"action\"] = (fn) => (event) => run(event, fn(event));\n\n /**\n * The `handle` hook: build a per-request runtime, store it on `event.locals`\n * so every other wrapper (and the user's own code) runs on it, run the user's\n * effect, then close the request scope.\n */\n const handle: ServerRuntime<R, E>[\"handle\"] = (fn) => async (input) => {\n const { event } = input;\n const scope = Scope.makeUnsafe();\n const requestServices = (await runtime.runPromise(\n Layer.buildWithScope(requestLayer(event), scope),\n )) as Context.Context<SvelteKitServices>;\n\n const entry: RequestRuntimeEntry = { owner, runtime: makeRequestRuntime(requestServices) };\n (event.locals as Record<PropertyKey, unknown>)[RequestRuntimeKey] = entry;\n\n try {\n return await (entry.runtime as RequestRuntime<R>).runPromise(fn(input));\n } finally {\n await runtime.runPromise(Scope.close(scope, Exit.void));\n }\n };\n\n return {\n runtime,\n serverLoad: (fn) => (event) => run(event, fn(event)),\n action,\n actions: (actionsMap) => {\n const wrapped: Record<string, Action<any, any, any>> = {};\n for (const [key, fn] of Object.entries(actionsMap)) {\n wrapped[key] = action(fn);\n }\n return wrapped as never;\n },\n handle,\n handleFetch: (fn) => (args) => run(args.event, fn(args)),\n handleError: (fn) => (args) => run(args.event, fn(args)),\n init: (effect) => async () => {\n // `init` runs outside of any request, so it executes directly on the\n // application runtime without the per-request layer.\n settleRequest(await runtime.runPromiseExit(effect));\n },\n requestRuntime: requestRuntimeOf,\n endpoint: (fn) => (event) => run(event, fn(event)),\n runPromise: run,\n runPromiseExit: runExit,\n dispose: () => runtime.dispose(),\n };\n};\n\n/**\n * A universal (client + server) Effect runtime bound to an application layer.\n *\n * Produced by {@link makeUniversalRuntime}. Because universal `load`\n * functions also run in the browser, the layer must only use services that\n * work in both environments.\n */\nexport interface UniversalRuntime<R = never, E = never> {\n /** The underlying managed runtime for the application layer. */\n readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit universal `load`.\n *\n * The effect gets `HttpClient` (backed by the load event's `fetch`) on both\n * client and server, plus the services of the application layer.\n */\n readonly load: <\n Params extends Record<string, string> = any,\n InputData extends Record<string, unknown> | null = any,\n ParentData extends Record<string, unknown> = any,\n OutputData extends Record<string, unknown> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: Parameters<Load<Params, InputData, ParentData, OutputData, RouteId>>[0],\n ) => Effect.Effect<OutputData, unknown, R | HttpClient.HttpClient>,\n ) => Load<Params, InputData, ParentData, OutputData, RouteId>;\n\n /**\n * Runs an effect with `HttpClient` backed by the given fetch implementation\n * plus the application layer.\n */\n readonly runPromise: <A>(\n fetchFn: typeof globalThis.fetch,\n effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>,\n ) => Promise<A>;\n\n /**\n * Disposes the underlying managed runtime, running the layer's finalizers.\n */\n readonly dispose: () => Promise<void>;\n}\n\n/**\n * Creates a {@link UniversalRuntime} from an application layer.\n *\n * @example\n * ```ts\n * // src/lib/runtime.ts\n * import { makeUniversalRuntime } from '@thomasfosterau/effect-sveltekit';\n * import { ApiClient } from '$lib/api';\n *\n * export const runtime = makeUniversalRuntime(ApiClient.layer);\n * ```\n *\n * ```ts\n * // src/routes/+page.ts\n * import { runtime } from '$lib/runtime';\n * import { Effect } from 'effect';\n * import { ApiClient } from '$lib/api';\n *\n * export const load = runtime.load((event) =>\n * Effect.gen(function* () {\n * const api = yield* ApiClient;\n * return { stories: yield* api.topStories() };\n * })\n * );\n * ```\n */\nexport const makeUniversalRuntime = <R = never, E = never>(\n layer?: Layer.Layer<R, E>,\n): UniversalRuntime<R, E> => {\n const runtime = ManagedRuntime.make((layer ?? Layer.empty) as Layer.Layer<R, E>);\n\n const run = <A>(\n fetchFn: typeof globalThis.fetch,\n effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>,\n event?: RequestEvent,\n ): Promise<A> => {\n // On the server the full request layer (HttpClient + SvelteKitRequestEvent)\n // is available; on the client only HttpClient backed by the event's fetch.\n const services = event ? requestLayer(event) : fetchLayer(fetchFn);\n const provided = Effect.provide(effect, services) as Effect.Effect<A, unknown, R>;\n return runtime.runPromiseExit(provided).then(settle);\n };\n\n return {\n runtime,\n load: (fn) => (event) => {\n const serverEvent = (event as { request?: unknown }).request\n ? (event as unknown as RequestEvent)\n : undefined;\n return run(event.fetch, fn(event), serverEvent);\n },\n runPromise: (fetchFn, effect) => run(fetchFn, effect),\n dispose: () => runtime.dispose(),\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-runtime.js","names":[],"sources":["../src/app-runtime.ts"],"sourcesContent":["/**\n * App-wide Effect runtimes for SvelteKit.\n *\n * `makeServerRuntime` / `makeUniversalRuntime` let an application define its\n * Effect services (database clients, API wrappers, config, ...) as a single\n * `Layer`, built once per server process and shared by every request, instead\n * of being rebuilt for each `load` / `action` / endpoint invocation.\n *\n * Per-request services — the {@link SvelteKitRequestEvent} and an `HttpClient`\n * backed by the event's `fetch` — are provided on top of the shared runtime\n * for each call, so SvelteKit's fetch behaviours (cookie forwarding, request\n * deduplication, relative URLs) keep working as expected.\n */\n\nimport type {\n Action,\n Actions,\n Handle,\n HandleFetch,\n HandleServerError,\n Load,\n RequestEvent,\n ServerInit,\n ServerLoad,\n} from \"@sveltejs/kit\";\nimport { Context, Effect, type Fiber, Exit, Layer, ManagedRuntime, Scope } from \"effect\";\nimport * as FetchHttpClient from \"effect/unstable/http/FetchHttpClient\";\nimport type * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport { SvelteKitRequestEvent } from \"./runtime.js\";\nimport type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport { causeToThrowable } from \"./internal/error.js\";\nimport type { ErrorTranslator } from \"./request-runtime.js\";\n\n/**\n * The services provided to every wrapped effect on the server, in addition to\n * the application's own layer.\n */\nexport type SvelteKitServices = HttpClient.HttpClient | SvelteKitRequestEvent;\n\n/**\n * A per-request Effect runtime: the application layer's services plus the\n * per-request {@link SvelteKitServices} (an `HttpClient` backed by the request's\n * `fetch`, and the {@link SvelteKitRequestEvent}), with the request services\n * built once for the request and shared.\n *\n * A {@link ServerRuntime}'s `handle` hook creates one per request and stores it\n * on `event.locals` (`App.Locals`). Every other wrapper for the same runtime\n * (`serverLoad`, `action`, `endpoint`, ...) then runs on it, so a request's\n * effects share one `HttpClient` and one request scope. Retrieve it from an\n * event with `ServerRuntime.requestRuntime` to run your own effects.\n */\nexport interface RequestRuntime<R = never> {\n /** Run an effect for this request, returning its `Exit`. */\n readonly runPromiseExit: <A>(\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<Exit.Exit<A, unknown>>;\n /** Run an effect for this request, throwing on failure (passing SvelteKit control flow through). */\n readonly runPromise: <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Promise<A>;\n /** Fork an effect for this request, returning the running fiber. */\n readonly runFork: <A>(\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Fiber.Fiber<A, unknown>;\n}\n\n/**\n * Symbol under which a {@link ServerRuntime}'s `handle` stores the per-request\n * {@link RequestRuntime} on `event.locals`. `Symbol.for` so the key is stable\n * across module instances.\n */\nconst RequestRuntimeKey: unique symbol = Symbol.for(\n \"@thomasfosterau/effect-sveltekit/requestRuntime\",\n);\n\n/**\n * The locals entry: the per-request runtime tagged with the `ServerRuntime`\n * that created it, so a different runtime's wrappers ignore it (and fall back\n * to building request services themselves) rather than running on the wrong\n * application layer.\n */\ninterface RequestRuntimeEntry {\n readonly owner: object;\n readonly runtime: RequestRuntime<any>;\n}\n\n/** Read the per-request runtime entry an event carries, if any. */\nconst requestRuntimeEntry = (event: RequestEvent): RequestRuntimeEntry | undefined =>\n (event.locals as Record<PropertyKey, unknown> | undefined)?.[RequestRuntimeKey] as\n | RequestRuntimeEntry\n | undefined;\n\n/** Layer with the per-request services for a server-side request. */\nconst requestLayer = (event: RequestEvent): Layer.Layer<SvelteKitServices> =>\n Layer.merge(\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, event.fetch))),\n Layer.succeed(SvelteKitRequestEvent, event),\n );\n\n/** Layer providing `HttpClient` backed by an arbitrary fetch implementation. */\nconst fetchLayer = (fetchFn: typeof globalThis.fetch): Layer.Layer<HttpClient.HttpClient> =>\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)));\n\n/** Build a settle that returns a success value or throws via the translator. */\nconst makeSettle =\n (translate: ErrorTranslator) =>\n <A>(exit: Exit.Exit<A, unknown>): A =>\n Exit.match(exit, {\n onFailure: (cause) => translate(cause),\n onSuccess: (value) => value,\n });\n\n/** The default settle: SvelteKit control flow through, anything else a 500. */\nconst settle = makeSettle((cause) => {\n throw causeToThrowable(cause);\n});\n\n/**\n * Options for {@link makeServerRuntime}.\n */\nexport interface ServerRuntimeOptions {\n /**\n * Translate a failed `Cause` into SvelteKit control flow at the boundary of\n * every wrapper (`serverLoad`, `action`, `actions`, `endpoint`, `handle`,\n * `handleFetch`, `handleError`, `init`). It may throw `redirect()` /\n * `error()` so a domain error catalogue surfaces as SvelteKit control flow\n * instead of a generic 500. Defaults to the package passthrough: SvelteKit's\n * own `redirect()` / `error()` are rethrown, anything else becomes an\n * `EffectCauseError` (a 500).\n */\n readonly translateError?: ErrorTranslator;\n}\n\n/**\n * A server-side Effect runtime bound to an application layer.\n *\n * Produced by {@link makeServerRuntime}. The wrapper methods mirror the\n * standalone helpers in `@thomasfosterau/effect-sveltekit/server`, but\n * effects additionally have access to the services `R` of the application\n * layer.\n */\nexport interface ServerRuntime<R = never, E = never> {\n /** The underlying managed runtime for the application layer. */\n readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `load`.\n */\n readonly serverLoad: <\n Params extends SvelteRouteParams = SvelteRouteParams,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>,\n ) => ServerLoad<Params, ParentData, OutputData, RouteId>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit form action.\n */\n readonly action: <\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>,\n ) => Action<Params, OutputData, RouteId>;\n\n /**\n * Wraps an object of Effect-based form actions.\n */\n readonly actions: <\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n actionsMap: Record<\n string,\n (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>\n >,\n ) => Actions<Params, OutputData, RouteId>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit `handle` hook.\n */\n readonly handle: (\n fn: (args: Parameters<Handle>[0]) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => Handle;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `handleFetch` hook,\n * which intercepts `event.fetch` calls made on the server. The effect runs\n * with the per-request services on top of the application layer and produces\n * the `Response` to use.\n */\n readonly handleFetch: (\n fn: (\n args: Parameters<HandleFetch>[0],\n ) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => HandleFetch;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `handleError` hook.\n *\n * The effect receives the error and the request event, and produces the\n * shape that SvelteKit exposes to the client (your `App.Error`, or nothing).\n * It runs with the per-request services on top of the application layer.\n */\n readonly handleError: (\n fn: (\n args: Parameters<HandleServerError>[0],\n ) => Effect.Effect<Awaited<ReturnType<HandleServerError>>, unknown, R | SvelteKitServices>,\n ) => HandleServerError;\n\n /**\n * Wraps an Effect as a SvelteKit server `init` hook, run once before the\n * server responds to its first request. There is no request event, so the\n * effect only has access to the application layer's services.\n */\n readonly init: (effect: Effect.Effect<void, unknown, R>) => ServerInit;\n\n /**\n * The per-request {@link RequestRuntime} this runtime's `handle` stored on the\n * event, or `undefined` when `handle` was not used for the request. Use it to\n * run your own effects on the same per-request runtime (shared `HttpClient`\n * and request scope) that `serverLoad` / `action` / `endpoint` use.\n *\n * @example\n * ```ts\n * // anywhere with the request event, e.g. another `handle` in the sequence\n * const rt = runtime.requestRuntime(event);\n * await rt?.runPromise(Effect.gen(function* () {\n * const user = yield* Auth.currentUser;\n * event.locals.user = user;\n * }));\n * ```\n */\n readonly requestRuntime: (event: RequestEvent) => RequestRuntime<R> | undefined;\n\n /**\n * Wraps an Effect-based function as a SvelteKit `+server.ts` endpoint\n * handler (`GET`, `POST`, ...).\n */\n readonly endpoint: <\n Params extends SvelteRouteParams = SvelteRouteParams,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => (event: RequestEvent<Params, RouteId>) => Promise<Response>;\n\n /**\n * Runs an effect for a specific request, providing both the application\n * layer and the per-request services. Failures (other than SvelteKit\n * control flow) are thrown as `EffectCauseError`.\n */\n readonly runPromise: <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<A>;\n\n /**\n * Runs an effect for a specific request and returns the Exit, without\n * translating failures into thrown errors.\n */\n readonly runPromiseExit: <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<Exit.Exit<A, unknown>>;\n\n /**\n * Disposes the underlying managed runtime, running the layer's finalizers.\n * Call this on server shutdown or in tests.\n */\n readonly dispose: () => Promise<void>;\n}\n\n/**\n * Creates a {@link ServerRuntime} from an application layer.\n *\n * The layer is built lazily on first use and shared by all requests for the\n * lifetime of the server process. Define it once in a server-only module and\n * import its wrappers from your routes:\n *\n * @example\n * ```ts\n * // src/lib/server/runtime.ts\n * import { makeServerRuntime } from '@thomasfosterau/effect-sveltekit/server';\n * import { Layer } from 'effect';\n * import { Database } from './database';\n *\n * export const runtime = makeServerRuntime(Database.layer);\n * ```\n *\n * ```ts\n * // src/routes/items/+page.server.ts\n * import { runtime } from '$lib/server/runtime';\n * import { Effect } from 'effect';\n * import { Database } from '$lib/server/database';\n *\n * export const load = runtime.serverLoad((event) =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return { items: yield* db.listItems() };\n * })\n * );\n * ```\n *\n * Note that the application layer is built outside of any request, so its\n * services cannot depend on the per-request `SvelteKitRequestEvent` or\n * `HttpClient`. Request-specific data should be passed to service methods as\n * arguments instead.\n */\nexport const makeServerRuntime = <R = never, E = never>(\n layer?: Layer.Layer<R, E>,\n options?: ServerRuntimeOptions,\n): ServerRuntime<R, E> => {\n const runtime = ManagedRuntime.make((layer ?? Layer.empty) as Layer.Layer<R, E>);\n\n // The boundary translator for this runtime's wrappers (defaults to the\n // package passthrough: SvelteKit control flow through, anything else a 500).\n const settleRequest = options?.translateError ? makeSettle(options.translateError) : settle;\n\n // Identity token tagging the per-request runtimes this runtime creates, so a\n // different runtime's wrappers don't run on the wrong application layer.\n const owner = {};\n\n /** A {@link ScopedRequestRuntime} that provides the prebuilt request services. */\n const makeRequestRuntime = (\n requestServices: Context.Context<SvelteKitServices>,\n ): RequestRuntime<R> => {\n const provide = <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) =>\n Effect.provideContext(effect, requestServices) as Effect.Effect<A, unknown, R>;\n return {\n runPromiseExit: (effect) =>\n runtime.runPromiseExit(provide(effect)) as Promise<Exit.Exit<any, unknown>>,\n runPromise: (effect) => runtime.runPromiseExit(provide(effect)).then(settleRequest),\n runFork: (effect) => runtime.runFork(provide(effect)) as Fiber.Fiber<any, unknown>,\n };\n };\n\n /** The per-request runtime for this runtime carried by the event, if any. */\n const requestRuntimeOf = (event: RequestEvent): RequestRuntime<R> | undefined => {\n const entry = requestRuntimeEntry(event);\n return entry?.owner === owner ? (entry.runtime as RequestRuntime<R>) : undefined;\n };\n\n const runExit = <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ): Promise<Exit.Exit<A, unknown>> => {\n // Reuse the per-request runtime `handle` installed, if any; otherwise build\n // the per-request services for this single call.\n const requestRuntime = requestRuntimeOf(event);\n if (requestRuntime !== undefined) return requestRuntime.runPromiseExit(effect);\n const provided = Effect.provide(effect, requestLayer(event)) as Effect.Effect<A, unknown, R>;\n return runtime.runPromiseExit(provided) as Promise<Exit.Exit<A, unknown>>;\n };\n\n const run = <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ): Promise<A> => runExit(event, effect).then(settleRequest);\n\n const action: ServerRuntime<R, E>[\"action\"] = (fn) => (event) => run(event, fn(event));\n\n /**\n * The `handle` hook: build a per-request runtime, store it on `event.locals`\n * so every other wrapper (and the user's own code) runs on it, run the user's\n * effect, then close the request scope.\n */\n const handle: ServerRuntime<R, E>[\"handle\"] = (fn) => async (input) => {\n const { event } = input;\n const scope = Scope.makeUnsafe();\n const requestServices = (await runtime.runPromise(\n Layer.buildWithScope(requestLayer(event), scope),\n )) as Context.Context<SvelteKitServices>;\n\n const entry: RequestRuntimeEntry = { owner, runtime: makeRequestRuntime(requestServices) };\n (event.locals as Record<PropertyKey, unknown>)[RequestRuntimeKey] = entry;\n\n try {\n return await (entry.runtime as RequestRuntime<R>).runPromise(fn(input));\n } finally {\n await runtime.runPromise(Scope.close(scope, Exit.void));\n }\n };\n\n return {\n runtime,\n serverLoad: (fn) => (event) => run(event, fn(event)),\n action,\n actions: (actionsMap) => {\n const wrapped: Record<string, Action<any, any, any>> = {};\n for (const [key, fn] of Object.entries(actionsMap)) {\n wrapped[key] = action(fn);\n }\n return wrapped as never;\n },\n handle,\n handleFetch: (fn) => (args) => run(args.event, fn(args)),\n handleError: (fn) => (args) => run(args.event, fn(args)),\n init: (effect) => async () => {\n // `init` runs outside of any request, so it executes directly on the\n // application runtime without the per-request layer.\n settleRequest(await runtime.runPromiseExit(effect));\n },\n requestRuntime: requestRuntimeOf,\n endpoint: (fn) => (event) => run(event, fn(event)),\n runPromise: run,\n runPromiseExit: runExit,\n dispose: () => runtime.dispose(),\n };\n};\n\n/**\n * A universal (client + server) Effect runtime bound to an application layer.\n *\n * Produced by {@link makeUniversalRuntime}. Because universal `load`\n * functions also run in the browser, the layer must only use services that\n * work in both environments.\n */\nexport interface UniversalRuntime<R = never, E = never> {\n /** The underlying managed runtime for the application layer. */\n readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit universal `load`.\n *\n * The effect gets `HttpClient` (backed by the load event's `fetch`) on both\n * client and server, plus the services of the application layer.\n */\n readonly load: <\n Params extends SvelteRouteParams = SvelteRouteParams,\n InputData extends Record<string, unknown> | null = any,\n ParentData extends Record<string, unknown> = any,\n OutputData extends Record<string, unknown> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: Parameters<Load<Params, InputData, ParentData, OutputData, RouteId>>[0],\n ) => Effect.Effect<OutputData, unknown, R | HttpClient.HttpClient>,\n ) => Load<Params, InputData, ParentData, OutputData, RouteId>;\n\n /**\n * Runs an effect with `HttpClient` backed by the given fetch implementation\n * plus the application layer.\n */\n readonly runPromise: <A>(\n fetchFn: typeof globalThis.fetch,\n effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>,\n ) => Promise<A>;\n\n /**\n * Disposes the underlying managed runtime, running the layer's finalizers.\n */\n readonly dispose: () => Promise<void>;\n}\n\n/**\n * Creates a {@link UniversalRuntime} from an application layer.\n *\n * @example\n * ```ts\n * // src/lib/runtime.ts\n * import { makeUniversalRuntime } from '@thomasfosterau/effect-sveltekit';\n * import { ApiClient } from '$lib/api';\n *\n * export const runtime = makeUniversalRuntime(ApiClient.layer);\n * ```\n *\n * ```ts\n * // src/routes/+page.ts\n * import { runtime } from '$lib/runtime';\n * import { Effect } from 'effect';\n * import { ApiClient } from '$lib/api';\n *\n * export const load = runtime.load((event) =>\n * Effect.gen(function* () {\n * const api = yield* ApiClient;\n * return { stories: yield* api.topStories() };\n * })\n * );\n * ```\n */\nexport const makeUniversalRuntime = <R = never, E = never>(\n layer?: Layer.Layer<R, E>,\n): UniversalRuntime<R, E> => {\n const runtime = ManagedRuntime.make((layer ?? Layer.empty) as Layer.Layer<R, E>);\n\n const run = <A>(\n fetchFn: typeof globalThis.fetch,\n effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>,\n event?: RequestEvent,\n ): Promise<A> => {\n // On the server the full request layer (HttpClient + SvelteKitRequestEvent)\n // is available; on the client only HttpClient backed by the event's fetch.\n const services = event ? requestLayer(event) : fetchLayer(fetchFn);\n const provided = Effect.provide(effect, services) as Effect.Effect<A, unknown, R>;\n return runtime.runPromiseExit(provided).then(settle);\n };\n\n return {\n runtime,\n load: (fn) => (event) => {\n const serverEvent = (event as { request?: unknown }).request\n ? (event as unknown as RequestEvent)\n : undefined;\n return run(event.fetch, fn(event), serverEvent);\n },\n runPromise: (fetchFn, effect) => run(fetchFn, effect),\n dispose: () => runtime.dispose(),\n };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { RequestEvent } from "@sveltejs/kit";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/route-params.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The route-parameter constraint SvelteKit uses for its own handler types
|
|
6
|
+
* (`RequestEvent`, `ServerLoad`, `Load`, `Action`, `RequestHandler`) — the
|
|
7
|
+
* generated `LayoutParams<'/'>`. For a root `+layout`, whose params span the
|
|
8
|
+
* whole route tree, this is a *partial* record (e.g. `{ type?: string; id?:
|
|
9
|
+
* string }`); for a concrete route it is the exact params (e.g. `{ id: string }`).
|
|
10
|
+
*
|
|
11
|
+
* The package's wrappers mirror this base (rather than hardcoding
|
|
12
|
+
* `Record<string, string>`) so they are never more restrictive than SvelteKit's
|
|
13
|
+
* own handlers. In particular, a partial-param root `+layout.server.ts` can carry
|
|
14
|
+
* an explicit `: LayoutServerLoad` annotation without the wrapper rejecting it.
|
|
15
|
+
*
|
|
16
|
+
* In a SvelteKit project this resolves to the generated `$app/types` params; in a
|
|
17
|
+
* bare type-check (no generated types) it falls back to `Record<string, string>`,
|
|
18
|
+
* exactly as SvelteKit's own `AppLayoutParams<'/'>` does.
|
|
19
|
+
*/
|
|
20
|
+
type SvelteRouteParams = RequestEvent["params"];
|
|
21
|
+
//#endregion
|
|
22
|
+
export { SvelteRouteParams };
|
|
23
|
+
//# sourceMappingURL=route-params.d.ts.map
|
package/dist/remote.server.d.ts
CHANGED
|
@@ -27,11 +27,9 @@ interface QueryLiveFn<Env> {
|
|
|
27
27
|
}
|
|
28
28
|
interface FormFn<Env> {
|
|
29
29
|
<Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteForm<void, Output>;
|
|
30
|
-
<Input
|
|
31
|
-
<Sch extends StandardSchemaV1
|
|
32
|
-
<S extends Schema.Top &
|
|
33
|
-
readonly Encoded: RemoteFormInput;
|
|
34
|
-
}, Output>(validate: S, fn: (data: S["Type"], issue: InvalidField<S["Encoded"]>) => Effect.Effect<Output, unknown, Env>): RemoteForm<S["Encoded"], Output>;
|
|
30
|
+
<Input, Output>(validate: "unchecked", fn: (data: Input, issue: InvalidField<Input & RemoteFormInput>) => Effect.Effect<Output, unknown, Env>): RemoteForm<Input & RemoteFormInput, Output>;
|
|
31
|
+
<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>;
|
|
32
|
+
<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>;
|
|
35
33
|
}
|
|
36
34
|
interface CommandFn<Env> {
|
|
37
35
|
<Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteCommand<void, Output>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.server.js","names":["isBareHandlerValue"],"sources":["../src/remote.server.ts"],"sourcesContent":["import * 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/unstable/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 extends RemoteFormInput, Output>(\n validate: \"unchecked\",\n fn: (data: Input, issue: InvalidField<Input>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<Input, Output>;\n <Sch extends StandardSchemaV1<RemoteFormInput, Record<string, any>>, Output>(\n validate: Sch,\n fn: (\n data: StandardSchemaV1.InferOutput<Sch>,\n issue: InvalidField<StandardSchemaV1.InferInput<Sch>>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top & { readonly Encoded: RemoteFormInput }, Output>(\n validate: S,\n fn: (data: S[\"Type\"], issue: InvalidField<S[\"Encoded\"]>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<S[\"Encoded\"], 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.server.js","names":["isBareHandlerValue"],"sources":["../src/remote.server.ts"],"sourcesContent":["import * 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/unstable/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,3 +1,4 @@
|
|
|
1
|
+
import { SvelteRouteParams } from "./internal/route-params.js";
|
|
1
2
|
import { Cause, Context, Effect, Exit, Fiber, Layer, ManagedRuntime } from "effect";
|
|
2
3
|
import { Action, Actions, RequestEvent, RequestHandler, ServerLoad } from "@sveltejs/kit";
|
|
3
4
|
|
|
@@ -107,10 +108,10 @@ interface AdapterEndpointOptions {
|
|
|
107
108
|
}
|
|
108
109
|
/** The wrappers produced by {@link makeRuntimeAdapter}, bound to a provider. */
|
|
109
110
|
interface RuntimeAdapter<R> {
|
|
110
|
-
readonly serverLoad: <Params extends
|
|
111
|
-
readonly action: <Params extends
|
|
112
|
-
readonly actions: <Params extends
|
|
113
|
-
readonly endpoint: <Params extends
|
|
111
|
+
readonly serverLoad: <Params extends SvelteRouteParams = SvelteRouteParams, ParentData extends Record<string, any> = any, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>) => ServerLoad<Params, ParentData, OutputData, RouteId>;
|
|
112
|
+
readonly action: <Params extends SvelteRouteParams = SvelteRouteParams, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>) => Action<Params, OutputData, RouteId>;
|
|
113
|
+
readonly actions: <Params extends SvelteRouteParams = SvelteRouteParams, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(map: Record<string, (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>>) => Actions<Params, OutputData, RouteId>;
|
|
114
|
+
readonly endpoint: <Params extends SvelteRouteParams = SvelteRouteParams, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, R>, options?: AdapterEndpointOptions) => RequestHandler<Params, RouteId>;
|
|
114
115
|
}
|
|
115
116
|
/**
|
|
116
117
|
* Bind the SvelteKit server wrappers to a {@link RequestRuntimeProvider} (and an
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-runtime.js","names":[],"sources":["../src/request-runtime.ts"],"sourcesContent":["/**\n * A general, Effect-idiomatic way to provide the per-request execution context\n * for SvelteKit server wrappers.\n *\n * Where {@link makeServerRuntime} bakes in \"the package owns a runtime, built\n * once per process,\" this module factors out the single decision *how to obtain\n * the execution context for this request* behind one open abstraction — a\n * {@link RequestRuntimeProvider}. Existing models (a build-once\n * `ManagedRuntime`, a layer built once), a consumer-owned runtime resolved from\n * `event.locals`, and a package-built per-request runtime are all just\n * {@link RequestRuntime} constructors of the same provider, not separate code\n * paths.\n *\n * {@link makeRuntimeAdapter} binds the `serverLoad` / `action` / `actions` /\n * `endpoint` wrappers to a provider (and an optional boundary error\n * translator), so the handler effect's requirement channel `R` is the\n * *provider's* services — your own application catalogue — rather than the fixed\n * {@link SvelteKitServices}.\n *\n * @example\n * ```ts\n * // The consumer owns a per-request ManagedRuntime on event.locals.runtime\n * // (rebuilt mid-request); the package adopts it, resolved at call time.\n * import { makeRuntimeAdapter, RequestRuntime } from \"@thomasfosterau/effect-sveltekit/server\";\n *\n * const { serverLoad, action, actions, endpoint } = makeRuntimeAdapter(\n * RequestRuntime.fromLocals((event) => event.locals.runtime),\n * { translateError: toSvelteKit },\n * );\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const db = yield* Database; // app service from locals.runtime\n * return { items: yield* db.list() };\n * }),\n * );\n * ```\n */\n\nimport {\n isHttpError,\n isRedirect,\n type Action,\n type Actions,\n type RequestEvent,\n type RequestHandler,\n type ServerLoad,\n} from \"@sveltejs/kit\";\nimport { Cause, Context, Effect, Exit, type Fiber, Layer, ManagedRuntime } from \"effect\";\nimport { causeToThrowable } from \"./internal/error.js\";\n\n/**\n * The runners a {@link RequestRuntimeProvider} resolves for a single request:\n * a way to run an `Effect<A, unknown, R>` that requires the provider's services\n * `R`. The error channel is `unknown` because a boundary translator (see\n * {@link makeRuntimeAdapter}) decides how failures surface.\n */\nexport interface RequestRuntimeRunners<in R> {\n /** Run the request's effect, keeping its `Exit` (never rejects). */\n readonly runPromiseExit: <A>(\n effect: Effect.Effect<A, unknown, R>,\n ) => Promise<Exit.Exit<A, unknown>>;\n /** Fork the request's effect, returning the running fiber. */\n readonly runFork: <A>(effect: Effect.Effect<A, unknown, R>) => Fiber.Fiber<A, unknown>;\n}\n\n/**\n * How to execute a request's effects. Resolved per request, at call time, so a\n * runtime the consumer rebuilds mid-request is honoured automatically.\n *\n * Implementations own the scope/lifecycle of whatever they build. `R` is the\n * set of services the handler effect may require; `E` is the layer construction\n * error type (informational — failures surface through the translator).\n */\nexport interface RequestRuntimeProvider<R, E = never> {\n /** Resolve the runners for a request. Consulted per call, never cached. */\n readonly run: (event: RequestEvent) => RequestRuntimeRunners<R>;\n /**\n * Optional teardown to run after the response is produced. Called by\n * {@link makeRuntimeAdapter} once the handler has settled — e.g. to route a\n * per-request resource's disposal through `ctx.waitUntil` on Workers.\n */\n readonly disposeAfterResponse?: (event: RequestEvent) => void;\n /** Phantom marker so `E` participates in inference. */\n readonly _E?: (_: never) => E;\n}\n\n/** Runners backed by an existing `ManagedRuntime`. */\nconst runnersFromManagedRuntime = <R>(\n rt: ManagedRuntime.ManagedRuntime<R, unknown>,\n): RequestRuntimeRunners<R> => ({\n runPromiseExit: (effect) => rt.runPromiseExit(effect) as Promise<Exit.Exit<never, unknown>>,\n runFork: (effect) => rt.runFork(effect) as Fiber.Fiber<never, unknown>,\n});\n\n/** Runners backed by a built service `Context` (run on the default scheduler). */\nconst runnersFromContext = <R>(context: Context.Context<R>): RequestRuntimeRunners<R> => ({\n runPromiseExit: (effect) => Effect.runPromiseExitWith(context)(effect),\n runFork: (effect) => Effect.runForkWith(context)(effect),\n});\n\n/** Runners for a target that is either a `ManagedRuntime` or a `Context`. */\nconst runnersFor = <R>(\n target: ManagedRuntime.ManagedRuntime<R, unknown> | Context.Context<R>,\n): RequestRuntimeRunners<R> =>\n ManagedRuntime.isManagedRuntime(target)\n ? runnersFromManagedRuntime(target as ManagedRuntime.ManagedRuntime<R, unknown>)\n : runnersFromContext(target as Context.Context<R>);\n\n/** Options for {@link RequestRuntime.fromLayerPerRequest}. */\nexport interface FromLayerPerRequestOptions {\n /**\n * Defer teardown of the per-request runtime to after the response is\n * produced, instead of awaiting `dispose()` inline. On Workers, route it\n * through `ctx.waitUntil` so disposal does not fight response streaming or\n * socket pinning:\n *\n * ```ts\n * { afterResponse: (event, dispose) => event.platform!.ctx.waitUntil(dispose()) }\n * ```\n */\n readonly afterResponse?: (event: RequestEvent, dispose: () => Promise<void>) => void;\n}\n\n/**\n * Constructors for {@link RequestRuntimeProvider}. Each expresses one way to\n * obtain a request's execution context; the wrappers don't care which.\n */\nexport const RequestRuntime = {\n /**\n * Adopt a `ManagedRuntime` the consumer already built (the build-once model,\n * expressed as a provider).\n */\n fromManagedRuntime: <R, E>(\n rt: ManagedRuntime.ManagedRuntime<R, E>,\n ): RequestRuntimeProvider<R, E> => {\n const runners = runnersFromManagedRuntime(rt as ManagedRuntime.ManagedRuntime<R, unknown>);\n return { run: () => runners };\n },\n\n /**\n * Let the package build a single `ManagedRuntime` from a layer, lazily on\n * first use and shared across requests (the existing build-once-per-process\n * model).\n */\n fromLayer: <R, E>(layer: Layer.Layer<R, E>): RequestRuntimeProvider<R, E> => {\n let runners: RequestRuntimeRunners<R> | undefined;\n return {\n run: () =>\n (runners ??= runnersFromManagedRuntime(\n ManagedRuntime.make(layer) as ManagedRuntime.ManagedRuntime<R, unknown>,\n )),\n };\n },\n\n /**\n * Adopt a runtime the consumer stashed on the request (e.g. on\n * `event.locals`). Resolved at call time, so a runtime rebuilt mid-request is\n * honoured automatically. The getter may return a `ManagedRuntime` or a\n * built service `Context`.\n */\n fromLocals: <R>(\n get: (event: RequestEvent) => ManagedRuntime.ManagedRuntime<R, unknown> | Context.Context<R>,\n ): RequestRuntimeProvider<R> => ({\n run: (event) => runnersFor(get(event)),\n }),\n\n /**\n * Let the package build *and dispose* a `ManagedRuntime` per request from a\n * layer factory. Each handler invocation builds its own runtime from\n * `layer(event)` and disposes it once the effect settles — so a per-request\n * resource (a DB pool) dies with the request. Use `opts.afterResponse` to\n * defer disposal to after the response (Workers `ctx.waitUntil`).\n *\n * For sharing one runtime across every load/action of a request, prefer\n * {@link RequestRuntime.fromLocals} with a runtime the consumer builds in\n * `hooks.server.ts`.\n */\n fromLayerPerRequest: <R, E>(\n layer: (event: RequestEvent) => Layer.Layer<R, E>,\n opts?: FromLayerPerRequestOptions,\n ): RequestRuntimeProvider<R, E> => ({\n run: (event) => {\n const dispose = (rt: ManagedRuntime.ManagedRuntime<R, E>) => () => rt.dispose();\n const teardown = (rt: ManagedRuntime.ManagedRuntime<R, E>): Promise<void> => {\n if (opts?.afterResponse) {\n opts.afterResponse(event, dispose(rt));\n return Promise.resolve();\n }\n return rt.dispose();\n };\n return {\n runPromiseExit: async (effect) => {\n const rt = ManagedRuntime.make(layer(event));\n try {\n return (await rt.runPromiseExit(effect)) as Exit.Exit<never, unknown>;\n } finally {\n await teardown(rt);\n }\n },\n runFork: (effect) => {\n const rt = ManagedRuntime.make(layer(event));\n const fiber = rt.runFork(effect) as Fiber.Fiber<never, unknown>;\n fiber.addObserver(() => void teardown(rt));\n return fiber;\n },\n };\n },\n }),\n};\n\n/** A boundary translator: turn a failed `Cause` into a thrown value. */\nexport type ErrorTranslator = (cause: Cause.Cause<unknown>) => never;\n\n/** Options shared by {@link makeRuntimeAdapter}. */\nexport interface RuntimeAdapterOptions {\n /**\n * Translate a failed `Cause` into SvelteKit control flow at the boundary.\n * Called for `serverLoad` / `action` / `actions` failures; it may throw\n * `redirect()` / `error()` (and, for remote forms, call `invalid()`).\n * Defaults to the package's passthrough: SvelteKit's own `redirect()` /\n * `error()` are rethrown, anything else becomes an `EffectCauseError` (a 500).\n */\n readonly translateError?: ErrorTranslator;\n}\n\n/** Options for the adapter's `endpoint` wrapper. */\nexport interface AdapterEndpointOptions {\n /**\n * Include full error details (`Cause.pretty`) in the default 500 JSON body.\n * Ignored when a `translateError` is configured. Defaults to `false`.\n */\n readonly exposeErrors?: boolean;\n}\n\n/** The wrappers produced by {@link makeRuntimeAdapter}, bound to a provider. */\nexport interface RuntimeAdapter<R> {\n readonly serverLoad: <\n Params extends Record<string, string> = any,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>,\n ) => ServerLoad<Params, ParentData, OutputData, RouteId>;\n readonly action: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>,\n ) => Action<Params, OutputData, RouteId>;\n readonly actions: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n map: Record<\n string,\n (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>\n >,\n ) => Actions<Params, OutputData, RouteId>;\n readonly endpoint: <\n Params extends Record<string, string> = any,\n RouteId extends string | null = any,\n >(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, R>,\n options?: AdapterEndpointOptions,\n ) => RequestHandler<Params, RouteId>;\n}\n\n/**\n * Bind the SvelteKit server wrappers to a {@link RequestRuntimeProvider} (and an\n * optional boundary error translator).\n *\n * Unlike the standalone `serverLoad` / `action` / `endpoint`, the handler\n * effect's required services `R` come from the provider — your own application\n * catalogue — not the fixed {@link SvelteKitServices}. The package does not\n * merge `HttpClient` / `SvelteKitRequestEvent` onto this path; add them to your\n * own layer if you want them.\n *\n * @example\n * ```ts\n * // Package owns the runtime per request on Workers, disposed via waitUntil.\n * const { serverLoad } = makeRuntimeAdapter(\n * RequestRuntime.fromLayerPerRequest((event) => AppLayer(event.platform!.env), {\n * afterResponse: (event, dispose) => event.platform!.ctx.waitUntil(dispose()),\n * }),\n * );\n * ```\n */\nexport const makeRuntimeAdapter = <R>(\n provider: RequestRuntimeProvider<R, any>,\n opts?: RuntimeAdapterOptions,\n): RuntimeAdapter<R> => {\n const translateError: ErrorTranslator =\n opts?.translateError ??\n ((cause) => {\n throw causeToThrowable(cause);\n });\n\n const runSettle = async <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R>,\n ): Promise<A> => {\n const exit = await provider.run(event).runPromiseExit(effect);\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n return translateError(exit.cause);\n } finally {\n provider.disposeAfterResponse?.(event);\n }\n };\n\n const action: RuntimeAdapter<R>[\"action\"] = (fn) => (event) => runSettle(event, fn(event));\n\n return {\n serverLoad: (fn) => (event) => runSettle(event, fn(event)),\n action,\n actions: (map) => {\n const wrapped: Record<string, Action<any, any, any>> = {};\n for (const [key, fn] of Object.entries(map)) wrapped[key] = action(fn);\n return wrapped as never;\n },\n endpoint: (fn, options) => async (event) => {\n const exit = await provider.run(event as RequestEvent).runPromiseExit(fn(event));\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n // A configured translator owns the whole failure path (control flow,\n // structured error bodies, ...).\n if (opts?.translateError) return opts.translateError(exit.cause);\n // Default: pass SvelteKit control flow through, otherwise a 500.\n const squashed = Cause.squash(exit.cause);\n if (isRedirect(squashed) || isHttpError(squashed)) throw squashed;\n console.error(\"Endpoint error:\", Cause.pretty(exit.cause));\n const message = options?.exposeErrors ? Cause.pretty(exit.cause) : \"Internal server error\";\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } finally {\n provider.disposeAfterResponse?.(event as RequestEvent);\n }\n },\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"request-runtime.js","names":[],"sources":["../src/request-runtime.ts"],"sourcesContent":["/**\n * A general, Effect-idiomatic way to provide the per-request execution context\n * for SvelteKit server wrappers.\n *\n * Where {@link makeServerRuntime} bakes in \"the package owns a runtime, built\n * once per process,\" this module factors out the single decision *how to obtain\n * the execution context for this request* behind one open abstraction — a\n * {@link RequestRuntimeProvider}. Existing models (a build-once\n * `ManagedRuntime`, a layer built once), a consumer-owned runtime resolved from\n * `event.locals`, and a package-built per-request runtime are all just\n * {@link RequestRuntime} constructors of the same provider, not separate code\n * paths.\n *\n * {@link makeRuntimeAdapter} binds the `serverLoad` / `action` / `actions` /\n * `endpoint` wrappers to a provider (and an optional boundary error\n * translator), so the handler effect's requirement channel `R` is the\n * *provider's* services — your own application catalogue — rather than the fixed\n * {@link SvelteKitServices}.\n *\n * @example\n * ```ts\n * // The consumer owns a per-request ManagedRuntime on event.locals.runtime\n * // (rebuilt mid-request); the package adopts it, resolved at call time.\n * import { makeRuntimeAdapter, RequestRuntime } from \"@thomasfosterau/effect-sveltekit/server\";\n *\n * const { serverLoad, action, actions, endpoint } = makeRuntimeAdapter(\n * RequestRuntime.fromLocals((event) => event.locals.runtime),\n * { translateError: toSvelteKit },\n * );\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const db = yield* Database; // app service from locals.runtime\n * return { items: yield* db.list() };\n * }),\n * );\n * ```\n */\n\nimport {\n isHttpError,\n isRedirect,\n type Action,\n type Actions,\n type RequestEvent,\n type RequestHandler,\n type ServerLoad,\n} from \"@sveltejs/kit\";\nimport { Cause, Context, Effect, Exit, type Fiber, Layer, ManagedRuntime } from \"effect\";\nimport type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport { causeToThrowable } from \"./internal/error.js\";\n\n/**\n * The runners a {@link RequestRuntimeProvider} resolves for a single request:\n * a way to run an `Effect<A, unknown, R>` that requires the provider's services\n * `R`. The error channel is `unknown` because a boundary translator (see\n * {@link makeRuntimeAdapter}) decides how failures surface.\n */\nexport interface RequestRuntimeRunners<in R> {\n /** Run the request's effect, keeping its `Exit` (never rejects). */\n readonly runPromiseExit: <A>(\n effect: Effect.Effect<A, unknown, R>,\n ) => Promise<Exit.Exit<A, unknown>>;\n /** Fork the request's effect, returning the running fiber. */\n readonly runFork: <A>(effect: Effect.Effect<A, unknown, R>) => Fiber.Fiber<A, unknown>;\n}\n\n/**\n * How to execute a request's effects. Resolved per request, at call time, so a\n * runtime the consumer rebuilds mid-request is honoured automatically.\n *\n * Implementations own the scope/lifecycle of whatever they build. `R` is the\n * set of services the handler effect may require; `E` is the layer construction\n * error type (informational — failures surface through the translator).\n */\nexport interface RequestRuntimeProvider<R, E = never> {\n /** Resolve the runners for a request. Consulted per call, never cached. */\n readonly run: (event: RequestEvent) => RequestRuntimeRunners<R>;\n /**\n * Optional teardown to run after the response is produced. Called by\n * {@link makeRuntimeAdapter} once the handler has settled — e.g. to route a\n * per-request resource's disposal through `ctx.waitUntil` on Workers.\n */\n readonly disposeAfterResponse?: (event: RequestEvent) => void;\n /** Phantom marker so `E` participates in inference. */\n readonly _E?: (_: never) => E;\n}\n\n/** Runners backed by an existing `ManagedRuntime`. */\nconst runnersFromManagedRuntime = <R>(\n rt: ManagedRuntime.ManagedRuntime<R, unknown>,\n): RequestRuntimeRunners<R> => ({\n runPromiseExit: (effect) => rt.runPromiseExit(effect) as Promise<Exit.Exit<never, unknown>>,\n runFork: (effect) => rt.runFork(effect) as Fiber.Fiber<never, unknown>,\n});\n\n/** Runners backed by a built service `Context` (run on the default scheduler). */\nconst runnersFromContext = <R>(context: Context.Context<R>): RequestRuntimeRunners<R> => ({\n runPromiseExit: (effect) => Effect.runPromiseExitWith(context)(effect),\n runFork: (effect) => Effect.runForkWith(context)(effect),\n});\n\n/** Runners for a target that is either a `ManagedRuntime` or a `Context`. */\nconst runnersFor = <R>(\n target: ManagedRuntime.ManagedRuntime<R, unknown> | Context.Context<R>,\n): RequestRuntimeRunners<R> =>\n ManagedRuntime.isManagedRuntime(target)\n ? runnersFromManagedRuntime(target as ManagedRuntime.ManagedRuntime<R, unknown>)\n : runnersFromContext(target as Context.Context<R>);\n\n/** Options for {@link RequestRuntime.fromLayerPerRequest}. */\nexport interface FromLayerPerRequestOptions {\n /**\n * Defer teardown of the per-request runtime to after the response is\n * produced, instead of awaiting `dispose()` inline. On Workers, route it\n * through `ctx.waitUntil` so disposal does not fight response streaming or\n * socket pinning:\n *\n * ```ts\n * { afterResponse: (event, dispose) => event.platform!.ctx.waitUntil(dispose()) }\n * ```\n */\n readonly afterResponse?: (event: RequestEvent, dispose: () => Promise<void>) => void;\n}\n\n/**\n * Constructors for {@link RequestRuntimeProvider}. Each expresses one way to\n * obtain a request's execution context; the wrappers don't care which.\n */\nexport const RequestRuntime = {\n /**\n * Adopt a `ManagedRuntime` the consumer already built (the build-once model,\n * expressed as a provider).\n */\n fromManagedRuntime: <R, E>(\n rt: ManagedRuntime.ManagedRuntime<R, E>,\n ): RequestRuntimeProvider<R, E> => {\n const runners = runnersFromManagedRuntime(rt as ManagedRuntime.ManagedRuntime<R, unknown>);\n return { run: () => runners };\n },\n\n /**\n * Let the package build a single `ManagedRuntime` from a layer, lazily on\n * first use and shared across requests (the existing build-once-per-process\n * model).\n */\n fromLayer: <R, E>(layer: Layer.Layer<R, E>): RequestRuntimeProvider<R, E> => {\n let runners: RequestRuntimeRunners<R> | undefined;\n return {\n run: () =>\n (runners ??= runnersFromManagedRuntime(\n ManagedRuntime.make(layer) as ManagedRuntime.ManagedRuntime<R, unknown>,\n )),\n };\n },\n\n /**\n * Adopt a runtime the consumer stashed on the request (e.g. on\n * `event.locals`). Resolved at call time, so a runtime rebuilt mid-request is\n * honoured automatically. The getter may return a `ManagedRuntime` or a\n * built service `Context`.\n */\n fromLocals: <R>(\n get: (event: RequestEvent) => ManagedRuntime.ManagedRuntime<R, unknown> | Context.Context<R>,\n ): RequestRuntimeProvider<R> => ({\n run: (event) => runnersFor(get(event)),\n }),\n\n /**\n * Let the package build *and dispose* a `ManagedRuntime` per request from a\n * layer factory. Each handler invocation builds its own runtime from\n * `layer(event)` and disposes it once the effect settles — so a per-request\n * resource (a DB pool) dies with the request. Use `opts.afterResponse` to\n * defer disposal to after the response (Workers `ctx.waitUntil`).\n *\n * For sharing one runtime across every load/action of a request, prefer\n * {@link RequestRuntime.fromLocals} with a runtime the consumer builds in\n * `hooks.server.ts`.\n */\n fromLayerPerRequest: <R, E>(\n layer: (event: RequestEvent) => Layer.Layer<R, E>,\n opts?: FromLayerPerRequestOptions,\n ): RequestRuntimeProvider<R, E> => ({\n run: (event) => {\n const dispose = (rt: ManagedRuntime.ManagedRuntime<R, E>) => () => rt.dispose();\n const teardown = (rt: ManagedRuntime.ManagedRuntime<R, E>): Promise<void> => {\n if (opts?.afterResponse) {\n opts.afterResponse(event, dispose(rt));\n return Promise.resolve();\n }\n return rt.dispose();\n };\n return {\n runPromiseExit: async (effect) => {\n const rt = ManagedRuntime.make(layer(event));\n try {\n return (await rt.runPromiseExit(effect)) as Exit.Exit<never, unknown>;\n } finally {\n await teardown(rt);\n }\n },\n runFork: (effect) => {\n const rt = ManagedRuntime.make(layer(event));\n const fiber = rt.runFork(effect) as Fiber.Fiber<never, unknown>;\n fiber.addObserver(() => void teardown(rt));\n return fiber;\n },\n };\n },\n }),\n};\n\n/** A boundary translator: turn a failed `Cause` into a thrown value. */\nexport type ErrorTranslator = (cause: Cause.Cause<unknown>) => never;\n\n/** Options shared by {@link makeRuntimeAdapter}. */\nexport interface RuntimeAdapterOptions {\n /**\n * Translate a failed `Cause` into SvelteKit control flow at the boundary.\n * Called for `serverLoad` / `action` / `actions` failures; it may throw\n * `redirect()` / `error()` (and, for remote forms, call `invalid()`).\n * Defaults to the package's passthrough: SvelteKit's own `redirect()` /\n * `error()` are rethrown, anything else becomes an `EffectCauseError` (a 500).\n */\n readonly translateError?: ErrorTranslator;\n}\n\n/** Options for the adapter's `endpoint` wrapper. */\nexport interface AdapterEndpointOptions {\n /**\n * Include full error details (`Cause.pretty`) in the default 500 JSON body.\n * Ignored when a `translateError` is configured. Defaults to `false`.\n */\n readonly exposeErrors?: boolean;\n}\n\n/** The wrappers produced by {@link makeRuntimeAdapter}, bound to a provider. */\nexport interface RuntimeAdapter<R> {\n readonly serverLoad: <\n Params extends SvelteRouteParams = SvelteRouteParams,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>,\n ) => ServerLoad<Params, ParentData, OutputData, RouteId>;\n readonly action: <\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>,\n ) => Action<Params, OutputData, RouteId>;\n readonly actions: <\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n map: Record<\n string,\n (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>\n >,\n ) => Actions<Params, OutputData, RouteId>;\n readonly endpoint: <\n Params extends SvelteRouteParams = SvelteRouteParams,\n RouteId extends string | null = any,\n >(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, R>,\n options?: AdapterEndpointOptions,\n ) => RequestHandler<Params, RouteId>;\n}\n\n/**\n * Bind the SvelteKit server wrappers to a {@link RequestRuntimeProvider} (and an\n * optional boundary error translator).\n *\n * Unlike the standalone `serverLoad` / `action` / `endpoint`, the handler\n * effect's required services `R` come from the provider — your own application\n * catalogue — not the fixed {@link SvelteKitServices}. The package does not\n * merge `HttpClient` / `SvelteKitRequestEvent` onto this path; add them to your\n * own layer if you want them.\n *\n * @example\n * ```ts\n * // Package owns the runtime per request on Workers, disposed via waitUntil.\n * const { serverLoad } = makeRuntimeAdapter(\n * RequestRuntime.fromLayerPerRequest((event) => AppLayer(event.platform!.env), {\n * afterResponse: (event, dispose) => event.platform!.ctx.waitUntil(dispose()),\n * }),\n * );\n * ```\n */\nexport const makeRuntimeAdapter = <R>(\n provider: RequestRuntimeProvider<R, any>,\n opts?: RuntimeAdapterOptions,\n): RuntimeAdapter<R> => {\n const translateError: ErrorTranslator =\n opts?.translateError ??\n ((cause) => {\n throw causeToThrowable(cause);\n });\n\n const runSettle = async <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R>,\n ): Promise<A> => {\n const exit = await provider.run(event).runPromiseExit(effect);\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n return translateError(exit.cause);\n } finally {\n provider.disposeAfterResponse?.(event);\n }\n };\n\n const action: RuntimeAdapter<R>[\"action\"] = (fn) => (event) => runSettle(event, fn(event));\n\n return {\n serverLoad: (fn) => (event) => runSettle(event, fn(event)),\n action,\n actions: (map) => {\n const wrapped: Record<string, Action<any, any, any>> = {};\n for (const [key, fn] of Object.entries(map)) wrapped[key] = action(fn);\n return wrapped as never;\n },\n endpoint: (fn, options) => async (event) => {\n const exit = await provider.run(event as RequestEvent).runPromiseExit(fn(event));\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n // A configured translator owns the whole failure path (control flow,\n // structured error bodies, ...).\n if (opts?.translateError) return opts.translateError(exit.cause);\n // Default: pass SvelteKit control flow through, otherwise a 500.\n const squashed = Cause.squash(exit.cause);\n if (isRedirect(squashed) || isHttpError(squashed)) throw squashed;\n console.error(\"Endpoint error:\", Cause.pretty(exit.cause));\n const message = options?.exposeErrors ? Cause.pretty(exit.cause) : \"Internal server error\";\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } finally {\n provider.disposeAfterResponse?.(event as RequestEvent);\n }\n },\n };\n};\n"],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SvelteRouteParams } from "./internal/route-params.js";
|
|
1
2
|
import { SvelteKitRequestEvent, SvelteKitRuntime, runWithSvelteKitRuntime } from "./runtime.js";
|
|
2
3
|
import { AdapterEndpointOptions, ErrorTranslator, FromLayerPerRequestOptions, RequestRuntime, RequestRuntimeProvider, RequestRuntimeRunners, RuntimeAdapter, RuntimeAdapterOptions, makeRuntimeAdapter } from "./request-runtime.js";
|
|
3
4
|
import { RequestRuntime as RequestRuntime$1, ServerRuntime, ServerRuntimeOptions, SvelteKitServices, makeServerRuntime } from "./app-runtime.js";
|
|
@@ -36,7 +37,7 @@ import { Action, Actions, RequestEvent, ServerLoad } from "@sveltejs/kit";
|
|
|
36
37
|
* );
|
|
37
38
|
* ```
|
|
38
39
|
*/
|
|
39
|
-
declare function serverLoad<Params extends
|
|
40
|
+
declare function serverLoad<Params extends SvelteRouteParams = SvelteRouteParams, ParentData extends Record<string, any> = any, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, SvelteKitServices>): ServerLoad<Params, ParentData, OutputData, RouteId>;
|
|
40
41
|
/**
|
|
41
42
|
* Wraps an Effect-based function to work as a SvelteKit action handler.
|
|
42
43
|
* The effect has access to platform services (HttpClient, the RequestEvent, etc.)
|
|
@@ -58,7 +59,7 @@ declare function serverLoad<Params extends Record<string, string> = any, ParentD
|
|
|
58
59
|
* };
|
|
59
60
|
* ```
|
|
60
61
|
*/
|
|
61
|
-
declare function action<Params extends
|
|
62
|
+
declare function action<Params extends SvelteRouteParams = SvelteRouteParams, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, SvelteKitServices>): Action<Params, OutputData, RouteId>;
|
|
62
63
|
/**
|
|
63
64
|
* Creates SvelteKit actions from an object of Effect-based action handlers.
|
|
64
65
|
* Each action will have access to platform services via the SvelteKit runtime.
|
|
@@ -74,7 +75,7 @@ declare function action<Params extends Record<string, string> = any, OutputData
|
|
|
74
75
|
* });
|
|
75
76
|
* ```
|
|
76
77
|
*/
|
|
77
|
-
declare function actions<Params extends
|
|
78
|
+
declare function actions<Params extends SvelteRouteParams = SvelteRouteParams, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(actionsMap: Record<string, (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, SvelteKitServices>>): Actions<Params, OutputData, RouteId>;
|
|
78
79
|
/**
|
|
79
80
|
* Wraps an Effect-based function to work as a SvelteKit API endpoint handler.
|
|
80
81
|
* The effect has access to platform services (HttpClient, the RequestEvent, etc.)
|
|
@@ -101,7 +102,7 @@ declare function actions<Params extends Record<string, string> = any, OutputData
|
|
|
101
102
|
* );
|
|
102
103
|
* ```
|
|
103
104
|
*/
|
|
104
|
-
declare function endpoint<Params extends
|
|
105
|
+
declare function endpoint<Params extends SvelteRouteParams = SvelteRouteParams, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, SvelteKitServices>, options?: {
|
|
105
106
|
exposeErrors?: boolean;
|
|
106
107
|
}): (event: RequestEvent<Params, RouteId>) => Promise<Response>;
|
|
107
108
|
//#endregion
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":["runtime"],"sources":["../src/server.ts"],"sourcesContent":["import type { Action, Actions, RequestEvent, ServerLoad } from \"@sveltejs/kit\";\nimport { Effect, Exit, Cause } from \"effect\";\nimport { isHttpError, isRedirect } from \"@sveltejs/kit\";\nimport type { SvelteKitServices } from \"./app-runtime.js\";\nimport { defaultServerRuntime as runtime } from \"./internal/default-runtime.js\";\n\nexport type { SvelteKitServices };\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit server load function.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with {@link makeServerRuntime} and use its `serverLoad`\n * instead.\n *\n * @example\n * ```ts\n * import { serverLoad } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return { data: yield* response.json };\n * })\n * );\n * ```\n */\nexport function serverLoad<\n Params extends
|
|
1
|
+
{"version":3,"file":"server.js","names":["runtime"],"sources":["../src/server.ts"],"sourcesContent":["import type { Action, Actions, RequestEvent, ServerLoad } from \"@sveltejs/kit\";\nimport { Effect, Exit, Cause } from \"effect\";\nimport { isHttpError, isRedirect } from \"@sveltejs/kit\";\nimport type { SvelteKitServices } from \"./app-runtime.js\";\nimport type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport { defaultServerRuntime as runtime } from \"./internal/default-runtime.js\";\n\nexport type { SvelteKitServices };\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit server load function.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with {@link makeServerRuntime} and use its `serverLoad`\n * instead.\n *\n * @example\n * ```ts\n * import { serverLoad } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return { data: yield* response.json };\n * })\n * );\n * ```\n */\nexport function serverLoad<\n Params extends SvelteRouteParams = SvelteRouteParams,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, SvelteKitServices>,\n): ServerLoad<Params, ParentData, OutputData, RouteId> {\n return runtime().serverLoad(fn);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit action handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { action } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = {\n * default: action((event) =>\n * Effect.gen(function* () {\n * const formData = yield* Effect.promise(() => event.request.formData());\n * // Process form data\n * return { success: true };\n * })\n * )\n * };\n * ```\n */\nexport function action<\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, SvelteKitServices>,\n): Action<Params, OutputData, RouteId> {\n return runtime().action(fn);\n}\n\n/**\n * Creates SvelteKit actions from an object of Effect-based action handlers.\n * Each action will have access to platform services via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { actions } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = actions({\n * create: (event) => Effect.succeed({ created: true }),\n * delete: (event) => Effect.succeed({ deleted: true })\n * });\n * ```\n */\nexport function actions<\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n actionsMap: Record<\n string,\n (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, SvelteKitServices>\n >,\n): Actions<Params, OutputData, RouteId> {\n return runtime().actions(actionsMap);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit API endpoint handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit. For any other failure, the error is logged\n * server-side and a generic error message is returned to clients. For\n * development/debugging, you can pass options to expose full error details.\n *\n * @example\n * ```ts\n * import { endpoint } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import { json } from '@sveltejs/kit';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const GET = endpoint((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return json(yield* response.json);\n * })\n * );\n * ```\n */\nexport function endpoint<\n Params extends SvelteRouteParams = SvelteRouteParams,\n RouteId extends string | null = any,\n>(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, SvelteKitServices>,\n options?: { exposeErrors?: boolean },\n): (event: RequestEvent<Params, RouteId>) => Promise<Response> {\n const { exposeErrors = false } = options ?? {};\n\n return async (event) => {\n const exit = await runtime().runPromiseExit(event as RequestEvent, fn(event));\n\n return Exit.match(exit, {\n onFailure: (cause) => {\n // Let SvelteKit handle its own control flow objects.\n const squashed = Cause.squash(cause);\n if (isRedirect(squashed) || isHttpError(squashed)) {\n throw squashed;\n }\n\n // Always log the full error server-side for debugging\n console.error(\"Endpoint error:\", Cause.pretty(cause));\n\n // Return generic error message to client unless explicitly enabled\n const clientMessage = exposeErrors ? Cause.pretty(cause) : \"Internal server error\";\n\n return new Response(JSON.stringify({ error: clientMessage }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n },\n onSuccess: (value) => value,\n });\n };\n}\n\n/**\n * Effect wrappers for the server-side hooks (`hooks.server.ts`): the `Hooks`\n * namespace exposes `handle`, `handleFetch`, `handleError`, and `init`.\n */\nexport * as Hooks from \"./server/Hooks.js\";\n\n/**\n * Create SvelteKit `+server.ts` endpoints backed by Effect: the `Endpoint`\n * namespace exposes `fromApi` (an `HttpApi`), `fromRouter` (an `HttpRouter`),\n * and `fromHttpEffect` (a single HTTP route effect).\n */\nexport * as Endpoint from \"./server/Endpoint.js\";\n\n/**\n * App-wide runtime: define your application's Effect services once and share\n * them across every request.\n */\nexport {\n makeServerRuntime,\n type ServerRuntime,\n type ServerRuntimeOptions,\n type RequestRuntime as ScopedRequestRuntime,\n} from \"./app-runtime.js\";\n\n/**\n * Export the SvelteKit runtime utilities for advanced use cases.\n */\nexport { SvelteKitRuntime, runWithSvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nexport { RemoteFunction } from \"./remote.server.js\";\n\n/**\n * A general, Effect-idiomatic seam for *how the per-request execution context is\n * obtained*. {@link RequestRuntime} constructs a {@link RequestRuntimeProvider}\n * (from a consumer-owned runtime on `event.locals`, a package-built\n * per-request runtime, a build-once `ManagedRuntime`, or a `Layer`), and\n * {@link makeRuntimeAdapter} binds the `serverLoad` / `action` / `actions` /\n * `endpoint` wrappers to it — so the handler effect's services `R` are your own\n * application catalogue, not the fixed {@link SvelteKitServices}. A\n * {@link RuntimeAdapterOptions.translateError} maps a failed `Cause` to\n * SvelteKit control flow at the boundary.\n */\nexport {\n RequestRuntime,\n makeRuntimeAdapter,\n type RequestRuntimeProvider,\n type RequestRuntimeRunners,\n type RuntimeAdapter,\n type RuntimeAdapterOptions,\n type AdapterEndpointOptions,\n type FromLayerPerRequestOptions,\n type ErrorTranslator,\n} from \"./request-runtime.js\";\n\n/**\n * SvelteKit `$env` ⇄ Effect `Config` (see {@link sveltekitConfig}). The\n * Effect Schema ⇄ SvelteKit `transport` hook lives on the universal `Hooks`\n * namespace (`Hooks.transport`) in `@thomasfosterau/effect-sveltekit`.\n */\nexport { sveltekitConfig, sveltekitConfigProvider } from \"./config.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,WAMd,IAGqD;CACrD,OAAOA,qBAAQ,EAAE,WAAW,EAAE;AAChC;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,OAKd,IAGqC;CACrC,OAAOA,qBAAQ,EAAE,OAAO,EAAE;AAC5B;;;;;;;;;;;;;;;;AAiBA,SAAgB,QAKd,YAIsC;CACtC,OAAOA,qBAAQ,EAAE,QAAQ,UAAU;AACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,SAId,IACA,SAC6D;CAC7D,MAAM,EAAE,eAAe,UAAU,WAAW,CAAC;CAE7C,OAAO,OAAO,UAAU;EACtB,MAAM,OAAO,MAAMA,qBAAQ,EAAE,eAAe,OAAuB,GAAG,KAAK,CAAC;EAE5E,OAAO,KAAK,MAAM,MAAM;GACtB,YAAY,UAAU;IAEpB,MAAM,WAAW,MAAM,OAAO,KAAK;IACnC,IAAI,WAAW,QAAQ,KAAK,YAAY,QAAQ,GAC9C,MAAM;IAIR,QAAQ,MAAM,mBAAmB,MAAM,OAAO,KAAK,CAAC;IAGpD,MAAM,gBAAgB,eAAe,MAAM,OAAO,KAAK,IAAI;IAE3D,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,cAAc,CAAC,GAAG;KAC5D,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;IAChD,CAAC;GACH;GACA,YAAY,UAAU;EACxB,CAAC;CACH;AACF"}
|
package/dist/universal.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SvelteRouteParams } from "./internal/route-params.js";
|
|
1
2
|
import { UniversalRuntime, makeUniversalRuntime } from "./app-runtime.js";
|
|
2
3
|
import { Effect } from "effect";
|
|
3
4
|
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
@@ -29,7 +30,7 @@ import { Load } from "@sveltejs/kit";
|
|
|
29
30
|
* );
|
|
30
31
|
* ```
|
|
31
32
|
*/
|
|
32
|
-
declare function load<Params extends
|
|
33
|
+
declare function load<Params extends SvelteRouteParams = SvelteRouteParams, InputData extends Record<string, unknown> | null = any, ParentData extends Record<string, unknown> = any, OutputData extends Record<string, unknown> | void = any, RouteId extends string | null = any>(fn: (event: Parameters<Load<Params, InputData, ParentData, OutputData, RouteId>>[0]) => Effect.Effect<OutputData, unknown, HttpClient.HttpClient>): Load<Params, InputData, ParentData, OutputData, RouteId>;
|
|
33
34
|
//#endregion
|
|
34
35
|
export { load };
|
|
35
36
|
//# sourceMappingURL=universal.d.ts.map
|
package/dist/universal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"universal.js","names":["runtime"],"sources":["../src/universal.ts"],"sourcesContent":["import type { Load } from \"@sveltejs/kit\";\nimport { Effect } from \"effect\";\nimport type * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport { defaultUniversalRuntime as runtime } from \"./internal/default-runtime.js\";\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit universal load function.\n *\n * The effect has access to `HttpClient` on both the server and the client:\n * it is backed by the load event's `fetch`, so relative URLs and SvelteKit's\n * fetch behaviours (deduplication, cookies forwarding) work as expected.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with `makeUniversalRuntime` and use its `load` instead.\n *\n * @example\n * ```ts\n * import { load as effectLoad } from '@thomasfosterau/effect-sveltekit';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n * import { Effect } from 'effect';\n *\n * export const load = effectLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(`/api/items/${event.params.id}`);\n * return { item: yield* response.json };\n * })\n * );\n * ```\n */\nexport function load<\n Params extends
|
|
1
|
+
{"version":3,"file":"universal.js","names":["runtime"],"sources":["../src/universal.ts"],"sourcesContent":["import type { Load } from \"@sveltejs/kit\";\nimport { Effect } from \"effect\";\nimport type * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport { defaultUniversalRuntime as runtime } from \"./internal/default-runtime.js\";\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit universal load function.\n *\n * The effect has access to `HttpClient` on both the server and the client:\n * it is backed by the load event's `fetch`, so relative URLs and SvelteKit's\n * fetch behaviours (deduplication, cookies forwarding) work as expected.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with `makeUniversalRuntime` and use its `load` instead.\n *\n * @example\n * ```ts\n * import { load as effectLoad } from '@thomasfosterau/effect-sveltekit';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n * import { Effect } from 'effect';\n *\n * export const load = effectLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(`/api/items/${event.params.id}`);\n * return { item: yield* response.json };\n * })\n * );\n * ```\n */\nexport function load<\n Params extends SvelteRouteParams = SvelteRouteParams,\n InputData extends Record<string, unknown> | null = any,\n ParentData extends Record<string, unknown> = any,\n OutputData extends Record<string, unknown> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: Parameters<Load<Params, InputData, ParentData, OutputData, RouteId>>[0],\n ) => Effect.Effect<OutputData, unknown, HttpClient.HttpClient>,\n): Load<Params, InputData, ParentData, OutputData, RouteId> {\n return runtime().load(fn);\n}\n\n/**\n * App-wide runtime: define your application's (universal) Effect services\n * once and share them across every `load` invocation.\n */\nexport { makeUniversalRuntime, type UniversalRuntime } from \"./app-runtime.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,KAOd,IAG0D;CAC1D,OAAOA,wBAAQ,EAAE,KAAK,EAAE;AAC1B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thomasfosterau/effect-sveltekit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Effect integration for SvelteKit: load/action/endpoint wrappers, remote functions, an effectKit() Vite plugin, and an Effect HTTP deployment adapter",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect",
|