@thomasfosterau/effect-sveltekit 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/api-client.d.ts +32 -0
  2. package/dist/api-client.js +44 -0
  3. package/dist/api-client.js.map +1 -0
  4. package/dist/app-runtime.d.ts +2 -1
  5. package/dist/app-runtime.js +13 -11
  6. package/dist/app-runtime.js.map +1 -1
  7. package/dist/client.d.ts +3 -1
  8. package/dist/client.js +3 -1
  9. package/dist/config.d.ts +33 -2
  10. package/dist/config.js +33 -2
  11. package/dist/config.js.map +1 -1
  12. package/dist/forms.d.ts +30 -0
  13. package/dist/forms.js +66 -0
  14. package/dist/forms.js.map +1 -0
  15. package/dist/index.d.ts +3 -2
  16. package/dist/index.js +3 -2
  17. package/dist/internal/enhance.d.ts +39 -0
  18. package/dist/internal/enhance.js +24 -0
  19. package/dist/internal/enhance.js.map +1 -0
  20. package/dist/internal/remote-run.d.ts +12 -1
  21. package/dist/internal/remote-run.js +3 -5
  22. package/dist/internal/remote-run.js.map +1 -1
  23. package/dist/internal/sse.js +37 -0
  24. package/dist/internal/sse.js.map +1 -0
  25. package/dist/kit.d.ts +130 -0
  26. package/dist/kit.js +185 -0
  27. package/dist/kit.js.map +1 -0
  28. package/dist/realtime.d.ts +105 -0
  29. package/dist/realtime.js +111 -0
  30. package/dist/realtime.js.map +1 -0
  31. package/dist/request-parse.d.ts +89 -0
  32. package/dist/request-parse.js +121 -0
  33. package/dist/request-parse.js.map +1 -0
  34. package/dist/request-runtime.js +17 -16
  35. package/dist/request-runtime.js.map +1 -1
  36. package/dist/request-services.d.ts +120 -0
  37. package/dist/request-services.js +120 -0
  38. package/dist/request-services.js.map +1 -0
  39. package/dist/server/Endpoint.d.ts +62 -2
  40. package/dist/server/Endpoint.js +47 -2
  41. package/dist/server/Endpoint.js.map +1 -1
  42. package/dist/server.d.ts +5 -1
  43. package/dist/server.js +5 -1
  44. package/dist/server.js.map +1 -1
  45. package/dist/streamed-load.d.ts +23 -0
  46. package/dist/streamed-load.js +74 -0
  47. package/dist/streamed-load.js.map +1 -0
  48. package/dist/testing.d.ts +175 -0
  49. package/dist/testing.js +153 -0
  50. package/dist/testing.js.map +1 -0
  51. package/package.json +6 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-services.js","names":[],"sources":["../src/request-services.ts"],"sourcesContent":["/**\n * Effect-native services for the three per-request surfaces on a SvelteKit\n * `RequestEvent` — cookies, response headers, and locals — each as a tagged\n * `Context.Service` that can be `yield*`-ed in any handler effect and mocked\n * in tests with `Layer.succeed`.\n *\n * All three services are provided automatically as part of the\n * {@link SvelteKitServices} per-request context when using\n * {@link makeServerRuntime}, the standalone `serverLoad`/`action`/`endpoint`\n * wrappers, or {@link makeRuntimeAdapter}. No extra layer setup is required.\n *\n * @example\n * ```ts\n * import { Effect } from \"effect\";\n * import {\n * SvelteKitCookies,\n * SvelteKitResponseHeaders,\n * SvelteKitLocals,\n * } from \"@thomasfosterau/effect-sveltekit/server\";\n *\n * export const load = serverLoad(() =>\n * Effect.gen(function* () {\n * const cookies = yield* SvelteKitCookies;\n * const session = yield* cookies.get(\"session\");\n *\n * const headers = yield* SvelteKitResponseHeaders;\n * yield* headers.set({ \"cache-control\": \"max-age=60, s-maxage=300\" });\n *\n * const locals = yield* SvelteKitLocals;\n * // locals is App.Locals — typed to your augmented interface\n * const user = locals.user;\n *\n * return { user };\n * }),\n * );\n * ```\n *\n * ## Testing\n *\n * Because these are tagged services, they can be replaced with stubs via\n * `Layer.succeed` in unit tests without involving a real `RequestEvent`:\n *\n * ```ts\n * import { Layer } from \"effect\";\n * import { SvelteKitCookies } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { runServerLoad, TestRequestEvent } from \"@thomasfosterau/effect-sveltekit/testing\";\n *\n * const MockCookies = Layer.succeed(SvelteKitCookies, {\n * get: (name) => Effect.succeed(name === \"session\" ? \"tok_test\" : undefined),\n * getAll: () => Effect.succeed([{ name: \"session\", value: \"tok_test\" }]),\n * set: () => Effect.void,\n * delete: () => Effect.void,\n * serialize: (n, v) => Effect.succeed(`${n}=${v}`),\n * });\n *\n * const data = await runServerLoad(myLoadFn, TestRequestEvent(), { layer: MockCookies });\n * ```\n */\n\nimport type { Cookies, RequestEvent } from \"@sveltejs/kit\";\nimport { Context, Effect, Layer } from \"effect\";\nimport { SvelteKitRequestEvent } from \"./runtime.js\";\n\n// ─── SvelteKitCookies ─────────────────────────────────────────────────────────\n\n/**\n * The Effect-native interface exposed by {@link SvelteKitCookies}. Each method\n * mirrors the corresponding `Cookies` method from `@sveltejs/kit` but returns\n * an `Effect` rather than a raw value or `void`.\n */\nexport interface SvelteKitCookiesInterface {\n /** Get a cookie by name. Returns `undefined` when the cookie is absent. */\n readonly get: (...args: Parameters<Cookies[\"get\"]>) => Effect.Effect<ReturnType<Cookies[\"get\"]>>;\n /** Get all cookies as `{ name, value }` pairs. */\n readonly getAll: (\n ...args: Parameters<Cookies[\"getAll\"]>\n ) => Effect.Effect<ReturnType<Cookies[\"getAll\"]>>;\n /** Set a cookie (adds a `set-cookie` response header). */\n readonly set: (...args: Parameters<Cookies[\"set\"]>) => Effect.Effect<void>;\n /** Delete a cookie by setting it to an empty value with a past expiry. */\n readonly delete: (...args: Parameters<Cookies[\"delete\"]>) => Effect.Effect<void>;\n /** Serialize a cookie into a `Set-Cookie` string without adding it to the response. */\n readonly serialize: (\n ...args: Parameters<Cookies[\"serialize\"]>\n ) => Effect.Effect<ReturnType<Cookies[\"serialize\"]>>;\n}\n\n/**\n * An Effect service wrapping the `RequestEvent.cookies` API.\n *\n * Available automatically in every server handler effect (it is part of\n * {@link SvelteKitServices}). Use `yield* SvelteKitCookies` to obtain the\n * service; call its methods (each returns an `Effect`) to read or write\n * cookies.\n *\n * @example\n * ```ts\n * const session = yield* (yield* SvelteKitCookies).get(\"session\");\n * ```\n */\nexport class SvelteKitCookies extends Context.Service<\n SvelteKitCookies,\n SvelteKitCookiesInterface\n>()(\"@thomasfosterau/effect-sveltekit/SvelteKitCookies\") {\n /**\n * Layer that builds the `SvelteKitCookies` service from the current\n * {@link SvelteKitRequestEvent}. Included in the per-request context\n * automatically; expose as a standalone layer for custom setups.\n */\n static readonly layer: Layer.Layer<SvelteKitCookies, never, SvelteKitRequestEvent> = Layer.effect(\n SvelteKitCookies,\n Effect.gen(function* () {\n const event = yield* SvelteKitRequestEvent;\n const { cookies } = event;\n return {\n get: (...args) => Effect.sync(() => cookies.get(...args)),\n getAll: (...args) => Effect.sync(() => cookies.getAll(...args)),\n set: (...args) => Effect.sync(() => cookies.set(...args)),\n delete: (...args) => Effect.sync(() => cookies.delete(...args)),\n serialize: (...args) => Effect.sync(() => cookies.serialize(...args)),\n };\n }),\n );\n}\n\n// ─── SvelteKitResponseHeaders ─────────────────────────────────────────────────\n\n/**\n * The Effect-native interface exposed by {@link SvelteKitResponseHeaders}.\n */\nexport interface SvelteKitResponseHeadersInterface {\n /**\n * Set one or more response headers. Equivalent to `event.setHeaders(headers)`.\n *\n * Note: You cannot set `set-cookie` headers here — use\n * {@link SvelteKitCookies} instead.\n */\n readonly set: (headers: Record<string, string>) => Effect.Effect<void>;\n}\n\n/**\n * An Effect service wrapping the `RequestEvent.setHeaders` API.\n *\n * Available automatically in every server handler effect. Use\n * `yield* SvelteKitResponseHeaders` to obtain the service, then call\n * `headers.set(...)`.\n *\n * @example\n * ```ts\n * const headers = yield* SvelteKitResponseHeaders;\n * yield* headers.set({ \"cache-control\": \"max-age=60, s-maxage=300\" });\n * ```\n */\nexport class SvelteKitResponseHeaders extends Context.Service<\n SvelteKitResponseHeaders,\n SvelteKitResponseHeadersInterface\n>()(\"@thomasfosterau/effect-sveltekit/SvelteKitResponseHeaders\") {\n /**\n * Layer that builds the `SvelteKitResponseHeaders` service from the current\n * {@link SvelteKitRequestEvent}.\n */\n static readonly layer: Layer.Layer<SvelteKitResponseHeaders, never, SvelteKitRequestEvent> =\n Layer.effect(\n SvelteKitResponseHeaders,\n Effect.gen(function* () {\n const event = yield* SvelteKitRequestEvent;\n return { set: (headers) => Effect.sync(() => event.setHeaders(headers)) };\n }),\n );\n}\n\n// ─── SvelteKitLocals ──────────────────────────────────────────────────────────\n\n/**\n * An Effect service that exposes the mutable `App.Locals` object for the\n * current request.\n *\n * `yield* SvelteKitLocals` returns the live `event.locals` reference — the\n * same object mutations in `hooks.server.ts` (e.g. setting `event.locals.user`\n * in a `handle` hook) write to. Mutations made to it inside handler effects\n * propagate back to subsequent hook middleware.\n *\n * Available automatically in every server handler effect.\n *\n * @example\n * ```ts\n * // Read a typed local (requires App.Locals augmentation in your app):\n * const { user } = yield* SvelteKitLocals;\n *\n * // Write a local (useful in handle hooks run via makeServerRuntime.handle):\n * const locals = yield* SvelteKitLocals;\n * locals.user = yield* Auth.currentUser;\n * ```\n */\nexport class SvelteKitLocals extends Context.Service<SvelteKitLocals, App.Locals>()(\n \"@thomasfosterau/effect-sveltekit/SvelteKitLocals\",\n) {\n /**\n * Layer that exposes `event.locals` (the mutable `App.Locals` reference)\n * from the current {@link SvelteKitRequestEvent}.\n */\n static readonly layer: Layer.Layer<SvelteKitLocals, never, SvelteKitRequestEvent> = Layer.effect(\n SvelteKitLocals,\n Effect.gen(function* () {\n const event = yield* SvelteKitRequestEvent;\n return event.locals;\n }),\n );\n}\n\n// ─── Combined layer ───────────────────────────────────────────────────────────\n\n/**\n * A convenience layer that provides all three request services\n * ({@link SvelteKitCookies}, {@link SvelteKitResponseHeaders},\n * {@link SvelteKitLocals}) from the current {@link SvelteKitRequestEvent}.\n *\n * This layer is merged into the per-request context automatically by\n * {@link makeServerRuntime}, the standalone wrappers, and\n * {@link makeRuntimeAdapter}. Expose it here for use in custom setups that\n * already have `SvelteKitRequestEvent` in scope.\n */\nexport const requestServicesLayer: Layer.Layer<\n SvelteKitCookies | SvelteKitResponseHeaders | SvelteKitLocals,\n never,\n SvelteKitRequestEvent\n> = Layer.mergeAll(SvelteKitCookies.layer, SvelteKitResponseHeaders.layer, SvelteKitLocals.layer);\n\n/**\n * Build all three request services directly from a {@link RequestEvent},\n * without going through {@link SvelteKitRequestEvent}. Used internally by\n * `requestLayer` in `app-runtime.ts` to avoid an extra indirection.\n */\nexport const requestServicesFrom = (\n event: RequestEvent,\n): {\n cookies: SvelteKitCookiesInterface;\n responseHeaders: SvelteKitResponseHeadersInterface;\n locals: App.Locals;\n} => {\n const { cookies } = event;\n return {\n cookies: {\n get: (...args) => Effect.sync(() => cookies.get(...args)),\n getAll: (...args) => Effect.sync(() => cookies.getAll(...args)),\n set: (...args) => Effect.sync(() => cookies.set(...args)),\n delete: (...args) => Effect.sync(() => cookies.delete(...args)),\n serialize: (...args) => Effect.sync(() => cookies.serialize(...args)),\n },\n responseHeaders: { set: (headers) => Effect.sync(() => event.setHeaders(headers)) },\n locals: event.locals,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAoGA,IAAa,mBAAb,MAAa,yBAAyB,QAAQ,QAG5C,EAAE,mDAAmD,EAAE;;;;;;CAMvD,OAAgB,QAAqE,MAAM,OACzF,kBACA,OAAO,IAAI,aAAa;EAEtB,MAAM,EAAE,YAAY,OADC;EAErB,OAAO;GACL,MAAM,GAAG,SAAS,OAAO,WAAW,QAAQ,IAAI,GAAG,IAAI,CAAC;GACxD,SAAS,GAAG,SAAS,OAAO,WAAW,QAAQ,OAAO,GAAG,IAAI,CAAC;GAC9D,MAAM,GAAG,SAAS,OAAO,WAAW,QAAQ,IAAI,GAAG,IAAI,CAAC;GACxD,SAAS,GAAG,SAAS,OAAO,WAAW,QAAQ,OAAO,GAAG,IAAI,CAAC;GAC9D,YAAY,GAAG,SAAS,OAAO,WAAW,QAAQ,UAAU,GAAG,IAAI,CAAC;EACtE;CACF,CAAC,CACH;AACF;;;;;;;;;;;;;;AA8BA,IAAa,2BAAb,MAAa,iCAAiC,QAAQ,QAGpD,EAAE,2DAA2D,EAAE;;;;;CAK/D,OAAgB,QACd,MAAM,OACJ,0BACA,OAAO,IAAI,aAAa;EACtB,MAAM,QAAQ,OAAO;EACrB,OAAO,EAAE,MAAM,YAAY,OAAO,WAAW,MAAM,WAAW,OAAO,CAAC,EAAE;CAC1E,CAAC,CACH;AACJ;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAa,kBAAb,MAAa,wBAAwB,QAAQ,QAAqC,EAChF,kDACF,EAAE;;;;;CAKA,OAAgB,QAAoE,MAAM,OACxF,iBACA,OAAO,IAAI,aAAa;EAEtB,QAAO,OADc,uBACR;CACf,CAAC,CACH;AACF;;;;;;;;;;;AAcA,MAAa,uBAIT,MAAM,SAAS,iBAAiB,OAAO,yBAAyB,OAAO,gBAAgB,KAAK;;;;;;AAOhG,MAAa,uBACX,UAKG;CACH,MAAM,EAAE,YAAY;CACpB,OAAO;EACL,SAAS;GACP,MAAM,GAAG,SAAS,OAAO,WAAW,QAAQ,IAAI,GAAG,IAAI,CAAC;GACxD,SAAS,GAAG,SAAS,OAAO,WAAW,QAAQ,OAAO,GAAG,IAAI,CAAC;GAC9D,MAAM,GAAG,SAAS,OAAO,WAAW,QAAQ,IAAI,GAAG,IAAI,CAAC;GACxD,SAAS,GAAG,SAAS,OAAO,WAAW,QAAQ,OAAO,GAAG,IAAI,CAAC;GAC9D,YAAY,GAAG,SAAS,OAAO,WAAW,QAAQ,UAAU,GAAG,IAAI,CAAC;EACtE;EACA,iBAAiB,EAAE,MAAM,YAAY,OAAO,WAAW,MAAM,WAAW,OAAO,CAAC,EAAE;EAClF,QAAQ,MAAM;CAChB;AACF"}
@@ -1,4 +1,4 @@
1
- import { Context, Effect, Layer, Scope } from "effect";
1
+ import { Context, Duration, Effect, Layer, Scope, Stream } 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,7 +8,7 @@ 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, RequestContext, fromApi, fromHttpEffect, fromRouter };
11
+ export { EndpointHandler, EndpointOptions, FromStreamOptions, RequestContext, fromApi, fromHttpEffect, fromRouter, fromStream };
12
12
  }
13
13
  /**
14
14
  * Resolve a request-scoped service context, if a resolver was given. The
@@ -127,6 +127,66 @@ declare const fromRouter: <A, E, R extends HttpRouter.HttpRouter | HttpRouter.Re
127
127
  declare const fromHttpEffect: <E, R = never, ProvidedE = never>(effect: Effect.Effect<HttpServerResponse.HttpServerResponse, E, R | HttpServerRequest.HttpServerRequest | Scope.Scope>, options?: EndpointOptions & {
128
128
  /** A layer providing the route's services (`R`), built once and shared. */readonly layer?: Layer.Layer<R, ProvidedE>;
129
129
  }) => EndpointHandler;
130
+ /** Options for {@link fromStream}: how to frame each element as an SSE event. */
131
+ interface FromStreamOptions<A, R = never> {
132
+ /**
133
+ * The event name (`event:` field). A string applies to every element, or a
134
+ * function derives it per element (return `undefined` for an unnamed event).
135
+ * Omit for the default unnamed `message` events.
136
+ */
137
+ readonly event?: string | ((value: A) => string | undefined);
138
+ /**
139
+ * Encode an element into the `data:` payload. Defaults to `JSON.stringify`.
140
+ * Multi-line output is split across multiple `data:` lines automatically.
141
+ */
142
+ readonly data?: (value: A) => string;
143
+ /** Derive an event id (`id:` field) the browser echoes on reconnect. */
144
+ readonly id?: (value: A) => string | undefined;
145
+ /**
146
+ * The client reconnection delay in milliseconds, sent once as a leading
147
+ * `retry:` field. The browser's `EventSource` reconnects automatically after
148
+ * this delay when the connection drops.
149
+ */
150
+ readonly retry?: number;
151
+ /**
152
+ * Send a comment heartbeat on this interval to keep the connection alive
153
+ * through proxies that time out idle streams. Accepts any `Duration.Input`
154
+ * (e.g. `"30 seconds"`).
155
+ */
156
+ readonly heartbeat?: Duration.Input;
157
+ /**
158
+ * A layer (or context) providing the stream's services `R`, applied per
159
+ * connection. Omit when the stream is self-contained (`R = never`).
160
+ */
161
+ readonly provide?: Layer.Layer<R> | Context.Context<R>;
162
+ }
163
+ /**
164
+ * Create a SvelteKit endpoint that streams an Effect `Stream` to the browser as
165
+ * [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
166
+ * (`text/event-stream`). Consume it client-side with `Realtime.fromEventSource`
167
+ * (from `@thomasfosterau/effect-sveltekit/client`) or the native `EventSource`.
168
+ *
169
+ * Each element is framed as one SSE event (`event` / `id` optional, `data`
170
+ * defaults to JSON). A `retry` sets the reconnect delay; a `heartbeat` keeps
171
+ * idle connections alive. The response closes when the stream ends; the stream
172
+ * is interrupted (running its finalizers) when the client disconnects.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * // src/routes/events/+server.ts
177
+ * import { Endpoint } from "@thomasfosterau/effect-sveltekit/server";
178
+ * import { Schedule, Stream } from "effect";
179
+ *
180
+ * const ticks = Stream.fromSchedule(Schedule.spaced("1 second"));
181
+ *
182
+ * export const GET = Endpoint.fromStream(ticks, {
183
+ * event: "tick",
184
+ * heartbeat: "30 seconds",
185
+ * retry: 3000,
186
+ * });
187
+ * ```
188
+ */
189
+ declare const fromStream: <A, E, R = never>(stream: Stream.Stream<A, E, R>, options?: FromStreamOptions<A, R>) => RequestHandler;
130
190
  //#endregion
131
191
  export { Endpoint_d_exports };
132
192
  //# sourceMappingURL=Endpoint.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
- import { Effect, Layer } from "effect";
2
+ import { HEARTBEAT_FRAME, SSE_HEADERS, encodeRetry, encodeSseFrame } from "../internal/sse.js";
3
+ import { Effect, Layer, Schedule, Stream } from "effect";
3
4
  import * as FileSystem from "effect/FileSystem";
4
5
  import * as Path from "effect/Path";
5
6
  import * as Etag from "effect/unstable/http/Etag";
@@ -11,7 +12,8 @@ import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder";
11
12
  var Endpoint_exports = /* @__PURE__ */ __exportAll({
12
13
  fromApi: () => fromApi,
13
14
  fromHttpEffect: () => fromHttpEffect,
14
- fromRouter: () => fromRouter
15
+ fromRouter: () => fromRouter,
16
+ fromStream: () => fromStream
15
17
  });
16
18
  const resolveRequestContext = async (request, context) => {
17
19
  if (context === void 0) return void 0;
@@ -112,6 +114,49 @@ const fromHttpEffect = (effect, options) => {
112
114
  }
113
115
  return make(HttpEffect.toWebHandler(effect), noopDispose, options?.prefix);
114
116
  };
117
+ /**
118
+ * Create a SvelteKit endpoint that streams an Effect `Stream` to the browser as
119
+ * [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
120
+ * (`text/event-stream`). Consume it client-side with `Realtime.fromEventSource`
121
+ * (from `@thomasfosterau/effect-sveltekit/client`) or the native `EventSource`.
122
+ *
123
+ * Each element is framed as one SSE event (`event` / `id` optional, `data`
124
+ * defaults to JSON). A `retry` sets the reconnect delay; a `heartbeat` keeps
125
+ * idle connections alive. The response closes when the stream ends; the stream
126
+ * is interrupted (running its finalizers) when the client disconnects.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * // src/routes/events/+server.ts
131
+ * import { Endpoint } from "@thomasfosterau/effect-sveltekit/server";
132
+ * import { Schedule, Stream } from "effect";
133
+ *
134
+ * const ticks = Stream.fromSchedule(Schedule.spaced("1 second"));
135
+ *
136
+ * export const GET = Endpoint.fromStream(ticks, {
137
+ * event: "tick",
138
+ * heartbeat: "30 seconds",
139
+ * retry: 3000,
140
+ * });
141
+ * ```
142
+ */
143
+ const fromStream = (stream, options) => {
144
+ const encodeData = options?.data ?? ((value) => JSON.stringify(value));
145
+ const eventOf = (value) => typeof options?.event === "function" ? options.event(value) : options?.event;
146
+ return (event) => {
147
+ const events = stream.pipe(Stream.map((value) => encodeSseFrame({
148
+ data: encodeData(value),
149
+ event: eventOf(value),
150
+ id: options?.id?.(value)
151
+ })));
152
+ const withRetry = options?.retry === void 0 ? events : Stream.prepend(events, [encodeRetry(options.retry)]);
153
+ const framed = options?.heartbeat === void 0 ? withRetry : Stream.merge(withRetry, Stream.fromSchedule(Schedule.spaced(options.heartbeat)).pipe(Stream.map(() => HEARTBEAT_FRAME)), { haltStrategy: "left" });
154
+ const bytes = Stream.encodeText(framed);
155
+ const provided = options?.provide === void 0 ? bytes : Stream.provide(bytes, options.provide);
156
+ const body = Stream.toReadableStream(provided);
157
+ return new Response(body, { headers: { ...SSE_HEADERS } });
158
+ };
159
+ };
115
160
  //#endregion
116
161
  export { Endpoint_exports };
117
162
 
@@ -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 { 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"}
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, type Duration, Effect, Layer, Schedule, Stream } 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\";\nimport { encodeRetry, encodeSseFrame, HEARTBEAT_FRAME, SSE_HEADERS } from \"../internal/sse.js\";\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\n/** Options for {@link fromStream}: how to frame each element as an SSE event. */\nexport interface FromStreamOptions<A, R = never> {\n /**\n * The event name (`event:` field). A string applies to every element, or a\n * function derives it per element (return `undefined` for an unnamed event).\n * Omit for the default unnamed `message` events.\n */\n readonly event?: string | ((value: A) => string | undefined);\n /**\n * Encode an element into the `data:` payload. Defaults to `JSON.stringify`.\n * Multi-line output is split across multiple `data:` lines automatically.\n */\n readonly data?: (value: A) => string;\n /** Derive an event id (`id:` field) the browser echoes on reconnect. */\n readonly id?: (value: A) => string | undefined;\n /**\n * The client reconnection delay in milliseconds, sent once as a leading\n * `retry:` field. The browser's `EventSource` reconnects automatically after\n * this delay when the connection drops.\n */\n readonly retry?: number;\n /**\n * Send a comment heartbeat on this interval to keep the connection alive\n * through proxies that time out idle streams. Accepts any `Duration.Input`\n * (e.g. `\"30 seconds\"`).\n */\n readonly heartbeat?: Duration.Input;\n /**\n * A layer (or context) providing the stream's services `R`, applied per\n * connection. Omit when the stream is self-contained (`R = never`).\n */\n readonly provide?: Layer.Layer<R> | Context.Context<R>;\n}\n\n/**\n * Create a SvelteKit endpoint that streams an Effect `Stream` to the browser as\n * [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)\n * (`text/event-stream`). Consume it client-side with `Realtime.fromEventSource`\n * (from `@thomasfosterau/effect-sveltekit/client`) or the native `EventSource`.\n *\n * Each element is framed as one SSE event (`event` / `id` optional, `data`\n * defaults to JSON). A `retry` sets the reconnect delay; a `heartbeat` keeps\n * idle connections alive. The response closes when the stream ends; the stream\n * is interrupted (running its finalizers) when the client disconnects.\n *\n * @example\n * ```ts\n * // src/routes/events/+server.ts\n * import { Endpoint } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Schedule, Stream } from \"effect\";\n *\n * const ticks = Stream.fromSchedule(Schedule.spaced(\"1 second\"));\n *\n * export const GET = Endpoint.fromStream(ticks, {\n * event: \"tick\",\n * heartbeat: \"30 seconds\",\n * retry: 3000,\n * });\n * ```\n */\nexport const fromStream = <A, E, R = never>(\n stream: Stream.Stream<A, E, R>,\n options?: FromStreamOptions<A, R>,\n): RequestHandler => {\n const encodeData = options?.data ?? ((value: A): string => JSON.stringify(value));\n const eventOf = (value: A): string | undefined =>\n typeof options?.event === \"function\" ? options.event(value) : options?.event;\n\n return (event) => {\n // Frame each element, then encode the resulting strings to bytes.\n const events = stream.pipe(\n Stream.map((value) =>\n encodeSseFrame({\n data: encodeData(value),\n event: eventOf(value),\n id: options?.id?.(value),\n }),\n ),\n );\n\n // A leading `retry:` field, if configured.\n const withRetry =\n options?.retry === undefined ? events : Stream.prepend(events, [encodeRetry(options.retry)]);\n\n // Merge in a heartbeat that halts as soon as the data stream ends.\n const framed =\n options?.heartbeat === undefined\n ? withRetry\n : Stream.merge(\n withRetry,\n Stream.fromSchedule(Schedule.spaced(options.heartbeat)).pipe(\n Stream.map(() => HEARTBEAT_FRAME),\n ),\n { haltStrategy: \"left\" },\n );\n\n const bytes = Stream.encodeText(framed);\n const provided =\n options?.provide === undefined ? bytes : Stream.provide(bytes, options.provide);\n\n const body = Stream.toReadableStream(provided as Stream.Stream<Uint8Array, E>);\n\n // SvelteKit forwards a streaming body Response as-is.\n void event;\n return new Response(body, { headers: { ...SSE_HEADERS } });\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAqEA,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;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,MAAa,cACX,QACA,YACmB;CACnB,MAAM,aAAa,SAAS,UAAU,UAAqB,KAAK,UAAU,KAAK;CAC/E,MAAM,WAAW,UACf,OAAO,SAAS,UAAU,aAAa,QAAQ,MAAM,KAAK,IAAI,SAAS;CAEzE,QAAQ,UAAU;EAEhB,MAAM,SAAS,OAAO,KACpB,OAAO,KAAK,UACV,eAAe;GACb,MAAM,WAAW,KAAK;GACtB,OAAO,QAAQ,KAAK;GACpB,IAAI,SAAS,KAAK,KAAK;EACzB,CAAC,CACH,CACF;EAGA,MAAM,YACJ,SAAS,UAAU,KAAA,IAAY,SAAS,OAAO,QAAQ,QAAQ,CAAC,YAAY,QAAQ,KAAK,CAAC,CAAC;EAG7F,MAAM,SACJ,SAAS,cAAc,KAAA,IACnB,YACA,OAAO,MACL,WACA,OAAO,aAAa,SAAS,OAAO,QAAQ,SAAS,CAAC,EAAE,KACtD,OAAO,UAAU,eAAe,CAClC,GACA,EAAE,cAAc,OAAO,CACzB;EAEN,MAAM,QAAQ,OAAO,WAAW,MAAM;EACtC,MAAM,WACJ,SAAS,YAAY,KAAA,IAAY,QAAQ,OAAO,QAAQ,OAAO,QAAQ,OAAO;EAEhF,MAAM,OAAO,OAAO,iBAAiB,QAAwC;EAI7E,OAAO,IAAI,SAAS,MAAM,EAAE,SAAS,EAAE,GAAG,YAAY,EAAE,CAAC;CAC3D;AACF"}
package/dist/server.d.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  import { SvelteRouteParams } from "./internal/route-params.js";
2
2
  import { SvelteKitRequestEvent, SvelteKitRuntime, runWithSvelteKitRuntime } from "./runtime.js";
3
3
  import { AdapterEndpointOptions, ErrorTranslator, FromLayerPerRequestOptions, RequestRuntime, RequestRuntimeProvider, RequestRuntimeRunners, RuntimeAdapter, RuntimeAdapterOptions, makeRuntimeAdapter } from "./request-runtime.js";
4
+ import { SvelteKitCookies, SvelteKitCookiesInterface, SvelteKitLocals, SvelteKitResponseHeaders, SvelteKitResponseHeadersInterface, requestServicesLayer } from "./request-services.js";
4
5
  import { RequestRuntime as RequestRuntime$1, ServerRuntime, ServerRuntimeOptions, SvelteKitServices, makeServerRuntime } from "./app-runtime.js";
5
6
  import { sveltekitConfig, sveltekitConfigProvider } from "./config.js";
6
7
  import { Hooks_d_exports } from "./server/Hooks.js";
7
8
  import { Endpoint_d_exports } from "./server/Endpoint.js";
8
9
  import { RemoteFunction } from "./remote.server.js";
10
+ import { ParseError, ParseIssue, decodeFormData, decodeJson, decodeParams, decodeSearchParams } from "./request-parse.js";
11
+ import { defer } from "./streamed-load.js";
12
+ import { KitActionFailure, KitError, KitErrorTranslator, KitRedirect, KitTranslatorCase, SvelteKit } from "./kit.js";
9
13
  import { Effect } from "effect";
10
14
  import { Action, Actions, RequestEvent, ServerLoad } from "@sveltejs/kit";
11
15
 
@@ -106,5 +110,5 @@ declare function endpoint<Params extends SvelteRouteParams = SvelteRouteParams,
106
110
  exposeErrors?: boolean;
107
111
  }): (event: RequestEvent<Params, RouteId>) => Promise<Response>;
108
112
  //#endregion
109
- 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 };
113
+ export { type AdapterEndpointOptions, Endpoint_d_exports as Endpoint, type ErrorTranslator, type FromLayerPerRequestOptions, Hooks_d_exports as Hooks, KitActionFailure, KitError, type KitErrorTranslator, KitRedirect, type KitTranslatorCase, ParseError, type ParseIssue, RemoteFunction, RequestRuntime, type RequestRuntimeProvider, type RequestRuntimeRunners, type RuntimeAdapter, type RuntimeAdapterOptions, type RequestRuntime$1 as ScopedRequestRuntime, type ServerRuntime, type ServerRuntimeOptions, SvelteKit, SvelteKitCookies, type SvelteKitCookiesInterface, SvelteKitLocals, SvelteKitRequestEvent, SvelteKitResponseHeaders, type SvelteKitResponseHeadersInterface, SvelteKitRuntime, type SvelteKitServices, action, actions, decodeFormData, decodeJson, decodeParams, decodeSearchParams, defer, endpoint, makeRuntimeAdapter, makeServerRuntime, requestServicesLayer, runWithSvelteKitRuntime, serverLoad, sveltekitConfig, sveltekitConfigProvider };
110
114
  //# sourceMappingURL=server.d.ts.map
package/dist/server.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { SvelteKitRequestEvent, SvelteKitRuntime, runWithSvelteKitRuntime } from "./runtime.js";
2
+ import { KitActionFailure, KitError, KitRedirect, SvelteKit } from "./kit.js";
3
+ import { SvelteKitCookies, SvelteKitLocals, SvelteKitResponseHeaders, requestServicesLayer } from "./request-services.js";
2
4
  import { makeServerRuntime } from "./app-runtime.js";
3
5
  import { defaultServerRuntime } from "./internal/default-runtime.js";
4
6
  import { sveltekitConfig, sveltekitConfigProvider } from "./config.js";
@@ -6,6 +8,8 @@ import { Hooks_exports } from "./server/Hooks.js";
6
8
  import { Endpoint_exports } from "./server/Endpoint.js";
7
9
  import { RemoteFunction } from "./remote.server.js";
8
10
  import { RequestRuntime, makeRuntimeAdapter } from "./request-runtime.js";
11
+ import { ParseError, decodeFormData, decodeJson, decodeParams, decodeSearchParams } from "./request-parse.js";
12
+ import { defer } from "./streamed-load.js";
9
13
  import { Cause, Exit } from "effect";
10
14
  import { isHttpError, isRedirect } from "@sveltejs/kit";
11
15
  //#region src/server.ts
@@ -127,6 +131,6 @@ function endpoint(fn, options) {
127
131
  };
128
132
  }
129
133
  //#endregion
130
- export { Endpoint_exports as Endpoint, Hooks_exports as Hooks, RemoteFunction, RequestRuntime, SvelteKitRequestEvent, SvelteKitRuntime, action, actions, endpoint, makeRuntimeAdapter, makeServerRuntime, runWithSvelteKitRuntime, serverLoad, sveltekitConfig, sveltekitConfigProvider };
134
+ export { Endpoint_exports as Endpoint, Hooks_exports as Hooks, KitActionFailure, KitError, KitRedirect, ParseError, RemoteFunction, RequestRuntime, SvelteKit, SvelteKitCookies, SvelteKitLocals, SvelteKitRequestEvent, SvelteKitResponseHeaders, SvelteKitRuntime, action, actions, decodeFormData, decodeJson, decodeParams, decodeSearchParams, defer, endpoint, makeRuntimeAdapter, makeServerRuntime, requestServicesLayer, runWithSvelteKitRuntime, serverLoad, sveltekitConfig, sveltekitConfigProvider };
131
135
 
132
136
  //# 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 type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport { defaultServerRuntime as runtime } from \"./internal/default-runtime.js\";\n\nexport type { SvelteKitServices };\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit server load function.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with {@link makeServerRuntime} and use its `serverLoad`\n * instead.\n *\n * @example\n * ```ts\n * import { serverLoad } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return { data: yield* response.json };\n * })\n * );\n * ```\n */\nexport function serverLoad<\n Params extends SvelteRouteParams = SvelteRouteParams,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, SvelteKitServices>,\n): ServerLoad<Params, ParentData, OutputData, RouteId> {\n return runtime().serverLoad(fn);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit action handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { action } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = {\n * default: action((event) =>\n * Effect.gen(function* () {\n * const formData = yield* Effect.promise(() => event.request.formData());\n * // Process form data\n * return { success: true };\n * })\n * )\n * };\n * ```\n */\nexport function action<\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, SvelteKitServices>,\n): Action<Params, OutputData, RouteId> {\n return runtime().action(fn);\n}\n\n/**\n * Creates SvelteKit actions from an object of Effect-based action handlers.\n * Each action will have access to platform services via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { actions } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = actions({\n * create: (event) => Effect.succeed({ created: true }),\n * delete: (event) => Effect.succeed({ deleted: true })\n * });\n * ```\n */\nexport function actions<\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n actionsMap: Record<\n string,\n (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, SvelteKitServices>\n >,\n): Actions<Params, OutputData, RouteId> {\n return runtime().actions(actionsMap);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit API endpoint handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit. For any other failure, the error is logged\n * server-side and a generic error message is returned to clients. For\n * development/debugging, you can pass options to expose full error details.\n *\n * @example\n * ```ts\n * import { endpoint } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import { json } from '@sveltejs/kit';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const GET = endpoint((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return json(yield* response.json);\n * })\n * );\n * ```\n */\nexport function endpoint<\n Params extends SvelteRouteParams = SvelteRouteParams,\n RouteId extends string | null = any,\n>(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, SvelteKitServices>,\n options?: { exposeErrors?: boolean },\n): (event: RequestEvent<Params, RouteId>) => Promise<Response> {\n const { exposeErrors = false } = options ?? {};\n\n return async (event) => {\n const exit = await runtime().runPromiseExit(event as RequestEvent, fn(event));\n\n return Exit.match(exit, {\n onFailure: (cause) => {\n // Let SvelteKit handle its own control flow objects.\n const squashed = Cause.squash(cause);\n if (isRedirect(squashed) || isHttpError(squashed)) {\n throw squashed;\n }\n\n // Always log the full error server-side for debugging\n console.error(\"Endpoint error:\", Cause.pretty(cause));\n\n // Return generic error message to client unless explicitly enabled\n const clientMessage = exposeErrors ? Cause.pretty(cause) : \"Internal server error\";\n\n return new Response(JSON.stringify({ error: clientMessage }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n },\n onSuccess: (value) => value,\n });\n };\n}\n\n/**\n * Effect wrappers for the server-side hooks (`hooks.server.ts`): the `Hooks`\n * namespace exposes `handle`, `handleFetch`, `handleError`, and `init`.\n */\nexport * as Hooks from \"./server/Hooks.js\";\n\n/**\n * Create SvelteKit `+server.ts` endpoints backed by Effect: the `Endpoint`\n * namespace exposes `fromApi` (an `HttpApi`), `fromRouter` (an `HttpRouter`),\n * and `fromHttpEffect` (a single HTTP route effect).\n */\nexport * as Endpoint from \"./server/Endpoint.js\";\n\n/**\n * App-wide runtime: define your application's Effect services once and share\n * them across every request.\n */\nexport {\n makeServerRuntime,\n type ServerRuntime,\n type ServerRuntimeOptions,\n type RequestRuntime as ScopedRequestRuntime,\n} from \"./app-runtime.js\";\n\n/**\n * Export the SvelteKit runtime utilities for advanced use cases.\n */\nexport { SvelteKitRuntime, runWithSvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nexport { RemoteFunction } from \"./remote.server.js\";\n\n/**\n * A general, Effect-idiomatic seam for *how the per-request execution context is\n * obtained*. {@link RequestRuntime} constructs a {@link RequestRuntimeProvider}\n * (from a consumer-owned runtime on `event.locals`, a package-built\n * per-request runtime, a build-once `ManagedRuntime`, or a `Layer`), and\n * {@link makeRuntimeAdapter} binds the `serverLoad` / `action` / `actions` /\n * `endpoint` wrappers to it — so the handler effect's services `R` are your own\n * application catalogue, not the fixed {@link SvelteKitServices}. A\n * {@link RuntimeAdapterOptions.translateError} maps a failed `Cause` to\n * SvelteKit control flow at the boundary.\n */\nexport {\n RequestRuntime,\n makeRuntimeAdapter,\n type RequestRuntimeProvider,\n type RequestRuntimeRunners,\n type RuntimeAdapter,\n type RuntimeAdapterOptions,\n type AdapterEndpointOptions,\n type FromLayerPerRequestOptions,\n type ErrorTranslator,\n} from \"./request-runtime.js\";\n\n/**\n * SvelteKit `$env` ⇄ Effect `Config` (see {@link sveltekitConfig}). The\n * Effect Schema ⇄ SvelteKit `transport` hook lives on the universal `Hooks`\n * namespace (`Hooks.transport`) in `@thomasfosterau/effect-sveltekit`.\n */\nexport { sveltekitConfig, sveltekitConfigProvider } from \"./config.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,WAMd,IAGqD;CACrD,OAAOA,qBAAQ,EAAE,WAAW,EAAE;AAChC;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,OAKd,IAGqC;CACrC,OAAOA,qBAAQ,EAAE,OAAO,EAAE;AAC5B;;;;;;;;;;;;;;;;AAiBA,SAAgB,QAKd,YAIsC;CACtC,OAAOA,qBAAQ,EAAE,QAAQ,UAAU;AACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,SAId,IACA,SAC6D;CAC7D,MAAM,EAAE,eAAe,UAAU,WAAW,CAAC;CAE7C,OAAO,OAAO,UAAU;EACtB,MAAM,OAAO,MAAMA,qBAAQ,EAAE,eAAe,OAAuB,GAAG,KAAK,CAAC;EAE5E,OAAO,KAAK,MAAM,MAAM;GACtB,YAAY,UAAU;IAEpB,MAAM,WAAW,MAAM,OAAO,KAAK;IACnC,IAAI,WAAW,QAAQ,KAAK,YAAY,QAAQ,GAC9C,MAAM;IAIR,QAAQ,MAAM,mBAAmB,MAAM,OAAO,KAAK,CAAC;IAGpD,MAAM,gBAAgB,eAAe,MAAM,OAAO,KAAK,IAAI;IAE3D,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,cAAc,CAAC,GAAG;KAC5D,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;IAChD,CAAC;GACH;GACA,YAAY,UAAU;EACxB,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"server.js","names":["runtime"],"sources":["../src/server.ts"],"sourcesContent":["import type { Action, Actions, RequestEvent, ServerLoad } from \"@sveltejs/kit\";\nimport { Effect, Exit, Cause } from \"effect\";\nimport { isHttpError, isRedirect } from \"@sveltejs/kit\";\nimport type { SvelteKitServices } from \"./app-runtime.js\";\nimport type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport { defaultServerRuntime as runtime } from \"./internal/default-runtime.js\";\n\nexport type { SvelteKitServices };\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit server load function.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with {@link makeServerRuntime} and use its `serverLoad`\n * instead.\n *\n * @example\n * ```ts\n * import { serverLoad } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return { data: yield* response.json };\n * })\n * );\n * ```\n */\nexport function serverLoad<\n Params extends SvelteRouteParams = SvelteRouteParams,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, SvelteKitServices>,\n): ServerLoad<Params, ParentData, OutputData, RouteId> {\n return runtime().serverLoad(fn);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit action handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { action } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = {\n * default: action((event) =>\n * Effect.gen(function* () {\n * const formData = yield* Effect.promise(() => event.request.formData());\n * // Process form data\n * return { success: true };\n * })\n * )\n * };\n * ```\n */\nexport function action<\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, SvelteKitServices>,\n): Action<Params, OutputData, RouteId> {\n return runtime().action(fn);\n}\n\n/**\n * Creates SvelteKit actions from an object of Effect-based action handlers.\n * Each action will have access to platform services via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { actions } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = actions({\n * create: (event) => Effect.succeed({ created: true }),\n * delete: (event) => Effect.succeed({ deleted: true })\n * });\n * ```\n */\nexport function actions<\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n actionsMap: Record<\n string,\n (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, SvelteKitServices>\n >,\n): Actions<Params, OutputData, RouteId> {\n return runtime().actions(actionsMap);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit API endpoint handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit. For any other failure, the error is logged\n * server-side and a generic error message is returned to clients. For\n * development/debugging, you can pass options to expose full error details.\n *\n * @example\n * ```ts\n * import { endpoint } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import { json } from '@sveltejs/kit';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const GET = endpoint((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return json(yield* response.json);\n * })\n * );\n * ```\n */\nexport function endpoint<\n Params extends SvelteRouteParams = SvelteRouteParams,\n RouteId extends string | null = any,\n>(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, SvelteKitServices>,\n options?: { exposeErrors?: boolean },\n): (event: RequestEvent<Params, RouteId>) => Promise<Response> {\n const { exposeErrors = false } = options ?? {};\n\n return async (event) => {\n const exit = await runtime().runPromiseExit(event as RequestEvent, fn(event));\n\n return Exit.match(exit, {\n onFailure: (cause) => {\n // Let SvelteKit handle its own control flow objects.\n const squashed = Cause.squash(cause);\n if (isRedirect(squashed) || isHttpError(squashed)) {\n throw squashed;\n }\n\n // Always log the full error server-side for debugging\n console.error(\"Endpoint error:\", Cause.pretty(cause));\n\n // Return generic error message to client unless explicitly enabled\n const clientMessage = exposeErrors ? Cause.pretty(cause) : \"Internal server error\";\n\n return new Response(JSON.stringify({ error: clientMessage }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n },\n onSuccess: (value) => value,\n });\n };\n}\n\n/**\n * Effect wrappers for the server-side hooks (`hooks.server.ts`): the `Hooks`\n * namespace exposes `handle`, `handleFetch`, `handleError`, and `init`.\n */\nexport * as Hooks from \"./server/Hooks.js\";\n\n/**\n * Create SvelteKit `+server.ts` endpoints backed by Effect: the `Endpoint`\n * namespace exposes `fromApi` (an `HttpApi`), `fromRouter` (an `HttpRouter`),\n * and `fromHttpEffect` (a single HTTP route effect).\n */\nexport * as Endpoint from \"./server/Endpoint.js\";\n\n/**\n * App-wide runtime: define your application's Effect services once and share\n * them across every request.\n */\nexport {\n makeServerRuntime,\n type ServerRuntime,\n type ServerRuntimeOptions,\n type RequestRuntime as ScopedRequestRuntime,\n} from \"./app-runtime.js\";\n\n/**\n * Export the SvelteKit runtime utilities for advanced use cases.\n */\nexport { SvelteKitRuntime, runWithSvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nexport { RemoteFunction } from \"./remote.server.js\";\n\n/**\n * A general, Effect-idiomatic seam for *how the per-request execution context is\n * obtained*. {@link RequestRuntime} constructs a {@link RequestRuntimeProvider}\n * (from a consumer-owned runtime on `event.locals`, a package-built\n * per-request runtime, a build-once `ManagedRuntime`, or a `Layer`), and\n * {@link makeRuntimeAdapter} binds the `serverLoad` / `action` / `actions` /\n * `endpoint` wrappers to it — so the handler effect's services `R` are your own\n * application catalogue, not the fixed {@link SvelteKitServices}. A\n * {@link RuntimeAdapterOptions.translateError} maps a failed `Cause` to\n * SvelteKit control flow at the boundary.\n */\nexport {\n RequestRuntime,\n makeRuntimeAdapter,\n type RequestRuntimeProvider,\n type RequestRuntimeRunners,\n type RuntimeAdapter,\n type RuntimeAdapterOptions,\n type AdapterEndpointOptions,\n type FromLayerPerRequestOptions,\n type ErrorTranslator,\n} from \"./request-runtime.js\";\n\n/**\n * SvelteKit `$env` ⇄ Effect `Config` (see {@link sveltekitConfig}). The\n * Effect Schema ⇄ SvelteKit `transport` hook lives on the universal `Hooks`\n * namespace (`Hooks.transport`) in `@thomasfosterau/effect-sveltekit`.\n */\nexport { sveltekitConfig, sveltekitConfigProvider } from \"./config.js\";\n\n/**\n * Schema-driven request parsing. Each helper reads a part of the current\n * request (route params, search params, JSON body, or form data), validates it\n * against an Effect `Schema` or Standard Schema V1 validator, and returns the\n * typed output. A validation failure surfaces as {@link ParseError}.\n *\n * All helpers require `SvelteKitRequestEvent` in the environment and compose\n * naturally inside `serverLoad` / `action` / `endpoint` handlers.\n */\nexport {\n decodeParams,\n decodeSearchParams,\n decodeJson,\n decodeFormData,\n ParseError,\n type ParseIssue,\n} from \"./request-parse.js\";\n\n/**\n * Streamed load helper. {@link defer} captures the current service context and\n * runs a slow Effect in the background, returning the `Promise` so SvelteKit\n * can stream the result to the browser while sending the initial HTML\n * immediately. Use inside `serverLoad` alongside eagerly-computed fields.\n */\nexport { defer } from \"./streamed-load.js\";\n\n/**\n * Effect-native SvelteKit control-flow helpers and the default boundary\n * translator.\n *\n * - `SvelteKit.redirect` / `SvelteKit.error` / `SvelteKit.fail` — yield typed\n * Effect failures instead of throwing SvelteKit control-flow objects.\n * - `SvelteKit.translate` — the default `translateError` wired into every\n * server wrapper; maps the Kit error types to `redirect()` / `error()` /\n * `fail()` at the request boundary.\n * - `SvelteKit.makeTranslator([...cases])` — compose `translate` with your own\n * domain-error matchers (consumer cases run before the Kit base cases).\n * - `KitRedirect`, `KitError`, `KitActionFailure` — the tagged error classes\n * for use with `Effect.catchTag`, `instanceof`, etc.\n * - `KitTranslatorCase` — the argument type for `makeTranslator`.\n */\nexport {\n SvelteKit,\n KitRedirect,\n KitError,\n KitActionFailure,\n type KitTranslatorCase,\n type KitErrorTranslator,\n} from \"./kit.js\";\n\n/**\n * Effect-native services for the three per-request surfaces of a SvelteKit\n * `RequestEvent`: cookies, response headers, and locals.\n *\n * Each is a tagged `Context.Service` that is provided automatically in every\n * server handler effect (part of {@link SvelteKitServices}). Use\n * `yield* SvelteKitCookies` / `yield* SvelteKitResponseHeaders` /\n * `yield* SvelteKitLocals` to access them; stub them with `Layer.succeed` in\n * tests without a real `RequestEvent`.\n */\nexport {\n SvelteKitCookies,\n SvelteKitResponseHeaders,\n SvelteKitLocals,\n requestServicesLayer,\n type SvelteKitCookiesInterface,\n type SvelteKitResponseHeadersInterface,\n} from \"./request-services.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,WAMd,IAGqD;CACrD,OAAOA,qBAAQ,EAAE,WAAW,EAAE;AAChC;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,OAKd,IAGqC;CACrC,OAAOA,qBAAQ,EAAE,OAAO,EAAE;AAC5B;;;;;;;;;;;;;;;;AAiBA,SAAgB,QAKd,YAIsC;CACtC,OAAOA,qBAAQ,EAAE,QAAQ,UAAU;AACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,SAId,IACA,SAC6D;CAC7D,MAAM,EAAE,eAAe,UAAU,WAAW,CAAC;CAE7C,OAAO,OAAO,UAAU;EACtB,MAAM,OAAO,MAAMA,qBAAQ,EAAE,eAAe,OAAuB,GAAG,KAAK,CAAC;EAE5E,OAAO,KAAK,MAAM,MAAM;GACtB,YAAY,UAAU;IAEpB,MAAM,WAAW,MAAM,OAAO,KAAK;IACnC,IAAI,WAAW,QAAQ,KAAK,YAAY,QAAQ,GAC9C,MAAM;IAIR,QAAQ,MAAM,mBAAmB,MAAM,OAAO,KAAK,CAAC;IAGpD,MAAM,gBAAgB,eAAe,MAAM,OAAO,KAAK,IAAI;IAE3D,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,cAAc,CAAC,GAAG;KAC5D,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;IAChD,CAAC;GACH;GACA,YAAY,UAAU;EACxB,CAAC;CACH;AACF"}
@@ -0,0 +1,23 @@
1
+ import { Effect } from "effect";
2
+
3
+ //#region src/streamed-load.d.ts
4
+ /**
5
+ * Capture the current service context and run `effect` in the background,
6
+ * returning the `Promise` immediately so SvelteKit can stream the result.
7
+ *
8
+ * The deferred effect runs with exactly the same services as the parent
9
+ * `serverLoad` handler — you can `yield*` the same application services.
10
+ *
11
+ * @param effect - The slow Effect to run in the background. Failures cause the
12
+ * returned Promise to reject; wrap with `Effect.orElse` for a fallback.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const commentsPromise = yield* defer(fetchComments(id));
17
+ * return { title, comments: commentsPromise };
18
+ * ```
19
+ */
20
+ declare const defer: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<Promise<A>, never, R>;
21
+ //#endregion
22
+ export { defer };
23
+ //# sourceMappingURL=streamed-load.d.ts.map
@@ -0,0 +1,74 @@
1
+ import { Effect } from "effect";
2
+ //#region src/streamed-load.ts
3
+ /**
4
+ * Helpers for SvelteKit's streamed (deferred-promise) `serverLoad` data.
5
+ *
6
+ * SvelteKit can stream fields of a `load` return value to the browser as they
7
+ * resolve when you return a `Promise` alongside eagerly-computed fields. This
8
+ * module provides {@link defer} — an Effect that captures the current service
9
+ * context and launches the slow effect in the background, handing SvelteKit
10
+ * the resulting `Promise`.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { serverLoad, defer } from '@thomasfosterau/effect-sveltekit/server';
15
+ * import { Effect } from 'effect';
16
+ *
17
+ * export const load = serverLoad((event) =>
18
+ * Effect.gen(function* () {
19
+ * // Eager: resolved before the initial HTML is sent.
20
+ * const title = yield* fetchTitle(event.params.id);
21
+ *
22
+ * // Deferred: SvelteKit streams this to the client as it resolves.
23
+ * const commentsPromise = yield* defer(fetchComments(event.params.id));
24
+ *
25
+ * return { title, comments: commentsPromise };
26
+ * }),
27
+ * );
28
+ * ```
29
+ *
30
+ * In `+page.svelte`, consume the deferred field with `{#await}`:
31
+ *
32
+ * ```svelte
33
+ * <script>
34
+ * let { data } = $props();
35
+ * <\/script>
36
+ *
37
+ * <h1>{data.title}</h1>
38
+ *
39
+ * {#await data.comments}
40
+ * <p>Loading comments…</p>
41
+ * {:then comments}
42
+ * {#each comments as c}<p>{c.body}</p>{/each}
43
+ * {:catch}
44
+ * <p>Failed to load comments.</p>
45
+ * {/await}
46
+ * ```
47
+ *
48
+ * **Error handling.** If the deferred Effect fails, the promise rejects.
49
+ * SvelteKit surfaces the rejection in the `{:catch}` block of `{#await}` in
50
+ * the component — it does *not* throw the page into its error boundary. To
51
+ * provide a fallback value instead, apply `Effect.orElse` / `Effect.catchAll`
52
+ * before passing the effect to `defer`.
53
+ */
54
+ /**
55
+ * Capture the current service context and run `effect` in the background,
56
+ * returning the `Promise` immediately so SvelteKit can stream the result.
57
+ *
58
+ * The deferred effect runs with exactly the same services as the parent
59
+ * `serverLoad` handler — you can `yield*` the same application services.
60
+ *
61
+ * @param effect - The slow Effect to run in the background. Failures cause the
62
+ * returned Promise to reject; wrap with `Effect.orElse` for a fallback.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const commentsPromise = yield* defer(fetchComments(id));
67
+ * return { title, comments: commentsPromise };
68
+ * ```
69
+ */
70
+ const defer = (effect) => Effect.map(Effect.context(), (ctx) => Effect.runPromiseWith(ctx)(effect));
71
+ //#endregion
72
+ export { defer };
73
+
74
+ //# sourceMappingURL=streamed-load.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamed-load.js","names":[],"sources":["../src/streamed-load.ts"],"sourcesContent":["/**\n * Helpers for SvelteKit's streamed (deferred-promise) `serverLoad` data.\n *\n * SvelteKit can stream fields of a `load` return value to the browser as they\n * resolve when you return a `Promise` alongside eagerly-computed fields. This\n * module provides {@link defer} — an Effect that captures the current service\n * context and launches the slow effect in the background, handing SvelteKit\n * the resulting `Promise`.\n *\n * @example\n * ```ts\n * import { serverLoad, defer } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * // Eager: resolved before the initial HTML is sent.\n * const title = yield* fetchTitle(event.params.id);\n *\n * // Deferred: SvelteKit streams this to the client as it resolves.\n * const commentsPromise = yield* defer(fetchComments(event.params.id));\n *\n * return { title, comments: commentsPromise };\n * }),\n * );\n * ```\n *\n * In `+page.svelte`, consume the deferred field with `{#await}`:\n *\n * ```svelte\n * <script>\n * let { data } = $props();\n * </script>\n *\n * <h1>{data.title}</h1>\n *\n * {#await data.comments}\n * <p>Loading comments…</p>\n * {:then comments}\n * {#each comments as c}<p>{c.body}</p>{/each}\n * {:catch}\n * <p>Failed to load comments.</p>\n * {/await}\n * ```\n *\n * **Error handling.** If the deferred Effect fails, the promise rejects.\n * SvelteKit surfaces the rejection in the `{:catch}` block of `{#await}` in\n * the component — it does *not* throw the page into its error boundary. To\n * provide a fallback value instead, apply `Effect.orElse` / `Effect.catchAll`\n * before passing the effect to `defer`.\n */\n\nimport { Effect } from \"effect\";\n\n/**\n * Capture the current service context and run `effect` in the background,\n * returning the `Promise` immediately so SvelteKit can stream the result.\n *\n * The deferred effect runs with exactly the same services as the parent\n * `serverLoad` handler — you can `yield*` the same application services.\n *\n * @param effect - The slow Effect to run in the background. Failures cause the\n * returned Promise to reject; wrap with `Effect.orElse` for a fallback.\n *\n * @example\n * ```ts\n * const commentsPromise = yield* defer(fetchComments(id));\n * return { title, comments: commentsPromise };\n * ```\n */\nexport const defer = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<Promise<A>, never, R> =>\n Effect.map(Effect.context<R>(), (ctx) => Effect.runPromiseWith(ctx)(effect));\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,MAAa,SACX,WAEA,OAAO,IAAI,OAAO,QAAW,IAAI,QAAQ,OAAO,eAAe,GAAG,EAAE,MAAM,CAAC"}