@thomasfosterau/effect-sveltekit 0.0.0 → 0.1.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.
@@ -60,18 +60,56 @@ export const handler = makeHandler({
60
60
  envPrefix: ${JSON.stringify(envPrefix)},
61
61
  });
62
62
  `;
63
- /** Template for the generated `<out>/index.js`. */
64
- const entryTemplate = (envPrefix) => `\
63
+ /** Template for the generated Node `<out>/index.js`. */
64
+ const nodeEntryTemplate = (envPrefix) => `\
65
65
  import { serve } from "${PACKAGE}/adapter/serve";
66
66
  import { handler } from "./handler.js";
67
67
 
68
68
  serve(handler, { envPrefix: ${JSON.stringify(envPrefix)} });
69
69
  `;
70
70
  /**
71
+ * Template for the generated Cloudflare Workers `<out>/index.js`.
72
+ *
73
+ * Exports a module worker (`default { fetch }`). Static client assets and
74
+ * prerendered pages are served from the `ASSETS` binding; everything else is
75
+ * server-side rendered by the SvelteKit `HttpApp`.
76
+ */
77
+ const fetchEntryTemplate = (envPrefix) => `\
78
+ import { Layer } from "effect";
79
+ import { makeHandlerWith, AssetSource, toWebHandler } from "${PACKAGE}/adapter/handler";
80
+ import { Server } from "./server/index.js";
81
+ import { manifest } from "./manifest.js";
82
+
83
+ let fetchHandler;
84
+
85
+ export default {
86
+ /** @param {Request} request @param {Record<string, any>} env @param {any} ctx */
87
+ fetch(request, env, ctx) {
88
+ fetchHandler ??= toWebHandler(
89
+ makeHandlerWith({
90
+ Server,
91
+ manifest,
92
+ env,
93
+ envPrefix: ${JSON.stringify(envPrefix)},
94
+ // Static assets come from the Workers ASSETS binding (configure it to
95
+ // serve the uploaded \`client/\` + \`prerendered/\` output).
96
+ assets: AssetSource.cloudflareAssets(env.ASSETS),
97
+ // \`read()\` from \`$app/server\` is not backed by a file system on workerd.
98
+ read: (file) => {
99
+ throw new Error(\`read() is not supported on this deployment (\${file})\`);
100
+ },
101
+ }),
102
+ Layer.empty,
103
+ );
104
+ return fetchHandler(request);
105
+ },
106
+ };
107
+ `;
108
+ /**
71
109
  * Creates the Effect SvelteKit adapter.
72
110
  */
73
111
  function effectAdapter(options = {}) {
74
- const { out = "build", precompress = true, envPrefix = "" } = options;
112
+ const { out = "build", precompress = true, envPrefix = "", target = "node" } = options;
75
113
  return {
76
114
  name: `${PACKAGE}/adapter`,
77
115
  async adapt(builder) {
@@ -94,9 +132,9 @@ function effectAdapter(options = {}) {
94
132
  ].join("\n\n"));
95
133
  builder.log.minor("Writing server entry");
96
134
  writeFileSync(`${out}/handler.js`, handlerTemplate(envPrefix));
97
- writeFileSync(`${out}/index.js`, entryTemplate(envPrefix));
135
+ writeFileSync(`${out}/index.js`, target === "fetch" ? fetchEntryTemplate(envPrefix) : nodeEntryTemplate(envPrefix));
98
136
  },
99
- supports: { read: () => true }
137
+ supports: { read: () => target !== "fetch" }
100
138
  };
101
139
  }
102
140
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/adapter/index.ts"],"sourcesContent":["/**\n * Effect deployment adapter for SvelteKit.\n *\n * Like `@sveltejs/adapter-node`, but the production server is an\n * `effect/unstable/http` HTTP server: requests are handled by an Effect HTTP\n * handler, configuration is read through `Config`, and startup/shutdown is\n * managed by Effect layers and scopes (SIGINT/SIGTERM trigger a graceful\n * shutdown that runs all finalizers).\n *\n * ```js\n * // svelte.config.js\n * import adapter from \"@thomasfosterau/effect-sveltekit/adapter\";\n *\n * const config = {\n * kit: { adapter: adapter() },\n * };\n *\n * export default config;\n * ```\n *\n * The build output (`build/` by default) contains:\n *\n * - `index.js` — runs the production server: `node build`\n * - `handler.js` — exports the SvelteKit app as a composable Effect HTTP\n * handler, for mounting inside an existing `effect/unstable/http` server\n * - `client/`, `server/`, `prerendered/`, `manifest.js` — the built app\n *\n * Unlike `adapter-node`, the server output is not bundled: the deployment\n * needs `node_modules` with the app's production dependencies installed\n * (including `@thomasfosterau/effect-sveltekit`, `effect`, and\n * `@effect/platform-node`).\n */\n\nimport { writeFileSync } from \"node:fs\";\nimport type { Adapter } from \"@sveltejs/kit\";\n\nexport interface EffectAdapterOptions {\n /**\n * The directory to write the production build to.\n * @default \"build\"\n */\n readonly out?: string;\n /**\n * Precompress static assets and prerendered pages with gzip and brotli.\n * @default true\n */\n readonly precompress?: boolean;\n /**\n * Prefix for the environment variables the production server reads\n * (`PORT`, `HOST`, `ORIGIN`, `PROTOCOL_HEADER`, `HOST_HEADER`,\n * `PORT_HEADER`, `ADDRESS_HEADER`, `XFF_DEPTH`).\n * @default \"\"\n */\n readonly envPrefix?: string;\n}\n\nconst PACKAGE = \"@thomasfosterau/effect-sveltekit\";\n\n/** Template for the generated `<out>/handler.js`. */\nconst handlerTemplate = (envPrefix: string): string => `\\\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport process from \"node:process\";\nimport { makeHandler } from \"${PACKAGE}/adapter/handler\";\nimport { Server } from \"./server/index.js\";\nimport { manifest, prerendered, base } from \"./manifest.js\";\n\nconst dir = dirname(fileURLToPath(import.meta.url));\n\n/**\n * The SvelteKit application as a composable Effect HTTP handler.\n *\n * Serve it with the generated index.js, or mount it inside any\n * \\`effect/unstable/http\\` HTTP server.\n */\nexport const handler = makeHandler({\n Server,\n manifest,\n prerendered,\n base,\n dir,\n env: process.env,\n envPrefix: ${JSON.stringify(envPrefix)},\n});\n`;\n\n/** Template for the generated `<out>/index.js`. */\nconst entryTemplate = (envPrefix: string): string => `\\\nimport { serve } from \"${PACKAGE}/adapter/serve\";\nimport { handler } from \"./handler.js\";\n\nserve(handler, { envPrefix: ${JSON.stringify(envPrefix)} });\n`;\n\n/**\n * Creates the Effect SvelteKit adapter.\n */\nexport function effectAdapter(options: EffectAdapterOptions = {}): Adapter {\n const { out = \"build\", precompress = true, envPrefix = \"\" } = options;\n\n return {\n name: `${PACKAGE}/adapter`,\n\n async adapt(builder) {\n const base = builder.config.kit.paths.base;\n\n builder.rimraf(out);\n builder.mkdirp(out);\n\n builder.log.minor(\"Copying assets\");\n builder.writeClient(`${out}/client${base}`);\n builder.writePrerendered(`${out}/prerendered${base}`);\n\n if (precompress) {\n builder.log.minor(\"Compressing assets\");\n await Promise.all([\n builder.compress(`${out}/client`),\n builder.compress(`${out}/prerendered`),\n ]);\n }\n\n builder.log.minor(\"Building server\");\n builder.writeServer(`${out}/server`);\n\n writeFileSync(\n `${out}/manifest.js`,\n [\n `export const manifest = ${builder.generateManifest({ relativePath: \"./server\" })};`,\n `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});`,\n `export const base = ${JSON.stringify(base)};`,\n ].join(\"\\n\\n\"),\n );\n\n builder.log.minor(\"Writing server entry\");\n writeFileSync(`${out}/handler.js`, handlerTemplate(envPrefix));\n writeFileSync(`${out}/index.js`, entryTemplate(envPrefix));\n },\n\n supports: {\n // `read()` from `$app/server` is backed by the platform FileSystem.\n read: () => true,\n },\n };\n}\n\nexport default effectAdapter;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,MAAM,UAAU;;AAGhB,MAAM,mBAAmB,cAA8B;;;;+BAIxB,QAAQ;;;;;;;;;;;;;;;;;;;eAmBxB,KAAK,UAAU,SAAS,EAAE;;;;AAKzC,MAAM,iBAAiB,cAA8B;yBAC5B,QAAQ;;;8BAGH,KAAK,UAAU,SAAS,EAAE;;;;;AAMxD,SAAgB,cAAc,UAAgC,CAAC,GAAY;CACzE,MAAM,EAAE,MAAM,SAAS,cAAc,MAAM,YAAY,OAAO;CAE9D,OAAO;EACL,MAAM,GAAG,QAAQ;EAEjB,MAAM,MAAM,SAAS;GACnB,MAAM,OAAO,QAAQ,OAAO,IAAI,MAAM;GAEtC,QAAQ,OAAO,GAAG;GAClB,QAAQ,OAAO,GAAG;GAElB,QAAQ,IAAI,MAAM,gBAAgB;GAClC,QAAQ,YAAY,GAAG,IAAI,SAAS,MAAM;GAC1C,QAAQ,iBAAiB,GAAG,IAAI,cAAc,MAAM;GAEpD,IAAI,aAAa;IACf,QAAQ,IAAI,MAAM,oBAAoB;IACtC,MAAM,QAAQ,IAAI,CAChB,QAAQ,SAAS,GAAG,IAAI,QAAQ,GAChC,QAAQ,SAAS,GAAG,IAAI,aAAa,CACvC,CAAC;GACH;GAEA,QAAQ,IAAI,MAAM,iBAAiB;GACnC,QAAQ,YAAY,GAAG,IAAI,QAAQ;GAEnC,cACE,GAAG,IAAI,eACP;IACE,2BAA2B,QAAQ,iBAAiB,EAAE,cAAc,WAAW,CAAC,EAAE;IAClF,sCAAsC,KAAK,UAAU,QAAQ,YAAY,KAAK,EAAE;IAChF,uBAAuB,KAAK,UAAU,IAAI,EAAE;GAC9C,EAAE,KAAK,MAAM,CACf;GAEA,QAAQ,IAAI,MAAM,sBAAsB;GACxC,cAAc,GAAG,IAAI,cAAc,gBAAgB,SAAS,CAAC;GAC7D,cAAc,GAAG,IAAI,YAAY,cAAc,SAAS,CAAC;EAC3D;EAEA,UAAU,EAER,YAAY,KACd;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/adapter/index.ts"],"sourcesContent":["/**\n * Effect deployment adapter for SvelteKit.\n *\n * Like `@sveltejs/adapter-node`, but the production server is an\n * `effect/unstable/http` HTTP server: requests are handled by an Effect HTTP\n * handler, configuration is read through `Config`, and startup/shutdown is\n * managed by Effect layers and scopes (SIGINT/SIGTERM trigger a graceful\n * shutdown that runs all finalizers).\n *\n * ```js\n * // svelte.config.js\n * import adapter from \"@thomasfosterau/effect-sveltekit/adapter\";\n *\n * const config = {\n * kit: { adapter: adapter() },\n * };\n *\n * export default config;\n * ```\n *\n * The build output (`build/` by default) contains:\n *\n * - `index.js` — runs the production server: `node build`\n * - `handler.js` — exports the SvelteKit app as a composable Effect HTTP\n * handler, for mounting inside an existing `effect/unstable/http` server\n * - `client/`, `server/`, `prerendered/`, `manifest.js` — the built app\n *\n * Unlike `adapter-node`, the server output is not bundled: the deployment\n * needs `node_modules` with the app's production dependencies installed\n * (including `@thomasfosterau/effect-sveltekit`, `effect`, and\n * `@effect/platform-node`).\n */\n\nimport { writeFileSync } from \"node:fs\";\nimport type { Adapter } from \"@sveltejs/kit\";\n\nexport interface EffectAdapterOptions {\n /**\n * The directory to write the production build to.\n * @default \"build\"\n */\n readonly out?: string;\n /**\n * Precompress static assets and prerendered pages with gzip and brotli.\n * @default true\n */\n readonly precompress?: boolean;\n /**\n * Prefix for the environment variables the production server reads\n * (`PORT`, `HOST`, `ORIGIN`, `PROTOCOL_HEADER`, `HOST_HEADER`,\n * `PORT_HEADER`, `ADDRESS_HEADER`, `XFF_DEPTH`).\n * @default \"\"\n */\n readonly envPrefix?: string;\n /**\n * The deployment target for the generated entry:\n *\n * - `\"node\"` (default) — a production Node HTTP server (`node build`) on\n * `@effect/platform-node`, serving assets from disk.\n * - `\"fetch\"` — a Web `fetch` handler suitable for Cloudflare Workers\n * (`build/index.js` exports `default { fetch }`), serving assets from the\n * `env.ASSETS` binding via `AssetSource.cloudflareAssets`.\n *\n * Both targets also write `build/handler.js` (the SvelteKit `HttpApp`), so you\n * can compose your own entry for any `effect/unstable/http` runtime.\n *\n * @default \"node\"\n */\n readonly target?: \"node\" | \"fetch\";\n}\n\nconst PACKAGE = \"@thomasfosterau/effect-sveltekit\";\n\n/** Template for the generated `<out>/handler.js`. */\nconst handlerTemplate = (envPrefix: string): string => `\\\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport process from \"node:process\";\nimport { makeHandler } from \"${PACKAGE}/adapter/handler\";\nimport { Server } from \"./server/index.js\";\nimport { manifest, prerendered, base } from \"./manifest.js\";\n\nconst dir = dirname(fileURLToPath(import.meta.url));\n\n/**\n * The SvelteKit application as a composable Effect HTTP handler.\n *\n * Serve it with the generated index.js, or mount it inside any\n * \\`effect/unstable/http\\` HTTP server.\n */\nexport const handler = makeHandler({\n Server,\n manifest,\n prerendered,\n base,\n dir,\n env: process.env,\n envPrefix: ${JSON.stringify(envPrefix)},\n});\n`;\n\n/** Template for the generated Node `<out>/index.js`. */\nconst nodeEntryTemplate = (envPrefix: string): string => `\\\nimport { serve } from \"${PACKAGE}/adapter/serve\";\nimport { handler } from \"./handler.js\";\n\nserve(handler, { envPrefix: ${JSON.stringify(envPrefix)} });\n`;\n\n/**\n * Template for the generated Cloudflare Workers `<out>/index.js`.\n *\n * Exports a module worker (`default { fetch }`). Static client assets and\n * prerendered pages are served from the `ASSETS` binding; everything else is\n * server-side rendered by the SvelteKit `HttpApp`.\n */\nconst fetchEntryTemplate = (envPrefix: string): string => `\\\nimport { Layer } from \"effect\";\nimport { makeHandlerWith, AssetSource, toWebHandler } from \"${PACKAGE}/adapter/handler\";\nimport { Server } from \"./server/index.js\";\nimport { manifest } from \"./manifest.js\";\n\nlet fetchHandler;\n\nexport default {\n /** @param {Request} request @param {Record<string, any>} env @param {any} ctx */\n fetch(request, env, ctx) {\n fetchHandler ??= toWebHandler(\n makeHandlerWith({\n Server,\n manifest,\n env,\n envPrefix: ${JSON.stringify(envPrefix)},\n // Static assets come from the Workers ASSETS binding (configure it to\n // serve the uploaded \\`client/\\` + \\`prerendered/\\` output).\n assets: AssetSource.cloudflareAssets(env.ASSETS),\n // \\`read()\\` from \\`$app/server\\` is not backed by a file system on workerd.\n read: (file) => {\n throw new Error(\\`read() is not supported on this deployment (\\${file})\\`);\n },\n }),\n Layer.empty,\n );\n return fetchHandler(request);\n },\n};\n`;\n\n/**\n * Creates the Effect SvelteKit adapter.\n */\nexport function effectAdapter(options: EffectAdapterOptions = {}): Adapter {\n const { out = \"build\", precompress = true, envPrefix = \"\", target = \"node\" } = options;\n\n return {\n name: `${PACKAGE}/adapter`,\n\n async adapt(builder) {\n const base = builder.config.kit.paths.base;\n\n builder.rimraf(out);\n builder.mkdirp(out);\n\n builder.log.minor(\"Copying assets\");\n builder.writeClient(`${out}/client${base}`);\n builder.writePrerendered(`${out}/prerendered${base}`);\n\n if (precompress) {\n builder.log.minor(\"Compressing assets\");\n await Promise.all([\n builder.compress(`${out}/client`),\n builder.compress(`${out}/prerendered`),\n ]);\n }\n\n builder.log.minor(\"Building server\");\n builder.writeServer(`${out}/server`);\n\n writeFileSync(\n `${out}/manifest.js`,\n [\n `export const manifest = ${builder.generateManifest({ relativePath: \"./server\" })};`,\n `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});`,\n `export const base = ${JSON.stringify(base)};`,\n ].join(\"\\n\\n\"),\n );\n\n builder.log.minor(\"Writing server entry\");\n writeFileSync(`${out}/handler.js`, handlerTemplate(envPrefix));\n writeFileSync(\n `${out}/index.js`,\n target === \"fetch\" ? fetchEntryTemplate(envPrefix) : nodeEntryTemplate(envPrefix),\n );\n },\n\n supports: {\n // On Node, `read()` from `$app/server` is backed by the platform\n // FileSystem. On the `fetch` (Workers) target it has no file system, so\n // routes using `read()` are unsupported there.\n read: () => target !== \"fetch\",\n },\n };\n}\n\nexport default effectAdapter;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuEA,MAAM,UAAU;;AAGhB,MAAM,mBAAmB,cAA8B;;;;+BAIxB,QAAQ;;;;;;;;;;;;;;;;;;;eAmBxB,KAAK,UAAU,SAAS,EAAE;;;;AAKzC,MAAM,qBAAqB,cAA8B;yBAChC,QAAQ;;;8BAGH,KAAK,UAAU,SAAS,EAAE;;;;;;;;;AAUxD,MAAM,sBAAsB,cAA8B;;8DAEI,QAAQ;;;;;;;;;;;;;;qBAcjD,KAAK,UAAU,SAAS,EAAE;;;;;;;;;;;;;;;;;;AAmB/C,SAAgB,cAAc,UAAgC,CAAC,GAAY;CACzE,MAAM,EAAE,MAAM,SAAS,cAAc,MAAM,YAAY,IAAI,SAAS,WAAW;CAE/E,OAAO;EACL,MAAM,GAAG,QAAQ;EAEjB,MAAM,MAAM,SAAS;GACnB,MAAM,OAAO,QAAQ,OAAO,IAAI,MAAM;GAEtC,QAAQ,OAAO,GAAG;GAClB,QAAQ,OAAO,GAAG;GAElB,QAAQ,IAAI,MAAM,gBAAgB;GAClC,QAAQ,YAAY,GAAG,IAAI,SAAS,MAAM;GAC1C,QAAQ,iBAAiB,GAAG,IAAI,cAAc,MAAM;GAEpD,IAAI,aAAa;IACf,QAAQ,IAAI,MAAM,oBAAoB;IACtC,MAAM,QAAQ,IAAI,CAChB,QAAQ,SAAS,GAAG,IAAI,QAAQ,GAChC,QAAQ,SAAS,GAAG,IAAI,aAAa,CACvC,CAAC;GACH;GAEA,QAAQ,IAAI,MAAM,iBAAiB;GACnC,QAAQ,YAAY,GAAG,IAAI,QAAQ;GAEnC,cACE,GAAG,IAAI,eACP;IACE,2BAA2B,QAAQ,iBAAiB,EAAE,cAAc,WAAW,CAAC,EAAE;IAClF,sCAAsC,KAAK,UAAU,QAAQ,YAAY,KAAK,EAAE;IAChF,uBAAuB,KAAK,UAAU,IAAI,EAAE;GAC9C,EAAE,KAAK,MAAM,CACf;GAEA,QAAQ,IAAI,MAAM,sBAAsB;GACxC,cAAc,GAAG,IAAI,cAAc,gBAAgB,SAAS,CAAC;GAC7D,cACE,GAAG,IAAI,YACP,WAAW,UAAU,mBAAmB,SAAS,IAAI,kBAAkB,SAAS,CAClF;EACF;EAEA,UAAU,EAIR,YAAY,WAAW,QACzB;CACF;AACF"}
@@ -1,4 +1,5 @@
1
1
  import { SvelteKitRequestEvent } from "./runtime.js";
2
+ import { ErrorTranslator } from "./request-runtime.js";
2
3
  import { Effect, Exit, Fiber, Layer, ManagedRuntime } from "effect";
3
4
  import * as HttpClient from "effect/unstable/http/HttpClient";
4
5
  import { Action, Actions, Handle, HandleFetch, HandleServerError, Load, RequestEvent, ServerInit, ServerLoad } from "@sveltejs/kit";
@@ -29,6 +30,21 @@ interface RequestRuntime<R = never> {
29
30
  /** Fork an effect for this request, returning the running fiber. */
30
31
  readonly runFork: <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Fiber.Fiber<A, unknown>;
31
32
  }
33
+ /**
34
+ * Options for {@link makeServerRuntime}.
35
+ */
36
+ interface ServerRuntimeOptions {
37
+ /**
38
+ * Translate a failed `Cause` into SvelteKit control flow at the boundary of
39
+ * every wrapper (`serverLoad`, `action`, `actions`, `endpoint`, `handle`,
40
+ * `handleFetch`, `handleError`, `init`). It may throw `redirect()` /
41
+ * `error()` so a domain error catalogue surfaces as SvelteKit control flow
42
+ * instead of a generic 500. Defaults to the package passthrough: SvelteKit's
43
+ * own `redirect()` / `error()` are rethrown, anything else becomes an
44
+ * `EffectCauseError` (a 500).
45
+ */
46
+ readonly translateError?: ErrorTranslator;
47
+ }
32
48
  /**
33
49
  * A server-side Effect runtime bound to an application layer.
34
50
  *
@@ -152,7 +168,7 @@ interface ServerRuntime<R = never, E = never> {
152
168
  * `HttpClient`. Request-specific data should be passed to service methods as
153
169
  * arguments instead.
154
170
  */
155
- declare const makeServerRuntime: <R = never, E = never>(layer?: Layer.Layer<R, E>) => ServerRuntime<R, E>;
171
+ declare const makeServerRuntime: <R = never, E = never>(layer?: Layer.Layer<R, E>, options?: ServerRuntimeOptions) => ServerRuntime<R, E>;
156
172
  /**
157
173
  * A universal (client + server) Effect runtime bound to an application layer.
158
174
  *
@@ -208,5 +224,5 @@ interface UniversalRuntime<R = never, E = never> {
208
224
  */
209
225
  declare const makeUniversalRuntime: <R = never, E = never>(layer?: Layer.Layer<R, E>) => UniversalRuntime<R, E>;
210
226
  //#endregion
211
- export { RequestRuntime, ServerRuntime, SvelteKitServices, UniversalRuntime, makeServerRuntime, makeUniversalRuntime };
227
+ export { RequestRuntime, ServerRuntime, ServerRuntimeOptions, SvelteKitServices, UniversalRuntime, makeServerRuntime, makeUniversalRuntime };
212
228
  //# sourceMappingURL=app-runtime.d.ts.map
@@ -15,13 +15,15 @@ const requestRuntimeEntry = (event) => event.locals?.[RequestRuntimeKey];
15
15
  const requestLayer = (event) => Layer.merge(FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, event.fetch))), Layer.succeed(SvelteKitRequestEvent, event));
16
16
  /** Layer providing `HttpClient` backed by an arbitrary fetch implementation. */
17
17
  const fetchLayer = (fetchFn) => FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)));
18
- /** Settle an exit the way SvelteKit expects: return the value or throw. */
19
- const settle = (exit) => Exit.match(exit, {
20
- onFailure: (cause) => {
21
- throw causeToThrowable(cause);
22
- },
18
+ /** Build a settle that returns a success value or throws via the translator. */
19
+ const makeSettle = (translate) => (exit) => Exit.match(exit, {
20
+ onFailure: (cause) => translate(cause),
23
21
  onSuccess: (value) => value
24
22
  });
23
+ /** The default settle: SvelteKit control flow through, anything else a 500. */
24
+ const settle = makeSettle((cause) => {
25
+ throw causeToThrowable(cause);
26
+ });
25
27
  /**
26
28
  * Creates a {@link ServerRuntime} from an application layer.
27
29
  *
@@ -58,15 +60,16 @@ const settle = (exit) => Exit.match(exit, {
58
60
  * `HttpClient`. Request-specific data should be passed to service methods as
59
61
  * arguments instead.
60
62
  */
61
- const makeServerRuntime = (layer) => {
63
+ const makeServerRuntime = (layer, options) => {
62
64
  const runtime = ManagedRuntime.make(layer ?? Layer.empty);
65
+ const settleRequest = options?.translateError ? makeSettle(options.translateError) : settle;
63
66
  const owner = {};
64
- /** A {@link RequestRuntime} that provides the prebuilt request services. */
67
+ /** A {@link ScopedRequestRuntime} that provides the prebuilt request services. */
65
68
  const makeRequestRuntime = (requestServices) => {
66
69
  const provide = (effect) => Effect.provideContext(effect, requestServices);
67
70
  return {
68
71
  runPromiseExit: (effect) => runtime.runPromiseExit(provide(effect)),
69
- runPromise: (effect) => runtime.runPromiseExit(provide(effect)).then(settle),
72
+ runPromise: (effect) => runtime.runPromiseExit(provide(effect)).then(settleRequest),
70
73
  runFork: (effect) => runtime.runFork(provide(effect))
71
74
  };
72
75
  };
@@ -81,7 +84,7 @@ const makeServerRuntime = (layer) => {
81
84
  const provided = Effect.provide(effect, requestLayer(event));
82
85
  return runtime.runPromiseExit(provided);
83
86
  };
84
- const run = (event, effect) => runExit(event, effect).then(settle);
87
+ const run = (event, effect) => runExit(event, effect).then(settleRequest);
85
88
  const action = (fn) => (event) => run(event, fn(event));
86
89
  /**
87
90
  * The `handle` hook: build a per-request runtime, store it on `event.locals`
@@ -115,7 +118,7 @@ const makeServerRuntime = (layer) => {
115
118
  handleFetch: (fn) => (args) => run(args.event, fn(args)),
116
119
  handleError: (fn) => (args) => run(args.event, fn(args)),
117
120
  init: (effect) => async () => {
118
- settle(await runtime.runPromiseExit(effect));
121
+ settleRequest(await runtime.runPromiseExit(effect));
119
122
  },
120
123
  requestRuntime: requestRuntimeOf,
121
124
  endpoint: (fn) => (event) => run(event, fn(event)),
@@ -1 +1 @@
1
- {"version":3,"file":"app-runtime.js","names":[],"sources":["../src/app-runtime.ts"],"sourcesContent":["/**\n * App-wide Effect runtimes for SvelteKit.\n *\n * `makeServerRuntime` / `makeUniversalRuntime` let an application define its\n * Effect services (database clients, API wrappers, config, ...) as a single\n * `Layer`, built once per server process and shared by every request, instead\n * of being rebuilt for each `load` / `action` / endpoint invocation.\n *\n * Per-request services — the {@link SvelteKitRequestEvent} and an `HttpClient`\n * backed by the event's `fetch` — are provided on top of the shared runtime\n * for each call, so SvelteKit's fetch behaviours (cookie forwarding, request\n * deduplication, relative URLs) keep working as expected.\n */\n\nimport type {\n Action,\n Actions,\n Handle,\n HandleFetch,\n HandleServerError,\n Load,\n RequestEvent,\n ServerInit,\n ServerLoad,\n} from \"@sveltejs/kit\";\nimport { Context, Effect, type Fiber, Exit, Layer, ManagedRuntime, Scope } from \"effect\";\nimport * as FetchHttpClient from \"effect/unstable/http/FetchHttpClient\";\nimport type * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport { SvelteKitRequestEvent } from \"./runtime.js\";\nimport { causeToThrowable } from \"./internal/error.js\";\n\n/**\n * The services provided to every wrapped effect on the server, in addition to\n * the application's own layer.\n */\nexport type SvelteKitServices = HttpClient.HttpClient | SvelteKitRequestEvent;\n\n/**\n * A per-request Effect runtime: the application layer's services plus the\n * per-request {@link SvelteKitServices} (an `HttpClient` backed by the request's\n * `fetch`, and the {@link SvelteKitRequestEvent}), with the request services\n * built once for the request and shared.\n *\n * A {@link ServerRuntime}'s `handle` hook creates one per request and stores it\n * on `event.locals` (`App.Locals`). Every other wrapper for the same runtime\n * (`serverLoad`, `action`, `endpoint`, ...) then runs on it, so a request's\n * effects share one `HttpClient` and one request scope. Retrieve it from an\n * event with `ServerRuntime.requestRuntime` to run your own effects.\n */\nexport interface RequestRuntime<R = never> {\n /** Run an effect for this request, returning its `Exit`. */\n readonly runPromiseExit: <A>(\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<Exit.Exit<A, unknown>>;\n /** Run an effect for this request, throwing on failure (passing SvelteKit control flow through). */\n readonly runPromise: <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Promise<A>;\n /** Fork an effect for this request, returning the running fiber. */\n readonly runFork: <A>(\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Fiber.Fiber<A, unknown>;\n}\n\n/**\n * Symbol under which a {@link ServerRuntime}'s `handle` stores the per-request\n * {@link RequestRuntime} on `event.locals`. `Symbol.for` so the key is stable\n * across module instances.\n */\nconst RequestRuntimeKey: unique symbol = Symbol.for(\n \"@thomasfosterau/effect-sveltekit/requestRuntime\",\n);\n\n/**\n * The locals entry: the per-request runtime tagged with the `ServerRuntime`\n * that created it, so a different runtime's wrappers ignore it (and fall back\n * to building request services themselves) rather than running on the wrong\n * application layer.\n */\ninterface RequestRuntimeEntry {\n readonly owner: object;\n readonly runtime: RequestRuntime<any>;\n}\n\n/** Read the per-request runtime entry an event carries, if any. */\nconst requestRuntimeEntry = (event: RequestEvent): RequestRuntimeEntry | undefined =>\n (event.locals as Record<PropertyKey, unknown> | undefined)?.[RequestRuntimeKey] as\n | RequestRuntimeEntry\n | undefined;\n\n/** Layer with the per-request services for a server-side request. */\nconst requestLayer = (event: RequestEvent): Layer.Layer<SvelteKitServices> =>\n Layer.merge(\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, event.fetch))),\n Layer.succeed(SvelteKitRequestEvent, event),\n );\n\n/** Layer providing `HttpClient` backed by an arbitrary fetch implementation. */\nconst fetchLayer = (fetchFn: typeof globalThis.fetch): Layer.Layer<HttpClient.HttpClient> =>\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)));\n\n/** Settle an exit the way SvelteKit expects: return the value or throw. */\nconst settle = <A>(exit: Exit.Exit<A, unknown>): A =>\n Exit.match(exit, {\n onFailure: (cause) => {\n throw causeToThrowable(cause);\n },\n onSuccess: (value) => value,\n });\n\n/**\n * A server-side Effect runtime bound to an application layer.\n *\n * Produced by {@link makeServerRuntime}. The wrapper methods mirror the\n * standalone helpers in `@thomasfosterau/effect-sveltekit/server`, but\n * effects additionally have access to the services `R` of the application\n * layer.\n */\nexport interface ServerRuntime<R = never, E = never> {\n /** The underlying managed runtime for the application layer. */\n readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `load`.\n */\n readonly serverLoad: <\n Params extends Record<string, string> = any,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>,\n ) => ServerLoad<Params, ParentData, OutputData, RouteId>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit form action.\n */\n readonly action: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>,\n ) => Action<Params, OutputData, RouteId>;\n\n /**\n * Wraps an object of Effect-based form actions.\n */\n readonly actions: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n actionsMap: Record<\n string,\n (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>\n >,\n ) => Actions<Params, OutputData, RouteId>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit `handle` hook.\n */\n readonly handle: (\n fn: (args: Parameters<Handle>[0]) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => Handle;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `handleFetch` hook,\n * which intercepts `event.fetch` calls made on the server. The effect runs\n * with the per-request services on top of the application layer and produces\n * the `Response` to use.\n */\n readonly handleFetch: (\n fn: (\n args: Parameters<HandleFetch>[0],\n ) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => HandleFetch;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `handleError` hook.\n *\n * The effect receives the error and the request event, and produces the\n * shape that SvelteKit exposes to the client (your `App.Error`, or nothing).\n * It runs with the per-request services on top of the application layer.\n */\n readonly handleError: (\n fn: (\n args: Parameters<HandleServerError>[0],\n ) => Effect.Effect<Awaited<ReturnType<HandleServerError>>, unknown, R | SvelteKitServices>,\n ) => HandleServerError;\n\n /**\n * Wraps an Effect as a SvelteKit server `init` hook, run once before the\n * server responds to its first request. There is no request event, so the\n * effect only has access to the application layer's services.\n */\n readonly init: (effect: Effect.Effect<void, unknown, R>) => ServerInit;\n\n /**\n * The per-request {@link RequestRuntime} this runtime's `handle` stored on the\n * event, or `undefined` when `handle` was not used for the request. Use it to\n * run your own effects on the same per-request runtime (shared `HttpClient`\n * and request scope) that `serverLoad` / `action` / `endpoint` use.\n *\n * @example\n * ```ts\n * // anywhere with the request event, e.g. another `handle` in the sequence\n * const rt = runtime.requestRuntime(event);\n * await rt?.runPromise(Effect.gen(function* () {\n * const user = yield* Auth.currentUser;\n * event.locals.user = user;\n * }));\n * ```\n */\n readonly requestRuntime: (event: RequestEvent) => RequestRuntime<R> | undefined;\n\n /**\n * Wraps an Effect-based function as a SvelteKit `+server.ts` endpoint\n * handler (`GET`, `POST`, ...).\n */\n readonly endpoint: <\n Params extends Record<string, string> = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => (event: RequestEvent<Params, RouteId>) => Promise<Response>;\n\n /**\n * Runs an effect for a specific request, providing both the application\n * layer and the per-request services. Failures (other than SvelteKit\n * control flow) are thrown as `EffectCauseError`.\n */\n readonly runPromise: <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<A>;\n\n /**\n * Runs an effect for a specific request and returns the Exit, without\n * translating failures into thrown errors.\n */\n readonly runPromiseExit: <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<Exit.Exit<A, unknown>>;\n\n /**\n * Disposes the underlying managed runtime, running the layer's finalizers.\n * Call this on server shutdown or in tests.\n */\n readonly dispose: () => Promise<void>;\n}\n\n/**\n * Creates a {@link ServerRuntime} from an application layer.\n *\n * The layer is built lazily on first use and shared by all requests for the\n * lifetime of the server process. Define it once in a server-only module and\n * import its wrappers from your routes:\n *\n * @example\n * ```ts\n * // src/lib/server/runtime.ts\n * import { makeServerRuntime } from '@thomasfosterau/effect-sveltekit/server';\n * import { Layer } from 'effect';\n * import { Database } from './database';\n *\n * export const runtime = makeServerRuntime(Database.layer);\n * ```\n *\n * ```ts\n * // src/routes/items/+page.server.ts\n * import { runtime } from '$lib/server/runtime';\n * import { Effect } from 'effect';\n * import { Database } from '$lib/server/database';\n *\n * export const load = runtime.serverLoad((event) =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return { items: yield* db.listItems() };\n * })\n * );\n * ```\n *\n * Note that the application layer is built outside of any request, so its\n * services cannot depend on the per-request `SvelteKitRequestEvent` or\n * `HttpClient`. Request-specific data should be passed to service methods as\n * arguments instead.\n */\nexport const makeServerRuntime = <R = never, E = never>(\n layer?: Layer.Layer<R, E>,\n): ServerRuntime<R, E> => {\n const runtime = ManagedRuntime.make((layer ?? Layer.empty) as Layer.Layer<R, E>);\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 RequestRuntime} 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(settle),\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(settle);\n\n const action: ServerRuntime<R, E>[\"action\"] = (fn) => (event) => run(event, fn(event));\n\n /**\n * The `handle` hook: build a per-request runtime, store it on `event.locals`\n * so every other wrapper (and the user's own code) runs on it, run the user's\n * effect, then close the request scope.\n */\n const handle: ServerRuntime<R, E>[\"handle\"] = (fn) => async (input) => {\n const { event } = input;\n const scope = Scope.makeUnsafe();\n const requestServices = (await runtime.runPromise(\n Layer.buildWithScope(requestLayer(event), scope),\n )) as Context.Context<SvelteKitServices>;\n\n const entry: RequestRuntimeEntry = { owner, runtime: makeRequestRuntime(requestServices) };\n (event.locals as Record<PropertyKey, unknown>)[RequestRuntimeKey] = entry;\n\n try {\n return await (entry.runtime as RequestRuntime<R>).runPromise(fn(input));\n } finally {\n await runtime.runPromise(Scope.close(scope, Exit.void));\n }\n };\n\n return {\n runtime,\n serverLoad: (fn) => (event) => run(event, fn(event)),\n action,\n actions: (actionsMap) => {\n const wrapped: Record<string, Action<any, any, any>> = {};\n for (const [key, fn] of Object.entries(actionsMap)) {\n wrapped[key] = action(fn);\n }\n return wrapped as never;\n },\n handle,\n handleFetch: (fn) => (args) => run(args.event, fn(args)),\n handleError: (fn) => (args) => run(args.event, fn(args)),\n init: (effect) => async () => {\n // `init` runs outside of any request, so it executes directly on the\n // application runtime without the per-request layer.\n settle(await runtime.runPromiseExit(effect));\n },\n requestRuntime: requestRuntimeOf,\n endpoint: (fn) => (event) => run(event, fn(event)),\n runPromise: run,\n runPromiseExit: runExit,\n dispose: () => runtime.dispose(),\n };\n};\n\n/**\n * A universal (client + server) Effect runtime bound to an application layer.\n *\n * Produced by {@link makeUniversalRuntime}. Because universal `load`\n * functions also run in the browser, the layer must only use services that\n * work in both environments.\n */\nexport interface UniversalRuntime<R = never, E = never> {\n /** The underlying managed runtime for the application layer. */\n readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit universal `load`.\n *\n * The effect gets `HttpClient` (backed by the load event's `fetch`) on both\n * client and server, plus the services of the application layer.\n */\n readonly load: <\n Params extends Record<string, string> = any,\n InputData extends Record<string, unknown> | null = any,\n ParentData extends Record<string, unknown> = any,\n OutputData extends Record<string, unknown> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: Parameters<Load<Params, InputData, ParentData, OutputData, RouteId>>[0],\n ) => Effect.Effect<OutputData, unknown, R | HttpClient.HttpClient>,\n ) => Load<Params, InputData, ParentData, OutputData, RouteId>;\n\n /**\n * Runs an effect with `HttpClient` backed by the given fetch implementation\n * plus the application layer.\n */\n readonly runPromise: <A>(\n fetchFn: typeof globalThis.fetch,\n effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>,\n ) => Promise<A>;\n\n /**\n * Disposes the underlying managed runtime, running the layer's finalizers.\n */\n readonly dispose: () => Promise<void>;\n}\n\n/**\n * Creates a {@link UniversalRuntime} from an application layer.\n *\n * @example\n * ```ts\n * // src/lib/runtime.ts\n * import { makeUniversalRuntime } from '@thomasfosterau/effect-sveltekit';\n * import { ApiClient } from '$lib/api';\n *\n * export const runtime = makeUniversalRuntime(ApiClient.layer);\n * ```\n *\n * ```ts\n * // src/routes/+page.ts\n * import { runtime } from '$lib/runtime';\n * import { Effect } from 'effect';\n * import { ApiClient } from '$lib/api';\n *\n * export const load = runtime.load((event) =>\n * Effect.gen(function* () {\n * const api = yield* ApiClient;\n * return { stories: yield* api.topStories() };\n * })\n * );\n * ```\n */\nexport const makeUniversalRuntime = <R = never, E = never>(\n layer?: Layer.Layer<R, E>,\n): UniversalRuntime<R, E> => {\n const runtime = ManagedRuntime.make((layer ?? Layer.empty) as Layer.Layer<R, E>);\n\n const run = <A>(\n fetchFn: typeof globalThis.fetch,\n effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>,\n event?: RequestEvent,\n ): Promise<A> => {\n // On the server the full request layer (HttpClient + SvelteKitRequestEvent)\n // is available; on the client only HttpClient backed by the event's fetch.\n const services = event ? requestLayer(event) : fetchLayer(fetchFn);\n const provided = Effect.provide(effect, services) as Effect.Effect<A, unknown, R>;\n return runtime.runPromiseExit(provided).then(settle);\n };\n\n return {\n runtime,\n load: (fn) => (event) => {\n const serverEvent = (event as { request?: unknown }).request\n ? (event as unknown as RequestEvent)\n : undefined;\n return run(event.fetch, fn(event), serverEvent);\n },\n runPromise: (fetchFn, effect) => run(fetchFn, effect),\n dispose: () => runtime.dispose(),\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-runtime.js","names":[],"sources":["../src/app-runtime.ts"],"sourcesContent":["/**\n * App-wide Effect runtimes for SvelteKit.\n *\n * `makeServerRuntime` / `makeUniversalRuntime` let an application define its\n * Effect services (database clients, API wrappers, config, ...) as a single\n * `Layer`, built once per server process and shared by every request, instead\n * of being rebuilt for each `load` / `action` / endpoint invocation.\n *\n * Per-request services — the {@link SvelteKitRequestEvent} and an `HttpClient`\n * backed by the event's `fetch` — are provided on top of the shared runtime\n * for each call, so SvelteKit's fetch behaviours (cookie forwarding, request\n * deduplication, relative URLs) keep working as expected.\n */\n\nimport type {\n Action,\n Actions,\n Handle,\n HandleFetch,\n HandleServerError,\n Load,\n RequestEvent,\n ServerInit,\n ServerLoad,\n} from \"@sveltejs/kit\";\nimport { Context, Effect, type Fiber, Exit, Layer, ManagedRuntime, Scope } from \"effect\";\nimport * as FetchHttpClient from \"effect/unstable/http/FetchHttpClient\";\nimport type * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport { SvelteKitRequestEvent } from \"./runtime.js\";\nimport { causeToThrowable } from \"./internal/error.js\";\nimport type { ErrorTranslator } from \"./request-runtime.js\";\n\n/**\n * The services provided to every wrapped effect on the server, in addition to\n * the application's own layer.\n */\nexport type SvelteKitServices = HttpClient.HttpClient | SvelteKitRequestEvent;\n\n/**\n * A per-request Effect runtime: the application layer's services plus the\n * per-request {@link SvelteKitServices} (an `HttpClient` backed by the request's\n * `fetch`, and the {@link SvelteKitRequestEvent}), with the request services\n * built once for the request and shared.\n *\n * A {@link ServerRuntime}'s `handle` hook creates one per request and stores it\n * on `event.locals` (`App.Locals`). Every other wrapper for the same runtime\n * (`serverLoad`, `action`, `endpoint`, ...) then runs on it, so a request's\n * effects share one `HttpClient` and one request scope. Retrieve it from an\n * event with `ServerRuntime.requestRuntime` to run your own effects.\n */\nexport interface RequestRuntime<R = never> {\n /** Run an effect for this request, returning its `Exit`. */\n readonly runPromiseExit: <A>(\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<Exit.Exit<A, unknown>>;\n /** Run an effect for this request, throwing on failure (passing SvelteKit control flow through). */\n readonly runPromise: <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Promise<A>;\n /** Fork an effect for this request, returning the running fiber. */\n readonly runFork: <A>(\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Fiber.Fiber<A, unknown>;\n}\n\n/**\n * Symbol under which a {@link ServerRuntime}'s `handle` stores the per-request\n * {@link RequestRuntime} on `event.locals`. `Symbol.for` so the key is stable\n * across module instances.\n */\nconst RequestRuntimeKey: unique symbol = Symbol.for(\n \"@thomasfosterau/effect-sveltekit/requestRuntime\",\n);\n\n/**\n * The locals entry: the per-request runtime tagged with the `ServerRuntime`\n * that created it, so a different runtime's wrappers ignore it (and fall back\n * to building request services themselves) rather than running on the wrong\n * application layer.\n */\ninterface RequestRuntimeEntry {\n readonly owner: object;\n readonly runtime: RequestRuntime<any>;\n}\n\n/** Read the per-request runtime entry an event carries, if any. */\nconst requestRuntimeEntry = (event: RequestEvent): RequestRuntimeEntry | undefined =>\n (event.locals as Record<PropertyKey, unknown> | undefined)?.[RequestRuntimeKey] as\n | RequestRuntimeEntry\n | undefined;\n\n/** Layer with the per-request services for a server-side request. */\nconst requestLayer = (event: RequestEvent): Layer.Layer<SvelteKitServices> =>\n Layer.merge(\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, event.fetch))),\n Layer.succeed(SvelteKitRequestEvent, event),\n );\n\n/** Layer providing `HttpClient` backed by an arbitrary fetch implementation. */\nconst fetchLayer = (fetchFn: typeof globalThis.fetch): Layer.Layer<HttpClient.HttpClient> =>\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)));\n\n/** Build a settle that returns a success value or throws via the translator. */\nconst makeSettle =\n (translate: ErrorTranslator) =>\n <A>(exit: Exit.Exit<A, unknown>): A =>\n Exit.match(exit, {\n onFailure: (cause) => translate(cause),\n onSuccess: (value) => value,\n });\n\n/** The default settle: SvelteKit control flow through, anything else a 500. */\nconst settle = makeSettle((cause) => {\n throw causeToThrowable(cause);\n});\n\n/**\n * Options for {@link makeServerRuntime}.\n */\nexport interface ServerRuntimeOptions {\n /**\n * Translate a failed `Cause` into SvelteKit control flow at the boundary of\n * every wrapper (`serverLoad`, `action`, `actions`, `endpoint`, `handle`,\n * `handleFetch`, `handleError`, `init`). It may throw `redirect()` /\n * `error()` so a domain error catalogue surfaces as SvelteKit control flow\n * instead of a generic 500. Defaults to the package passthrough: SvelteKit's\n * own `redirect()` / `error()` are rethrown, anything else becomes an\n * `EffectCauseError` (a 500).\n */\n readonly translateError?: ErrorTranslator;\n}\n\n/**\n * A server-side Effect runtime bound to an application layer.\n *\n * Produced by {@link makeServerRuntime}. The wrapper methods mirror the\n * standalone helpers in `@thomasfosterau/effect-sveltekit/server`, but\n * effects additionally have access to the services `R` of the application\n * layer.\n */\nexport interface ServerRuntime<R = never, E = never> {\n /** The underlying managed runtime for the application layer. */\n readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `load`.\n */\n readonly serverLoad: <\n Params extends Record<string, string> = any,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>,\n ) => ServerLoad<Params, ParentData, OutputData, RouteId>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit form action.\n */\n readonly action: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>,\n ) => Action<Params, OutputData, RouteId>;\n\n /**\n * Wraps an object of Effect-based form actions.\n */\n readonly actions: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n actionsMap: Record<\n string,\n (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>\n >,\n ) => Actions<Params, OutputData, RouteId>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit `handle` hook.\n */\n readonly handle: (\n fn: (args: Parameters<Handle>[0]) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => Handle;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `handleFetch` hook,\n * which intercepts `event.fetch` calls made on the server. The effect runs\n * with the per-request services on top of the application layer and produces\n * the `Response` to use.\n */\n readonly handleFetch: (\n fn: (\n args: Parameters<HandleFetch>[0],\n ) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => HandleFetch;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `handleError` hook.\n *\n * The effect receives the error and the request event, and produces the\n * shape that SvelteKit exposes to the client (your `App.Error`, or nothing).\n * It runs with the per-request services on top of the application layer.\n */\n readonly handleError: (\n fn: (\n args: Parameters<HandleServerError>[0],\n ) => Effect.Effect<Awaited<ReturnType<HandleServerError>>, unknown, R | SvelteKitServices>,\n ) => HandleServerError;\n\n /**\n * Wraps an Effect as a SvelteKit server `init` hook, run once before the\n * server responds to its first request. There is no request event, so the\n * effect only has access to the application layer's services.\n */\n readonly init: (effect: Effect.Effect<void, unknown, R>) => ServerInit;\n\n /**\n * The per-request {@link RequestRuntime} this runtime's `handle` stored on the\n * event, or `undefined` when `handle` was not used for the request. Use it to\n * run your own effects on the same per-request runtime (shared `HttpClient`\n * and request scope) that `serverLoad` / `action` / `endpoint` use.\n *\n * @example\n * ```ts\n * // anywhere with the request event, e.g. another `handle` in the sequence\n * const rt = runtime.requestRuntime(event);\n * await rt?.runPromise(Effect.gen(function* () {\n * const user = yield* Auth.currentUser;\n * event.locals.user = user;\n * }));\n * ```\n */\n readonly requestRuntime: (event: RequestEvent) => RequestRuntime<R> | undefined;\n\n /**\n * Wraps an Effect-based function as a SvelteKit `+server.ts` endpoint\n * handler (`GET`, `POST`, ...).\n */\n readonly endpoint: <\n Params extends Record<string, string> = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => (event: RequestEvent<Params, RouteId>) => Promise<Response>;\n\n /**\n * Runs an effect for a specific request, providing both the application\n * layer and the per-request services. Failures (other than SvelteKit\n * control flow) are thrown as `EffectCauseError`.\n */\n readonly runPromise: <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<A>;\n\n /**\n * Runs an effect for a specific request and returns the Exit, without\n * translating failures into thrown errors.\n */\n readonly runPromiseExit: <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<Exit.Exit<A, unknown>>;\n\n /**\n * Disposes the underlying managed runtime, running the layer's finalizers.\n * Call this on server shutdown or in tests.\n */\n readonly dispose: () => Promise<void>;\n}\n\n/**\n * Creates a {@link ServerRuntime} from an application layer.\n *\n * The layer is built lazily on first use and shared by all requests for the\n * lifetime of the server process. Define it once in a server-only module and\n * import its wrappers from your routes:\n *\n * @example\n * ```ts\n * // src/lib/server/runtime.ts\n * import { makeServerRuntime } from '@thomasfosterau/effect-sveltekit/server';\n * import { Layer } from 'effect';\n * import { Database } from './database';\n *\n * export const runtime = makeServerRuntime(Database.layer);\n * ```\n *\n * ```ts\n * // src/routes/items/+page.server.ts\n * import { runtime } from '$lib/server/runtime';\n * import { Effect } from 'effect';\n * import { Database } from '$lib/server/database';\n *\n * export const load = runtime.serverLoad((event) =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return { items: yield* db.listItems() };\n * })\n * );\n * ```\n *\n * Note that the application layer is built outside of any request, so its\n * services cannot depend on the per-request `SvelteKitRequestEvent` or\n * `HttpClient`. Request-specific data should be passed to service methods as\n * arguments instead.\n */\nexport const makeServerRuntime = <R = never, E = never>(\n layer?: Layer.Layer<R, E>,\n options?: ServerRuntimeOptions,\n): ServerRuntime<R, E> => {\n const runtime = ManagedRuntime.make((layer ?? Layer.empty) as Layer.Layer<R, E>);\n\n // The boundary translator for this runtime's wrappers (defaults to the\n // package passthrough: SvelteKit control flow through, anything else a 500).\n const settleRequest = options?.translateError ? makeSettle(options.translateError) : settle;\n\n // Identity token tagging the per-request runtimes this runtime creates, so a\n // different runtime's wrappers don't run on the wrong application layer.\n const owner = {};\n\n /** A {@link ScopedRequestRuntime} that provides the prebuilt request services. */\n const makeRequestRuntime = (\n requestServices: Context.Context<SvelteKitServices>,\n ): RequestRuntime<R> => {\n const provide = <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) =>\n Effect.provideContext(effect, requestServices) as Effect.Effect<A, unknown, R>;\n return {\n runPromiseExit: (effect) =>\n runtime.runPromiseExit(provide(effect)) as Promise<Exit.Exit<any, unknown>>,\n runPromise: (effect) => runtime.runPromiseExit(provide(effect)).then(settleRequest),\n runFork: (effect) => runtime.runFork(provide(effect)) as Fiber.Fiber<any, unknown>,\n };\n };\n\n /** The per-request runtime for this runtime carried by the event, if any. */\n const requestRuntimeOf = (event: RequestEvent): RequestRuntime<R> | undefined => {\n const entry = requestRuntimeEntry(event);\n return entry?.owner === owner ? (entry.runtime as RequestRuntime<R>) : undefined;\n };\n\n const runExit = <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ): Promise<Exit.Exit<A, unknown>> => {\n // Reuse the per-request runtime `handle` installed, if any; otherwise build\n // the per-request services for this single call.\n const requestRuntime = requestRuntimeOf(event);\n if (requestRuntime !== undefined) return requestRuntime.runPromiseExit(effect);\n const provided = Effect.provide(effect, requestLayer(event)) as Effect.Effect<A, unknown, R>;\n return runtime.runPromiseExit(provided) as Promise<Exit.Exit<A, unknown>>;\n };\n\n const run = <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ): Promise<A> => runExit(event, effect).then(settleRequest);\n\n const action: ServerRuntime<R, E>[\"action\"] = (fn) => (event) => run(event, fn(event));\n\n /**\n * The `handle` hook: build a per-request runtime, store it on `event.locals`\n * so every other wrapper (and the user's own code) runs on it, run the user's\n * effect, then close the request scope.\n */\n const handle: ServerRuntime<R, E>[\"handle\"] = (fn) => async (input) => {\n const { event } = input;\n const scope = Scope.makeUnsafe();\n const requestServices = (await runtime.runPromise(\n Layer.buildWithScope(requestLayer(event), scope),\n )) as Context.Context<SvelteKitServices>;\n\n const entry: RequestRuntimeEntry = { owner, runtime: makeRequestRuntime(requestServices) };\n (event.locals as Record<PropertyKey, unknown>)[RequestRuntimeKey] = entry;\n\n try {\n return await (entry.runtime as RequestRuntime<R>).runPromise(fn(input));\n } finally {\n await runtime.runPromise(Scope.close(scope, Exit.void));\n }\n };\n\n return {\n runtime,\n serverLoad: (fn) => (event) => run(event, fn(event)),\n action,\n actions: (actionsMap) => {\n const wrapped: Record<string, Action<any, any, any>> = {};\n for (const [key, fn] of Object.entries(actionsMap)) {\n wrapped[key] = action(fn);\n }\n return wrapped as never;\n },\n handle,\n handleFetch: (fn) => (args) => run(args.event, fn(args)),\n handleError: (fn) => (args) => run(args.event, fn(args)),\n init: (effect) => async () => {\n // `init` runs outside of any request, so it executes directly on the\n // application runtime without the per-request layer.\n settleRequest(await runtime.runPromiseExit(effect));\n },\n requestRuntime: requestRuntimeOf,\n endpoint: (fn) => (event) => run(event, fn(event)),\n runPromise: run,\n runPromiseExit: runExit,\n dispose: () => runtime.dispose(),\n };\n};\n\n/**\n * A universal (client + server) Effect runtime bound to an application layer.\n *\n * Produced by {@link makeUniversalRuntime}. Because universal `load`\n * functions also run in the browser, the layer must only use services that\n * work in both environments.\n */\nexport interface UniversalRuntime<R = never, E = never> {\n /** The underlying managed runtime for the application layer. */\n readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit universal `load`.\n *\n * The effect gets `HttpClient` (backed by the load event's `fetch`) on both\n * client and server, plus the services of the application layer.\n */\n readonly load: <\n Params extends Record<string, string> = any,\n InputData extends Record<string, unknown> | null = any,\n ParentData extends Record<string, unknown> = any,\n OutputData extends Record<string, unknown> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: Parameters<Load<Params, InputData, ParentData, OutputData, RouteId>>[0],\n ) => Effect.Effect<OutputData, unknown, R | HttpClient.HttpClient>,\n ) => Load<Params, InputData, ParentData, OutputData, RouteId>;\n\n /**\n * Runs an effect with `HttpClient` backed by the given fetch implementation\n * plus the application layer.\n */\n readonly runPromise: <A>(\n fetchFn: typeof globalThis.fetch,\n effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>,\n ) => Promise<A>;\n\n /**\n * Disposes the underlying managed runtime, running the layer's finalizers.\n */\n readonly dispose: () => Promise<void>;\n}\n\n/**\n * Creates a {@link UniversalRuntime} from an application layer.\n *\n * @example\n * ```ts\n * // src/lib/runtime.ts\n * import { makeUniversalRuntime } from '@thomasfosterau/effect-sveltekit';\n * import { ApiClient } from '$lib/api';\n *\n * export const runtime = makeUniversalRuntime(ApiClient.layer);\n * ```\n *\n * ```ts\n * // src/routes/+page.ts\n * import { runtime } from '$lib/runtime';\n * import { Effect } from 'effect';\n * import { ApiClient } from '$lib/api';\n *\n * export const load = runtime.load((event) =>\n * Effect.gen(function* () {\n * const api = yield* ApiClient;\n * return { stories: yield* api.topStories() };\n * })\n * );\n * ```\n */\nexport const makeUniversalRuntime = <R = never, E = never>(\n layer?: Layer.Layer<R, E>,\n): UniversalRuntime<R, E> => {\n const runtime = ManagedRuntime.make((layer ?? Layer.empty) as Layer.Layer<R, E>);\n\n const run = <A>(\n fetchFn: typeof globalThis.fetch,\n effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>,\n event?: RequestEvent,\n ): Promise<A> => {\n // On the server the full request layer (HttpClient + SvelteKitRequestEvent)\n // is available; on the client only HttpClient backed by the event's fetch.\n const services = event ? requestLayer(event) : fetchLayer(fetchFn);\n const provided = Effect.provide(effect, services) as Effect.Effect<A, unknown, R>;\n return runtime.runPromiseExit(provided).then(settle);\n };\n\n return {\n runtime,\n load: (fn) => (event) => {\n const serverEvent = (event as { request?: unknown }).request\n ? (event as unknown as RequestEvent)\n : undefined;\n return run(event.fetch, fn(event), serverEvent);\n },\n runPromise: (fetchFn, effect) => run(fetchFn, effect),\n dispose: () => runtime.dispose(),\n };\n};\n"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { Effect, Exit } from "effect";
2
+ import { InvalidField } from "@sveltejs/kit";
3
+
4
+ //#region src/internal/remote-run.d.ts
5
+ /**
6
+ * Maps a remote *form* handler's failure into SvelteKit's field issues, given
7
+ * the form's parsed `data` and the `issue` helper. It may call `invalid()` so a
8
+ * structured/nested validation error drives field-level invalidation instead of
9
+ * a generic failure. Receives the squashed handler failure.
10
+ */
11
+ type FormErrorMapper = (error: unknown, ctx: {
12
+ readonly issue: InvalidField<any>;
13
+ readonly data: unknown;
14
+ }) => never;
15
+ //#endregion
16
+ export { FormErrorMapper };
17
+ //# sourceMappingURL=remote-run.d.ts.map
@@ -0,0 +1,39 @@
1
+ import { causeToThrowable } from "./error.js";
2
+ import { Cause, Exit } from "effect";
3
+ //#region src/internal/remote-run.ts
4
+ /** The default translator: SvelteKit control flow through, anything else a 500. */
5
+ const defaultTranslate = (cause) => {
6
+ throw causeToThrowable(cause);
7
+ };
8
+ /** Build the `run` / `runForm` helpers from an exit resolver and a translator. */
9
+ const makeRunHelpers = (resolve, translate, formError) => {
10
+ const run = (effect) => {
11
+ const { runExit, dispose } = resolve();
12
+ return runExit(effect).then((exit) => {
13
+ try {
14
+ return Exit.isSuccess(exit) ? exit.value : translate(exit.cause);
15
+ } finally {
16
+ dispose?.();
17
+ }
18
+ });
19
+ };
20
+ const runForm = (effect, ctx) => {
21
+ const { runExit, dispose } = resolve();
22
+ return runExit(effect).then((exit) => {
23
+ try {
24
+ if (Exit.isSuccess(exit)) return exit.value;
25
+ return formError ? formError(Cause.squash(exit.cause), ctx) : translate(exit.cause);
26
+ } finally {
27
+ dispose?.();
28
+ }
29
+ });
30
+ };
31
+ return {
32
+ run,
33
+ runForm
34
+ };
35
+ };
36
+ //#endregion
37
+ export { defaultTranslate, makeRunHelpers };
38
+
39
+ //# sourceMappingURL=remote-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-run.js","names":[],"sources":["../../src/internal/remote-run.ts"],"sourcesContent":["/**\n * Run/settle helpers for the remote-function wrappers, factored out so they can\n * be unit-tested without importing `$app/server` (a SvelteKit virtual module\n * unavailable outside an app build). The `$app/server`-dependent resolvers live\n * in `remote.server.ts`; this module only deals in `Exit`s.\n */\n\nimport type { InvalidField } from \"@sveltejs/kit\";\nimport { Cause, type Effect, Exit } from \"effect\";\nimport { causeToThrowable } from \"./error.js\";\nimport type { ErrorTranslator } from \"../request-runtime.js\";\n\n/** The default translator: SvelteKit control flow through, anything else a 500. */\nexport const defaultTranslate: ErrorTranslator = (cause) => {\n throw causeToThrowable(cause);\n};\n\n/**\n * Maps a remote *form* handler's failure into SvelteKit's field issues, given\n * the form's parsed `data` and the `issue` helper. It may call `invalid()` so a\n * structured/nested validation error drives field-level invalidation instead of\n * a generic failure. Receives the squashed handler failure.\n */\nexport type FormErrorMapper = (\n error: unknown,\n ctx: { readonly issue: InvalidField<any>; readonly data: unknown },\n) => never;\n\n/** Runs a remote handler's effect and settles it (value or thrown via translator). */\nexport type Runner = <A>(effect: Effect.Effect<A, unknown, any>) => Promise<A>;\n\n/** Like {@link Runner}, but with the form's `issue` / `data` for error interop. */\nexport type FormRunner = <A>(\n effect: Effect.Effect<A, unknown, any>,\n ctx: { readonly issue: InvalidField<any>; readonly data: unknown },\n) => Promise<A>;\n\nexport interface RunHelpers {\n readonly run: Runner;\n readonly runForm: FormRunner;\n}\n\n/** How to obtain an `Exit` for the current request, plus optional teardown. */\nexport type ExitResolver = () => {\n readonly runExit: <A>(effect: Effect.Effect<A, unknown, any>) => Promise<Exit.Exit<A, unknown>>;\n readonly dispose?: () => void;\n};\n\n/** Build the `run` / `runForm` helpers from an exit resolver and a translator. */\nexport const makeRunHelpers = (\n resolve: ExitResolver,\n translate: ErrorTranslator,\n formError?: FormErrorMapper,\n): RunHelpers => {\n const run: Runner = (effect) => {\n const { runExit, dispose } = resolve();\n return runExit(effect).then((exit) => {\n try {\n return Exit.isSuccess(exit) ? exit.value : translate(exit.cause);\n } finally {\n dispose?.();\n }\n });\n };\n const runForm: FormRunner = (effect, ctx) => {\n const { runExit, dispose } = resolve();\n return runExit(effect).then((exit) => {\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n return formError ? formError(Cause.squash(exit.cause), ctx) : translate(exit.cause);\n } finally {\n dispose?.();\n }\n });\n };\n return { run, runForm };\n};\n"],"mappings":";;;;AAaA,MAAa,oBAAqC,UAAU;CAC1D,MAAM,iBAAiB,KAAK;AAC9B;;AAkCA,MAAa,kBACX,SACA,WACA,cACe;CACf,MAAM,OAAe,WAAW;EAC9B,MAAM,EAAE,SAAS,YAAY,QAAQ;EACrC,OAAO,QAAQ,MAAM,EAAE,MAAM,SAAS;GACpC,IAAI;IACF,OAAO,KAAK,UAAU,IAAI,IAAI,KAAK,QAAQ,UAAU,KAAK,KAAK;GACjE,UAAU;IACR,UAAU;GACZ;EACF,CAAC;CACH;CACA,MAAM,WAAuB,QAAQ,QAAQ;EAC3C,MAAM,EAAE,SAAS,YAAY,QAAQ;EACrC,OAAO,QAAQ,MAAM,EAAE,MAAM,SAAS;GACpC,IAAI;IACF,IAAI,KAAK,UAAU,IAAI,GAAG,OAAO,KAAK;IACtC,OAAO,YAAY,UAAU,MAAM,OAAO,KAAK,KAAK,GAAG,GAAG,IAAI,UAAU,KAAK,KAAK;GACpF,UAAU;IACR,UAAU;GACZ;EACF,CAAC;CACH;CACA,OAAO;EAAE;EAAK;CAAQ;AACxB"}
@@ -0,0 +1,23 @@
1
+ import { Schema } from "effect";
2
+ //#region src/internal/remote-validators.ts
3
+ /**
4
+ * Detects a Standard Schema validator via its `~standard` marker property.
5
+ *
6
+ * This cannot be a simple `typeof value === "function"` check on the handler
7
+ * position: some Standard Schema implementations (such as Effect's
8
+ * `Schema.toStandardSchemaV1`) return callable class values, which would be
9
+ * mistaken for handler functions.
10
+ */
11
+ const isStandardSchema = (value) => value !== null && (typeof value === "object" || typeof value === "function") && "~standard" in value;
12
+ /** True when the argument is the no-validation (bare handler) overload. */
13
+ const isBareHandler = (value) => typeof value === "function" && !isStandardSchema(value) && !Schema.isSchema(value);
14
+ /**
15
+ * Normalise a validator argument: a bare Effect `Schema` is wrapped with
16
+ * `Schema.toStandardSchemaV1` so callers can pass it directly; a Standard Schema
17
+ * or the `"unchecked"` marker passes through untouched.
18
+ */
19
+ const toStandardValidator = (value) => Schema.isSchema(value) ? Schema.toStandardSchemaV1(value) : value;
20
+ //#endregion
21
+ export { isBareHandler, toStandardValidator };
22
+
23
+ //# sourceMappingURL=remote-validators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-validators.js","names":[],"sources":["../../src/internal/remote-validators.ts"],"sourcesContent":["/**\n * Validator-argument helpers for the remote-function wrappers, factored out so\n * they can be unit-tested without importing `$app/server` (which is a SvelteKit\n * virtual module unavailable outside an app build).\n */\n\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { Schema } from \"effect\";\n\n/**\n * Detects a Standard Schema validator via its `~standard` marker property.\n *\n * This cannot be a simple `typeof value === \"function\"` check on the handler\n * position: some Standard Schema implementations (such as Effect's\n * `Schema.toStandardSchemaV1`) return callable class values, which would be\n * mistaken for handler functions.\n */\nexport const isStandardSchema = (value: unknown): value is StandardSchemaV1 =>\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n \"~standard\" in value;\n\n/** True when the argument is the no-validation (bare handler) overload. */\nexport const isBareHandler = (value: unknown): boolean =>\n typeof value === \"function\" && !isStandardSchema(value) && !Schema.isSchema(value);\n\n/**\n * Normalise a validator argument: a bare Effect `Schema` is wrapped with\n * `Schema.toStandardSchemaV1` so callers can pass it directly; a Standard Schema\n * or the `\"unchecked\"` marker passes through untouched.\n */\nexport const toStandardValidator = (value: unknown): unknown =>\n Schema.isSchema(value)\n ? (Schema.toStandardSchemaV1 as (schema: Schema.Top) => StandardSchemaV1)(value)\n : value;\n"],"mappings":";;;;;;;;;;AAiBA,MAAa,oBAAoB,UAC/B,UAAU,SACT,OAAO,UAAU,YAAY,OAAO,UAAU,eAC/C,eAAe;;AAGjB,MAAa,iBAAiB,UAC5B,OAAO,UAAU,cAAc,CAAC,iBAAiB,KAAK,KAAK,CAAC,OAAO,SAAS,KAAK;;;;;;AAOnF,MAAa,uBAAuB,UAClC,OAAO,SAAS,KAAK,IAChB,OAAO,mBAAgE,KAAK,IAC7E"}
@@ -1,47 +1,121 @@
1
1
  import { SvelteKitRequestEvent } from "./runtime.js";
2
- import { Effect } from "effect";
2
+ import { ErrorTranslator, RequestRuntimeProvider } from "./request-runtime.js";
3
+ import { FormErrorMapper } from "./internal/remote-run.js";
4
+ import { Effect, Schema } from "effect";
3
5
  import * as HttpClient from "effect/unstable/http/HttpClient";
4
6
  import { InvalidField, RemoteCommand, RemoteForm, RemoteFormInput, RemotePrerenderFunction, RemoteQueryFunction } from "@sveltejs/kit";
5
7
  import { StandardSchemaV1 } from "@standard-schema/spec";
6
8
 
7
9
  //#region src/remote.server.d.ts
8
10
  type SvelteKitEffectEnv = HttpClient.HttpClient | SvelteKitRequestEvent;
9
- declare function queryBatch<Input, Output>(validate: "unchecked", fn: (args: Input[]) => Effect.Effect<(arg: Input, idx: number) => Output, unknown, SvelteKitEffectEnv>): RemoteQueryFunction<Input, Output>;
10
- declare function queryBatch<Schema extends StandardSchemaV1, Output>(schema: Schema, fn: (args: StandardSchemaV1.InferOutput<Schema>[]) => Effect.Effect<(arg: StandardSchemaV1.InferOutput<Schema>, idx: number) => Output, unknown, SvelteKitEffectEnv>): RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output>;
11
- declare function queryLive<Input, Output>(validate: "unchecked", fn: (arg: Input) => Effect.Effect<AsyncIterable<Output>, unknown, SvelteKitEffectEnv>): RemoteQueryFunction<Input, Output>;
12
- declare function queryLive<Schema extends StandardSchemaV1, Output>(schema: Schema, fn: (arg: StandardSchemaV1.InferOutput<Schema>) => Effect.Effect<AsyncIterable<Output>, unknown, SvelteKitEffectEnv>): RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output>;
13
- declare function form<Output>(fn: () => Effect.Effect<Output, unknown, SvelteKitEffectEnv>): RemoteForm<void, Output>;
14
- declare function form<Input extends RemoteFormInput, Output>(validate: "unchecked", fn: (data: Input, issue: InvalidField<Input>) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>): RemoteForm<Input, Output>;
15
- declare function form<Schema extends StandardSchemaV1<RemoteFormInput, Record<string, any>>, Output>(validate: Schema, fn: (data: StandardSchemaV1.InferOutput<Schema>, issue: InvalidField<StandardSchemaV1.InferInput<Schema>>) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>): RemoteForm<StandardSchemaV1.InferInput<Schema>, Output>;
16
- declare function command<Output>(fn: () => Effect.Effect<Output, unknown, SvelteKitEffectEnv>): RemoteCommand<void, Output>;
17
- declare function command<Input, Output>(validate: "unchecked", fn: (arg: Input) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>): RemoteCommand<Input, Output>;
18
- declare function command<Schema extends StandardSchemaV1, Output>(validate: Schema, fn: (arg: StandardSchemaV1.InferOutput<Schema>) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>): RemoteCommand<StandardSchemaV1.InferInput<Schema>, Output>;
19
- declare function prerender<Output>(fn: () => Effect.Effect<Output, unknown, SvelteKitEffectEnv>, options?: {
20
- inputs?: () => Promise<void[]> | void[];
21
- dynamic?: boolean;
22
- }): RemotePrerenderFunction<void, Output>;
23
- declare function prerender<Input, Output>(validate: "unchecked", fn: (arg: Input) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>, options?: {
24
- inputs?: () => Promise<Input[]> | Input[];
25
- dynamic?: boolean;
26
- }): RemotePrerenderFunction<Input, Output>;
27
- declare function prerender<Schema extends StandardSchemaV1, Output>(schema: Schema, fn: (arg: StandardSchemaV1.InferOutput<Schema>) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>, options?: {
28
- inputs?: () => Promise<StandardSchemaV1.InferInput<Schema>[]> | StandardSchemaV1.InferInput<Schema>[];
29
- dynamic?: boolean;
30
- }): RemotePrerenderFunction<StandardSchemaV1.InferInput<Schema>, Output>;
11
+ /** The public shape of the `query` helper, parameterised by its handler env. */
12
+ interface QueryFn<Env> {
13
+ <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<void, Output>;
14
+ <Input, Output>(validate: "unchecked", fn: (arg: Input) => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<Input, Output>;
15
+ <Sch extends StandardSchemaV1, Output>(schema: Sch, fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;
16
+ <S extends Schema.Top, Output>(schema: S, fn: (arg: S["Type"]) => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<S["Encoded"], Output>;
17
+ }
18
+ interface QueryBatchFn<Env> {
19
+ <Input, Output>(validate: "unchecked", fn: (args: Input[]) => Effect.Effect<(arg: Input, idx: number) => Output, unknown, Env>): RemoteQueryFunction<Input, Output>;
20
+ <Sch extends StandardSchemaV1, Output>(schema: Sch, fn: (args: StandardSchemaV1.InferOutput<Sch>[]) => Effect.Effect<(arg: StandardSchemaV1.InferOutput<Sch>, idx: number) => Output, unknown, Env>): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;
21
+ <S extends Schema.Top, Output>(schema: S, fn: (args: S["Type"][]) => Effect.Effect<(arg: S["Type"], idx: number) => Output, unknown, Env>): RemoteQueryFunction<S["Encoded"], Output>;
22
+ }
23
+ interface QueryLiveFn<Env> {
24
+ <Input, Output>(validate: "unchecked", fn: (arg: Input) => Effect.Effect<AsyncIterable<Output>, unknown, Env>): RemoteQueryFunction<Input, Output>;
25
+ <Sch extends StandardSchemaV1, Output>(schema: Sch, fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<AsyncIterable<Output>, unknown, Env>): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;
26
+ <S extends Schema.Top, Output>(schema: S, fn: (arg: S["Type"]) => Effect.Effect<AsyncIterable<Output>, unknown, Env>): RemoteQueryFunction<S["Encoded"], Output>;
27
+ }
28
+ interface FormFn<Env> {
29
+ <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteForm<void, Output>;
30
+ <Input extends RemoteFormInput, Output>(validate: "unchecked", fn: (data: Input, issue: InvalidField<Input>) => Effect.Effect<Output, unknown, Env>): RemoteForm<Input, Output>;
31
+ <Sch extends StandardSchemaV1<RemoteFormInput, Record<string, any>>, Output>(validate: Sch, fn: (data: StandardSchemaV1.InferOutput<Sch>, issue: InvalidField<StandardSchemaV1.InferInput<Sch>>) => Effect.Effect<Output, unknown, Env>): RemoteForm<StandardSchemaV1.InferInput<Sch>, Output>;
32
+ <S extends Schema.Top & {
33
+ readonly Encoded: RemoteFormInput;
34
+ }, Output>(validate: S, fn: (data: S["Type"], issue: InvalidField<S["Encoded"]>) => Effect.Effect<Output, unknown, Env>): RemoteForm<S["Encoded"], Output>;
35
+ }
36
+ interface CommandFn<Env> {
37
+ <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteCommand<void, Output>;
38
+ <Input, Output>(validate: "unchecked", fn: (arg: Input) => Effect.Effect<Output, unknown, Env>): RemoteCommand<Input, Output>;
39
+ <Sch extends StandardSchemaV1, Output>(validate: Sch, fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>): RemoteCommand<StandardSchemaV1.InferInput<Sch>, Output>;
40
+ <S extends Schema.Top, Output>(validate: S, fn: (arg: S["Type"]) => Effect.Effect<Output, unknown, Env>): RemoteCommand<S["Encoded"], Output>;
41
+ }
42
+ interface PrerenderFn<Env> {
43
+ <Output>(fn: () => Effect.Effect<Output, unknown, Env>, options?: {
44
+ inputs?: () => Promise<void[]> | void[];
45
+ dynamic?: boolean;
46
+ }): RemotePrerenderFunction<void, Output>;
47
+ <Input, Output>(validate: "unchecked", fn: (arg: Input) => Effect.Effect<Output, unknown, Env>, options?: {
48
+ inputs?: () => Promise<Input[]> | Input[];
49
+ dynamic?: boolean;
50
+ }): RemotePrerenderFunction<Input, Output>;
51
+ <Sch extends StandardSchemaV1, Output>(schema: Sch, fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>, options?: {
52
+ inputs?: () => Promise<StandardSchemaV1.InferInput<Sch>[]> | StandardSchemaV1.InferInput<Sch>[];
53
+ dynamic?: boolean;
54
+ }): RemotePrerenderFunction<StandardSchemaV1.InferInput<Sch>, Output>;
55
+ <S extends Schema.Top, Output>(schema: S, fn: (arg: S["Type"]) => Effect.Effect<Output, unknown, Env>, options?: {
56
+ inputs?: () => Promise<S["Encoded"][]> | S["Encoded"][];
57
+ dynamic?: boolean;
58
+ }): RemotePrerenderFunction<S["Encoded"], Output>;
59
+ }
31
60
  declare function requested<Result = unknown>(...args: unknown[]): Result;
32
- type QueryNamespace = {
33
- <Output>(fn: () => Effect.Effect<Output, unknown, SvelteKitEffectEnv>): RemoteQueryFunction<void, Output>;
34
- <Input, Output>(validate: "unchecked", fn: (arg: Input) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>): RemoteQueryFunction<Input, Output>;
35
- <Schema extends StandardSchemaV1, Output>(schema: Schema, fn: (arg: StandardSchemaV1.InferOutput<Schema>) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>): RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output>;
36
- batch: typeof queryBatch;
37
- live: typeof queryLive;
61
+ /** A `query` with its `batch` / `live` helpers attached. */
62
+ type QueryNamespace<Env> = QueryFn<Env> & {
63
+ batch: QueryBatchFn<Env>;
64
+ live: QueryLiveFn<Env>;
38
65
  };
66
+ /** The set of remote-function helpers bound to a particular runner. */
67
+ interface RemoteFunctions<Env> {
68
+ readonly query: QueryNamespace<Env>;
69
+ readonly form: FormFn<Env>;
70
+ readonly command: CommandFn<Env>;
71
+ readonly prerender: PrerenderFn<Env>;
72
+ }
73
+ /**
74
+ * Bind the remote-function helpers to a {@link RequestRuntimeProvider} (and an
75
+ * optional boundary error translator), so their handlers run on the provider's
76
+ * services `Env` — your own application catalogue — instead of the fixed
77
+ * {@link SvelteKitServices}. The Standard-Schema / `"unchecked"` validation
78
+ * argument and the native return types (`RemoteQueryFunction`, `RemoteForm`,
79
+ * ...) are exactly as the standalone helpers.
80
+ *
81
+ * For remote forms, the failure path runs `opts.formError` when provided —
82
+ * mapping a structured/nested error shape into SvelteKit's field issues (via the
83
+ * `issue` helper / `invalid()`) so a domain validation failure drives
84
+ * field-level invalidation rather than a generic failure. Otherwise the form
85
+ * falls back to `translateError`.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * const { query, form, command } = RemoteFunction.withRuntime(
90
+ * RequestRuntime.fromLocals((event) => event.locals.runtime),
91
+ * {
92
+ * translateError: toSvelteKit,
93
+ * formError: (error, { issue }) => {
94
+ * if (error instanceof ValidationFailure) issue.field(error.field, error.message);
95
+ * invalid();
96
+ * },
97
+ * },
98
+ * );
99
+ *
100
+ * export const items = query(() =>
101
+ * Effect.gen(function* () {
102
+ * const db = yield* Database;
103
+ * return yield* db.list();
104
+ * }),
105
+ * );
106
+ * ```
107
+ */
108
+ declare function withRuntime<R>(provider: RequestRuntimeProvider<R, any>, opts?: {
109
+ translateError?: ErrorTranslator;
110
+ formError?: FormErrorMapper;
111
+ }): RemoteFunctions<R>;
39
112
  declare const RemoteFunction: {
40
- query: QueryNamespace;
41
- form: typeof form;
42
- command: typeof command;
43
- prerender: typeof prerender;
113
+ query: QueryNamespace<SvelteKitEffectEnv>;
114
+ form: FormFn<SvelteKitEffectEnv>;
115
+ command: CommandFn<SvelteKitEffectEnv>;
116
+ prerender: PrerenderFn<SvelteKitEffectEnv>;
44
117
  requested: typeof requested;
118
+ withRuntime: typeof withRuntime;
45
119
  };
46
120
  //#endregion
47
121
  export { RemoteFunction };