@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.
@@ -1,4 +1,4 @@
1
- import { Effect, Layer, Scope } from "effect";
1
+ import { Context, Effect, Layer, Scope } from "effect";
2
2
  import { RequestHandler } from "@sveltejs/kit";
3
3
  import * as HttpRouter from "effect/unstable/http/HttpRouter";
4
4
  import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
@@ -8,8 +8,15 @@ import * as HttpApiGroup from "effect/unstable/httpapi/HttpApiGroup";
8
8
 
9
9
  //#region src/server/Endpoint.d.ts
10
10
  declare namespace Endpoint_d_exports {
11
- export { EndpointHandler, EndpointOptions, fromApi, fromHttpEffect, fromRouter };
11
+ export { EndpointHandler, EndpointOptions, RequestContext, fromApi, fromHttpEffect, fromRouter };
12
12
  }
13
+ /**
14
+ * Resolve a request-scoped service context, if a resolver was given. The
15
+ * resolver may return a `Context` directly or an `Effect` producing one (run
16
+ * with no requirements), so a consumer can read the request to build
17
+ * session-aware services per dispatch.
18
+ */
19
+ type RequestContext<RequestServices> = (request: Request) => Context.Context<RequestServices> | Effect.Effect<Context.Context<RequestServices>, never, never>;
13
20
  /**
14
21
  * A SvelteKit endpoint handler — the built-in
15
22
  * [`RequestHandler`](https://svelte.dev/docs/kit/@sveltejs-kit#RequestHandler) —
@@ -49,9 +56,27 @@ interface EndpointOptions {
49
56
  * export const fallback = Endpoint.fromApi(api, ApiLive, { prefix: "/api" });
50
57
  * ```
51
58
  */
52
- declare const fromApi: <Id extends string, Groups extends HttpApiGroup.Any, E>(api: HttpApi.HttpApi<Id, Groups>, layer: Layer.Layer<HttpApiGroup.ToService<Id, Groups>, E>, options?: EndpointOptions & {
59
+ declare const fromApi: <Id extends string, Groups extends HttpApiGroup.Any, E, LayerRequires = never, RequestServices = never>(api: HttpApi.HttpApi<Id, Groups>, layer: Layer.Layer<HttpApiGroup.ToService<Id, Groups>, E, LayerRequires>, options?: EndpointOptions & {
53
60
  /** Serve the OpenAPI spec for the API at this path (e.g. `"/openapi.json"`). */readonly openapiPath?: `/${string}`; /** Add Effect's request logger middleware. Defaults to `false` (quiet). */
54
61
  readonly logger?: boolean;
62
+ /**
63
+ * Provide the request-scoped services the API groups require, per request.
64
+ * The build-once platform layer (`HttpPlatform` / `FileSystem` / `Path` /
65
+ * `Etag`) stays shared; only this slice is resolved and merged for each
66
+ * dispatch — so a session-aware, request-pooled API can run on Workers
67
+ * without rebuilding its platform layer.
68
+ *
69
+ * The resolver may return a `Context` or an `Effect` producing one. Leave
70
+ * the matching services unprovided in `layer` and supply them here:
71
+ *
72
+ * ```ts
73
+ * Endpoint.fromApi(api, ApiLive, {
74
+ * prefix: "/api",
75
+ * context: (request) => Context.make(CurrentUser, userFor(request)),
76
+ * });
77
+ * ```
78
+ */
79
+ readonly context?: RequestContext<RequestServices>;
55
80
  }) => EndpointHandler;
56
81
  /**
57
82
  * Create a SvelteKit endpoint that serves an Effect `HttpRouter` (provided by
@@ -72,8 +97,14 @@ declare const fromApi: <Id extends string, Groups extends HttpApiGroup.Any, E>(a
72
97
  * export const fallback = Endpoint.fromRouter(RouterLive, { prefix: "/api" });
73
98
  * ```
74
99
  */
75
- declare const fromRouter: <A, E, R extends HttpRouter.HttpRouter | HttpRouter.Request<"Requires", any> | HttpRouter.Request<"GlobalRequires", any> | HttpRouter.Request<"Error", any> | HttpRouter.Request<"GlobalError", any>>(layer: Layer.Layer<A, E, R>, options?: EndpointOptions & {
100
+ declare const fromRouter: <A, E, R extends HttpRouter.HttpRouter | HttpRouter.Request<"Requires", any> | HttpRouter.Request<"GlobalRequires", any> | HttpRouter.Request<"Error", any> | HttpRouter.Request<"GlobalError", any>, RequestServices = never>(layer: Layer.Layer<A, E, R>, options?: EndpointOptions & {
76
101
  /** Add Effect's request logger middleware. Defaults to `false` (quiet). */readonly logger?: boolean;
102
+ /**
103
+ * Provide the request-scoped services the router's routes require, per
104
+ * request (the routes' `HttpRouter.Request<"Requires", ...>` services). The
105
+ * resolver may return a `Context` or an `Effect` producing one.
106
+ */
107
+ readonly context?: RequestContext<RequestServices>;
77
108
  }) => EndpointHandler;
78
109
  /**
79
110
  * Create a SvelteKit endpoint from a single Effect HTTP route — an effect
@@ -1,5 +1,5 @@
1
1
  import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
- import { Layer } from "effect";
2
+ import { Effect, Layer } from "effect";
3
3
  import * as FileSystem from "effect/FileSystem";
4
4
  import * as Path from "effect/Path";
5
5
  import * as Etag from "effect/unstable/http/Etag";
@@ -13,6 +13,11 @@ var Endpoint_exports = /* @__PURE__ */ __exportAll({
13
13
  fromHttpEffect: () => fromHttpEffect,
14
14
  fromRouter: () => fromRouter
15
15
  });
16
+ const resolveRequestContext = async (request, context) => {
17
+ if (context === void 0) return void 0;
18
+ const result = context(request);
19
+ return Effect.isEffect(result) ? Effect.runPromise(result) : result;
20
+ };
16
21
  const noopDispose = () => Promise.resolve();
17
22
  /** Remove `prefix` from the request's path (respecting `/` boundaries). */
18
23
  const stripPrefix = (request, prefix) => {
@@ -26,8 +31,12 @@ const stripPrefix = (request, prefix) => {
26
31
  return new Request(url, request);
27
32
  };
28
33
  /** Wrap a Web handler (and its dispose) as a SvelteKit endpoint handler. */
29
- const make = (webHandler, dispose, prefix) => {
30
- const handler = (event) => webHandler(prefix === void 0 ? event.request : stripPrefix(event.request, prefix));
34
+ const make = (webHandler, dispose, prefix, context) => {
35
+ const handler = async (event) => {
36
+ const request = prefix === void 0 ? event.request : stripPrefix(event.request, prefix);
37
+ if (context === void 0) return webHandler(request);
38
+ return webHandler(request, await resolveRequestContext(request, context));
39
+ };
31
40
  return Object.assign(handler, { dispose });
32
41
  };
33
42
  /** The platform services an `HttpApi` needs, with a no-op file system. */
@@ -53,7 +62,7 @@ const apiPlatform = Layer.mergeAll(Path.layer, Etag.layerWeak, HttpPlatform.laye
53
62
  const fromApi = (api, layer, options) => {
54
63
  const appLayer = HttpApiBuilder.layer(api, options?.openapiPath ? { openapiPath: options.openapiPath } : void 0).pipe(Layer.provide(layer), Layer.provide(apiPlatform));
55
64
  const { handler, dispose } = HttpRouter.toWebHandler(appLayer, { disableLogger: options?.logger !== true });
56
- return make(handler, dispose, options?.prefix);
65
+ return make(handler, dispose, options?.prefix, options?.context);
57
66
  };
58
67
  /**
59
68
  * Create a SvelteKit endpoint that serves an Effect `HttpRouter` (provided by
@@ -76,7 +85,7 @@ const fromApi = (api, layer, options) => {
76
85
  */
77
86
  const fromRouter = (layer, options) => {
78
87
  const { handler, dispose } = HttpRouter.toWebHandler(layer, { disableLogger: options?.logger !== true });
79
- return make(handler, dispose, options?.prefix);
88
+ return make(handler, dispose, options?.prefix, options?.context);
80
89
  };
81
90
  /**
82
91
  * Create a SvelteKit endpoint from a single Effect HTTP route — an effect
@@ -1 +1 @@
1
- {"version":3,"file":"Endpoint.js","names":[],"sources":["../../src/server/Endpoint.ts"],"sourcesContent":["/**\n * Create SvelteKit server endpoints (`+server.ts`) backed by Effect — the\n * `Endpoint` namespace of `@thomasfosterau/effect-sveltekit/server`.\n *\n * A SvelteKit endpoint is a `RequestHandler` (`(event) => Response`). Effect can\n * turn an [`HttpApi`](https://effect.website), an `HttpRouter`, or a single\n * route effect into a Web `fetch` handler; these helpers wrap one as a SvelteKit\n * endpoint handler so a route can be served by Effect. Mount it on a catch-all\n * route (`[...path]`) and assign it to `fallback` so every method and sub-path\n * reaches it:\n *\n * @example\n * ```ts\n * // src/routes/api/[...path]/+server.ts\n * import { Endpoint } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { api } from \"$lib/server/api\";\n * import { ApiLive } from \"$lib/server/handlers\"; // HttpApiBuilder.group(...) + your services\n *\n * // `prefix` strips the SvelteKit mount path so the API's own paths\n * // (e.g. \"/users\") match.\n * export const fallback = Endpoint.fromApi(api, ApiLive, { prefix: \"/api\" });\n * ```\n *\n * The layer backing an endpoint is built once, lazily, on the first request and\n * shared across subsequent requests (like `makeServerRuntime`). The returned\n * handler carries a `dispose()` that closes the layer's scope — call it on\n * shutdown if the endpoint holds resources.\n *\n * The Effect handlers receive an Effect-native `HttpServerRequest` (built from\n * `event.request`), not the SvelteKit `RequestEvent`.\n */\n\nimport type { RequestHandler } from \"@sveltejs/kit\";\nimport { Layer } from \"effect\";\nimport type { Effect, Scope } from \"effect\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Path from \"effect/Path\";\nimport * as Etag from \"effect/unstable/http/Etag\";\nimport * as HttpEffect from \"effect/unstable/http/HttpEffect\";\nimport * as HttpPlatform from \"effect/unstable/http/HttpPlatform\";\nimport * as HttpRouter from \"effect/unstable/http/HttpRouter\";\nimport type * as HttpServerRequest from \"effect/unstable/http/HttpServerRequest\";\nimport type * as HttpServerResponse from \"effect/unstable/http/HttpServerResponse\";\nimport type * as HttpApi from \"effect/unstable/httpapi/HttpApi\";\nimport * as HttpApiBuilder from \"effect/unstable/httpapi/HttpApiBuilder\";\nimport type * as HttpApiGroup from \"effect/unstable/httpapi/HttpApiGroup\";\n\n/** A Web `fetch`-style handler, as produced by Effect's `toWebHandler` family. */\ntype WebHandler = (request: Request) => Promise<Response>;\n\n/**\n * A SvelteKit endpoint handler — the built-in\n * [`RequestHandler`](https://svelte.dev/docs/kit/@sveltejs-kit#RequestHandler) —\n * backed by an Effect HTTP construct. Assign it to a `+server.ts` export\n * (`fallback`, `GET`, ...). `dispose()` closes the scope of the layer behind it.\n */\nexport type EndpointHandler = RequestHandler & {\n /** Close the scope of the layer backing the endpoint (runs its finalizers). */\n readonly dispose: () => Promise<void>;\n};\n\n/** Shared options for every `Endpoint.from*` helper. */\nexport interface EndpointOptions {\n /**\n * A leading path segment to strip from each request before it reaches the\n * Effect handler — the SvelteKit mount point. For an endpoint mounted at\n * `src/routes/api/[...path]/+server.ts` whose routes are defined relative to\n * the API root, pass `prefix: \"/api\"` so `/api/users` is dispatched as\n * `/users`. The match respects path boundaries (`/api` does not strip\n * `/apiary`). Defaults to no stripping.\n */\n readonly prefix?: string;\n}\n\nconst noopDispose = (): Promise<void> => Promise.resolve();\n\n/** Remove `prefix` from the request's path (respecting `/` boundaries). */\nconst stripPrefix = (request: Request, prefix: string): Request => {\n const normalized = prefix.endsWith(\"/\") ? prefix.slice(0, -1) : prefix;\n if (normalized === \"\") return request;\n\n const url = new URL(request.url);\n const path = url.pathname;\n if (path === normalized) {\n url.pathname = \"/\";\n } else if (path.startsWith(`${normalized}/`)) {\n url.pathname = path.slice(normalized.length);\n } else {\n return request;\n }\n return new Request(url, request);\n};\n\n/** Wrap a Web handler (and its dispose) as a SvelteKit endpoint handler. */\nconst make = (\n webHandler: WebHandler,\n dispose: () => Promise<void>,\n prefix: string | undefined,\n): EndpointHandler => {\n const handler: RequestHandler = (event) =>\n webHandler(prefix === undefined ? event.request : stripPrefix(event.request, prefix));\n return Object.assign(handler, { dispose });\n};\n\n/** The platform services an `HttpApi` needs, with a no-op file system. */\nconst apiPlatform = Layer.mergeAll(Path.layer, Etag.layerWeak, HttpPlatform.layer).pipe(\n Layer.provideMerge(FileSystem.layerNoop({})),\n);\n\n/**\n * Create a SvelteKit endpoint that serves an Effect\n * [`HttpApi`](https://effect.website).\n *\n * `layer` implements the API's groups (built with `HttpApiBuilder.group`), with\n * any application services already provided. The platform services an `HttpApi`\n * requires (`HttpPlatform`, `FileSystem`, `Path`, `Etag`) are supplied for you.\n *\n * @example\n * ```ts\n * // src/routes/api/[...path]/+server.ts\n * import { Endpoint } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { api } from \"$lib/server/api\";\n * import { ApiLive } from \"$lib/server/handlers\";\n *\n * export const fallback = Endpoint.fromApi(api, ApiLive, { prefix: \"/api\" });\n * ```\n */\nexport const fromApi = <Id extends string, Groups extends HttpApiGroup.Any, E>(\n api: HttpApi.HttpApi<Id, Groups>,\n layer: Layer.Layer<HttpApiGroup.ToService<Id, Groups>, E>,\n options?: EndpointOptions & {\n /** Serve the OpenAPI spec for the API at this path (e.g. `\"/openapi.json\"`). */\n readonly openapiPath?: `/${string}`;\n /** Add Effect's request logger middleware. Defaults to `false` (quiet). */\n readonly logger?: boolean;\n },\n): EndpointHandler => {\n const appLayer = HttpApiBuilder.layer(\n api,\n options?.openapiPath ? { openapiPath: options.openapiPath } : undefined,\n ).pipe(Layer.provide(layer), Layer.provide(apiPlatform));\n\n const { handler, dispose } = HttpRouter.toWebHandler(\n appLayer as Layer.Layer<never, E, HttpRouter.HttpRouter>,\n { disableLogger: options?.logger !== true },\n );\n return make(handler, dispose, options?.prefix);\n};\n\n/**\n * Create a SvelteKit endpoint that serves an Effect `HttpRouter` (provided by\n * `layer`). The router's routes must be self-contained (any services they need\n * are provided within `layer`).\n *\n * @example\n * ```ts\n * // src/routes/api/[...path]/+server.ts\n * import { Endpoint } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Effect } from \"effect\";\n * import { HttpRouter, HttpServerResponse } from \"effect/unstable/http\";\n *\n * const RouterLive = HttpRouter.use((router) =>\n * router.add(\"GET\", \"/hello\", Effect.succeed(HttpServerResponse.text(\"hi\"))),\n * );\n *\n * export const fallback = Endpoint.fromRouter(RouterLive, { prefix: \"/api\" });\n * ```\n */\nexport const fromRouter = <\n A,\n E,\n R extends\n | HttpRouter.HttpRouter\n | HttpRouter.Request<\"Requires\", any>\n | HttpRouter.Request<\"GlobalRequires\", any>\n | HttpRouter.Request<\"Error\", any>\n | HttpRouter.Request<\"GlobalError\", any>,\n>(\n layer: Layer.Layer<A, E, R>,\n options?: EndpointOptions & {\n /** Add Effect's request logger middleware. Defaults to `false` (quiet). */\n readonly logger?: boolean;\n },\n): EndpointHandler => {\n const { handler, dispose } = HttpRouter.toWebHandler(layer, {\n disableLogger: options?.logger !== true,\n });\n return make(handler as WebHandler, dispose, options?.prefix);\n};\n\n/**\n * Create a SvelteKit endpoint from a single Effect HTTP route — an effect\n * producing an `HttpServerResponse` for the current `HttpServerRequest`. Pass\n * `options.layer` to provide the route's services; the layer is built once and\n * shared.\n *\n * @example\n * ```ts\n * // src/routes/health/+server.ts\n * import { Endpoint } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Effect } from \"effect\";\n * import { HttpServerResponse } from \"effect/unstable/http\";\n *\n * export const GET = Endpoint.fromHttpEffect(\n * Effect.succeed(HttpServerResponse.text(\"ok\")),\n * );\n * ```\n */\nexport const fromHttpEffect = <E, R = never, ProvidedE = never>(\n effect: Effect.Effect<\n HttpServerResponse.HttpServerResponse,\n E,\n R | HttpServerRequest.HttpServerRequest | Scope.Scope\n >,\n options?: EndpointOptions & {\n /** A layer providing the route's services (`R`), built once and shared. */\n readonly layer?: Layer.Layer<R, ProvidedE>;\n },\n): EndpointHandler => {\n if (options?.layer) {\n const { handler, dispose } = HttpEffect.toWebHandlerLayer(effect, options.layer);\n return make(handler as WebHandler, dispose, options.prefix);\n }\n const handler = HttpEffect.toWebHandler(\n effect as Effect.Effect<\n HttpServerResponse.HttpServerResponse,\n E,\n HttpServerRequest.HttpServerRequest | Scope.Scope\n >,\n );\n return make(handler, noopDispose, options?.prefix);\n};\n"],"mappings":";;;;;;;;;;;;;;;AA0EA,MAAM,oBAAmC,QAAQ,QAAQ;;AAGzD,MAAM,eAAe,SAAkB,WAA4B;CACjE,MAAM,aAAa,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI;CAChE,IAAI,eAAe,IAAI,OAAO;CAE9B,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;CAC/B,MAAM,OAAO,IAAI;CACjB,IAAI,SAAS,YACX,IAAI,WAAW;MACV,IAAI,KAAK,WAAW,GAAG,WAAW,EAAE,GACzC,IAAI,WAAW,KAAK,MAAM,WAAW,MAAM;MAE3C,OAAO;CAET,OAAO,IAAI,QAAQ,KAAK,OAAO;AACjC;;AAGA,MAAM,QACJ,YACA,SACA,WACoB;CACpB,MAAM,WAA2B,UAC/B,WAAW,WAAW,KAAA,IAAY,MAAM,UAAU,YAAY,MAAM,SAAS,MAAM,CAAC;CACtF,OAAO,OAAO,OAAO,SAAS,EAAE,QAAQ,CAAC;AAC3C;;AAGA,MAAM,cAAc,MAAM,SAAS,KAAK,OAAO,KAAK,WAAW,aAAa,KAAK,EAAE,KACjF,MAAM,aAAa,WAAW,UAAU,CAAC,CAAC,CAAC,CAC7C;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,WACX,KACA,OACA,YAMoB;CACpB,MAAM,WAAW,eAAe,MAC9B,KACA,SAAS,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,KAAA,CAChE,EAAE,KAAK,MAAM,QAAQ,KAAK,GAAG,MAAM,QAAQ,WAAW,CAAC;CAEvD,MAAM,EAAE,SAAS,YAAY,WAAW,aACtC,UACA,EAAE,eAAe,SAAS,WAAW,KAAK,CAC5C;CACA,OAAO,KAAK,SAAS,SAAS,SAAS,MAAM;AAC/C;;;;;;;;;;;;;;;;;;;;AAqBA,MAAa,cAUX,OACA,YAIoB;CACpB,MAAM,EAAE,SAAS,YAAY,WAAW,aAAa,OAAO,EAC1D,eAAe,SAAS,WAAW,KACrC,CAAC;CACD,OAAO,KAAK,SAAuB,SAAS,SAAS,MAAM;AAC7D;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,kBACX,QAKA,YAIoB;CACpB,IAAI,SAAS,OAAO;EAClB,MAAM,EAAE,SAAS,YAAY,WAAW,kBAAkB,QAAQ,QAAQ,KAAK;EAC/E,OAAO,KAAK,SAAuB,SAAS,QAAQ,MAAM;CAC5D;CAQA,OAAO,KAPS,WAAW,aACzB,MAMgB,GAAG,aAAa,SAAS,MAAM;AACnD"}
1
+ {"version":3,"file":"Endpoint.js","names":[],"sources":["../../src/server/Endpoint.ts"],"sourcesContent":["/**\n * Create SvelteKit server endpoints (`+server.ts`) backed by Effect — the\n * `Endpoint` namespace of `@thomasfosterau/effect-sveltekit/server`.\n *\n * A SvelteKit endpoint is a `RequestHandler` (`(event) => Response`). Effect can\n * turn an [`HttpApi`](https://effect.website), an `HttpRouter`, or a single\n * route effect into a Web `fetch` handler; these helpers wrap one as a SvelteKit\n * endpoint handler so a route can be served by Effect. Mount it on a catch-all\n * route (`[...path]`) and assign it to `fallback` so every method and sub-path\n * reaches it:\n *\n * @example\n * ```ts\n * // src/routes/api/[...path]/+server.ts\n * import { Endpoint } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { api } from \"$lib/server/api\";\n * import { ApiLive } from \"$lib/server/handlers\"; // HttpApiBuilder.group(...) + your services\n *\n * // `prefix` strips the SvelteKit mount path so the API's own paths\n * // (e.g. \"/users\") match.\n * export const fallback = Endpoint.fromApi(api, ApiLive, { prefix: \"/api\" });\n * ```\n *\n * The layer backing an endpoint is built once, lazily, on the first request and\n * shared across subsequent requests (like `makeServerRuntime`). The returned\n * handler carries a `dispose()` that closes the layer's scope — call it on\n * shutdown if the endpoint holds resources.\n *\n * The Effect handlers receive an Effect-native `HttpServerRequest` (built from\n * `event.request`), not the SvelteKit `RequestEvent`.\n */\n\nimport type { RequestHandler } from \"@sveltejs/kit\";\nimport { Context, Effect, Layer } from \"effect\";\nimport type { Scope } from \"effect\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Path from \"effect/Path\";\nimport * as Etag from \"effect/unstable/http/Etag\";\nimport * as HttpEffect from \"effect/unstable/http/HttpEffect\";\nimport * as HttpPlatform from \"effect/unstable/http/HttpPlatform\";\nimport * as HttpRouter from \"effect/unstable/http/HttpRouter\";\nimport type * as HttpServerRequest from \"effect/unstable/http/HttpServerRequest\";\nimport type * as HttpServerResponse from \"effect/unstable/http/HttpServerResponse\";\nimport type * as HttpApi from \"effect/unstable/httpapi/HttpApi\";\nimport * as HttpApiBuilder from \"effect/unstable/httpapi/HttpApiBuilder\";\nimport type * as HttpApiGroup from \"effect/unstable/httpapi/HttpApiGroup\";\n\n/** A Web `fetch`-style handler, as produced by Effect's `toWebHandler` family. */\ntype WebHandler = (request: Request) => Promise<Response>;\n\n/** A Web handler that also accepts a per-request service context. */\ntype WebHandlerWithContext = (\n request: Request,\n context?: Context.Context<never>,\n) => Promise<Response>;\n\n/**\n * Resolve a request-scoped service context, if a resolver was given. The\n * resolver may return a `Context` directly or an `Effect` producing one (run\n * with no requirements), so a consumer can read the request to build\n * session-aware services per dispatch.\n */\nexport type RequestContext<RequestServices> = (\n request: Request,\n) =>\n | Context.Context<RequestServices>\n | Effect.Effect<Context.Context<RequestServices>, never, never>;\n\nconst resolveRequestContext = async <RequestServices>(\n request: Request,\n context: RequestContext<RequestServices> | undefined,\n): Promise<Context.Context<RequestServices> | undefined> => {\n if (context === undefined) return undefined;\n const result = context(request);\n return Effect.isEffect(result) ? Effect.runPromise(result) : result;\n};\n\n/**\n * A SvelteKit endpoint handler — the built-in\n * [`RequestHandler`](https://svelte.dev/docs/kit/@sveltejs-kit#RequestHandler) —\n * backed by an Effect HTTP construct. Assign it to a `+server.ts` export\n * (`fallback`, `GET`, ...). `dispose()` closes the scope of the layer behind it.\n */\nexport type EndpointHandler = RequestHandler & {\n /** Close the scope of the layer backing the endpoint (runs its finalizers). */\n readonly dispose: () => Promise<void>;\n};\n\n/** Shared options for every `Endpoint.from*` helper. */\nexport interface EndpointOptions {\n /**\n * A leading path segment to strip from each request before it reaches the\n * Effect handler — the SvelteKit mount point. For an endpoint mounted at\n * `src/routes/api/[...path]/+server.ts` whose routes are defined relative to\n * the API root, pass `prefix: \"/api\"` so `/api/users` is dispatched as\n * `/users`. The match respects path boundaries (`/api` does not strip\n * `/apiary`). Defaults to no stripping.\n */\n readonly prefix?: string;\n}\n\nconst noopDispose = (): Promise<void> => Promise.resolve();\n\n/** Remove `prefix` from the request's path (respecting `/` boundaries). */\nconst stripPrefix = (request: Request, prefix: string): Request => {\n const normalized = prefix.endsWith(\"/\") ? prefix.slice(0, -1) : prefix;\n if (normalized === \"\") return request;\n\n const url = new URL(request.url);\n const path = url.pathname;\n if (path === normalized) {\n url.pathname = \"/\";\n } else if (path.startsWith(`${normalized}/`)) {\n url.pathname = path.slice(normalized.length);\n } else {\n return request;\n }\n return new Request(url, request);\n};\n\n/** Wrap a Web handler (and its dispose) as a SvelteKit endpoint handler. */\nconst make = <RequestServices = never>(\n webHandler: WebHandler | WebHandlerWithContext,\n dispose: () => Promise<void>,\n prefix: string | undefined,\n context?: RequestContext<RequestServices>,\n): EndpointHandler => {\n const handler: RequestHandler = async (event) => {\n const request = prefix === undefined ? event.request : stripPrefix(event.request, prefix);\n if (context === undefined) return (webHandler as WebHandler)(request);\n const ctx = await resolveRequestContext(request, context);\n return (webHandler as WebHandlerWithContext)(\n request,\n ctx as Context.Context<never> | undefined,\n );\n };\n return Object.assign(handler, { dispose });\n};\n\n/** The platform services an `HttpApi` needs, with a no-op file system. */\nconst apiPlatform = Layer.mergeAll(Path.layer, Etag.layerWeak, HttpPlatform.layer).pipe(\n Layer.provideMerge(FileSystem.layerNoop({})),\n);\n\n/**\n * Create a SvelteKit endpoint that serves an Effect\n * [`HttpApi`](https://effect.website).\n *\n * `layer` implements the API's groups (built with `HttpApiBuilder.group`), with\n * any application services already provided. The platform services an `HttpApi`\n * requires (`HttpPlatform`, `FileSystem`, `Path`, `Etag`) are supplied for you.\n *\n * @example\n * ```ts\n * // src/routes/api/[...path]/+server.ts\n * import { Endpoint } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { api } from \"$lib/server/api\";\n * import { ApiLive } from \"$lib/server/handlers\";\n *\n * export const fallback = Endpoint.fromApi(api, ApiLive, { prefix: \"/api\" });\n * ```\n */\nexport const fromApi = <\n Id extends string,\n Groups extends HttpApiGroup.Any,\n E,\n LayerRequires = never,\n RequestServices = never,\n>(\n api: HttpApi.HttpApi<Id, Groups>,\n layer: Layer.Layer<HttpApiGroup.ToService<Id, Groups>, E, LayerRequires>,\n options?: EndpointOptions & {\n /** Serve the OpenAPI spec for the API at this path (e.g. `\"/openapi.json\"`). */\n readonly openapiPath?: `/${string}`;\n /** Add Effect's request logger middleware. Defaults to `false` (quiet). */\n readonly logger?: boolean;\n /**\n * Provide the request-scoped services the API groups require, per request.\n * The build-once platform layer (`HttpPlatform` / `FileSystem` / `Path` /\n * `Etag`) stays shared; only this slice is resolved and merged for each\n * dispatch — so a session-aware, request-pooled API can run on Workers\n * without rebuilding its platform layer.\n *\n * The resolver may return a `Context` or an `Effect` producing one. Leave\n * the matching services unprovided in `layer` and supply them here:\n *\n * ```ts\n * Endpoint.fromApi(api, ApiLive, {\n * prefix: \"/api\",\n * context: (request) => Context.make(CurrentUser, userFor(request)),\n * });\n * ```\n */\n readonly context?: RequestContext<RequestServices>;\n },\n): EndpointHandler => {\n const appLayer = HttpApiBuilder.layer(\n api,\n options?.openapiPath ? { openapiPath: options.openapiPath } : undefined,\n ).pipe(Layer.provide(layer), Layer.provide(apiPlatform));\n\n // The request-scoped services are supplied per dispatch via the web handler's\n // context argument, so they are hidden from the build-once layer here.\n const { handler, dispose } = HttpRouter.toWebHandler(\n appLayer as unknown as Layer.Layer<never, E, HttpRouter.HttpRouter>,\n { disableLogger: options?.logger !== true },\n );\n return make<RequestServices>(\n handler as WebHandlerWithContext,\n dispose,\n options?.prefix,\n options?.context,\n );\n};\n\n/**\n * Create a SvelteKit endpoint that serves an Effect `HttpRouter` (provided by\n * `layer`). The router's routes must be self-contained (any services they need\n * are provided within `layer`).\n *\n * @example\n * ```ts\n * // src/routes/api/[...path]/+server.ts\n * import { Endpoint } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Effect } from \"effect\";\n * import { HttpRouter, HttpServerResponse } from \"effect/unstable/http\";\n *\n * const RouterLive = HttpRouter.use((router) =>\n * router.add(\"GET\", \"/hello\", Effect.succeed(HttpServerResponse.text(\"hi\"))),\n * );\n *\n * export const fallback = Endpoint.fromRouter(RouterLive, { prefix: \"/api\" });\n * ```\n */\nexport const fromRouter = <\n A,\n E,\n R extends\n | HttpRouter.HttpRouter\n | HttpRouter.Request<\"Requires\", any>\n | HttpRouter.Request<\"GlobalRequires\", any>\n | HttpRouter.Request<\"Error\", any>\n | HttpRouter.Request<\"GlobalError\", any>,\n RequestServices = never,\n>(\n layer: Layer.Layer<A, E, R>,\n options?: EndpointOptions & {\n /** Add Effect's request logger middleware. Defaults to `false` (quiet). */\n readonly logger?: boolean;\n /**\n * Provide the request-scoped services the router's routes require, per\n * request (the routes' `HttpRouter.Request<\"Requires\", ...>` services). The\n * resolver may return a `Context` or an `Effect` producing one.\n */\n readonly context?: RequestContext<RequestServices>;\n },\n): EndpointHandler => {\n const { handler, dispose } = HttpRouter.toWebHandler(layer, {\n disableLogger: options?.logger !== true,\n });\n return make<RequestServices>(\n handler as WebHandlerWithContext,\n dispose,\n options?.prefix,\n options?.context,\n );\n};\n\n/**\n * Create a SvelteKit endpoint from a single Effect HTTP route — an effect\n * producing an `HttpServerResponse` for the current `HttpServerRequest`. Pass\n * `options.layer` to provide the route's services; the layer is built once and\n * shared.\n *\n * @example\n * ```ts\n * // src/routes/health/+server.ts\n * import { Endpoint } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Effect } from \"effect\";\n * import { HttpServerResponse } from \"effect/unstable/http\";\n *\n * export const GET = Endpoint.fromHttpEffect(\n * Effect.succeed(HttpServerResponse.text(\"ok\")),\n * );\n * ```\n */\nexport const fromHttpEffect = <E, R = never, ProvidedE = never>(\n effect: Effect.Effect<\n HttpServerResponse.HttpServerResponse,\n E,\n R | HttpServerRequest.HttpServerRequest | Scope.Scope\n >,\n options?: EndpointOptions & {\n /** A layer providing the route's services (`R`), built once and shared. */\n readonly layer?: Layer.Layer<R, ProvidedE>;\n },\n): EndpointHandler => {\n if (options?.layer) {\n const { handler, dispose } = HttpEffect.toWebHandlerLayer(effect, options.layer);\n return make(handler as WebHandler, dispose, options.prefix);\n }\n const handler = HttpEffect.toWebHandler(\n effect as Effect.Effect<\n HttpServerResponse.HttpServerResponse,\n E,\n HttpServerRequest.HttpServerRequest | Scope.Scope\n >,\n );\n return make(handler, noopDispose, options?.prefix);\n};\n"],"mappings":";;;;;;;;;;;;;;;AAoEA,MAAM,wBAAwB,OAC5B,SACA,YAC0D;CAC1D,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;CAClC,MAAM,SAAS,QAAQ,OAAO;CAC9B,OAAO,OAAO,SAAS,MAAM,IAAI,OAAO,WAAW,MAAM,IAAI;AAC/D;AA0BA,MAAM,oBAAmC,QAAQ,QAAQ;;AAGzD,MAAM,eAAe,SAAkB,WAA4B;CACjE,MAAM,aAAa,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI;CAChE,IAAI,eAAe,IAAI,OAAO;CAE9B,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;CAC/B,MAAM,OAAO,IAAI;CACjB,IAAI,SAAS,YACX,IAAI,WAAW;MACV,IAAI,KAAK,WAAW,GAAG,WAAW,EAAE,GACzC,IAAI,WAAW,KAAK,MAAM,WAAW,MAAM;MAE3C,OAAO;CAET,OAAO,IAAI,QAAQ,KAAK,OAAO;AACjC;;AAGA,MAAM,QACJ,YACA,SACA,QACA,YACoB;CACpB,MAAM,UAA0B,OAAO,UAAU;EAC/C,MAAM,UAAU,WAAW,KAAA,IAAY,MAAM,UAAU,YAAY,MAAM,SAAS,MAAM;EACxF,IAAI,YAAY,KAAA,GAAW,OAAQ,WAA0B,OAAO;EAEpE,OAAQ,WACN,SACA,MAHgB,sBAAsB,SAAS,OAAO,CAIxD;CACF;CACA,OAAO,OAAO,OAAO,SAAS,EAAE,QAAQ,CAAC;AAC3C;;AAGA,MAAM,cAAc,MAAM,SAAS,KAAK,OAAO,KAAK,WAAW,aAAa,KAAK,EAAE,KACjF,MAAM,aAAa,WAAW,UAAU,CAAC,CAAC,CAAC,CAC7C;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,WAOX,KACA,OACA,YAwBoB;CACpB,MAAM,WAAW,eAAe,MAC9B,KACA,SAAS,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,KAAA,CAChE,EAAE,KAAK,MAAM,QAAQ,KAAK,GAAG,MAAM,QAAQ,WAAW,CAAC;CAIvD,MAAM,EAAE,SAAS,YAAY,WAAW,aACtC,UACA,EAAE,eAAe,SAAS,WAAW,KAAK,CAC5C;CACA,OAAO,KACL,SACA,SACA,SAAS,QACT,SAAS,OACX;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,MAAa,cAWX,OACA,YAUoB;CACpB,MAAM,EAAE,SAAS,YAAY,WAAW,aAAa,OAAO,EAC1D,eAAe,SAAS,WAAW,KACrC,CAAC;CACD,OAAO,KACL,SACA,SACA,SAAS,QACT,SAAS,OACX;AACF;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,kBACX,QAKA,YAIoB;CACpB,IAAI,SAAS,OAAO;EAClB,MAAM,EAAE,SAAS,YAAY,WAAW,kBAAkB,QAAQ,QAAQ,KAAK;EAC/E,OAAO,KAAK,SAAuB,SAAS,QAAQ,MAAM;CAC5D;CAQA,OAAO,KAPS,WAAW,aACzB,MAMgB,GAAG,aAAa,SAAS,MAAM;AACnD"}
package/dist/server.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { SvelteKitRequestEvent, SvelteKitRuntime, runWithSvelteKitRuntime } from "./runtime.js";
2
- import { RequestRuntime, ServerRuntime, SvelteKitServices, makeServerRuntime } from "./app-runtime.js";
2
+ import { AdapterEndpointOptions, ErrorTranslator, FromLayerPerRequestOptions, RequestRuntime, RequestRuntimeProvider, RequestRuntimeRunners, RuntimeAdapter, RuntimeAdapterOptions, makeRuntimeAdapter } from "./request-runtime.js";
3
+ import { RequestRuntime as RequestRuntime$1, ServerRuntime, ServerRuntimeOptions, SvelteKitServices, makeServerRuntime } from "./app-runtime.js";
3
4
  import { sveltekitConfig, sveltekitConfigProvider } from "./config.js";
4
5
  import { Hooks_d_exports } from "./server/Hooks.js";
5
6
  import { Endpoint_d_exports } from "./server/Endpoint.js";
@@ -104,5 +105,5 @@ declare function endpoint<Params extends Record<string, string> = any, RouteId e
104
105
  exposeErrors?: boolean;
105
106
  }): (event: RequestEvent<Params, RouteId>) => Promise<Response>;
106
107
  //#endregion
107
- export { Endpoint_d_exports as Endpoint, Hooks_d_exports as Hooks, RemoteFunction, type RequestRuntime, type ServerRuntime, SvelteKitRequestEvent, SvelteKitRuntime, type SvelteKitServices, action, actions, endpoint, makeServerRuntime, runWithSvelteKitRuntime, serverLoad, sveltekitConfig, sveltekitConfigProvider };
108
+ export { type AdapterEndpointOptions, Endpoint_d_exports as Endpoint, type ErrorTranslator, type FromLayerPerRequestOptions, Hooks_d_exports as Hooks, RemoteFunction, RequestRuntime, type RequestRuntimeProvider, type RequestRuntimeRunners, type RuntimeAdapter, type RuntimeAdapterOptions, type RequestRuntime$1 as ScopedRequestRuntime, type ServerRuntime, type ServerRuntimeOptions, SvelteKitRequestEvent, SvelteKitRuntime, type SvelteKitServices, action, actions, endpoint, makeRuntimeAdapter, makeServerRuntime, runWithSvelteKitRuntime, serverLoad, sveltekitConfig, sveltekitConfigProvider };
108
109
  //# sourceMappingURL=server.d.ts.map
package/dist/server.js CHANGED
@@ -5,6 +5,7 @@ import { sveltekitConfig, sveltekitConfigProvider } from "./config.js";
5
5
  import { Hooks_exports } from "./server/Hooks.js";
6
6
  import { Endpoint_exports } from "./server/Endpoint.js";
7
7
  import { RemoteFunction } from "./remote.server.js";
8
+ import { RequestRuntime, makeRuntimeAdapter } from "./request-runtime.js";
8
9
  import { Cause, Exit } from "effect";
9
10
  import { isHttpError, isRedirect } from "@sveltejs/kit";
10
11
  //#region src/server.ts
@@ -126,6 +127,6 @@ function endpoint(fn, options) {
126
127
  };
127
128
  }
128
129
  //#endregion
129
- export { Endpoint_exports as Endpoint, Hooks_exports as Hooks, RemoteFunction, SvelteKitRequestEvent, SvelteKitRuntime, action, actions, endpoint, makeServerRuntime, runWithSvelteKitRuntime, serverLoad, sveltekitConfig, sveltekitConfigProvider };
130
+ export { Endpoint_exports as Endpoint, Hooks_exports as Hooks, RemoteFunction, RequestRuntime, SvelteKitRequestEvent, SvelteKitRuntime, action, actions, endpoint, makeRuntimeAdapter, makeServerRuntime, runWithSvelteKitRuntime, serverLoad, sveltekitConfig, sveltekitConfigProvider };
130
131
 
131
132
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","names":["runtime"],"sources":["../src/server.ts"],"sourcesContent":["import type { Action, Actions, RequestEvent, ServerLoad } from \"@sveltejs/kit\";\nimport { Effect, Exit, Cause } from \"effect\";\nimport { isHttpError, isRedirect } from \"@sveltejs/kit\";\nimport type { SvelteKitServices } from \"./app-runtime.js\";\nimport { defaultServerRuntime as runtime } from \"./internal/default-runtime.js\";\n\nexport type { SvelteKitServices };\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit server load function.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with {@link makeServerRuntime} and use its `serverLoad`\n * instead.\n *\n * @example\n * ```ts\n * import { serverLoad } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return { data: yield* response.json };\n * })\n * );\n * ```\n */\nexport function serverLoad<\n Params extends 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, SvelteKitServices>,\n): ServerLoad<Params, ParentData, OutputData, RouteId> {\n return runtime().serverLoad(fn);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit action handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { action } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = {\n * default: action((event) =>\n * Effect.gen(function* () {\n * const formData = yield* Effect.promise(() => event.request.formData());\n * // Process form data\n * return { success: true };\n * })\n * )\n * };\n * ```\n */\nexport function action<\n Params extends 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, SvelteKitServices>,\n): Action<Params, OutputData, RouteId> {\n return runtime().action(fn);\n}\n\n/**\n * Creates SvelteKit actions from an object of Effect-based action handlers.\n * Each action will have access to platform services via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { actions } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = actions({\n * create: (event) => Effect.succeed({ created: true }),\n * delete: (event) => Effect.succeed({ deleted: true })\n * });\n * ```\n */\nexport function actions<\n Params extends 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 (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, SvelteKitServices>\n >,\n): Actions<Params, OutputData, RouteId> {\n return runtime().actions(actionsMap);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit API endpoint handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit. For any other failure, the error is logged\n * server-side and a generic error message is returned to clients. For\n * development/debugging, you can pass options to expose full error details.\n *\n * @example\n * ```ts\n * import { endpoint } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import { json } from '@sveltejs/kit';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const GET = endpoint((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return json(yield* response.json);\n * })\n * );\n * ```\n */\nexport function endpoint<\n Params extends Record<string, string> = any,\n RouteId extends string | null = any,\n>(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, SvelteKitServices>,\n options?: { exposeErrors?: boolean },\n): (event: RequestEvent<Params, RouteId>) => Promise<Response> {\n const { exposeErrors = false } = options ?? {};\n\n return async (event) => {\n const exit = await runtime().runPromiseExit(event as RequestEvent, fn(event));\n\n return Exit.match(exit, {\n onFailure: (cause) => {\n // Let SvelteKit handle its own control flow objects.\n const squashed = Cause.squash(cause);\n if (isRedirect(squashed) || isHttpError(squashed)) {\n throw squashed;\n }\n\n // Always log the full error server-side for debugging\n console.error(\"Endpoint error:\", Cause.pretty(cause));\n\n // Return generic error message to client unless explicitly enabled\n const clientMessage = exposeErrors ? Cause.pretty(cause) : \"Internal server error\";\n\n return new Response(JSON.stringify({ error: clientMessage }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n },\n onSuccess: (value) => value,\n });\n };\n}\n\n/**\n * Effect wrappers for the server-side hooks (`hooks.server.ts`): the `Hooks`\n * namespace exposes `handle`, `handleFetch`, `handleError`, and `init`.\n */\nexport * as Hooks from \"./server/Hooks.js\";\n\n/**\n * Create SvelteKit `+server.ts` endpoints backed by Effect: the `Endpoint`\n * namespace exposes `fromApi` (an `HttpApi`), `fromRouter` (an `HttpRouter`),\n * and `fromHttpEffect` (a single HTTP route effect).\n */\nexport * as Endpoint from \"./server/Endpoint.js\";\n\n/**\n * App-wide runtime: define your application's Effect services once and share\n * them across every request.\n */\nexport { makeServerRuntime, type ServerRuntime, type RequestRuntime } from \"./app-runtime.js\";\n\n/**\n * Export the SvelteKit runtime utilities for advanced use cases.\n */\nexport { SvelteKitRuntime, runWithSvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nexport { RemoteFunction } from \"./remote.server.js\";\n\n/**\n * SvelteKit `$env` ⇄ Effect `Config` (see {@link sveltekitConfig}). The\n * Effect Schema ⇄ SvelteKit `transport` hook lives on the universal `Hooks`\n * namespace (`Hooks.transport`) in `@thomasfosterau/effect-sveltekit`.\n */\nexport { sveltekitConfig, sveltekitConfigProvider } from \"./config.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,WAMd,IAGqD;CACrD,OAAOA,qBAAQ,EAAE,WAAW,EAAE;AAChC;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,OAKd,IAGqC;CACrC,OAAOA,qBAAQ,EAAE,OAAO,EAAE;AAC5B;;;;;;;;;;;;;;;;AAiBA,SAAgB,QAKd,YAIsC;CACtC,OAAOA,qBAAQ,EAAE,QAAQ,UAAU;AACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,SAId,IACA,SAC6D;CAC7D,MAAM,EAAE,eAAe,UAAU,WAAW,CAAC;CAE7C,OAAO,OAAO,UAAU;EACtB,MAAM,OAAO,MAAMA,qBAAQ,EAAE,eAAe,OAAuB,GAAG,KAAK,CAAC;EAE5E,OAAO,KAAK,MAAM,MAAM;GACtB,YAAY,UAAU;IAEpB,MAAM,WAAW,MAAM,OAAO,KAAK;IACnC,IAAI,WAAW,QAAQ,KAAK,YAAY,QAAQ,GAC9C,MAAM;IAIR,QAAQ,MAAM,mBAAmB,MAAM,OAAO,KAAK,CAAC;IAGpD,MAAM,gBAAgB,eAAe,MAAM,OAAO,KAAK,IAAI;IAE3D,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,cAAc,CAAC,GAAG;KAC5D,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;IAChD,CAAC;GACH;GACA,YAAY,UAAU;EACxB,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"server.js","names":["runtime"],"sources":["../src/server.ts"],"sourcesContent":["import type { Action, Actions, RequestEvent, ServerLoad } from \"@sveltejs/kit\";\nimport { Effect, Exit, Cause } from \"effect\";\nimport { isHttpError, isRedirect } from \"@sveltejs/kit\";\nimport type { SvelteKitServices } from \"./app-runtime.js\";\nimport { defaultServerRuntime as runtime } from \"./internal/default-runtime.js\";\n\nexport type { SvelteKitServices };\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit server load function.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with {@link makeServerRuntime} and use its `serverLoad`\n * instead.\n *\n * @example\n * ```ts\n * import { serverLoad } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return { data: yield* response.json };\n * })\n * );\n * ```\n */\nexport function serverLoad<\n Params extends 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, SvelteKitServices>,\n): ServerLoad<Params, ParentData, OutputData, RouteId> {\n return runtime().serverLoad(fn);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit action handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { action } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = {\n * default: action((event) =>\n * Effect.gen(function* () {\n * const formData = yield* Effect.promise(() => event.request.formData());\n * // Process form data\n * return { success: true };\n * })\n * )\n * };\n * ```\n */\nexport function action<\n Params extends 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, SvelteKitServices>,\n): Action<Params, OutputData, RouteId> {\n return runtime().action(fn);\n}\n\n/**\n * Creates SvelteKit actions from an object of Effect-based action handlers.\n * Each action will have access to platform services via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { actions } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = actions({\n * create: (event) => Effect.succeed({ created: true }),\n * delete: (event) => Effect.succeed({ deleted: true })\n * });\n * ```\n */\nexport function actions<\n Params extends 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 (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, SvelteKitServices>\n >,\n): Actions<Params, OutputData, RouteId> {\n return runtime().actions(actionsMap);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit API endpoint handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit. For any other failure, the error is logged\n * server-side and a generic error message is returned to clients. For\n * development/debugging, you can pass options to expose full error details.\n *\n * @example\n * ```ts\n * import { endpoint } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import { json } from '@sveltejs/kit';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const GET = endpoint((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return json(yield* response.json);\n * })\n * );\n * ```\n */\nexport function endpoint<\n Params extends Record<string, string> = any,\n RouteId extends string | null = any,\n>(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, SvelteKitServices>,\n options?: { exposeErrors?: boolean },\n): (event: RequestEvent<Params, RouteId>) => Promise<Response> {\n const { exposeErrors = false } = options ?? {};\n\n return async (event) => {\n const exit = await runtime().runPromiseExit(event as RequestEvent, fn(event));\n\n return Exit.match(exit, {\n onFailure: (cause) => {\n // Let SvelteKit handle its own control flow objects.\n const squashed = Cause.squash(cause);\n if (isRedirect(squashed) || isHttpError(squashed)) {\n throw squashed;\n }\n\n // Always log the full error server-side for debugging\n console.error(\"Endpoint error:\", Cause.pretty(cause));\n\n // Return generic error message to client unless explicitly enabled\n const clientMessage = exposeErrors ? Cause.pretty(cause) : \"Internal server error\";\n\n return new Response(JSON.stringify({ error: clientMessage }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n },\n onSuccess: (value) => value,\n });\n };\n}\n\n/**\n * Effect wrappers for the server-side hooks (`hooks.server.ts`): the `Hooks`\n * namespace exposes `handle`, `handleFetch`, `handleError`, and `init`.\n */\nexport * as Hooks from \"./server/Hooks.js\";\n\n/**\n * Create SvelteKit `+server.ts` endpoints backed by Effect: the `Endpoint`\n * namespace exposes `fromApi` (an `HttpApi`), `fromRouter` (an `HttpRouter`),\n * and `fromHttpEffect` (a single HTTP route effect).\n */\nexport * as Endpoint from \"./server/Endpoint.js\";\n\n/**\n * App-wide runtime: define your application's Effect services once and share\n * them across every request.\n */\nexport {\n makeServerRuntime,\n type ServerRuntime,\n type ServerRuntimeOptions,\n type RequestRuntime as ScopedRequestRuntime,\n} from \"./app-runtime.js\";\n\n/**\n * Export the SvelteKit runtime utilities for advanced use cases.\n */\nexport { SvelteKitRuntime, runWithSvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nexport { RemoteFunction } from \"./remote.server.js\";\n\n/**\n * A general, Effect-idiomatic seam for *how the per-request execution context is\n * obtained*. {@link RequestRuntime} constructs a {@link RequestRuntimeProvider}\n * (from a consumer-owned runtime on `event.locals`, a package-built\n * per-request runtime, a build-once `ManagedRuntime`, or a `Layer`), and\n * {@link makeRuntimeAdapter} binds the `serverLoad` / `action` / `actions` /\n * `endpoint` wrappers to it — so the handler effect's services `R` are your own\n * application catalogue, not the fixed {@link SvelteKitServices}. A\n * {@link RuntimeAdapterOptions.translateError} maps a failed `Cause` to\n * SvelteKit control flow at the boundary.\n */\nexport {\n RequestRuntime,\n makeRuntimeAdapter,\n type RequestRuntimeProvider,\n type RequestRuntimeRunners,\n type RuntimeAdapter,\n type RuntimeAdapterOptions,\n type AdapterEndpointOptions,\n type FromLayerPerRequestOptions,\n type ErrorTranslator,\n} from \"./request-runtime.js\";\n\n/**\n * SvelteKit `$env` ⇄ Effect `Config` (see {@link sveltekitConfig}). The\n * Effect Schema ⇄ SvelteKit `transport` hook lives on the universal `Hooks`\n * namespace (`Hooks.transport`) in `@thomasfosterau/effect-sveltekit`.\n */\nexport { sveltekitConfig, sveltekitConfigProvider } from \"./config.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,WAMd,IAGqD;CACrD,OAAOA,qBAAQ,EAAE,WAAW,EAAE;AAChC;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,OAKd,IAGqC;CACrC,OAAOA,qBAAQ,EAAE,OAAO,EAAE;AAC5B;;;;;;;;;;;;;;;;AAiBA,SAAgB,QAKd,YAIsC;CACtC,OAAOA,qBAAQ,EAAE,QAAQ,UAAU;AACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,SAId,IACA,SAC6D;CAC7D,MAAM,EAAE,eAAe,UAAU,WAAW,CAAC;CAE7C,OAAO,OAAO,UAAU;EACtB,MAAM,OAAO,MAAMA,qBAAQ,EAAE,eAAe,OAAuB,GAAG,KAAK,CAAC;EAE5E,OAAO,KAAK,MAAM,MAAM;GACtB,YAAY,UAAU;IAEpB,MAAM,WAAW,MAAM,OAAO,KAAK;IACnC,IAAI,WAAW,QAAQ,KAAK,YAAY,QAAQ,GAC9C,MAAM;IAIR,QAAQ,MAAM,mBAAmB,MAAM,OAAO,KAAK,CAAC;IAGpD,MAAM,gBAAgB,eAAe,MAAM,OAAO,KAAK,IAAI;IAE3D,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,cAAc,CAAC,GAAG;KAC5D,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;IAChD,CAAC;GACH;GACA,YAAY,UAAU;EACxB,CAAC;CACH;AACF"}
@@ -13,11 +13,49 @@ import { Transport, Transporter } from "@sveltejs/kit";
13
13
  */
14
14
  declare const transporter: <T, E>(schema: Schema.Codec<T, E>) => Transporter<T, readonly [E]>;
15
15
  /**
16
- * Assembles a SvelteKit `transport` object from a record of Effect schemas,
17
- * applying {@link transporter} to each. The keys are arbitrary labels (they
18
- * only need to be stable between server and client).
16
+ * Builds a deliberately-lossy {@link Transporter} for a non-POJO wrapper whose
17
+ * contents should *not* round-trip — the value is detected on the server and
18
+ * reconstructed fresh on the client.
19
+ *
20
+ * Unlike {@link transporter} (which needs a round-tripping {@link Schema.Codec}),
21
+ * this is for values that have no meaningful serialization but still need a
22
+ * `transport` entry so SvelteKit doesn't reject them: `encode` returns a truthy
23
+ * marker for matching values, and `decode` ignores it and produces a fresh value
24
+ * from {@link MarkerTransporterOptions.make}.
25
+ *
26
+ * ```ts
27
+ * // A non-serializable handle reconstructed as a fresh, empty instance:
28
+ * export const transport = Hooks.transport({
29
+ * Live: Hooks.markerTransporter({
30
+ * guard: (v): v is LiveHandle => v instanceof LiveHandle,
31
+ * make: () => LiveHandle.empty(),
32
+ * }),
33
+ * });
34
+ * ```
35
+ */
36
+ declare const markerTransporter: <T>(options: MarkerTransporterOptions<T>) => Transporter<T, unknown>;
37
+ /** Options for {@link markerTransporter}. */
38
+ interface MarkerTransporterOptions<T> {
39
+ /** Detect, on the server, values this transporter handles. */
40
+ readonly guard: (value: unknown) => value is T;
41
+ /** Produce a fresh value on the client (the payload is discarded). */
42
+ readonly make: () => T;
43
+ /**
44
+ * The truthy marker `encode` emits (SvelteKit treats a falsy `encode` result
45
+ * as "not this type"). Defaults to `1`.
46
+ */
47
+ readonly marker?: unknown;
48
+ }
49
+ /** An entry accepted by {@link transport}: an Effect schema or a ready Transporter. */
50
+ type TransportEntry = Schema.Codec<any, any> | Transporter<any, any>;
51
+ /**
52
+ * Assembles a SvelteKit `transport` object from a record of entries. Each entry
53
+ * is either an Effect {@link Schema.Codec} (wrapped with {@link transporter}) or
54
+ * a ready {@link Transporter} (e.g. from {@link markerTransporter}), so
55
+ * round-tripping and marker-style values can be mixed. The keys are arbitrary
56
+ * labels (they only need to be stable between server and client).
19
57
  */
20
- declare const transport: (schemas: Record<string, Schema.Codec<any, any>>) => Transport;
58
+ declare const transport: (entries: Record<string, TransportEntry>) => Transport;
21
59
  //#endregion
22
- export { transport, transporter };
60
+ export { MarkerTransporterOptions, TransportEntry, markerTransporter, transport, transporter };
23
61
  //# sourceMappingURL=transport.d.ts.map
package/dist/transport.js CHANGED
@@ -19,16 +19,46 @@ const transporter = (schema) => {
19
19
  };
20
20
  };
21
21
  /**
22
- * Assembles a SvelteKit `transport` object from a record of Effect schemas,
23
- * applying {@link transporter} to each. The keys are arbitrary labels (they
24
- * only need to be stable between server and client).
22
+ * Builds a deliberately-lossy {@link Transporter} for a non-POJO wrapper whose
23
+ * contents should *not* round-trip — the value is detected on the server and
24
+ * reconstructed fresh on the client.
25
+ *
26
+ * Unlike {@link transporter} (which needs a round-tripping {@link Schema.Codec}),
27
+ * this is for values that have no meaningful serialization but still need a
28
+ * `transport` entry so SvelteKit doesn't reject them: `encode` returns a truthy
29
+ * marker for matching values, and `decode` ignores it and produces a fresh value
30
+ * from {@link MarkerTransporterOptions.make}.
31
+ *
32
+ * ```ts
33
+ * // A non-serializable handle reconstructed as a fresh, empty instance:
34
+ * export const transport = Hooks.transport({
35
+ * Live: Hooks.markerTransporter({
36
+ * guard: (v): v is LiveHandle => v instanceof LiveHandle,
37
+ * make: () => LiveHandle.empty(),
38
+ * }),
39
+ * });
40
+ * ```
41
+ */
42
+ const markerTransporter = (options) => {
43
+ const marker = options.marker ?? 1;
44
+ return {
45
+ encode: (value) => options.guard(value) ? marker : false,
46
+ decode: () => options.make()
47
+ };
48
+ };
49
+ /**
50
+ * Assembles a SvelteKit `transport` object from a record of entries. Each entry
51
+ * is either an Effect {@link Schema.Codec} (wrapped with {@link transporter}) or
52
+ * a ready {@link Transporter} (e.g. from {@link markerTransporter}), so
53
+ * round-tripping and marker-style values can be mixed. The keys are arbitrary
54
+ * labels (they only need to be stable between server and client).
25
55
  */
26
- const transport = (schemas) => {
56
+ const transport = (entries) => {
27
57
  const result = {};
28
- for (const [name, schema] of Object.entries(schemas)) result[name] = transporter(schema);
58
+ for (const [name, entry] of Object.entries(entries)) result[name] = Schema.isSchema(entry) ? transporter(entry) : entry;
29
59
  return result;
30
60
  };
31
61
  //#endregion
32
- export { transport, transporter };
62
+ export { markerTransporter, transport, transporter };
33
63
 
34
64
  //# sourceMappingURL=transport.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","names":[],"sources":["../src/transport.ts"],"sourcesContent":["/**\n * Effect Schema ⇄ SvelteKit `transport` hook.\n *\n * SvelteKit's universal [`transport`](https://svelte.dev/docs/kit/hooks#Universal-hooks-transport)\n * hook moves custom (non-POJO) values across the server/client boundary — for\n * data returned from `load` functions and from remote functions. Each entry is\n * a {@link Transporter}: a sync `encode` (server) / `decode` (client) pair,\n * where `encode` returns `false` for values it does not handle.\n *\n * An Effect {@link Schema.Codec} is exactly such a pair. {@link transporter}\n * turns a schema into a `Transporter`, and {@link transport} assembles a whole\n * `transport` object from a record of schemas — so `Option`, `DateTime`,\n * branded types, `Schema.Class` instances, tagged errors, etc. round-trip with\n * no hand-written serialization.\n *\n * ```ts\n * // src/hooks.ts\n * import { Hooks } from \"@thomasfosterau/effect-sveltekit\";\n * import { Schema } from \"effect\";\n *\n * export const transport = Hooks.transport({\n * Option: Schema.Option(Schema.Number),\n * Date: Schema.Date,\n * });\n * ```\n */\n\nimport type { Transport, Transporter } from \"@sveltejs/kit\";\nimport { Schema } from \"effect\";\n\n/**\n * Builds a SvelteKit {@link Transporter} from an Effect {@link Schema.Codec}.\n *\n * The decoded `Type` is detected with `Schema.is`; matching values are encoded\n * with `Schema.encodeSync` and wrapped in a single-element tuple. The wrapper\n * guarantees the encoding is truthy — SvelteKit treats a falsy `encode` result\n * (e.g. an encoded `0`, `\"\"`, `false`, or `null`) as \"not this type\" — and is\n * unwrapped again by `decode` on the client.\n */\nexport const transporter = <T, E>(schema: Schema.Codec<T, E>): Transporter<T, readonly [E]> => {\n const guard = Schema.is(schema);\n const encode = Schema.encodeSync(schema);\n const decode = Schema.decodeSync(schema);\n return {\n encode: (value) => (guard(value) ? ([encode(value)] as const) : false),\n decode: ([data]) => decode(data),\n };\n};\n\n/**\n * Assembles a SvelteKit `transport` object from a record of Effect schemas,\n * applying {@link transporter} to each. The keys are arbitrary labels (they\n * only need to be stable between server and client).\n */\nexport const transport = (schemas: Record<string, Schema.Codec<any, any>>): Transport => {\n const result: Transport = {};\n for (const [name, schema] of Object.entries(schemas)) {\n result[name] = transporter(schema);\n }\n return result;\n};\n"],"mappings":";;;;;;;;;;;AAuCA,MAAa,eAAqB,WAA6D;CAC7F,MAAM,QAAQ,OAAO,GAAG,MAAM;CAC9B,MAAM,SAAS,OAAO,WAAW,MAAM;CACvC,MAAM,SAAS,OAAO,WAAW,MAAM;CACvC,OAAO;EACL,SAAS,UAAW,MAAM,KAAK,IAAK,CAAC,OAAO,KAAK,CAAC,IAAc;EAChE,SAAS,CAAC,UAAU,OAAO,IAAI;CACjC;AACF;;;;;;AAOA,MAAa,aAAa,YAA+D;CACvF,MAAM,SAAoB,CAAC;CAC3B,KAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,OAAO,GACjD,OAAO,QAAQ,YAAY,MAAM;CAEnC,OAAO;AACT"}
1
+ {"version":3,"file":"transport.js","names":[],"sources":["../src/transport.ts"],"sourcesContent":["/**\n * Effect Schema ⇄ SvelteKit `transport` hook.\n *\n * SvelteKit's universal [`transport`](https://svelte.dev/docs/kit/hooks#Universal-hooks-transport)\n * hook moves custom (non-POJO) values across the server/client boundary — for\n * data returned from `load` functions and from remote functions. Each entry is\n * a {@link Transporter}: a sync `encode` (server) / `decode` (client) pair,\n * where `encode` returns `false` for values it does not handle.\n *\n * An Effect {@link Schema.Codec} is exactly such a pair. {@link transporter}\n * turns a schema into a `Transporter`, and {@link transport} assembles a whole\n * `transport` object from a record of schemas — so `Option`, `DateTime`,\n * branded types, `Schema.Class` instances, tagged errors, etc. round-trip with\n * no hand-written serialization.\n *\n * ```ts\n * // src/hooks.ts\n * import { Hooks } from \"@thomasfosterau/effect-sveltekit\";\n * import { Schema } from \"effect\";\n *\n * export const transport = Hooks.transport({\n * Option: Schema.Option(Schema.Number),\n * Date: Schema.Date,\n * });\n * ```\n */\n\nimport type { Transport, Transporter } from \"@sveltejs/kit\";\nimport { Schema } from \"effect\";\n\n/**\n * Builds a SvelteKit {@link Transporter} from an Effect {@link Schema.Codec}.\n *\n * The decoded `Type` is detected with `Schema.is`; matching values are encoded\n * with `Schema.encodeSync` and wrapped in a single-element tuple. The wrapper\n * guarantees the encoding is truthy — SvelteKit treats a falsy `encode` result\n * (e.g. an encoded `0`, `\"\"`, `false`, or `null`) as \"not this type\" — and is\n * unwrapped again by `decode` on the client.\n */\nexport const transporter = <T, E>(schema: Schema.Codec<T, E>): Transporter<T, readonly [E]> => {\n const guard = Schema.is(schema);\n const encode = Schema.encodeSync(schema);\n const decode = Schema.decodeSync(schema);\n return {\n encode: (value) => (guard(value) ? ([encode(value)] as const) : false),\n decode: ([data]) => decode(data),\n };\n};\n\n/**\n * Builds a deliberately-lossy {@link Transporter} for a non-POJO wrapper whose\n * contents should *not* round-trip — the value is detected on the server and\n * reconstructed fresh on the client.\n *\n * Unlike {@link transporter} (which needs a round-tripping {@link Schema.Codec}),\n * this is for values that have no meaningful serialization but still need a\n * `transport` entry so SvelteKit doesn't reject them: `encode` returns a truthy\n * marker for matching values, and `decode` ignores it and produces a fresh value\n * from {@link MarkerTransporterOptions.make}.\n *\n * ```ts\n * // A non-serializable handle reconstructed as a fresh, empty instance:\n * export const transport = Hooks.transport({\n * Live: Hooks.markerTransporter({\n * guard: (v): v is LiveHandle => v instanceof LiveHandle,\n * make: () => LiveHandle.empty(),\n * }),\n * });\n * ```\n */\nexport const markerTransporter = <T>(\n options: MarkerTransporterOptions<T>,\n): Transporter<T, unknown> => {\n const marker = options.marker ?? 1;\n return {\n encode: (value) => (options.guard(value) ? marker : false),\n decode: () => options.make(),\n };\n};\n\n/** Options for {@link markerTransporter}. */\nexport interface MarkerTransporterOptions<T> {\n /** Detect, on the server, values this transporter handles. */\n readonly guard: (value: unknown) => value is T;\n /** Produce a fresh value on the client (the payload is discarded). */\n readonly make: () => T;\n /**\n * The truthy marker `encode` emits (SvelteKit treats a falsy `encode` result\n * as \"not this type\"). Defaults to `1`.\n */\n readonly marker?: unknown;\n}\n\n/** An entry accepted by {@link transport}: an Effect schema or a ready Transporter. */\nexport type TransportEntry = Schema.Codec<any, any> | Transporter<any, any>;\n\n/**\n * Assembles a SvelteKit `transport` object from a record of entries. Each entry\n * is either an Effect {@link Schema.Codec} (wrapped with {@link transporter}) or\n * a ready {@link Transporter} (e.g. from {@link markerTransporter}), so\n * round-tripping and marker-style values can be mixed. The keys are arbitrary\n * labels (they only need to be stable between server and client).\n */\nexport const transport = (entries: Record<string, TransportEntry>): Transport => {\n const result: Transport = {};\n for (const [name, entry] of Object.entries(entries)) {\n result[name] = Schema.isSchema(entry)\n ? transporter(entry as Schema.Codec<any, any>)\n : (entry as Transporter<any, any>);\n }\n return result;\n};\n"],"mappings":";;;;;;;;;;;AAuCA,MAAa,eAAqB,WAA6D;CAC7F,MAAM,QAAQ,OAAO,GAAG,MAAM;CAC9B,MAAM,SAAS,OAAO,WAAW,MAAM;CACvC,MAAM,SAAS,OAAO,WAAW,MAAM;CACvC,OAAO;EACL,SAAS,UAAW,MAAM,KAAK,IAAK,CAAC,OAAO,KAAK,CAAC,IAAc;EAChE,SAAS,CAAC,UAAU,OAAO,IAAI;CACjC;AACF;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,qBACX,YAC4B;CAC5B,MAAM,SAAS,QAAQ,UAAU;CACjC,OAAO;EACL,SAAS,UAAW,QAAQ,MAAM,KAAK,IAAI,SAAS;EACpD,cAAc,QAAQ,KAAK;CAC7B;AACF;;;;;;;;AAyBA,MAAa,aAAa,YAAuD;CAC/E,MAAM,SAAoB,CAAC;CAC3B,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAO,GAChD,OAAO,QAAQ,OAAO,SAAS,KAAK,IAChC,YAAY,KAA+B,IAC1C;CAEP,OAAO;AACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thomasfosterau/effect-sveltekit",
3
- "version": "0.0.0",
3
+ "version": "0.1.0",
4
4
  "description": "Effect integration for SvelteKit: load/action/endpoint wrappers, remote functions, an effectKit() Vite plugin, and an Effect HTTP deployment adapter",
5
5
  "keywords": [
6
6
  "effect",
@@ -70,7 +70,7 @@
70
70
  "dependencies": {
71
71
  "@standard-schema/spec": "^1.1.0",
72
72
  "magic-string": "^0.30.0",
73
- "@thomasfosterau/effect-svelte": "^0.0.0"
73
+ "@thomasfosterau/effect-svelte": "^0.1.0"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@effect/platform-node": "^4.0.0-beta.75",