@thomasfosterau/effect-sveltekit 0.5.0 → 0.7.0
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/LICENSE.md +9 -0
- package/README.md +178 -83
- package/dist/Hooks.d.ts +3 -3
- package/dist/Hooks.js +1 -1
- package/dist/Hooks.js.map +1 -1
- package/dist/Navigation.d.ts +70 -17
- package/dist/Navigation.js +93 -19
- package/dist/Navigation.js.map +1 -1
- package/dist/Remote.d.ts +54 -35
- package/dist/Remote.js +22 -11
- package/dist/Remote.js.map +1 -1
- package/dist/State.d.ts +79 -6
- package/dist/State.js +117 -27
- package/dist/State.js.map +1 -1
- package/dist/adapter/assets.d.ts +24 -7
- package/dist/adapter/assets.js +58 -37
- package/dist/adapter/assets.js.map +1 -1
- package/dist/adapter/handler.d.ts +27 -12
- package/dist/adapter/handler.js +53 -26
- package/dist/adapter/handler.js.map +1 -1
- package/dist/adapter/index.d.ts +3 -3
- package/dist/adapter/index.js +1 -1
- package/dist/adapter/index.js.map +1 -1
- package/dist/adapter/serve.d.ts +6 -5
- package/dist/adapter/serve.js +3 -1
- package/dist/adapter/serve.js.map +1 -1
- package/dist/api-client.d.ts +3 -4
- package/dist/api-client.js +2 -1
- package/dist/api-client.js.map +1 -1
- package/dist/app-runtime.d.ts +12 -9
- package/dist/app-runtime.js +23 -14
- package/dist/app-runtime.js.map +1 -1
- package/dist/client/Hooks.d.ts +4 -4
- package/dist/client/Hooks.js +2 -1
- package/dist/client/Hooks.js.map +1 -1
- package/dist/client.d.ts +2 -2
- package/dist/config.d.ts +7 -5
- package/dist/config.js +5 -34
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +14 -16
- package/dist/errors.js +20 -12
- package/dist/errors.js.map +1 -1
- package/dist/forms.d.ts +70 -8
- package/dist/forms.js +69 -5
- package/dist/forms.js.map +1 -1
- package/dist/internal/app-state.d.ts +42 -5
- package/dist/internal/app-state.js +64 -15
- package/dist/internal/app-state.js.map +1 -1
- package/dist/internal/auto.d.ts +23 -14
- package/dist/internal/auto.js +50 -40
- package/dist/internal/auto.js.map +1 -1
- package/dist/internal/enhance.d.ts +114 -14
- package/dist/internal/enhance.js +33 -7
- package/dist/internal/enhance.js.map +1 -1
- package/dist/internal/error.d.ts +13 -0
- package/dist/internal/error.js +4 -3
- package/dist/internal/error.js.map +1 -1
- package/dist/internal/navigation-events.js +3 -1
- package/dist/internal/navigation-events.js.map +1 -1
- package/dist/internal/remote-run.d.ts +6 -6
- package/dist/internal/remote-run.js +13 -16
- package/dist/internal/remote-run.js.map +1 -1
- package/dist/internal/remote-validators.js +4 -3
- package/dist/internal/remote-validators.js.map +1 -1
- package/dist/internal/route-params.d.ts +1 -2
- package/dist/internal/sse.js +2 -1
- package/dist/internal/sse.js.map +1 -1
- package/dist/internal/watch.svelte.js +33 -0
- package/dist/internal/watch.svelte.js.map +1 -0
- package/dist/kit.d.ts +12 -11
- package/dist/kit.js +6 -2
- package/dist/kit.js.map +1 -1
- package/dist/realtime.d.ts +49 -30
- package/dist/realtime.js +90 -46
- package/dist/realtime.js.map +1 -1
- package/dist/remote-functions.d.ts +34 -18
- package/dist/remote-functions.js +78 -46
- package/dist/remote-functions.js.map +1 -1
- package/dist/remote-reactive.d.ts +8 -4
- package/dist/remote-reactive.js.map +1 -1
- package/dist/request-parse.d.ts +13 -13
- package/dist/request-parse.js +29 -15
- package/dist/request-parse.js.map +1 -1
- package/dist/request-runtime.d.ts +16 -11
- package/dist/request-runtime.js +28 -29
- package/dist/request-runtime.js.map +1 -1
- package/dist/request-services.d.ts +10 -8
- package/dist/request-services.js +3 -1
- package/dist/request-services.js.map +1 -1
- package/dist/response-headers.d.ts +2 -3
- package/dist/response-headers.js +2 -1
- package/dist/response-headers.js.map +1 -1
- package/dist/runtime.d.ts +8 -5
- package/dist/runtime.js +4 -1
- package/dist/runtime.js.map +1 -1
- package/dist/server/Endpoint.d.ts +17 -11
- package/dist/server/Endpoint.js +18 -4
- package/dist/server/Endpoint.js.map +1 -1
- package/dist/server/Hooks.d.ts +6 -6
- package/dist/server/Hooks.js +1 -1
- package/dist/server/Hooks.js.map +1 -1
- package/dist/server.d.ts +8 -7
- package/dist/server.js +8 -8
- package/dist/server.js.map +1 -1
- package/dist/streamed-load.d.ts +2 -3
- package/dist/streamed-load.js +1 -1
- package/dist/streamed-load.js.map +1 -1
- package/dist/testing.d.ts +15 -14
- package/dist/testing.js +27 -7
- package/dist/testing.js.map +1 -1
- package/dist/transport.d.ts +6 -7
- package/dist/transport.js +3 -6
- package/dist/transport.js.map +1 -1
- package/dist/universal.d.ts +3 -3
- package/dist/universal.js +1 -1
- package/dist/universal.js.map +1 -1
- package/dist/vite.d.ts +4 -4
- package/dist/vite.js +102 -103
- package/dist/vite.js.map +1 -1
- package/package.json +14 -11
- package/LICENSE +0 -21
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 { Action, Actions, Load, RequestEvent, ServerLoad } from \"@sveltejs/kit\";\nimport type { Handle, HandleFetch, HandleServerError, ServerInit } from \"@sveltejs/kit/hooks\";\nimport { Context, Effect, type Fiber, Exit, Layer, ManagedRuntime, Scope } from \"effect\";\nimport * as FetchHttpClient from \"effect/http/FetchHttpClient\";\nimport type * as HttpClient from \"effect/http/HttpClient\";\nimport { SvelteKitRequestEvent } from \"./runtime.js\";\nimport type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport type { ErrorTranslator } from \"./request-runtime.js\";\nimport { SvelteKit } from \"./kit.js\";\nimport {\n SvelteKitCookies,\n SvelteKitLocals,\n SvelteKitResponseHeaders,\n requestServicesFrom,\n} from \"./request-services.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 =\n | HttpClient.HttpClient\n | SvelteKitRequestEvent\n | SvelteKitCookies\n | SvelteKitResponseHeaders\n | SvelteKitLocals;\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 const services = requestServicesFrom(event);\n return Layer.mergeAll(\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, event.fetch))),\n Layer.succeed(SvelteKitRequestEvent, event),\n Layer.succeed(SvelteKitCookies, services.cookies),\n Layer.succeed(SvelteKitResponseHeaders, services.responseHeaders),\n Layer.succeed(SvelteKitLocals, services.locals),\n );\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: Kit control-flow errors through, anything else a 500. */\nconst settle = makeSettle(SvelteKit.translate);\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.ts';\n * import { Effect } from 'effect';\n * import { Database } from '#lib/server/database.ts';\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) =>\n run(\n event,\n fn(event).pipe(\n Effect.withSpan(`action ${event.route.id ?? \"(unknown)\"}`, { kind: \"server\" }),\n ),\n );\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(\n fn(input).pipe(\n Effect.withSpan(`handle ${event.route.id ?? \"(unknown)\"}`, { kind: \"server\" }),\n ),\n );\n } finally {\n await runtime.runPromise(Scope.close(scope, Exit.void));\n }\n };\n\n return {\n runtime,\n serverLoad: (fn) => (event) =>\n run(\n event,\n fn(event).pipe(\n Effect.withSpan(`serverLoad ${event.route.id ?? \"(unknown)\"}`, { kind: \"server\" }),\n ),\n ),\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) =>\n run(\n args.event,\n fn(args).pipe(\n Effect.withSpan(`handleFetch ${args.event.route.id ?? \"(unknown)\"}`, { kind: \"server\" }),\n ),\n ),\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) =>\n run(\n event,\n fn(event).pipe(\n Effect.withSpan(`${event.request.method} ${event.route.id ?? \"(unknown)\"}`, {\n kind: \"server\",\n }),\n ),\n ),\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.ts';\n *\n * export const runtime = makeUniversalRuntime(ApiClient.layer);\n * ```\n *\n * ```ts\n * // src/routes/+page.ts\n * import { runtime } from '#lib/runtime.ts';\n * import { Effect } from 'effect';\n * import { ApiClient } from '#lib/api.ts';\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 { Action, Actions, Load, RequestEvent, ServerLoad } from \"@sveltejs/kit\";\nimport type { Handle, HandleFetch, HandleServerError, ServerInit } from \"@sveltejs/kit/hooks\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport type * as Fiber from \"effect/Fiber\";\nimport * as Exit from \"effect/Exit\";\nimport * as Layer from \"effect/Layer\";\nimport * as ManagedRuntime from \"effect/ManagedRuntime\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport * as Result from \"effect/Result\";\nimport * as Scope from \"effect/Scope\";\nimport * as FetchHttpClient from \"effect/http/FetchHttpClient\";\nimport type * as HttpClient from \"effect/http/HttpClient\";\nimport { SvelteKitRequestEvent } from \"./runtime.js\";\nimport type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport type { ErrorTranslator } from \"./request-runtime.js\";\nimport { SvelteKit } from \"./kit.js\";\nimport {\n SvelteKitCookies,\n SvelteKitLocals,\n SvelteKitResponseHeaders,\n requestServicesFrom,\n} from \"./request-services.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 =\n | HttpClient.HttpClient\n | SvelteKitRequestEvent\n | SvelteKitCookies\n | SvelteKitResponseHeaders\n | SvelteKitLocals;\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/** `event.locals` as this module sees it: possibly carrying a {@link RequestRuntimeEntry}. */\ninterface LocalsWithRequestRuntime {\n [RequestRuntimeKey]?: RequestRuntimeEntry | undefined;\n}\n\n/** Read the per-request runtime entry an event carries, if any. */\nconst requestRuntimeEntry = (event: RequestEvent): RequestRuntimeEntry | undefined =>\n // SAFETY: `RequestRuntimeKey` is this package's namespaced symbol, and the\n // only value ever stored under it on `locals` is a `RequestRuntimeEntry`.\n (event.locals as LocalsWithRequestRuntime | undefined)?.[RequestRuntimeKey];\n\n/** Whether a universal load event is a server-side `RequestEvent` (it carries a `request`). */\nconst isRequestEvent = <Event>(event: Event): event is Event & RequestEvent =>\n Predicate.hasProperty(event, \"request\") && Boolean(event.request);\n\n/** Layer with the per-request services for a server-side request. */\nconst requestLayer = (event: RequestEvent): Layer.Layer<SvelteKitServices> => {\n const services = requestServicesFrom(event);\n return Layer.mergeAll(\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, event.fetch))),\n Layer.succeed(SvelteKitRequestEvent, event),\n Layer.succeed(SvelteKitCookies, services.cookies),\n Layer.succeed(SvelteKitResponseHeaders, services.responseHeaders),\n Layer.succeed(SvelteKitLocals, services.locals),\n );\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: Kit control-flow errors through, anything else a 500. */\nconst settle = makeSettle(SvelteKit.translate);\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.ts';\n * import { Effect } from 'effect';\n * import { Database } from '#lib/server/database.ts';\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 // SAFETY: a runtime made without a layer has the defaults `R = never` and\n // `E = never`, which `Layer.empty` provides.\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 // SAFETY: providing the `SvelteKitServices` leaves at most `R`; the\n // `Exclude` only stays unresolved while `R` is generic.\n Effect.provideContext(effect, requestServices) as Effect.Effect<A, unknown, R>;\n return {\n runPromiseExit: (effect) => runtime.runPromiseExit(provide(effect)),\n runPromise: (effect) => runtime.runPromiseExit(provide(effect)).then(settleRequest),\n runFork: (effect) => runtime.runFork(provide(effect)),\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 // SAFETY: an entry tagged with this runtime's `owner` was created by its\n // `handle`, from `makeRequestRuntime`, so it is a `RequestRuntime<R>`.\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 // SAFETY: the request layer provides the `SvelteKitServices`, leaving at\n // most `R`; the `Exclude` only stays unresolved while `R` is generic.\n const provided = Effect.provide(effect, requestLayer(event)) as Effect.Effect<A, unknown, R>;\n return runtime.runPromiseExit(provided);\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) =>\n run(\n event,\n fn(event).pipe(\n Effect.withSpan(`action ${event.route.id ?? \"(unknown)\"}`, { kind: \"server\" }),\n ),\n );\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: Context.Context<SvelteKitServices> = await runtime.runPromise(\n Layer.buildWithScope(requestLayer(event), scope),\n );\n\n const requestRuntime = makeRequestRuntime(requestServices);\n const entry: RequestRuntimeEntry = { owner, runtime: requestRuntime };\n // SAFETY: `locals` is the request's plain, writable object; the namespaced\n // symbol key cannot clash with the app's own `App.Locals` fields.\n (event.locals as LocalsWithRequestRuntime)[RequestRuntimeKey] = entry;\n\n // Close the request scope once the user's effect settles, also when\n // building it throws, then surface its outcome (or the close's failure).\n const closeScope = () => runtime.runPromise(Scope.close(scope, Exit.void));\n const running = Result.try(() =>\n requestRuntime.runPromise(\n fn(input).pipe(\n Effect.withSpan(`handle ${event.route.id ?? \"(unknown)\"}`, { kind: \"server\" }),\n ),\n ),\n );\n return Result.match(running, {\n onSuccess: (response) => response.finally(closeScope),\n onFailure: (thrown) =>\n closeScope().then(() => {\n throw thrown;\n }),\n });\n };\n\n return {\n runtime,\n serverLoad: (fn) => (event) =>\n run(\n event,\n fn(event).pipe(\n Effect.withSpan(`serverLoad ${event.route.id ?? \"(unknown)\"}`, { kind: \"server\" }),\n ),\n ),\n action,\n actions: (actionsMap) => Rec.map(actionsMap, (fn) => action(fn)),\n handle,\n handleFetch: (fn) => (args) =>\n run(\n args.event,\n fn(args).pipe(\n Effect.withSpan(`handleFetch ${args.event.route.id ?? \"(unknown)\"}`, { kind: \"server\" }),\n ),\n ),\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) =>\n run(\n event,\n fn(event).pipe(\n Effect.withSpan(`${event.request.method} ${event.route.id ?? \"(unknown)\"}`, {\n kind: \"server\",\n }),\n ),\n ),\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.ts';\n *\n * export const runtime = makeUniversalRuntime(ApiClient.layer);\n * ```\n *\n * ```ts\n * // src/routes/+page.ts\n * import { runtime } from '#lib/runtime.ts';\n * import { Effect } from 'effect';\n * import { ApiClient } from '#lib/api.ts';\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 // SAFETY: a runtime made without a layer has the defaults `R = never` and\n // `E = never`, which `Layer.empty` provides.\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 // SAFETY: both layers provide `HttpClient`, leaving at most `R`; the\n // `Exclude` only stays unresolved while `R` is generic.\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 return run(event.fetch, fn(event), isRequestEvent(event) ? event : undefined);\n },\n runPromise: (fetchFn, effect) => run(fetchFn, effect),\n dispose: () => runtime.dispose(),\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAgFA,MAAM,oBAAmC,OAAO,IAC9C,iDACF;;AAmBA,MAAM,uBAAuB,UAG1B,MAAM,SAAkD;;AAG3D,MAAM,kBAAyB,UAC7B,UAAU,YAAY,OAAO,SAAS,KAAK,QAAQ,MAAM,OAAO;;AAGlE,MAAM,gBAAgB,UAAwD;CAC5E,MAAM,WAAW,oBAAoB,KAAK;CAC1C,OAAO,MAAM,SACX,gBAAgB,MAAM,KAAK,MAAM,QAAQ,MAAM,QAAQ,gBAAgB,OAAO,MAAM,KAAK,CAAC,CAAC,GAC3F,MAAM,QAAQ,uBAAuB,KAAK,GAC1C,MAAM,QAAQ,kBAAkB,SAAS,OAAO,GAChD,MAAM,QAAQ,0BAA0B,SAAS,eAAe,GAChE,MAAM,QAAQ,iBAAiB,SAAS,MAAM,CAChD;AACF;;AAGA,MAAM,cAAc,YAClB,gBAAgB,MAAM,KAAK,MAAM,QAAQ,MAAM,QAAQ,gBAAgB,OAAO,OAAO,CAAC,CAAC;;AAGzF,MAAM,cACH,eACG,SACF,KAAK,MAAM,MAAM;CACf,YAAY,UAAU,UAAU,KAAK;CACrC,YAAY,UAAU;AACxB,CAAC;;AAGL,MAAM,SAAS,WAAW,UAAU,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6M7C,MAAa,qBACX,OACA,YACwB;CAGxB,MAAM,UAAU,eAAe,KAAM,SAAS,MAAM,KAA2B;CAI/E,MAAM,gBAAgB,SAAS,iBAAiB,WAAW,QAAQ,cAAc,IAAI;CAIrF,MAAM,QAAQ,CAAC;;CAGf,MAAM,sBACJ,oBACsB;EACtB,MAAM,WAAc,WAGlB,OAAO,eAAe,QAAQ,eAAe;EAC/C,OAAO;GACL,iBAAiB,WAAW,QAAQ,eAAe,QAAQ,MAAM,CAAC;GAClE,aAAa,WAAW,QAAQ,eAAe,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK,aAAa;GAClF,UAAU,WAAW,QAAQ,QAAQ,QAAQ,MAAM,CAAC;EACtD;CACF;;CAGA,MAAM,oBAAoB,UAAuD;EAC/E,MAAM,QAAQ,oBAAoB,KAAK;EAGvC,OAAO,OAAO,UAAU,QAAS,MAAM,UAAgC,KAAA;CACzE;CAEA,MAAM,WACJ,OACA,WACmC;EAGnC,MAAM,iBAAiB,iBAAiB,KAAK;EAC7C,IAAI,mBAAmB,KAAA,GAAW,OAAO,eAAe,eAAe,MAAM;EAG7E,MAAM,WAAW,OAAO,QAAQ,QAAQ,aAAa,KAAK,CAAC;EAC3D,OAAO,QAAQ,eAAe,QAAQ;CACxC;CAEA,MAAM,OACJ,OACA,WACe,QAAQ,OAAO,MAAM,CAAC,CAAC,KAAK,aAAa;CAE1D,MAAM,UAAyC,QAAQ,UACrD,IACE,OACA,GAAG,KAAK,CAAC,CAAC,KACR,OAAO,SAAS,UAAU,MAAM,MAAM,MAAM,eAAe,EAAE,MAAM,SAAS,CAAC,CAC/E,CACF;;;;;;CAOF,MAAM,UAAyC,OAAO,OAAO,UAAU;EACrE,MAAM,EAAE,UAAU;EAClB,MAAM,QAAQ,MAAM,WAAW;EAC/B,MAAM,kBAAsD,MAAM,QAAQ,WACxE,MAAM,eAAe,aAAa,KAAK,GAAG,KAAK,CACjD;EAEA,MAAM,iBAAiB,mBAAmB,eAAe;EACzD,MAAM,QAA6B;GAAE;GAAO,SAAS;EAAe;EAGpE,MAAO,OAAoC,qBAAqB;EAIhE,MAAM,mBAAmB,QAAQ,WAAW,MAAM,MAAM,OAAO,KAAK,IAAI,CAAC;EACzE,MAAM,UAAU,OAAO,UACrB,eAAe,WACb,GAAG,KAAK,CAAC,CAAC,KACR,OAAO,SAAS,UAAU,MAAM,MAAM,MAAM,eAAe,EAAE,MAAM,SAAS,CAAC,CAC/E,CACF,CACF;EACA,OAAO,OAAO,MAAM,SAAS;GAC3B,YAAY,aAAa,SAAS,QAAQ,UAAU;GACpD,YAAY,WACV,WAAW,CAAC,CAAC,WAAW;IACtB,MAAM;GACR,CAAC;EACL,CAAC;CACH;CAEA,OAAO;EACL;EACA,aAAa,QAAQ,UACnB,IACE,OACA,GAAG,KAAK,CAAC,CAAC,KACR,OAAO,SAAS,cAAc,MAAM,MAAM,MAAM,eAAe,EAAE,MAAM,SAAS,CAAC,CACnF,CACF;EACF;EACA,UAAU,eAAe,IAAI,IAAI,aAAa,OAAO,OAAO,EAAE,CAAC;EAC/D;EACA,cAAc,QAAQ,SACpB,IACE,KAAK,OACL,GAAG,IAAI,CAAC,CAAC,KACP,OAAO,SAAS,eAAe,KAAK,MAAM,MAAM,MAAM,eAAe,EAAE,MAAM,SAAS,CAAC,CACzF,CACF;EACF,cAAc,QAAQ,SAAS,IAAI,KAAK,OAAO,GAAG,IAAI,CAAC;EACvD,OAAO,WAAW,YAAY;GAG5B,cAAc,MAAM,QAAQ,eAAe,MAAM,CAAC;EACpD;EACA,gBAAgB;EAChB,WAAW,QAAQ,UACjB,IACE,OACA,GAAG,KAAK,CAAC,CAAC,KACR,OAAO,SAAS,GAAG,MAAM,QAAQ,OAAO,GAAG,MAAM,MAAM,MAAM,eAAe,EAC1E,MAAM,SACR,CAAC,CACH,CACF;EACF,YAAY;EACZ,gBAAgB;EAChB,eAAe,QAAQ,QAAQ;CACjC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEA,MAAa,wBACX,UAC2B;CAG3B,MAAM,UAAU,eAAe,KAAM,SAAS,MAAM,KAA2B;CAE/E,MAAM,OACJ,SACA,QACA,UACe;EAGf,MAAM,WAAW,QAAQ,aAAa,KAAK,IAAI,WAAW,OAAO;EAGjE,MAAM,WAAW,OAAO,QAAQ,QAAQ,QAAQ;EAChD,OAAO,QAAQ,eAAe,QAAQ,CAAC,CAAC,KAAK,MAAM;CACrD;CAEA,OAAO;EACL;EACA,OAAO,QAAQ,UAAU;GACvB,OAAO,IAAI,MAAM,OAAO,GAAG,KAAK,GAAG,eAAe,KAAK,IAAI,QAAQ,KAAA,CAAS;EAC9E;EACA,aAAa,SAAS,WAAW,IAAI,SAAS,MAAM;EACpD,eAAe,QAAQ,QAAQ;CACjC;AACF"}
|
package/dist/client/Hooks.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
2
|
import { ClientInit, HandleClientError } from "@sveltejs/kit/hooks";
|
|
3
3
|
declare namespace Hooks_d_exports {
|
|
4
4
|
export { handleError, init };
|
|
@@ -24,7 +24,7 @@ declare namespace Hooks_d_exports {
|
|
|
24
24
|
* );
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
|
-
declare function handleError(fn: (args: Parameters<HandleClientError>[0]) => Effect.Effect<Awaited<ReturnType<HandleClientError>>, unknown>): HandleClientError;
|
|
27
|
+
export declare function handleError(fn: (args: Parameters<HandleClientError>[0]) => Effect.Effect<Awaited<ReturnType<HandleClientError>>, unknown>): HandleClientError;
|
|
28
28
|
/**
|
|
29
29
|
* Wraps an Effect as the SvelteKit client
|
|
30
30
|
* [`init`](https://svelte.dev/docs/kit/hooks#Shared-hooks-init) hook, which
|
|
@@ -40,7 +40,7 @@ declare function handleError(fn: (args: Parameters<HandleClientError>[0]) => Eff
|
|
|
40
40
|
* export const init = Hooks.init(Effect.logInfo('client app started'));
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
|
-
declare function init(effect: Effect.Effect<void, unknown>): ClientInit;
|
|
43
|
+
export declare function init(effect: Effect.Effect<void, unknown>): ClientInit;
|
|
44
44
|
//#endregion
|
|
45
|
-
export { Hooks_d_exports
|
|
45
|
+
export { Hooks_d_exports };
|
|
46
46
|
//# sourceMappingURL=Hooks.d.ts.map
|
package/dist/client/Hooks.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { causeToThrowable } from "../internal/error.js";
|
|
3
|
-
import
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Exit from "effect/Exit";
|
|
4
5
|
//#region src/client/Hooks.ts
|
|
5
6
|
var Hooks_exports = /* @__PURE__ */ __exportAll({
|
|
6
7
|
handleError: () => handleError,
|
package/dist/client/Hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Hooks.js","names":[],"sources":["../../src/client/Hooks.ts"],"sourcesContent":["/**\n * Effect wrappers for SvelteKit's client-side hooks (`src/hooks.client.ts`),\n * exposed as the `Hooks` namespace of `@thomasfosterau/effect-sveltekit/client`.\n *\n * These mirror the server hook helpers in `../server/Hooks.ts`, but run on the\n * client: there is no request context, so each effect runs on a bare Effect\n * runtime. For application services, run the effect through your own runtime\n * instead. Kept free of `$app/*` imports so it stays importable (and testable)\n * outside of a SvelteKit app.\n *\n * @example\n * ```ts\n * // src/hooks.client.ts\n * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';\n * import { Effect } from 'effect';\n *\n * export const handleError = Hooks.handleError(({ error, event }) =>\n * Effect.as(Effect.logError(`navigation to ${event.url.pathname} failed`, error), {\n * message: 'Something went wrong',\n * })\n * );\n * ```\n */\n\nimport type { ClientInit, HandleClientError } from \"@sveltejs/kit/hooks\";\nimport
|
|
1
|
+
{"version":3,"file":"Hooks.js","names":[],"sources":["../../src/client/Hooks.ts"],"sourcesContent":["/**\n * Effect wrappers for SvelteKit's client-side hooks (`src/hooks.client.ts`),\n * exposed as the `Hooks` namespace of `@thomasfosterau/effect-sveltekit/client`.\n *\n * These mirror the server hook helpers in `../server/Hooks.ts`, but run on the\n * client: there is no request context, so each effect runs on a bare Effect\n * runtime. For application services, run the effect through your own runtime\n * instead. Kept free of `$app/*` imports so it stays importable (and testable)\n * outside of a SvelteKit app.\n *\n * @example\n * ```ts\n * // src/hooks.client.ts\n * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';\n * import { Effect } from 'effect';\n *\n * export const handleError = Hooks.handleError(({ error, event }) =>\n * Effect.as(Effect.logError(`navigation to ${event.url.pathname} failed`, error), {\n * message: 'Something went wrong',\n * })\n * );\n * ```\n */\n\nimport type { ClientInit, HandleClientError } from \"@sveltejs/kit/hooks\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport { causeToThrowable } from \"../internal/error.js\";\n\n/** Run a bare Effect (no request context) and settle it the way a hook expects. */\nconst runBareHook = async <A>(effect: Effect.Effect<A, unknown>): Promise<A> =>\n Exit.match(await Effect.runPromiseExit(effect), {\n onFailure: (cause) => {\n throw causeToThrowable(cause);\n },\n onSuccess: (value) => value,\n });\n\n/**\n * Wraps an Effect-based function as the SvelteKit client\n * [`handleError`](https://svelte.dev/docs/kit/hooks#Shared-hooks-handleError)\n * hook, which runs when an unexpected error is thrown while navigating in the\n * browser. The effect receives the error and navigation event and produces the\n * shape exposed to the page (your `App.Error`, or nothing).\n *\n * @example\n * ```ts\n * // src/hooks.client.ts\n * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';\n * import { Effect } from 'effect';\n *\n * export const handleError = Hooks.handleError(({ error, event }) =>\n * Effect.gen(function* () {\n * yield* Effect.logError(`navigation to ${event.url.pathname} failed`, error);\n * return { message: 'Something went wrong' };\n * })\n * );\n * ```\n */\nexport function handleError(\n fn: (\n args: Parameters<HandleClientError>[0],\n ) => Effect.Effect<Awaited<ReturnType<HandleClientError>>, unknown>,\n): HandleClientError {\n return (args) => runBareHook(fn(args));\n}\n\n/**\n * Wraps an Effect as the SvelteKit client\n * [`init`](https://svelte.dev/docs/kit/hooks#Shared-hooks-init) hook, which\n * runs once when the app starts in the browser. The effect runs on a bare\n * Effect runtime.\n *\n * @example\n * ```ts\n * // src/hooks.client.ts\n * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';\n * import { Effect } from 'effect';\n *\n * export const init = Hooks.init(Effect.logInfo('client app started'));\n * ```\n */\nexport function init(effect: Effect.Effect<void, unknown>): ClientInit {\n return () => runBareHook(effect);\n}\n"],"mappings":";;;;;;;;;;AA8BA,MAAM,cAAc,OAAU,WAC5B,KAAK,MAAM,MAAM,OAAO,eAAe,MAAM,GAAG;CAC9C,YAAY,UAAU;EACpB,MAAM,iBAAiB,KAAK;CAC9B;CACA,YAAY,UAAU;AACxB,CAAC;;;;;;;;;;;;;;;;;;;;;;AAuBH,SAAgB,YACd,IAGmB;CACnB,QAAQ,SAAS,YAAY,GAAG,IAAI,CAAC;AACvC;;;;;;;;;;;;;;;;AAiBA,SAAgB,KAAK,QAAkD;CACrE,aAAa,YAAY,MAAM;AACjC"}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Navigation_d_exports } from "./Navigation.js";
|
|
2
2
|
import { State_d_exports } from "./State.js";
|
|
3
3
|
import { Remote_d_exports } from "./Remote.js";
|
|
4
|
-
import { useRemoteQuery } from "./remote-reactive.js";
|
|
4
|
+
import { RemoteQueryResult, useRemoteQuery } from "./remote-reactive.js";
|
|
5
5
|
import { realtime_d_exports } from "./realtime.js";
|
|
6
6
|
import { forms_d_exports } from "./forms.js";
|
|
7
7
|
import { Hooks_d_exports } from "./client/Hooks.js";
|
|
8
|
-
export { forms_d_exports as Forms, Hooks_d_exports as Hooks, Navigation_d_exports as Navigation, realtime_d_exports as Realtime, Remote_d_exports as Remote, State_d_exports as State, useRemoteQuery };
|
|
8
|
+
export { forms_d_exports as Forms, Hooks_d_exports as Hooks, Navigation_d_exports as Navigation, realtime_d_exports as Realtime, Remote_d_exports as Remote, type RemoteQueryResult, State_d_exports as State, useRemoteQuery };
|
package/dist/config.d.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import * as ConfigProvider from "effect/ConfigProvider";
|
|
4
|
+
import * as Config from "effect/Config";
|
|
2
5
|
//#region src/config.d.ts
|
|
3
6
|
/**
|
|
4
7
|
* Creates a {@link ConfigProvider} backed by a SvelteKit env record (e.g. the
|
|
5
8
|
* `env` export of `$env/dynamic/private`, or `import.meta.env`-style objects).
|
|
6
9
|
*/
|
|
7
|
-
declare const sveltekitConfigProvider: (env: Record<string, string | undefined>) => ConfigProvider.ConfigProvider;
|
|
10
|
+
export declare const sveltekitConfigProvider: (env: Record<string, string | undefined>) => ConfigProvider.ConfigProvider;
|
|
8
11
|
/**
|
|
9
12
|
* Creates a `Layer` that installs {@link sveltekitConfigProvider} as the
|
|
10
13
|
* ambient config provider. Provide it to a server/universal runtime so the
|
|
11
14
|
* application layer's `Config` reads resolve from SvelteKit's environment.
|
|
12
15
|
*/
|
|
13
|
-
declare const sveltekitConfig: (env: Record<string, string | undefined>) => Layer.Layer<never>;
|
|
16
|
+
export declare const sveltekitConfig: (env: Record<string, string | undefined>) => Layer.Layer<never>;
|
|
14
17
|
/**
|
|
15
18
|
* Read a typed {@link Config} value directly from a SvelteKit env record, in
|
|
16
19
|
* one call — the typed bridge for reading configuration without wiring a
|
|
@@ -41,7 +44,6 @@ declare const sveltekitConfig: (env: Record<string, string | undefined>) => Laye
|
|
|
41
44
|
* // settings: Effect<{ apiUrl: string; analytics: string }, ConfigError>
|
|
42
45
|
* ```
|
|
43
46
|
*/
|
|
44
|
-
declare const readConfig: <A>(env: Record<string, string | undefined>, config: Config.Config<A>) => Effect.Effect<A, Config.ConfigError>;
|
|
47
|
+
export declare const readConfig: <A>(env: Record<string, string | undefined>, config: Config.Config<A>) => Effect.Effect<A, Config.ConfigError>;
|
|
45
48
|
//#endregion
|
|
46
|
-
export { readConfig, sveltekitConfig, sveltekitConfigProvider };
|
|
47
49
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.js
CHANGED
|
@@ -1,39 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Predicate from "effect/Predicate";
|
|
3
|
+
import * as Rec from "effect/Record";
|
|
4
|
+
import * as ConfigProvider from "effect/ConfigProvider";
|
|
2
5
|
//#region src/config.ts
|
|
3
|
-
/**
|
|
4
|
-
* SvelteKit `$env` ⇄ Effect `Config`.
|
|
5
|
-
*
|
|
6
|
-
* Effect services read configuration through {@link Config} / a
|
|
7
|
-
* {@link ConfigProvider}, while SvelteKit exposes configuration through its
|
|
8
|
-
* `$env/static/*` and `$env/dynamic/*` virtual modules. These helpers bridge
|
|
9
|
-
* the two: pass a SvelteKit env record and get back a `ConfigProvider` (or a
|
|
10
|
-
* `Layer` that installs it), so any `Config.String("DATABASE_URL")` resolves
|
|
11
|
-
* from SvelteKit's environment.
|
|
12
|
-
*
|
|
13
|
-
* ```ts
|
|
14
|
-
* // src/lib/server/runtime.ts
|
|
15
|
-
* import { env } from "$env/dynamic/private";
|
|
16
|
-
* import { sveltekitConfig } from "@thomasfosterau/effect-sveltekit/server";
|
|
17
|
-
* import { makeServerRuntime } from "@thomasfosterau/effect-sveltekit/server";
|
|
18
|
-
* import { Layer } from "effect";
|
|
19
|
-
* import { Database } from "./database";
|
|
20
|
-
*
|
|
21
|
-
* export const runtime = makeServerRuntime(
|
|
22
|
-
* // Database.layer reads Config.String("DATABASE_URL"), now backed by $env
|
|
23
|
-
* Database.layer.pipe(Layer.provide(sveltekitConfig(env))),
|
|
24
|
-
* );
|
|
25
|
-
* ```
|
|
26
|
-
*
|
|
27
|
-
* `ConfigProvider.fromEnv` joins/splits path segments on `_`, so nested config
|
|
28
|
-
* (`Config.String("HOST").pipe(...nested("DATABASE"))`) resolves a
|
|
29
|
-
* `DATABASE_HOST` env var just like it does from `process.env`.
|
|
30
|
-
*/
|
|
31
6
|
/** Drop `undefined` values so the record satisfies `ConfigProvider.fromEnv`. */
|
|
32
|
-
const definedEntries = (env) =>
|
|
33
|
-
const out = {};
|
|
34
|
-
for (const [key, value] of Object.entries(env)) if (value !== void 0) out[key] = value;
|
|
35
|
-
return out;
|
|
36
|
-
};
|
|
7
|
+
const definedEntries = (env) => Rec.filter(env, Predicate.isNotUndefined);
|
|
37
8
|
/**
|
|
38
9
|
* Creates a {@link ConfigProvider} backed by a SvelteKit env record (e.g. the
|
|
39
10
|
* `env` export of `$env/dynamic/private`, or `import.meta.env`-style objects).
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * SvelteKit `$env` ⇄ Effect `Config`.\n *\n * Effect services read configuration through {@link Config} / a\n * {@link ConfigProvider}, while SvelteKit exposes configuration through its\n * `$env/static/*` and `$env/dynamic/*` virtual modules. These helpers bridge\n * the two: pass a SvelteKit env record and get back a `ConfigProvider` (or a\n * `Layer` that installs it), so any `Config.String(\"DATABASE_URL\")` resolves\n * from SvelteKit's environment.\n *\n * ```ts\n * // src/lib/server/runtime.ts\n * import { env } from \"$env/dynamic/private\";\n * import { sveltekitConfig } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { makeServerRuntime } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Layer } from \"effect\";\n * import { Database } from \"./database\";\n *\n * export const runtime = makeServerRuntime(\n * // Database.layer reads Config.String(\"DATABASE_URL\"), now backed by $env\n * Database.layer.pipe(Layer.provide(sveltekitConfig(env))),\n * );\n * ```\n *\n * `ConfigProvider.fromEnv` joins/splits path segments on `_`, so nested config\n * (`Config.String(\"HOST\").pipe(...nested(\"DATABASE\"))`) resolves a\n * `DATABASE_HOST` env var just like it does from `process.env`.\n */\n\nimport
|
|
1
|
+
{"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * SvelteKit `$env` ⇄ Effect `Config`.\n *\n * Effect services read configuration through {@link Config} / a\n * {@link ConfigProvider}, while SvelteKit exposes configuration through its\n * `$env/static/*` and `$env/dynamic/*` virtual modules. These helpers bridge\n * the two: pass a SvelteKit env record and get back a `ConfigProvider` (or a\n * `Layer` that installs it), so any `Config.String(\"DATABASE_URL\")` resolves\n * from SvelteKit's environment.\n *\n * ```ts\n * // src/lib/server/runtime.ts\n * import { env } from \"$env/dynamic/private\";\n * import { sveltekitConfig } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { makeServerRuntime } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Layer } from \"effect\";\n * import { Database } from \"./database\";\n *\n * export const runtime = makeServerRuntime(\n * // Database.layer reads Config.String(\"DATABASE_URL\"), now backed by $env\n * Database.layer.pipe(Layer.provide(sveltekitConfig(env))),\n * );\n * ```\n *\n * `ConfigProvider.fromEnv` joins/splits path segments on `_`, so nested config\n * (`Config.String(\"HOST\").pipe(...nested(\"DATABASE\"))`) resolves a\n * `DATABASE_HOST` env var just like it does from `process.env`.\n */\n\nimport type * as Config from \"effect/Config\";\nimport * as ConfigProvider from \"effect/ConfigProvider\";\nimport * as Effect from \"effect/Effect\";\nimport type * as Layer from \"effect/Layer\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\n\n/** Drop `undefined` values so the record satisfies `ConfigProvider.fromEnv`. */\nconst definedEntries = (env: Record<string, string | undefined>): Record<string, string> =>\n Rec.filter(env, Predicate.isNotUndefined);\n\n/**\n * Creates a {@link ConfigProvider} backed by a SvelteKit env record (e.g. the\n * `env` export of `$env/dynamic/private`, or `import.meta.env`-style objects).\n */\nexport const sveltekitConfigProvider = (\n env: Record<string, string | undefined>,\n): ConfigProvider.ConfigProvider => ConfigProvider.fromEnv({ env: definedEntries(env) });\n\n/**\n * Creates a `Layer` that installs {@link sveltekitConfigProvider} as the\n * ambient config provider. Provide it to a server/universal runtime so the\n * application layer's `Config` reads resolve from SvelteKit's environment.\n */\nexport const sveltekitConfig = (env: Record<string, string | undefined>): Layer.Layer<never> =>\n ConfigProvider.layer(sveltekitConfigProvider(env));\n\n/**\n * Read a typed {@link Config} value directly from a SvelteKit env record, in\n * one call — the typed bridge for reading configuration without wiring a\n * provider into a runtime first.\n *\n * Which `$env` module you pass decides *when* the value is resolved:\n *\n * - `$env/static/public` / `$env/static/private` are **build-time** — inlined\n * and fixed for the deployment (public values are safe in client bundles).\n * - `$env/dynamic/public` / `$env/dynamic/private` are **runtime** — read from\n * the process environment per request, so they can change between deploys of\n * the same build.\n *\n * @example\n * ```ts\n * // Build-time public config (usable on the client)\n * import { env } from \"$env/static/public\";\n * import { readConfig } from \"@thomasfosterau/effect-sveltekit\";\n * import { Config, Effect } from \"effect\";\n *\n * const settings = readConfig(\n * env,\n * Config.all({\n * apiUrl: Config.String(\"PUBLIC_API_URL\"),\n * analytics: Config.String(\"PUBLIC_ANALYTICS_KEY\").pipe(Config.withDefault(\"\")),\n * }),\n * );\n * // settings: Effect<{ apiUrl: string; analytics: string }, ConfigError>\n * ```\n */\nexport const readConfig = <A>(\n env: Record<string, string | undefined>,\n config: Config.Config<A>,\n): Effect.Effect<A, Config.ConfigError> => Effect.provide(config, sveltekitConfig(env));\n"],"mappings":";;;;;;AAqCA,MAAM,kBAAkB,QACtB,IAAI,OAAO,KAAK,UAAU,cAAc;;;;;AAM1C,MAAa,2BACX,QACkC,eAAe,QAAQ,EAAE,KAAK,eAAe,GAAG,EAAE,CAAC;;;;;;AAOvF,MAAa,mBAAmB,QAC9B,eAAe,MAAM,wBAAwB,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCnD,MAAa,cACX,KACA,WACyC,OAAO,QAAQ,QAAQ,gBAAgB,GAAG,CAAC"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { KitErrorTranslator } from "./kit.js";
|
|
2
2
|
declare namespace errors_d_exports {
|
|
3
|
-
export {
|
|
3
|
+
export { Catalogue, CatalogueEntry, ErrorStatus, Tagged, ToSvelteKitOptions, rethrowSquashed, toSvelteKit };
|
|
4
4
|
}
|
|
5
5
|
/** Any value carrying a string `_tag` — `Schema.TaggedError`, `Data.TaggedError`, … */
|
|
6
|
-
interface Tagged {
|
|
6
|
+
export interface Tagged {
|
|
7
7
|
readonly _tag: string;
|
|
8
8
|
}
|
|
9
|
-
/** An error `_tag` with arbitrary fields: the element type of an untyped catalogue. */
|
|
10
|
-
type AnyTagged = Tagged & {
|
|
11
|
-
readonly [field: string]: any;
|
|
12
|
-
};
|
|
13
9
|
/** A 4xx / 5xx status for SvelteKit's `error()`. */
|
|
14
|
-
type ErrorStatus = number;
|
|
10
|
+
export type ErrorStatus = number;
|
|
15
11
|
/**
|
|
16
12
|
* How one tag is translated.
|
|
17
13
|
*
|
|
@@ -26,7 +22,7 @@ type ErrorStatus = number;
|
|
|
26
22
|
* - a function — full control: throw any SvelteKit primitive (`error`,
|
|
27
23
|
* `redirect`, `invalid`, …) or anything else.
|
|
28
24
|
*/
|
|
29
|
-
type CatalogueEntry<E> = ErrorStatus | {
|
|
25
|
+
export type CatalogueEntry<E> = ErrorStatus | {
|
|
30
26
|
readonly status: ErrorStatus;
|
|
31
27
|
readonly message?: ((error: E) => string | App.Error) | undefined;
|
|
32
28
|
} | "status" | "redirect" | ((error: E) => never);
|
|
@@ -34,11 +30,11 @@ type CatalogueEntry<E> = ErrorStatus | {
|
|
|
34
30
|
* A tag-keyed catalogue. With an explicit error union `E`, keys are limited to
|
|
35
31
|
* `E`'s tags and each entry's functions receive the matching member.
|
|
36
32
|
*/
|
|
37
|
-
type Catalogue<E extends Tagged> = { readonly [K in E["_tag"]]?: CatalogueEntry<Extract<E, {
|
|
33
|
+
export type Catalogue<E extends Tagged> = { readonly [K in E["_tag"]]?: CatalogueEntry<Extract<E, {
|
|
38
34
|
readonly _tag: K;
|
|
39
35
|
}>>; };
|
|
40
36
|
/** Options for {@link toSvelteKit}. */
|
|
41
|
-
interface ToSvelteKitOptions {
|
|
37
|
+
export interface ToSvelteKitOptions {
|
|
42
38
|
/**
|
|
43
39
|
* What to do with a cause whose error is not in the catalogue. Defaults to
|
|
44
40
|
* {@link SvelteKit.translate}: the Kit control-flow errors
|
|
@@ -52,7 +48,7 @@ interface ToSvelteKitOptions {
|
|
|
52
48
|
readonly fallback?: KitErrorTranslator | undefined;
|
|
53
49
|
}
|
|
54
50
|
/** A {@link ToSvelteKitOptions.fallback} that rethrows `Cause.squash(cause)` unchanged. */
|
|
55
|
-
declare const rethrowSquashed: KitErrorTranslator;
|
|
51
|
+
export declare const rethrowSquashed: KitErrorTranslator;
|
|
56
52
|
/**
|
|
57
53
|
* Build a `translateError` from a tag-keyed catalogue.
|
|
58
54
|
*
|
|
@@ -63,15 +59,17 @@ declare const rethrowSquashed: KitErrorTranslator;
|
|
|
63
59
|
* {@link SvelteKit.translate}).
|
|
64
60
|
*
|
|
65
61
|
* Pass the application's error union as the type argument to have the keys and
|
|
66
|
-
* each entry's callbacks checked against it
|
|
67
|
-
*
|
|
62
|
+
* each entry's callbacks checked against it (a callback reads the matching
|
|
63
|
+
* member's fields); without it any tag is a key, and a callback receives only
|
|
64
|
+
* a {@link Tagged}.
|
|
68
65
|
*
|
|
69
66
|
* @example
|
|
70
67
|
* ```ts
|
|
71
68
|
* import { Errors } from "@thomasfosterau/effect-sveltekit/server";
|
|
72
69
|
* import { error } from "@sveltejs/kit";
|
|
70
|
+
* import type { AppError } from "$lib/errors"; // your tagged-error union
|
|
73
71
|
*
|
|
74
|
-
* const translateError = Errors.toSvelteKit({
|
|
72
|
+
* const translateError = Errors.toSvelteKit<AppError>({
|
|
75
73
|
* NotFound: 404,
|
|
76
74
|
* Unauthorized: 401,
|
|
77
75
|
* Forbidden: 403,
|
|
@@ -82,7 +80,7 @@ declare const rethrowSquashed: KitErrorTranslator;
|
|
|
82
80
|
* });
|
|
83
81
|
* ```
|
|
84
82
|
*/
|
|
85
|
-
declare const toSvelteKit: <E extends Tagged =
|
|
83
|
+
export declare const toSvelteKit: <E extends Tagged = Tagged>(catalogue: Catalogue<E>, options?: ToSvelteKitOptions) => KitErrorTranslator;
|
|
86
84
|
//#endregion
|
|
87
|
-
export {
|
|
85
|
+
export { errors_d_exports };
|
|
88
86
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { throwKitError } from "./internal/error.js";
|
|
3
3
|
import { SvelteKit } from "./kit.js";
|
|
4
|
-
import
|
|
4
|
+
import * as Predicate from "effect/Predicate";
|
|
5
5
|
import { redirect } from "@sveltejs/kit";
|
|
6
|
+
import * as Cause from "effect/Cause";
|
|
6
7
|
//#region src/errors.ts
|
|
7
8
|
/**
|
|
8
9
|
* A `translateError` built from a **tagged-error catalogue** instead of a
|
|
@@ -65,19 +66,24 @@ const rethrowSquashed = (cause) => {
|
|
|
65
66
|
throw Cause.squash(cause);
|
|
66
67
|
};
|
|
67
68
|
const isTagged = (value) => Predicate.hasProperty(value, "_tag") && Predicate.isString(value._tag);
|
|
68
|
-
const isErrorStatus = (status) =>
|
|
69
|
-
const isRedirectStatus = (status) =>
|
|
69
|
+
const isErrorStatus = (status) => status !== void 0 && Number.isInteger(status) && status >= 400 && status <= 599;
|
|
70
|
+
const isRedirectStatus = (status) => status !== void 0 && Number.isInteger(status) && status >= 300 && status <= 308;
|
|
70
71
|
/** The error's own non-empty `message` field, else its `_tag`. */
|
|
71
|
-
const defaultBody = (error) => Object.hasOwn(error, "message") && Predicate.isString(error.message) && error.message !== "" ? error.message : error._tag;
|
|
72
|
+
const defaultBody = (error) => Object.hasOwn(error, "message") && Predicate.hasProperty(error, "message") && Predicate.isString(error.message) && error.message !== "" ? error.message : error._tag;
|
|
72
73
|
const throwError = (status, body) => throwKitError(status, body);
|
|
74
|
+
/** The error's numeric `status` field, if it has one. */
|
|
75
|
+
const statusField = (error) => Predicate.hasProperty(error, "status") && Predicate.isNumber(error.status) ? error.status : void 0;
|
|
76
|
+
/** The error's redirect target: its `to` field, else its `location`, if a string or `URL`. */
|
|
77
|
+
const locationField = (error) => {
|
|
78
|
+
const location = (Predicate.hasProperty(error, "to") ? error.to : void 0) ?? (Predicate.hasProperty(error, "location") ? error.location : void 0);
|
|
79
|
+
return Predicate.isString(location) || location instanceof URL ? location : void 0;
|
|
80
|
+
};
|
|
73
81
|
const apply = (entry, error) => {
|
|
74
82
|
if (Predicate.isNumber(entry)) return throwError(entry, defaultBody(error));
|
|
75
83
|
if (Predicate.isFunction(entry)) return entry(error);
|
|
76
|
-
|
|
77
|
-
if (entry === "
|
|
78
|
-
|
|
79
|
-
return redirect(isRedirectStatus(error.status) ? error.status : 303, Predicate.isString(location) || location instanceof URL ? location : "/");
|
|
80
|
-
}
|
|
84
|
+
const status = statusField(error);
|
|
85
|
+
if (entry === "status") return throwError(isErrorStatus(status) ? status : 500, defaultBody(error));
|
|
86
|
+
if (entry === "redirect") return redirect(isRedirectStatus(status) ? status : 303, locationField(error) ?? "/");
|
|
81
87
|
return throwError(entry.status, entry.message === void 0 ? defaultBody(error) : entry.message(error));
|
|
82
88
|
};
|
|
83
89
|
/**
|
|
@@ -90,15 +96,17 @@ const apply = (entry, error) => {
|
|
|
90
96
|
* {@link SvelteKit.translate}).
|
|
91
97
|
*
|
|
92
98
|
* Pass the application's error union as the type argument to have the keys and
|
|
93
|
-
* each entry's callbacks checked against it
|
|
94
|
-
*
|
|
99
|
+
* each entry's callbacks checked against it (a callback reads the matching
|
|
100
|
+
* member's fields); without it any tag is a key, and a callback receives only
|
|
101
|
+
* a {@link Tagged}.
|
|
95
102
|
*
|
|
96
103
|
* @example
|
|
97
104
|
* ```ts
|
|
98
105
|
* import { Errors } from "@thomasfosterau/effect-sveltekit/server";
|
|
99
106
|
* import { error } from "@sveltejs/kit";
|
|
107
|
+
* import type { AppError } from "$lib/errors"; // your tagged-error union
|
|
100
108
|
*
|
|
101
|
-
* const translateError = Errors.toSvelteKit({
|
|
109
|
+
* const translateError = Errors.toSvelteKit<AppError>({
|
|
102
110
|
* NotFound: 404,
|
|
103
111
|
* Unauthorized: 401,
|
|
104
112
|
* Forbidden: 403,
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":["kitRedirect"],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * A `translateError` built from a **tagged-error catalogue** instead of a\n * hand-written switch.\n *\n * Every application maps the same handful of domain errors to SvelteKit control\n * flow at the request boundary — `NotFound → error(404)`, `Forbidden → 403`,\n * `Unauthorized → 401`, `Redirect → redirect()`, the catch-all HTTP error →\n * `error(status)` — and re-throws everything else so `handleError` sees the\n * defect. {@link toSvelteKit} takes that mapping as data, keyed by `_tag`, and\n * returns the {@link KitErrorTranslator} that `makeRuntimeAdapter` /\n * `RemoteFunction.withRuntime` accept.\n *\n * @example\n * ```ts\n * // src/lib/server/effect/kit.ts\n * import { Errors, makeRuntimeAdapter, RequestRuntime } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Layer, Schema } from \"effect\";\n *\n * // The application's tagged-error catalogue (usually imported from a shared models package).\n * class NotFound extends Schema.TaggedError<NotFound>()(\"NotFound\", {\n * resource: Schema.String,\n * id: Schema.String,\n * }) {}\n * class Forbidden extends Schema.TaggedError<Forbidden>()(\"Forbidden\", {\n * message: Schema.optional(Schema.String),\n * }) {}\n * class Redirect extends Schema.TaggedError<Redirect>()(\"Redirect\", {\n * to: Schema.String,\n * status: Schema.Number,\n * }) {}\n * class HttpError extends Schema.TaggedError<HttpError>()(\"HttpError\", {\n * status: Schema.Number,\n * message: Schema.String,\n * }) {}\n *\n * type AppError = NotFound | Forbidden | Redirect | HttpError;\n *\n * // Apps typically use `RequestRuntime.fromLocals((event) => event.locals.runtime)`.\n * const AppLayer = Layer.empty;\n *\n * export const { serverLoad, endpoint } = makeRuntimeAdapter(RequestRuntime.fromLayer(AppLayer), {\n * translateError: Errors.toSvelteKit<AppError>({\n * NotFound: { status: 404, message: (e) => `${e.resource} ${e.id} not found` },\n * Forbidden: 403, // body: the error's own `message`, else \"Forbidden\"\n * Redirect: \"redirect\", // redirect(e.status, e.to)\n * HttpError: \"status\", // error(e.status, e.message)\n * }),\n * });\n * ```\n *\n * @module\n */\n\nimport { redirect as kitRedirect } from \"@sveltejs/kit\";\nimport { Cause, Predicate } from \"effect\";\nimport { throwKitError } from \"./internal/error.js\";\nimport { SvelteKit, type KitErrorTranslator } from \"./kit.js\";\n\n/** Any value carrying a string `_tag` — `Schema.TaggedError`, `Data.TaggedError`, … */\nexport interface Tagged {\n readonly _tag: string;\n}\n\n/** An error `_tag` with arbitrary fields: the element type of an untyped catalogue. */\nexport type AnyTagged = Tagged & { readonly [field: string]: any };\n\n/** A 4xx / 5xx status for SvelteKit's `error()`. */\nexport type ErrorStatus = number;\n\n/**\n * How one tag is translated.\n *\n * - `404` (a status) — `error(status, body)`. The body is the error's own\n * non-empty `message` field, else its `_tag`.\n * - `{ status, message }` — `error(status, message(e))`; `message` may return a\n * string or an `App.Error` object.\n * - `\"status\"` — `error(e.status, body)`, for a catch-all error that carries its\n * own status (a non-4xx/5xx status becomes `500`).\n * - `\"redirect\"` — `redirect(e.status, e.to ?? e.location)`; a missing or\n * non-3xx status becomes `303`.\n * - a function — full control: throw any SvelteKit primitive (`error`,\n * `redirect`, `invalid`, …) or anything else.\n */\nexport type CatalogueEntry<E> =\n | ErrorStatus\n | {\n readonly status: ErrorStatus;\n readonly message?: ((error: E) => string | App.Error) | undefined;\n }\n | \"status\"\n | \"redirect\"\n | ((error: E) => never);\n\n/**\n * A tag-keyed catalogue. With an explicit error union `E`, keys are limited to\n * `E`'s tags and each entry's functions receive the matching member.\n */\nexport type Catalogue<E extends Tagged> = {\n readonly [K in E[\"_tag\"]]?: CatalogueEntry<Extract<E, { readonly _tag: K }>>;\n};\n\n/** Options for {@link toSvelteKit}. */\nexport interface ToSvelteKitOptions {\n /**\n * What to do with a cause whose error is not in the catalogue. Defaults to\n * {@link SvelteKit.translate}: the Kit control-flow errors\n * (`SvelteKit.redirect` / `.error` / `.fail`) and SvelteKit's own thrown\n * `redirect()` / `error()` pass through, anything else becomes an\n * `EffectCauseError` (a 500 whose `cause` is the full Effect `Cause`).\n *\n * Pass {@link rethrowSquashed} to rethrow the squashed error unchanged\n * instead, so `handleError` sees the original value.\n */\n readonly fallback?: KitErrorTranslator | undefined;\n}\n\n/** A {@link ToSvelteKitOptions.fallback} that rethrows `Cause.squash(cause)` unchanged. */\nexport const rethrowSquashed: KitErrorTranslator = (cause) => {\n throw Cause.squash(cause);\n};\n\nconst isTagged = (value: unknown): value is AnyTagged =>\n Predicate.hasProperty(value, \"_tag\") && Predicate.isString(value._tag);\n\nconst isErrorStatus = (status: unknown): status is number =>\n Predicate.isNumber(status) && Number.isInteger(status) && status >= 400 && status <= 599;\n\nconst isRedirectStatus = (\n status: unknown,\n): status is 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 =>\n Predicate.isNumber(status) && Number.isInteger(status) && status >= 300 && status <= 308;\n\n/** The error's own non-empty `message` field, else its `_tag`. */\nconst defaultBody = (error: AnyTagged): string =>\n Object.hasOwn(error, \"message\") && Predicate.isString(error.message) && error.message !== \"\"\n ? error.message\n : error._tag;\n\nconst throwError = (status: number, body: string | App.Error): never =>\n throwKitError(status, body as string | Readonly<Record<string, unknown>>);\n\nconst apply = (entry: CatalogueEntry<AnyTagged>, error: AnyTagged): never => {\n if (Predicate.isNumber(entry)) return throwError(entry, defaultBody(error));\n if (Predicate.isFunction(entry)) return entry(error);\n if (entry === \"status\") {\n return throwError(isErrorStatus(error.status) ? error.status : 500, defaultBody(error));\n }\n if (entry === \"redirect\") {\n const location: unknown = error.to ?? error.location;\n return kitRedirect(\n isRedirectStatus(error.status) ? error.status : 303,\n Predicate.isString(location) || location instanceof URL ? location : \"/\",\n );\n }\n return throwError(\n entry.status,\n entry.message === undefined ? defaultBody(error) : entry.message(error),\n );\n};\n\n/**\n * Build a `translateError` from a tag-keyed catalogue.\n *\n * The translator squashes the failed `Cause` (the first typed failure, else the\n * first defect — so a tagged error that was thrown also matches), looks its\n * `_tag` up in `catalogue`, and throws the matching SvelteKit primitive. Errors\n * outside the catalogue go to `options.fallback` (by default\n * {@link SvelteKit.translate}).\n *\n * Pass the application's error union as the type argument to have the keys and\n * each entry's callbacks checked against it; without it the catalogue is\n * untyped (`Record<string, CatalogueEntry<AnyTagged>>`).\n *\n * @example\n * ```ts\n * import { Errors } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { error } from \"@sveltejs/kit\";\n *\n * const translateError = Errors.toSvelteKit({\n * NotFound: 404,\n * Unauthorized: 401,\n * Forbidden: 403,\n * Redirect: \"redirect\",\n * HttpError: \"status\",\n * // Full control for anything bespoke, e.g. a JSON:API error document:\n * JsonapiError: (e) => error(e.status, JSON.stringify(e.toDocument())),\n * });\n * ```\n */\nexport const toSvelteKit = <E extends Tagged = AnyTagged>(\n catalogue: Catalogue<E>,\n options: ToSvelteKitOptions = {},\n): KitErrorTranslator => {\n const entries = catalogue as Readonly<Record<string, CatalogueEntry<AnyTagged> | undefined>>;\n const fallback = options.fallback ?? SvelteKit.translate;\n return (cause) => {\n const squashed = Cause.squash(cause);\n if (isTagged(squashed) && Object.hasOwn(entries, squashed._tag)) {\n const entry = entries[squashed._tag];\n if (entry !== undefined) return apply(entry, squashed);\n }\n return fallback(cause);\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqHA,MAAa,mBAAuC,UAAU;CAC5D,MAAM,MAAM,OAAO,KAAK;AAC1B;AAEA,MAAM,YAAY,UAChB,UAAU,YAAY,OAAO,MAAM,KAAK,UAAU,SAAS,MAAM,IAAI;AAEvE,MAAM,iBAAiB,WACrB,UAAU,SAAS,MAAM,KAAK,OAAO,UAAU,MAAM,KAAK,UAAU,OAAO,UAAU;AAEvF,MAAM,oBACJ,WAEA,UAAU,SAAS,MAAM,KAAK,OAAO,UAAU,MAAM,KAAK,UAAU,OAAO,UAAU;;AAGvF,MAAM,eAAe,UACnB,OAAO,OAAO,OAAO,SAAS,KAAK,UAAU,SAAS,MAAM,OAAO,KAAK,MAAM,YAAY,KACtF,MAAM,UACN,MAAM;AAEZ,MAAM,cAAc,QAAgB,SAClC,cAAc,QAAQ,IAAkD;AAE1E,MAAM,SAAS,OAAkC,UAA4B;CAC3E,IAAI,UAAU,SAAS,KAAK,GAAG,OAAO,WAAW,OAAO,YAAY,KAAK,CAAC;CAC1E,IAAI,UAAU,WAAW,KAAK,GAAG,OAAO,MAAM,KAAK;CACnD,IAAI,UAAU,UACZ,OAAO,WAAW,cAAc,MAAM,MAAM,IAAI,MAAM,SAAS,KAAK,YAAY,KAAK,CAAC;CAExF,IAAI,UAAU,YAAY;EACxB,MAAM,WAAoB,MAAM,MAAM,MAAM;EAC5C,OAAOA,SACL,iBAAiB,MAAM,MAAM,IAAI,MAAM,SAAS,KAChD,UAAU,SAAS,QAAQ,KAAK,oBAAoB,MAAM,WAAW,GACvE;CACF;CACA,OAAO,WACL,MAAM,QACN,MAAM,YAAY,KAAA,IAAY,YAAY,KAAK,IAAI,MAAM,QAAQ,KAAK,CACxE;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,eACX,WACA,UAA8B,CAAC,MACR;CACvB,MAAM,UAAU;CAChB,MAAM,WAAW,QAAQ,YAAY,UAAU;CAC/C,QAAQ,UAAU;EAChB,MAAM,WAAW,MAAM,OAAO,KAAK;EACnC,IAAI,SAAS,QAAQ,KAAK,OAAO,OAAO,SAAS,SAAS,IAAI,GAAG;GAC/D,MAAM,QAAQ,QAAQ,SAAS;GAC/B,IAAI,UAAU,KAAA,GAAW,OAAO,MAAM,OAAO,QAAQ;EACvD;EACA,OAAO,SAAS,KAAK;CACvB;AACF"}
|
|
1
|
+
{"version":3,"file":"errors.js","names":["kitRedirect"],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * A `translateError` built from a **tagged-error catalogue** instead of a\n * hand-written switch.\n *\n * Every application maps the same handful of domain errors to SvelteKit control\n * flow at the request boundary — `NotFound → error(404)`, `Forbidden → 403`,\n * `Unauthorized → 401`, `Redirect → redirect()`, the catch-all HTTP error →\n * `error(status)` — and re-throws everything else so `handleError` sees the\n * defect. {@link toSvelteKit} takes that mapping as data, keyed by `_tag`, and\n * returns the {@link KitErrorTranslator} that `makeRuntimeAdapter` /\n * `RemoteFunction.withRuntime` accept.\n *\n * @example\n * ```ts\n * // src/lib/server/effect/kit.ts\n * import { Errors, makeRuntimeAdapter, RequestRuntime } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Layer, Schema } from \"effect\";\n *\n * // The application's tagged-error catalogue (usually imported from a shared models package).\n * class NotFound extends Schema.TaggedError<NotFound>()(\"NotFound\", {\n * resource: Schema.String,\n * id: Schema.String,\n * }) {}\n * class Forbidden extends Schema.TaggedError<Forbidden>()(\"Forbidden\", {\n * message: Schema.optional(Schema.String),\n * }) {}\n * class Redirect extends Schema.TaggedError<Redirect>()(\"Redirect\", {\n * to: Schema.String,\n * status: Schema.Number,\n * }) {}\n * class HttpError extends Schema.TaggedError<HttpError>()(\"HttpError\", {\n * status: Schema.Number,\n * message: Schema.String,\n * }) {}\n *\n * type AppError = NotFound | Forbidden | Redirect | HttpError;\n *\n * // Apps typically use `RequestRuntime.fromLocals((event) => event.locals.runtime)`.\n * const AppLayer = Layer.empty;\n *\n * export const { serverLoad, endpoint } = makeRuntimeAdapter(RequestRuntime.fromLayer(AppLayer), {\n * translateError: Errors.toSvelteKit<AppError>({\n * NotFound: { status: 404, message: (e) => `${e.resource} ${e.id} not found` },\n * Forbidden: 403, // body: the error's own `message`, else \"Forbidden\"\n * Redirect: \"redirect\", // redirect(e.status, e.to)\n * HttpError: \"status\", // error(e.status, e.message)\n * }),\n * });\n * ```\n *\n * @module\n */\n\nimport { redirect as kitRedirect } from \"@sveltejs/kit\";\nimport * as Cause from \"effect/Cause\";\nimport * as Predicate from \"effect/Predicate\";\nimport { throwKitError } from \"./internal/error.js\";\nimport { SvelteKit, type KitErrorTranslator } from \"./kit.js\";\n\n/** Any value carrying a string `_tag` — `Schema.TaggedError`, `Data.TaggedError`, … */\nexport interface Tagged {\n readonly _tag: string;\n}\n\n/** A 4xx / 5xx status for SvelteKit's `error()`. */\nexport type ErrorStatus = number;\n\n/**\n * How one tag is translated.\n *\n * - `404` (a status) — `error(status, body)`. The body is the error's own\n * non-empty `message` field, else its `_tag`.\n * - `{ status, message }` — `error(status, message(e))`; `message` may return a\n * string or an `App.Error` object.\n * - `\"status\"` — `error(e.status, body)`, for a catch-all error that carries its\n * own status (a non-4xx/5xx status becomes `500`).\n * - `\"redirect\"` — `redirect(e.status, e.to ?? e.location)`; a missing or\n * non-3xx status becomes `303`.\n * - a function — full control: throw any SvelteKit primitive (`error`,\n * `redirect`, `invalid`, …) or anything else.\n */\nexport type CatalogueEntry<E> =\n | ErrorStatus\n | {\n readonly status: ErrorStatus;\n readonly message?: ((error: E) => string | App.Error) | undefined;\n }\n | \"status\"\n | \"redirect\"\n | ((error: E) => never);\n\n/**\n * A tag-keyed catalogue. With an explicit error union `E`, keys are limited to\n * `E`'s tags and each entry's functions receive the matching member.\n */\nexport type Catalogue<E extends Tagged> = {\n readonly [K in E[\"_tag\"]]?: CatalogueEntry<Extract<E, { readonly _tag: K }>>;\n};\n\n/** Options for {@link toSvelteKit}. */\nexport interface ToSvelteKitOptions {\n /**\n * What to do with a cause whose error is not in the catalogue. Defaults to\n * {@link SvelteKit.translate}: the Kit control-flow errors\n * (`SvelteKit.redirect` / `.error` / `.fail`) and SvelteKit's own thrown\n * `redirect()` / `error()` pass through, anything else becomes an\n * `EffectCauseError` (a 500 whose `cause` is the full Effect `Cause`).\n *\n * Pass {@link rethrowSquashed} to rethrow the squashed error unchanged\n * instead, so `handleError` sees the original value.\n */\n readonly fallback?: KitErrorTranslator | undefined;\n}\n\n/** A {@link ToSvelteKitOptions.fallback} that rethrows `Cause.squash(cause)` unchanged. */\nexport const rethrowSquashed: KitErrorTranslator = (cause) => {\n throw Cause.squash(cause);\n};\n\nconst isTagged = (value: unknown): value is Tagged =>\n Predicate.hasProperty(value, \"_tag\") && Predicate.isString(value._tag);\n\nconst isErrorStatus = (status: number | undefined): status is number =>\n status !== undefined && Number.isInteger(status) && status >= 400 && status <= 599;\n\nconst isRedirectStatus = (\n status: number | undefined,\n): status is 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 =>\n status !== undefined && Number.isInteger(status) && status >= 300 && status <= 308;\n\n/** The error's own non-empty `message` field, else its `_tag`. */\nconst defaultBody = (error: Tagged): string =>\n Object.hasOwn(error, \"message\") &&\n Predicate.hasProperty(error, \"message\") &&\n Predicate.isString(error.message) &&\n error.message !== \"\"\n ? error.message\n : error._tag;\n\nconst throwError = (status: number, body: string | App.Error): never => throwKitError(status, body);\n\n/** The error's numeric `status` field, if it has one. */\nconst statusField = (error: Tagged): number | undefined =>\n Predicate.hasProperty(error, \"status\") && Predicate.isNumber(error.status)\n ? error.status\n : undefined;\n\n/** The error's redirect target: its `to` field, else its `location`, if a string or `URL`. */\nconst locationField = (error: Tagged): string | URL | undefined => {\n const to = Predicate.hasProperty(error, \"to\") ? error.to : undefined;\n const location = to ?? (Predicate.hasProperty(error, \"location\") ? error.location : undefined);\n return Predicate.isString(location) || location instanceof URL ? location : undefined;\n};\n\nconst apply = (entry: CatalogueEntry<Tagged>, error: Tagged): never => {\n if (Predicate.isNumber(entry)) return throwError(entry, defaultBody(error));\n if (Predicate.isFunction(entry)) return entry(error);\n const status = statusField(error);\n if (entry === \"status\") {\n return throwError(isErrorStatus(status) ? status : 500, defaultBody(error));\n }\n if (entry === \"redirect\") {\n return kitRedirect(isRedirectStatus(status) ? status : 303, locationField(error) ?? \"/\");\n }\n return throwError(\n entry.status,\n entry.message === undefined ? defaultBody(error) : entry.message(error),\n );\n};\n\n/**\n * Build a `translateError` from a tag-keyed catalogue.\n *\n * The translator squashes the failed `Cause` (the first typed failure, else the\n * first defect — so a tagged error that was thrown also matches), looks its\n * `_tag` up in `catalogue`, and throws the matching SvelteKit primitive. Errors\n * outside the catalogue go to `options.fallback` (by default\n * {@link SvelteKit.translate}).\n *\n * Pass the application's error union as the type argument to have the keys and\n * each entry's callbacks checked against it (a callback reads the matching\n * member's fields); without it any tag is a key, and a callback receives only\n * a {@link Tagged}.\n *\n * @example\n * ```ts\n * import { Errors } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { error } from \"@sveltejs/kit\";\n * import type { AppError } from \"$lib/errors\"; // your tagged-error union\n *\n * const translateError = Errors.toSvelteKit<AppError>({\n * NotFound: 404,\n * Unauthorized: 401,\n * Forbidden: 403,\n * Redirect: \"redirect\",\n * HttpError: \"status\",\n * // Full control for anything bespoke, e.g. a JSON:API error document:\n * JsonapiError: (e) => error(e.status, JSON.stringify(e.toDocument())),\n * });\n * ```\n */\nexport const toSvelteKit = <E extends Tagged = Tagged>(\n catalogue: Catalogue<E>,\n options: ToSvelteKitOptions = {},\n): KitErrorTranslator => {\n // SAFETY: every key of a `Catalogue<E>` is one of `E`'s tags and its entry is\n // a `CatalogueEntry` of the matching member, which `apply` only ever calls\n // with an error of that tag; the lookup below is guarded by `Object.hasOwn`,\n // so absent keys read `undefined`.\n const entries = catalogue as Readonly<Record<string, CatalogueEntry<Tagged> | undefined>>;\n const fallback = options.fallback ?? SvelteKit.translate;\n return (cause) => {\n const squashed = Cause.squash(cause);\n if (isTagged(squashed) && Object.hasOwn(entries, squashed._tag)) {\n const entry = entries[squashed._tag];\n if (entry !== undefined) return apply(entry, squashed);\n }\n return fallback(cause);\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmHA,MAAa,mBAAuC,UAAU;CAC5D,MAAM,MAAM,OAAO,KAAK;AAC1B;AAEA,MAAM,YAAY,UAChB,UAAU,YAAY,OAAO,MAAM,KAAK,UAAU,SAAS,MAAM,IAAI;AAEvE,MAAM,iBAAiB,WACrB,WAAW,KAAA,KAAa,OAAO,UAAU,MAAM,KAAK,UAAU,OAAO,UAAU;AAEjF,MAAM,oBACJ,WAEA,WAAW,KAAA,KAAa,OAAO,UAAU,MAAM,KAAK,UAAU,OAAO,UAAU;;AAGjF,MAAM,eAAe,UACnB,OAAO,OAAO,OAAO,SAAS,KAC9B,UAAU,YAAY,OAAO,SAAS,KACtC,UAAU,SAAS,MAAM,OAAO,KAChC,MAAM,YAAY,KACd,MAAM,UACN,MAAM;AAEZ,MAAM,cAAc,QAAgB,SAAoC,cAAc,QAAQ,IAAI;;AAGlG,MAAM,eAAe,UACnB,UAAU,YAAY,OAAO,QAAQ,KAAK,UAAU,SAAS,MAAM,MAAM,IACrE,MAAM,SACN,KAAA;;AAGN,MAAM,iBAAiB,UAA4C;CAEjE,MAAM,YADK,UAAU,YAAY,OAAO,IAAI,IAAI,MAAM,KAAK,KAAA,OACnC,UAAU,YAAY,OAAO,UAAU,IAAI,MAAM,WAAW,KAAA;CACpF,OAAO,UAAU,SAAS,QAAQ,KAAK,oBAAoB,MAAM,WAAW,KAAA;AAC9E;AAEA,MAAM,SAAS,OAA+B,UAAyB;CACrE,IAAI,UAAU,SAAS,KAAK,GAAG,OAAO,WAAW,OAAO,YAAY,KAAK,CAAC;CAC1E,IAAI,UAAU,WAAW,KAAK,GAAG,OAAO,MAAM,KAAK;CACnD,MAAM,SAAS,YAAY,KAAK;CAChC,IAAI,UAAU,UACZ,OAAO,WAAW,cAAc,MAAM,IAAI,SAAS,KAAK,YAAY,KAAK,CAAC;CAE5E,IAAI,UAAU,YACZ,OAAOA,SAAY,iBAAiB,MAAM,IAAI,SAAS,KAAK,cAAc,KAAK,KAAK,GAAG;CAEzF,OAAO,WACL,MAAM,QACN,MAAM,YAAY,KAAA,IAAY,YAAY,KAAK,IAAI,MAAM,QAAQ,KAAK,CACxE;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAa,eACX,WACA,UAA8B,CAAC,MACR;CAKvB,MAAM,UAAU;CAChB,MAAM,WAAW,QAAQ,YAAY,UAAU;CAC/C,QAAQ,UAAU;EAChB,MAAM,WAAW,MAAM,OAAO,KAAK;EACnC,IAAI,SAAS,QAAQ,KAAK,OAAO,OAAO,SAAS,SAAS,IAAI,GAAG;GAC/D,MAAM,QAAQ,QAAQ,SAAS;GAC/B,IAAI,UAAU,KAAA,GAAW,OAAO,MAAM,OAAO,QAAQ;EACvD;EACA,OAAO,SAAS,KAAK;CACvB;AACF"}
|
package/dist/forms.d.ts
CHANGED
|
@@ -1,29 +1,91 @@
|
|
|
1
|
-
import { EnhanceHandlers, EnhanceOptions, EnhanceRuntime, ResultOptions, SubmitInput } from "./internal/enhance.js";
|
|
1
|
+
import { EnhanceHandlerKey, EnhanceHandlers, EnhanceHandlersBy, EnhanceOptions, EnhanceOptionsArgs, EnhanceRuntime, HandlerErrors, HandlerKeys, OnErrorAccepts, OnErrorInput, RequiredServices, ResultOptions, SubmitInput } from "./internal/enhance.js";
|
|
2
2
|
import "./forms.js";
|
|
3
|
-
import
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Schema from "effect/Schema";
|
|
4
5
|
import { ActionResult } from "$app/forms";
|
|
5
6
|
declare namespace forms_d_exports {
|
|
6
|
-
export { EnhanceHandlers, EnhanceOptions, EnhanceRuntime, ResultOptions, SubmitInput, applyAction$1 as applyAction, deserialize$1 as deserialize, enhance };
|
|
7
|
+
export { ApplyActionError, EnhanceHandlers, EnhanceOptions, EnhanceOptionsArgs, EnhanceRuntime, ResultOptions, SubmitInput, applyAction$1 as applyAction, deserialize$1 as deserialize, enhance };
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
9
10
|
* Build a `SubmitFunction` for `use:enhance` from Effect-based handlers.
|
|
10
11
|
*
|
|
11
12
|
* `onSubmit` runs before the request (call `input.cancel()` to abort);
|
|
12
13
|
* `onResult` runs on the response with SvelteKit's `update()`. Omit `onResult`
|
|
13
|
-
* to keep SvelteKit's default behaviour.
|
|
14
|
-
* require application services
|
|
14
|
+
* to keep SvelteKit's default behaviour. When the handlers (or `onError`)
|
|
15
|
+
* require application services, pass `{ runtime }` (a `Context` or
|
|
16
|
+
* `ManagedRuntime` providing them): it is then required, and a call without it
|
|
17
|
+
* does not type-check. Each handler may require different services; the runtime
|
|
18
|
+
* must provide all of them. A missing runtime is reported on the call, and one
|
|
19
|
+
* that lacks a service on the runtime, never on a handler; the message may at
|
|
20
|
+
* first name only the services of the handlers TypeScript types first (such as
|
|
21
|
+
* an `onError` passed by name), and the rest once the runtime provides those.
|
|
22
|
+
* Handlers that require no services run on the default runtime, and the options
|
|
23
|
+
* may be omitted; an effect typed as requiring `any` or `unknown` is treated as
|
|
24
|
+
* requiring no services, so no runtime is demanded. A function that wraps
|
|
25
|
+
* `enhance` for handlers requiring any `R` forwards its options as
|
|
26
|
+
* `...options: EnhanceOptionsArgs<R>` (or takes a required
|
|
27
|
+
* `{ runtime: EnhanceRuntime<R> }`): an `EnhanceOptions<R>`, whose runtime is
|
|
28
|
+
* optional, does not type-check there.
|
|
29
|
+
*
|
|
30
|
+
* Either handler may fail. Add an `onError` handler to handle the error
|
|
31
|
+
* (typed as the union of the handlers' error types when written after them;
|
|
32
|
+
* `unknown` when written before a handler with unannotated parameters): a
|
|
33
|
+
* failed `onSubmit` also cancels the submission, and a failed `onResult` does
|
|
34
|
+
* not fall back to SvelteKit's default behaviour. Without `onError`, or for a
|
|
35
|
+
* defect or interruption, the promise SvelteKit awaits rejects (with the error
|
|
36
|
+
* itself for a failure), which SvelteKit leaves unhandled.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const submit = Forms.enhance({
|
|
41
|
+
* onResult: ({ result }) => Forms.applyAction(result),
|
|
42
|
+
* // error: ApplyActionError
|
|
43
|
+
* onError: (error) => Effect.logWarning("applyAction failed", error.cause),
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare const enhance: <RSubmit = unknown, RResult = unknown, RError = unknown, ESubmit = any, EResult = any, Keys extends EnhanceHandlerKey = never, E = OnErrorInput<HandlerErrors<Keys, ESubmit, EResult>>>(handlers: EnhanceHandlersBy<RSubmit, RResult, RError, ESubmit, EResult, E> & HandlerKeys<Keys> & NoInfer<OnErrorAccepts<RError, HandlerErrors<Keys, ESubmit, EResult>, E>>, ...rest: NoInfer<EnhanceOptionsArgs<RequiredServices<RSubmit> | RequiredServices<RResult> | RequiredServices<RError>>>) => import("$app/forms").SubmitFunction;
|
|
48
|
+
declare const ApplyActionError_base: Schema.Class<ApplyActionError, Schema.TaggedStruct<"ApplyActionError", {
|
|
49
|
+
/** The rejection reason, or the value thrown, as SvelteKit produced it. */
|
|
50
|
+
readonly cause: Schema.Defect;
|
|
51
|
+
}>, import("effect/Cause").YieldableError>;
|
|
52
|
+
/**
|
|
53
|
+
* {@link applyAction} rejected or threw. `cause` is SvelteKit's original
|
|
54
|
+
* reason, unchanged.
|
|
55
|
+
*
|
|
56
|
+
* SvelteKit 3 throws when `applyAction` is called during server rendering, and
|
|
57
|
+
* follows a `redirect` result with a client-side navigation that rejects if it
|
|
58
|
+
* fails: for example, a `location` that cannot be parsed even relative to the
|
|
59
|
+
* page's base URL, or a `beforeNavigate` / `onNavigate` callback that throws. Recover with
|
|
60
|
+
* `Effect.catchTag("ApplyActionError", ...)`.
|
|
15
61
|
*/
|
|
16
|
-
declare
|
|
62
|
+
export declare class ApplyActionError extends ApplyActionError_base {}
|
|
17
63
|
/**
|
|
18
64
|
* Apply an `ActionResult` to the page (updating `page.form` / `page.status`,
|
|
19
65
|
* resetting the form, etc.) as an Effect. Wraps `$app/forms`' `applyAction`.
|
|
66
|
+
*
|
|
67
|
+
* Fails with an {@link ApplyActionError} if SvelteKit rejects or throws: for
|
|
68
|
+
* example, when the navigation that follows a `redirect` result fails, or when
|
|
69
|
+
* called during server rendering.
|
|
70
|
+
*
|
|
71
|
+
* {@link enhance}'s `onResult` can return it directly; its `onError` handler
|
|
72
|
+
* then receives the `ApplyActionError`. Without `onError`, the error rejects
|
|
73
|
+
* the promise SvelteKit awaits.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* const submit = Forms.enhance({
|
|
78
|
+
* onResult: ({ result }) => Forms.applyAction(result),
|
|
79
|
+
* onError: (error) => Effect.logWarning("applyAction failed", error.cause),
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
20
82
|
*/
|
|
21
|
-
declare const applyAction$1: <Success extends Record<string, unknown> | undefined = Record<string, unknown>, Failure extends Record<string, unknown> | undefined = Record<string, unknown>>(result: ActionResult<Success, Failure>) => Effect.Effect<void>;
|
|
83
|
+
declare const applyAction$1: <Success extends Record<string, unknown> | undefined = Record<string, unknown>, Failure extends Record<string, unknown> | undefined = Record<string, unknown>>(result: ActionResult<Success, Failure>) => Effect.Effect<void, ApplyActionError>;
|
|
22
84
|
/**
|
|
23
85
|
* Deserialize a raw form-action response body into an `ActionResult`, as an
|
|
24
86
|
* Effect. Wraps `$app/forms`' `deserialize`; a malformed body fails the effect.
|
|
25
87
|
*/
|
|
26
88
|
declare const deserialize$1: <Success extends Record<string, unknown> | undefined = Record<string, unknown>, Failure extends Record<string, unknown> | undefined = Record<string, unknown>>(result: string) => Effect.Effect<ActionResult<Success, Failure>, unknown>;
|
|
27
89
|
//#endregion
|
|
28
|
-
export { type EnhanceHandlers, type EnhanceOptions, type EnhanceRuntime, type ResultOptions, type SubmitInput, applyAction$1 as applyAction, deserialize$1 as deserialize,
|
|
90
|
+
export { type EnhanceHandlers, type EnhanceOptions, type EnhanceOptionsArgs, type EnhanceRuntime, type ResultOptions, type SubmitInput, applyAction$1 as applyAction, deserialize$1 as deserialize, forms_d_exports };
|
|
29
91
|
//# sourceMappingURL=forms.d.ts.map
|