@thomasfosterau/effect-sveltekit 0.1.1 → 0.2.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.
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":"enhance.js","names":[],"sources":["../../src/internal/enhance.ts"],"sourcesContent":["/**\n * The runtime-agnostic core of the `use:enhance` bridge, kept free of any\n * `$app/*` import so it can be unit-tested outside a SvelteKit app build.\n */\n\nimport type { SubmitFunction } from \"@sveltejs/kit\";\nimport { Context, Effect, ManagedRuntime } from \"effect\";\n\n/** The input SvelteKit passes to a `SubmitFunction` before the request is sent. */\nexport type SubmitInput = Parameters<SubmitFunction>[0];\n\n/** The result callback a `SubmitFunction` may return, run once the action responds. */\ntype ResultCallback = Extract<Awaited<ReturnType<SubmitFunction>>, (...args: never) => unknown>;\n\n/** The options passed to the result callback (`result`, `update`, ...). */\nexport type ResultOptions = Parameters<ResultCallback>[0];\n\n/** A runtime the handler effects can run against (a `Context` or `ManagedRuntime`). */\nexport type EnhanceRuntime<R> = Context.Context<R> | ManagedRuntime.ManagedRuntime<R, never>;\n\n/** Effect-based handlers for the two phases of an enhanced form submission. */\nexport interface EnhanceHandlers<R = never> {\n /**\n * Runs before the request is sent. Call `input.cancel()` to abort the\n * submission (the result phase then never runs). Use for optimistic UI or\n * validation.\n */\n readonly onSubmit?: (input: SubmitInput) => Effect.Effect<void, never, R>;\n /**\n * Runs once the action responds, with the `ActionResult` and SvelteKit's\n * `update()` (which applies the default behaviour — updating `page.form`,\n * resetting the form, invalidating). If omitted, SvelteKit's default\n * behaviour applies automatically.\n */\n readonly onResult?: (options: ResultOptions) => Effect.Effect<void, never, R>;\n}\n\n/** Options for {@link makeSubmitFunction}. */\nexport interface EnhanceOptions<R = never> {\n /**\n * The runtime to run the handler effects against. Omit when the handlers\n * require no services (`R = never`); they then run on the default runtime.\n */\n readonly runtime?: EnhanceRuntime<R>;\n}\n\n/** Run an `Effect<A, never, R>` on the given runtime (or the default when none). */\nconst runPromiseWith =\n <R>(runtime: EnhanceRuntime<R> | undefined) =>\n <A>(effect: Effect.Effect<A, never, R>): Promise<A> => {\n if (runtime === undefined) return Effect.runPromise(effect as Effect.Effect<A, never, never>);\n return Context.isContext(runtime)\n ? Effect.runPromiseWith(runtime)(effect)\n : runtime.runPromise(effect);\n };\n\n/**\n * Build a SvelteKit `SubmitFunction` from Effect-based handlers. See\n * `Forms.enhance` for the public entry point and documentation.\n */\nexport const makeSubmitFunction = <R = never>(\n handlers: EnhanceHandlers<R>,\n options?: EnhanceOptions<R>,\n): SubmitFunction => {\n const run = runPromiseWith(options?.runtime);\n return async (input) => {\n if (handlers.onSubmit) await run(handlers.onSubmit(input));\n // Returning `undefined` lets SvelteKit apply its default result behaviour.\n if (!handlers.onResult) return undefined;\n const onResult = handlers.onResult;\n return (resultOptions) => run(onResult(resultOptions));\n };\n};\n"],"mappings":";;;AA+CA,MAAM,kBACA,aACA,WAAmD;CACrD,IAAI,YAAY,KAAA,GAAW,OAAO,OAAO,WAAW,MAAwC;CAC5F,OAAO,QAAQ,UAAU,OAAO,IAC5B,OAAO,eAAe,OAAO,EAAE,MAAM,IACrC,QAAQ,WAAW,MAAM;AAC/B;;;;;AAMF,MAAa,sBACX,UACA,YACmB;CACnB,MAAM,MAAM,eAAe,SAAS,OAAO;CAC3C,OAAO,OAAO,UAAU;EACtB,IAAI,SAAS,UAAU,MAAM,IAAI,SAAS,SAAS,KAAK,CAAC;EAEzD,IAAI,CAAC,SAAS,UAAU,OAAO,KAAA;EAC/B,MAAM,WAAW,SAAS;EAC1B,QAAQ,kBAAkB,IAAI,SAAS,aAAa,CAAC;CACvD;AACF"}
@@ -12,6 +12,17 @@ type FormErrorMapper = (error: unknown, ctx: {
12
12
  readonly issue: InvalidField<any>;
13
13
  readonly data: unknown;
14
14
  }) => never;
15
+ /** Runs a remote handler's effect and settles it (value or thrown via translator). */
16
+ type Runner = <A>(effect: Effect.Effect<A, unknown, any>) => Promise<A>;
17
+ /** Like {@link Runner}, but with the form's `issue` / `data` for error interop. */
18
+ type FormRunner = <A>(effect: Effect.Effect<A, unknown, any>, ctx: {
19
+ readonly issue: InvalidField<any>;
20
+ readonly data: unknown;
21
+ }) => Promise<A>;
22
+ interface RunHelpers {
23
+ readonly run: Runner;
24
+ readonly runForm: FormRunner;
25
+ }
15
26
  //#endregion
16
- export { FormErrorMapper };
27
+ export { FormErrorMapper, RunHelpers };
17
28
  //# sourceMappingURL=remote-run.d.ts.map
@@ -1,10 +1,8 @@
1
- import { causeToThrowable } from "./error.js";
1
+ import { SvelteKit } from "../kit.js";
2
2
  import { Cause, Exit } from "effect";
3
3
  //#region src/internal/remote-run.ts
4
- /** The default translator: SvelteKit control flow through, anything else a 500. */
5
- const defaultTranslate = (cause) => {
6
- throw causeToThrowable(cause);
7
- };
4
+ /** The default translator: Kit control-flow errors through, anything else a 500. */
5
+ const defaultTranslate = SvelteKit.translate;
8
6
  /** Build the `run` / `runForm` helpers from an exit resolver and a translator. */
9
7
  const makeRunHelpers = (resolve, translate, formError) => {
10
8
  const run = (effect) => {
@@ -1 +1 @@
1
- {"version":3,"file":"remote-run.js","names":[],"sources":["../../src/internal/remote-run.ts"],"sourcesContent":["/**\n * Run/settle helpers for the remote-function wrappers, factored out so they can\n * be unit-tested without importing `$app/server` (a SvelteKit virtual module\n * unavailable outside an app build). The `$app/server`-dependent resolvers live\n * in `remote.server.ts`; this module only deals in `Exit`s.\n */\n\nimport type { InvalidField } from \"@sveltejs/kit\";\nimport { Cause, type Effect, Exit } from \"effect\";\nimport { causeToThrowable } from \"./error.js\";\nimport type { ErrorTranslator } from \"../request-runtime.js\";\n\n/** The default translator: SvelteKit control flow through, anything else a 500. */\nexport const defaultTranslate: ErrorTranslator = (cause) => {\n throw causeToThrowable(cause);\n};\n\n/**\n * Maps a remote *form* handler's failure into SvelteKit's field issues, given\n * the form's parsed `data` and the `issue` helper. It may call `invalid()` so a\n * structured/nested validation error drives field-level invalidation instead of\n * a generic failure. Receives the squashed handler failure.\n */\nexport type FormErrorMapper = (\n error: unknown,\n ctx: { readonly issue: InvalidField<any>; readonly data: unknown },\n) => never;\n\n/** Runs a remote handler's effect and settles it (value or thrown via translator). */\nexport type Runner = <A>(effect: Effect.Effect<A, unknown, any>) => Promise<A>;\n\n/** Like {@link Runner}, but with the form's `issue` / `data` for error interop. */\nexport type FormRunner = <A>(\n effect: Effect.Effect<A, unknown, any>,\n ctx: { readonly issue: InvalidField<any>; readonly data: unknown },\n) => Promise<A>;\n\nexport interface RunHelpers {\n readonly run: Runner;\n readonly runForm: FormRunner;\n}\n\n/** How to obtain an `Exit` for the current request, plus optional teardown. */\nexport type ExitResolver = () => {\n readonly runExit: <A>(effect: Effect.Effect<A, unknown, any>) => Promise<Exit.Exit<A, unknown>>;\n readonly dispose?: () => void;\n};\n\n/** Build the `run` / `runForm` helpers from an exit resolver and a translator. */\nexport const makeRunHelpers = (\n resolve: ExitResolver,\n translate: ErrorTranslator,\n formError?: FormErrorMapper,\n): RunHelpers => {\n const run: Runner = (effect) => {\n const { runExit, dispose } = resolve();\n return runExit(effect).then((exit) => {\n try {\n return Exit.isSuccess(exit) ? exit.value : translate(exit.cause);\n } finally {\n dispose?.();\n }\n });\n };\n const runForm: FormRunner = (effect, ctx) => {\n const { runExit, dispose } = resolve();\n return runExit(effect).then((exit) => {\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n return formError ? formError(Cause.squash(exit.cause), ctx) : translate(exit.cause);\n } finally {\n dispose?.();\n }\n });\n };\n return { run, runForm };\n};\n"],"mappings":";;;;AAaA,MAAa,oBAAqC,UAAU;CAC1D,MAAM,iBAAiB,KAAK;AAC9B;;AAkCA,MAAa,kBACX,SACA,WACA,cACe;CACf,MAAM,OAAe,WAAW;EAC9B,MAAM,EAAE,SAAS,YAAY,QAAQ;EACrC,OAAO,QAAQ,MAAM,EAAE,MAAM,SAAS;GACpC,IAAI;IACF,OAAO,KAAK,UAAU,IAAI,IAAI,KAAK,QAAQ,UAAU,KAAK,KAAK;GACjE,UAAU;IACR,UAAU;GACZ;EACF,CAAC;CACH;CACA,MAAM,WAAuB,QAAQ,QAAQ;EAC3C,MAAM,EAAE,SAAS,YAAY,QAAQ;EACrC,OAAO,QAAQ,MAAM,EAAE,MAAM,SAAS;GACpC,IAAI;IACF,IAAI,KAAK,UAAU,IAAI,GAAG,OAAO,KAAK;IACtC,OAAO,YAAY,UAAU,MAAM,OAAO,KAAK,KAAK,GAAG,GAAG,IAAI,UAAU,KAAK,KAAK;GACpF,UAAU;IACR,UAAU;GACZ;EACF,CAAC;CACH;CACA,OAAO;EAAE;EAAK;CAAQ;AACxB"}
1
+ {"version":3,"file":"remote-run.js","names":[],"sources":["../../src/internal/remote-run.ts"],"sourcesContent":["/**\n * Run/settle helpers for the remote-function wrappers, factored out so they can\n * be unit-tested without importing `$app/server` (a SvelteKit virtual module\n * unavailable outside an app build). The `$app/server`-dependent resolvers live\n * in `remote.server.ts`; this module only deals in `Exit`s.\n */\n\nimport type { InvalidField } from \"@sveltejs/kit\";\nimport { Cause, type Effect, Exit } from \"effect\";\nimport type { ErrorTranslator } from \"../request-runtime.js\";\nimport { SvelteKit } from \"../kit.js\";\n\n/** The default translator: Kit control-flow errors through, anything else a 500. */\nexport const defaultTranslate: ErrorTranslator = SvelteKit.translate;\n\n/**\n * Maps a remote *form* handler's failure into SvelteKit's field issues, given\n * the form's parsed `data` and the `issue` helper. It may call `invalid()` so a\n * structured/nested validation error drives field-level invalidation instead of\n * a generic failure. Receives the squashed handler failure.\n */\nexport type FormErrorMapper = (\n error: unknown,\n ctx: { readonly issue: InvalidField<any>; readonly data: unknown },\n) => never;\n\n/** Runs a remote handler's effect and settles it (value or thrown via translator). */\nexport type Runner = <A>(effect: Effect.Effect<A, unknown, any>) => Promise<A>;\n\n/** Like {@link Runner}, but with the form's `issue` / `data` for error interop. */\nexport type FormRunner = <A>(\n effect: Effect.Effect<A, unknown, any>,\n ctx: { readonly issue: InvalidField<any>; readonly data: unknown },\n) => Promise<A>;\n\nexport interface RunHelpers {\n readonly run: Runner;\n readonly runForm: FormRunner;\n}\n\n/** How to obtain an `Exit` for the current request, plus optional teardown. */\nexport type ExitResolver = () => {\n readonly runExit: <A>(effect: Effect.Effect<A, unknown, any>) => Promise<Exit.Exit<A, unknown>>;\n readonly dispose?: () => void;\n};\n\n/** Build the `run` / `runForm` helpers from an exit resolver and a translator. */\nexport const makeRunHelpers = (\n resolve: ExitResolver,\n translate: ErrorTranslator,\n formError?: FormErrorMapper,\n): RunHelpers => {\n const run: Runner = (effect) => {\n const { runExit, dispose } = resolve();\n return runExit(effect).then((exit) => {\n try {\n return Exit.isSuccess(exit) ? exit.value : translate(exit.cause);\n } finally {\n dispose?.();\n }\n });\n };\n const runForm: FormRunner = (effect, ctx) => {\n const { runExit, dispose } = resolve();\n return runExit(effect).then((exit) => {\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n return formError ? formError(Cause.squash(exit.cause), ctx) : translate(exit.cause);\n } finally {\n dispose?.();\n }\n });\n };\n return { run, runForm };\n};\n"],"mappings":";;;;AAaA,MAAa,mBAAoC,UAAU;;AAkC3D,MAAa,kBACX,SACA,WACA,cACe;CACf,MAAM,OAAe,WAAW;EAC9B,MAAM,EAAE,SAAS,YAAY,QAAQ;EACrC,OAAO,QAAQ,MAAM,EAAE,MAAM,SAAS;GACpC,IAAI;IACF,OAAO,KAAK,UAAU,IAAI,IAAI,KAAK,QAAQ,UAAU,KAAK,KAAK;GACjE,UAAU;IACR,UAAU;GACZ;EACF,CAAC;CACH;CACA,MAAM,WAAuB,QAAQ,QAAQ;EAC3C,MAAM,EAAE,SAAS,YAAY,QAAQ;EACrC,OAAO,QAAQ,MAAM,EAAE,MAAM,SAAS;GACpC,IAAI;IACF,IAAI,KAAK,UAAU,IAAI,GAAG,OAAO,KAAK;IACtC,OAAO,YAAY,UAAU,MAAM,OAAO,KAAK,KAAK,GAAG,GAAG,IAAI,UAAU,KAAK,KAAK;GACpF,UAAU;IACR,UAAU;GACZ;EACF,CAAC;CACH;CACA,OAAO;EAAE;EAAK;CAAQ;AACxB"}
@@ -0,0 +1,37 @@
1
+ //#region src/internal/sse.ts
2
+ /**
3
+ * Server-Sent Events framing helpers, factored out of the endpoint wrapper so
4
+ * the wire format can be unit-tested in isolation.
5
+ *
6
+ * See the [SSE specification](https://html.spec.whatwg.org/multipage/server-sent-events.html).
7
+ */
8
+ /** The response headers required for a `text/event-stream` body. */
9
+ const SSE_HEADERS = {
10
+ "Content-Type": "text/event-stream",
11
+ "Cache-Control": "no-cache, no-transform",
12
+ Connection: "keep-alive"
13
+ };
14
+ /**
15
+ * Encode a single SSE event as its wire representation, terminated by the
16
+ * blank line that dispatches it.
17
+ *
18
+ * `data` is split on newlines into one `data:` line each (per the spec, a bare
19
+ * newline in a value would otherwise be ambiguous). `event` / `id` are emitted
20
+ * only when present.
21
+ */
22
+ const encodeSseFrame = (frame) => {
23
+ let out = "";
24
+ if (frame.id !== void 0) out += `id: ${frame.id}\n`;
25
+ if (frame.event !== void 0) out += `event: ${frame.event}\n`;
26
+ for (const line of frame.data.split("\n")) out += `data: ${line}\n`;
27
+ out += "\n";
28
+ return out;
29
+ };
30
+ /** Encode the one-shot `retry:` field that sets the client reconnection delay. */
31
+ const encodeRetry = (milliseconds) => `retry: ${milliseconds}\n\n`;
32
+ /** A comment line used as a heartbeat/keepalive (ignored by the client). */
33
+ const HEARTBEAT_FRAME = ": heartbeat\n\n";
34
+ //#endregion
35
+ export { HEARTBEAT_FRAME, SSE_HEADERS, encodeRetry, encodeSseFrame };
36
+
37
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","names":[],"sources":["../../src/internal/sse.ts"],"sourcesContent":["/**\n * Server-Sent Events framing helpers, factored out of the endpoint wrapper so\n * the wire format can be unit-tested in isolation.\n *\n * See the [SSE specification](https://html.spec.whatwg.org/multipage/server-sent-events.html).\n */\n\n/** The response headers required for a `text/event-stream` body. */\nexport const SSE_HEADERS: Readonly<Record<string, string>> = {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\",\n};\n\n/** The fields of a single SSE event. */\nexport interface SseFrame {\n /** The event payload. Multi-line strings are split into multiple `data:` lines. */\n readonly data: string;\n /** Optional event name (`event:` field). */\n readonly event?: string | undefined;\n /** Optional event id (`id:` field), which the browser echoes on reconnect. */\n readonly id?: string | undefined;\n}\n\n/**\n * Encode a single SSE event as its wire representation, terminated by the\n * blank line that dispatches it.\n *\n * `data` is split on newlines into one `data:` line each (per the spec, a bare\n * newline in a value would otherwise be ambiguous). `event` / `id` are emitted\n * only when present.\n */\nexport const encodeSseFrame = (frame: SseFrame): string => {\n let out = \"\";\n if (frame.id !== undefined) out += `id: ${frame.id}\\n`;\n if (frame.event !== undefined) out += `event: ${frame.event}\\n`;\n // Split into per-line `data:` fields so embedded newlines survive.\n for (const line of frame.data.split(\"\\n\")) out += `data: ${line}\\n`;\n out += \"\\n\";\n return out;\n};\n\n/** Encode the one-shot `retry:` field that sets the client reconnection delay. */\nexport const encodeRetry = (milliseconds: number): string => `retry: ${milliseconds}\\n\\n`;\n\n/** A comment line used as a heartbeat/keepalive (ignored by the client). */\nexport const HEARTBEAT_FRAME = \": heartbeat\\n\\n\";\n"],"mappings":";;;;;;;;AAQA,MAAa,cAAgD;CAC3D,gBAAgB;CAChB,iBAAiB;CACjB,YAAY;AACd;;;;;;;;;AAoBA,MAAa,kBAAkB,UAA4B;CACzD,IAAI,MAAM;CACV,IAAI,MAAM,OAAO,KAAA,GAAW,OAAO,OAAO,MAAM,GAAG;CACnD,IAAI,MAAM,UAAU,KAAA,GAAW,OAAO,UAAU,MAAM,MAAM;CAE5D,KAAK,MAAM,QAAQ,MAAM,KAAK,MAAM,IAAI,GAAG,OAAO,SAAS,KAAK;CAChE,OAAO;CACP,OAAO;AACT;;AAGA,MAAa,eAAe,iBAAiC,UAAU,aAAa;;AAGpF,MAAa,kBAAkB"}
package/dist/kit.d.ts ADDED
@@ -0,0 +1,130 @@
1
+ import { Cause, Effect } from "effect";
2
+
3
+ //#region src/kit.d.ts
4
+ /**
5
+ * An Effect-native redirect. Yield via {@link SvelteKit.redirect}; translated
6
+ * at the request boundary by {@link SvelteKit.translate}.
7
+ */
8
+ declare class KitRedirect extends Error {
9
+ readonly status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308;
10
+ readonly location: string | URL;
11
+ readonly _tag: "KitRedirect";
12
+ constructor(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308, location: string | URL);
13
+ }
14
+ /**
15
+ * An Effect-native HTTP error. Yield via {@link SvelteKit.error}; translated
16
+ * at the request boundary by {@link SvelteKit.translate}.
17
+ */
18
+ declare class KitError extends Error {
19
+ readonly status: number;
20
+ readonly body?: string | Record<string, unknown> | undefined;
21
+ readonly _tag: "KitError";
22
+ constructor(status: number, body?: string | Record<string, unknown> | undefined);
23
+ }
24
+ /**
25
+ * An Effect-native form-action failure. Yield via {@link SvelteKit.fail};
26
+ * translated at the request boundary by {@link SvelteKit.translate}.
27
+ */
28
+ declare class KitActionFailure<T extends Record<string, unknown> = Record<string, unknown>> extends Error {
29
+ readonly status: number;
30
+ readonly data: T;
31
+ readonly _tag: "KitActionFailure";
32
+ constructor(status: number, data: T);
33
+ }
34
+ /**
35
+ * A single domain-error case for {@link SvelteKit.makeTranslator}.
36
+ *
37
+ * Receives the squashed error value. Throw (e.g. `error()` / `redirect()`) to
38
+ * handle the case; return normally to pass through to the next case.
39
+ */
40
+ type KitTranslatorCase = (error: unknown) => undefined | void;
41
+ /**
42
+ * A boundary translator: turn a failed `Cause` into thrown SvelteKit control
43
+ * flow or an `ActionFailure` return value.
44
+ *
45
+ * The Kit base translator ({@link SvelteKit.translate}) handles
46
+ * {@link KitRedirect}, {@link KitError}, and {@link KitActionFailure}. Build a
47
+ * custom translator via {@link SvelteKit.makeTranslator}.
48
+ */
49
+ type KitErrorTranslator = (cause: Cause.Cause<unknown>) => never;
50
+ /**
51
+ * Effect-native SvelteKit control-flow helpers and the default boundary
52
+ * translator.
53
+ *
54
+ * **Error types**: {@link KitRedirect}, {@link KitError}, {@link KitActionFailure}
55
+ * are tagged Effect failures — use them with `Effect.catchTag`, `Match.tag`,
56
+ * and `instanceof` checks.
57
+ *
58
+ * **Default translator**: `SvelteKit.translate` is wired in as the default
59
+ * `translateError` for every server wrapper (`serverLoad`, `action`,
60
+ * `makeRuntimeAdapter`, `RemoteFunction.withRuntime`, etc.).
61
+ */
62
+ declare const SvelteKit: {
63
+ /**
64
+ * Fail with a redirect. Equivalent to SvelteKit's `redirect()` but as an
65
+ * Effect failure instead of a thrown exception.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * yield* SvelteKit.redirect(303, "/login");
70
+ * ```
71
+ */
72
+ readonly redirect: (status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308, location: string | URL) => Effect.Effect<never, KitRedirect>;
73
+ /**
74
+ * Fail with an HTTP error. Equivalent to SvelteKit's `error()` but as an
75
+ * Effect failure instead of a thrown exception.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * yield* SvelteKit.error(404, "Not found");
80
+ * yield* SvelteKit.error(403, { message: "Forbidden" });
81
+ * ```
82
+ */
83
+ readonly error: (status: number, body?: string | Record<string, unknown>) => Effect.Effect<never, KitError>;
84
+ /**
85
+ * Fail with a form-action failure. Equivalent to SvelteKit's `fail()` but as
86
+ * an Effect failure instead of a return value.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * yield* SvelteKit.fail(422, { email: "already taken" });
91
+ * ```
92
+ */
93
+ readonly fail: <T extends Record<string, unknown>>(status: number, data: T) => Effect.Effect<never, KitActionFailure<T>>;
94
+ /**
95
+ * The turnkey boundary translator. Converts {@link KitRedirect} →
96
+ * `redirect()`, {@link KitError} → `error()`, {@link KitActionFailure} →
97
+ * `fail()`, and anything else → `EffectCauseError` (500).
98
+ *
99
+ * This is the default `translateError` for every server wrapper. To extend it
100
+ * with your own domain errors, use {@link SvelteKit.makeTranslator}.
101
+ */
102
+ readonly translate: KitErrorTranslator;
103
+ /**
104
+ * Build a custom boundary translator by prepending domain-error cases before
105
+ * the Kit base cases.
106
+ *
107
+ * Each case function receives the squashed error value. Throw (e.g. call
108
+ * `error()` / `redirect()` from `@sveltejs/kit`) to handle the case; return
109
+ * normally to pass through to the next case or the Kit base translator.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * import { error } from "@sveltejs/kit";
114
+ *
115
+ * const translateError = SvelteKit.makeTranslator([
116
+ * (e) => { if (e instanceof NotFound) error(404, "Not found"); },
117
+ * (e) => { if (e instanceof Forbidden) error(403, "Forbidden"); },
118
+ * ]);
119
+ *
120
+ * const { serverLoad } = makeRuntimeAdapter(
121
+ * RequestRuntime.fromLayer(AppLayer),
122
+ * { translateError },
123
+ * );
124
+ * ```
125
+ */
126
+ readonly makeTranslator: (extraCases: ReadonlyArray<KitTranslatorCase>) => KitErrorTranslator;
127
+ };
128
+ //#endregion
129
+ export { KitActionFailure, KitError, KitErrorTranslator, KitRedirect, KitTranslatorCase, SvelteKit };
130
+ //# sourceMappingURL=kit.d.ts.map
package/dist/kit.js ADDED
@@ -0,0 +1,185 @@
1
+ import { causeToThrowable } from "./internal/error.js";
2
+ import { Cause, Effect } from "effect";
3
+ import { error, fail, redirect } from "@sveltejs/kit";
4
+ //#region src/kit.ts
5
+ /**
6
+ * Effect-native SvelteKit control-flow errors and a turnkey boundary translator.
7
+ *
8
+ * Every server consumer previously needed to hand-roll their own `translateError`
9
+ * that mapped domain errors → `redirect()` / `error()` / `fail()`. This module
10
+ * ships the catalogue those consumers all reinvented:
11
+ *
12
+ * - {@link KitRedirect}, {@link KitError}, {@link KitActionFailure} — tagged
13
+ * Effect failure types for each SvelteKit control-flow call.
14
+ * - {@link SvelteKit.redirect}, {@link SvelteKit.error}, {@link SvelteKit.fail}
15
+ * — helpers that `yield*` a typed failure instead of throwing.
16
+ * - {@link SvelteKit.translate} — the base `ErrorTranslator` that converts those
17
+ * failures to the appropriate SvelteKit call at the request boundary.
18
+ * - {@link SvelteKit.makeTranslator} — compose `translate` with your own
19
+ * domain-error cases (consumer cases run first).
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { SvelteKit, makeRuntimeAdapter, RequestRuntime } from "@thomasfosterau/effect-sveltekit/server";
24
+ * import { Effect } from "effect";
25
+ *
26
+ * // Yield kit errors instead of throwing:
27
+ * const load = serverLoad(() =>
28
+ * Effect.gen(function* () {
29
+ * const user = yield* getUser;
30
+ * if (!user) yield* SvelteKit.redirect(303, "/login");
31
+ * if (!user.active) yield* SvelteKit.error(403, "Account disabled");
32
+ * return { user };
33
+ * })
34
+ * );
35
+ *
36
+ * // Compose with your own domain errors:
37
+ * import { error } from "@sveltejs/kit";
38
+ * const { serverLoad } = makeRuntimeAdapter(
39
+ * RequestRuntime.fromLayer(AppLayer),
40
+ * {
41
+ * translateError: SvelteKit.makeTranslator([
42
+ * (e) => { if (e instanceof NotFound) error(404, "Not found"); },
43
+ * ]),
44
+ * },
45
+ * );
46
+ * ```
47
+ */
48
+ /**
49
+ * An Effect-native redirect. Yield via {@link SvelteKit.redirect}; translated
50
+ * at the request boundary by {@link SvelteKit.translate}.
51
+ */
52
+ var KitRedirect = class extends Error {
53
+ status;
54
+ location;
55
+ _tag = "KitRedirect";
56
+ constructor(status, location) {
57
+ super(`Redirect to ${String(location)}`);
58
+ this.status = status;
59
+ this.location = location;
60
+ this.name = "KitRedirect";
61
+ }
62
+ };
63
+ /**
64
+ * An Effect-native HTTP error. Yield via {@link SvelteKit.error}; translated
65
+ * at the request boundary by {@link SvelteKit.translate}.
66
+ */
67
+ var KitError = class extends Error {
68
+ status;
69
+ body;
70
+ _tag = "KitError";
71
+ constructor(status, body) {
72
+ super(`HTTP ${status}`);
73
+ this.status = status;
74
+ this.body = body;
75
+ this.name = "KitError";
76
+ }
77
+ };
78
+ /**
79
+ * An Effect-native form-action failure. Yield via {@link SvelteKit.fail};
80
+ * translated at the request boundary by {@link SvelteKit.translate}.
81
+ */
82
+ var KitActionFailure = class extends Error {
83
+ status;
84
+ data;
85
+ _tag = "KitActionFailure";
86
+ constructor(status, data) {
87
+ super(`Action failure ${status}`);
88
+ this.status = status;
89
+ this.data = data;
90
+ this.name = "KitActionFailure";
91
+ }
92
+ };
93
+ const baseTranslate = (cause) => {
94
+ const squashed = Cause.squash(cause);
95
+ if (squashed instanceof KitRedirect) redirect(squashed.status, squashed.location);
96
+ if (squashed instanceof KitError) error(squashed.status, squashed.body);
97
+ if (squashed instanceof KitActionFailure) return fail(squashed.status, squashed.data);
98
+ throw causeToThrowable(cause);
99
+ };
100
+ /**
101
+ * Effect-native SvelteKit control-flow helpers and the default boundary
102
+ * translator.
103
+ *
104
+ * **Error types**: {@link KitRedirect}, {@link KitError}, {@link KitActionFailure}
105
+ * are tagged Effect failures — use them with `Effect.catchTag`, `Match.tag`,
106
+ * and `instanceof` checks.
107
+ *
108
+ * **Default translator**: `SvelteKit.translate` is wired in as the default
109
+ * `translateError` for every server wrapper (`serverLoad`, `action`,
110
+ * `makeRuntimeAdapter`, `RemoteFunction.withRuntime`, etc.).
111
+ */
112
+ const SvelteKit = {
113
+ /**
114
+ * Fail with a redirect. Equivalent to SvelteKit's `redirect()` but as an
115
+ * Effect failure instead of a thrown exception.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * yield* SvelteKit.redirect(303, "/login");
120
+ * ```
121
+ */
122
+ redirect: (status, location) => Effect.fail(new KitRedirect(status, location)),
123
+ /**
124
+ * Fail with an HTTP error. Equivalent to SvelteKit's `error()` but as an
125
+ * Effect failure instead of a thrown exception.
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * yield* SvelteKit.error(404, "Not found");
130
+ * yield* SvelteKit.error(403, { message: "Forbidden" });
131
+ * ```
132
+ */
133
+ error: (status, body) => Effect.fail(new KitError(status, body)),
134
+ /**
135
+ * Fail with a form-action failure. Equivalent to SvelteKit's `fail()` but as
136
+ * an Effect failure instead of a return value.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * yield* SvelteKit.fail(422, { email: "already taken" });
141
+ * ```
142
+ */
143
+ fail: (status, data) => Effect.fail(new KitActionFailure(status, data)),
144
+ /**
145
+ * The turnkey boundary translator. Converts {@link KitRedirect} →
146
+ * `redirect()`, {@link KitError} → `error()`, {@link KitActionFailure} →
147
+ * `fail()`, and anything else → `EffectCauseError` (500).
148
+ *
149
+ * This is the default `translateError` for every server wrapper. To extend it
150
+ * with your own domain errors, use {@link SvelteKit.makeTranslator}.
151
+ */
152
+ translate: baseTranslate,
153
+ /**
154
+ * Build a custom boundary translator by prepending domain-error cases before
155
+ * the Kit base cases.
156
+ *
157
+ * Each case function receives the squashed error value. Throw (e.g. call
158
+ * `error()` / `redirect()` from `@sveltejs/kit`) to handle the case; return
159
+ * normally to pass through to the next case or the Kit base translator.
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * import { error } from "@sveltejs/kit";
164
+ *
165
+ * const translateError = SvelteKit.makeTranslator([
166
+ * (e) => { if (e instanceof NotFound) error(404, "Not found"); },
167
+ * (e) => { if (e instanceof Forbidden) error(403, "Forbidden"); },
168
+ * ]);
169
+ *
170
+ * const { serverLoad } = makeRuntimeAdapter(
171
+ * RequestRuntime.fromLayer(AppLayer),
172
+ * { translateError },
173
+ * );
174
+ * ```
175
+ */
176
+ makeTranslator: (extraCases) => (cause) => {
177
+ const squashed = Cause.squash(cause);
178
+ for (const matcher of extraCases) matcher(squashed);
179
+ return baseTranslate(cause);
180
+ }
181
+ };
182
+ //#endregion
183
+ export { KitActionFailure, KitError, KitRedirect, SvelteKit };
184
+
185
+ //# sourceMappingURL=kit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kit.js","names":["kitFail"],"sources":["../src/kit.ts"],"sourcesContent":["/**\n * Effect-native SvelteKit control-flow errors and a turnkey boundary translator.\n *\n * Every server consumer previously needed to hand-roll their own `translateError`\n * that mapped domain errors → `redirect()` / `error()` / `fail()`. This module\n * ships the catalogue those consumers all reinvented:\n *\n * - {@link KitRedirect}, {@link KitError}, {@link KitActionFailure} — tagged\n * Effect failure types for each SvelteKit control-flow call.\n * - {@link SvelteKit.redirect}, {@link SvelteKit.error}, {@link SvelteKit.fail}\n * — helpers that `yield*` a typed failure instead of throwing.\n * - {@link SvelteKit.translate} — the base `ErrorTranslator` that converts those\n * failures to the appropriate SvelteKit call at the request boundary.\n * - {@link SvelteKit.makeTranslator} — compose `translate` with your own\n * domain-error cases (consumer cases run first).\n *\n * @example\n * ```ts\n * import { SvelteKit, makeRuntimeAdapter, RequestRuntime } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Effect } from \"effect\";\n *\n * // Yield kit errors instead of throwing:\n * const load = serverLoad(() =>\n * Effect.gen(function* () {\n * const user = yield* getUser;\n * if (!user) yield* SvelteKit.redirect(303, \"/login\");\n * if (!user.active) yield* SvelteKit.error(403, \"Account disabled\");\n * return { user };\n * })\n * );\n *\n * // Compose with your own domain errors:\n * import { error } from \"@sveltejs/kit\";\n * const { serverLoad } = makeRuntimeAdapter(\n * RequestRuntime.fromLayer(AppLayer),\n * {\n * translateError: SvelteKit.makeTranslator([\n * (e) => { if (e instanceof NotFound) error(404, \"Not found\"); },\n * ]),\n * },\n * );\n * ```\n */\n\nimport { error as kitError, fail as kitFail, redirect as kitRedirect } from \"@sveltejs/kit\";\nimport { Cause, Effect } from \"effect\";\nimport { causeToThrowable } from \"./internal/error.js\";\n\n// ─── Tagged error classes ────────────────────────────────────────────────────\n\n/**\n * An Effect-native redirect. Yield via {@link SvelteKit.redirect}; translated\n * at the request boundary by {@link SvelteKit.translate}.\n */\nexport class KitRedirect extends Error {\n readonly _tag = \"KitRedirect\" as const;\n\n constructor(\n readonly status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308,\n readonly location: string | URL,\n ) {\n super(`Redirect to ${String(location)}`);\n this.name = \"KitRedirect\";\n }\n}\n\n/**\n * An Effect-native HTTP error. Yield via {@link SvelteKit.error}; translated\n * at the request boundary by {@link SvelteKit.translate}.\n */\nexport class KitError extends Error {\n readonly _tag = \"KitError\" as const;\n\n constructor(\n readonly status: number,\n readonly body?: string | Record<string, unknown>,\n ) {\n super(`HTTP ${status}`);\n this.name = \"KitError\";\n }\n}\n\n/**\n * An Effect-native form-action failure. Yield via {@link SvelteKit.fail};\n * translated at the request boundary by {@link SvelteKit.translate}.\n */\nexport class KitActionFailure<\n T extends Record<string, unknown> = Record<string, unknown>,\n> extends Error {\n readonly _tag = \"KitActionFailure\" as const;\n\n constructor(\n readonly status: number,\n readonly data: T,\n ) {\n super(`Action failure ${status}`);\n this.name = \"KitActionFailure\";\n }\n}\n\n// ─── Translator types ────────────────────────────────────────────────────────\n\n/**\n * A single domain-error case for {@link SvelteKit.makeTranslator}.\n *\n * Receives the squashed error value. Throw (e.g. `error()` / `redirect()`) to\n * handle the case; return normally to pass through to the next case.\n */\nexport type KitTranslatorCase = (error: unknown) => undefined | void;\n\n/**\n * A boundary translator: turn a failed `Cause` into thrown SvelteKit control\n * flow or an `ActionFailure` return value.\n *\n * The Kit base translator ({@link SvelteKit.translate}) handles\n * {@link KitRedirect}, {@link KitError}, and {@link KitActionFailure}. Build a\n * custom translator via {@link SvelteKit.makeTranslator}.\n */\nexport type KitErrorTranslator = (cause: Cause.Cause<unknown>) => never;\n\n// ─── Base translator ─────────────────────────────────────────────────────────\n\nconst baseTranslate: KitErrorTranslator = (cause) => {\n const squashed = Cause.squash(cause);\n\n if (squashed instanceof KitRedirect) {\n // redirect() is typed as () => never — throws internally.\n kitRedirect(squashed.status, squashed.location as string);\n }\n\n if (squashed instanceof KitError) {\n // error() is typed as () => never — throws internally.\n kitError(squashed.status as Parameters<typeof kitError>[0], squashed.body as any);\n }\n\n if (squashed instanceof KitActionFailure) {\n // fail() returns an ActionFailure (doesn't throw). SvelteKit detects it as\n // the action's return value. Typed as never to satisfy ErrorTranslator while\n // allowing makeRuntimeAdapter's `return translateError(cause)` to propagate\n // the ActionFailure to SvelteKit correctly at runtime.\n return kitFail(squashed.status, squashed.data) as never;\n }\n\n throw causeToThrowable(cause);\n};\n\n// ─── Public namespace ─────────────────────────────────────────────────────────\n\n/**\n * Effect-native SvelteKit control-flow helpers and the default boundary\n * translator.\n *\n * **Error types**: {@link KitRedirect}, {@link KitError}, {@link KitActionFailure}\n * are tagged Effect failures — use them with `Effect.catchTag`, `Match.tag`,\n * and `instanceof` checks.\n *\n * **Default translator**: `SvelteKit.translate` is wired in as the default\n * `translateError` for every server wrapper (`serverLoad`, `action`,\n * `makeRuntimeAdapter`, `RemoteFunction.withRuntime`, etc.).\n */\nexport const SvelteKit = {\n /**\n * Fail with a redirect. Equivalent to SvelteKit's `redirect()` but as an\n * Effect failure instead of a thrown exception.\n *\n * @example\n * ```ts\n * yield* SvelteKit.redirect(303, \"/login\");\n * ```\n */\n redirect: (\n status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308,\n location: string | URL,\n ): Effect.Effect<never, KitRedirect> => Effect.fail(new KitRedirect(status, location)),\n\n /**\n * Fail with an HTTP error. Equivalent to SvelteKit's `error()` but as an\n * Effect failure instead of a thrown exception.\n *\n * @example\n * ```ts\n * yield* SvelteKit.error(404, \"Not found\");\n * yield* SvelteKit.error(403, { message: \"Forbidden\" });\n * ```\n */\n error: (\n status: number,\n body?: string | Record<string, unknown>,\n ): Effect.Effect<never, KitError> => Effect.fail(new KitError(status, body)),\n\n /**\n * Fail with a form-action failure. Equivalent to SvelteKit's `fail()` but as\n * an Effect failure instead of a return value.\n *\n * @example\n * ```ts\n * yield* SvelteKit.fail(422, { email: \"already taken\" });\n * ```\n */\n fail: <T extends Record<string, unknown>>(\n status: number,\n data: T,\n ): Effect.Effect<never, KitActionFailure<T>> => Effect.fail(new KitActionFailure(status, data)),\n\n /**\n * The turnkey boundary translator. Converts {@link KitRedirect} →\n * `redirect()`, {@link KitError} → `error()`, {@link KitActionFailure} →\n * `fail()`, and anything else → `EffectCauseError` (500).\n *\n * This is the default `translateError` for every server wrapper. To extend it\n * with your own domain errors, use {@link SvelteKit.makeTranslator}.\n */\n translate: baseTranslate,\n\n /**\n * Build a custom boundary translator by prepending domain-error cases before\n * the Kit base cases.\n *\n * Each case function receives the squashed error value. Throw (e.g. call\n * `error()` / `redirect()` from `@sveltejs/kit`) to handle the case; return\n * normally to pass through to the next case or the Kit base translator.\n *\n * @example\n * ```ts\n * import { error } from \"@sveltejs/kit\";\n *\n * const translateError = SvelteKit.makeTranslator([\n * (e) => { if (e instanceof NotFound) error(404, \"Not found\"); },\n * (e) => { if (e instanceof Forbidden) error(403, \"Forbidden\"); },\n * ]);\n *\n * const { serverLoad } = makeRuntimeAdapter(\n * RequestRuntime.fromLayer(AppLayer),\n * { translateError },\n * );\n * ```\n */\n makeTranslator:\n (extraCases: ReadonlyArray<KitTranslatorCase>): KitErrorTranslator =>\n (cause) => {\n const squashed = Cause.squash(cause);\n for (const matcher of extraCases) {\n matcher(squashed);\n }\n return baseTranslate(cause);\n },\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,IAAa,cAAb,cAAiC,MAAM;CAI1B;CACA;CAJX,OAAgB;CAEhB,YACE,QACA,UACA;EACA,MAAM,eAAe,OAAO,QAAQ,GAAG;EAH9B,KAAA,SAAA;EACA,KAAA,WAAA;EAGT,KAAK,OAAO;CACd;AACF;;;;;AAMA,IAAa,WAAb,cAA8B,MAAM;CAIvB;CACA;CAJX,OAAgB;CAEhB,YACE,QACA,MACA;EACA,MAAM,QAAQ,QAAQ;EAHb,KAAA,SAAA;EACA,KAAA,OAAA;EAGT,KAAK,OAAO;CACd;AACF;;;;;AAMA,IAAa,mBAAb,cAEU,MAAM;CAIH;CACA;CAJX,OAAgB;CAEhB,YACE,QACA,MACA;EACA,MAAM,kBAAkB,QAAQ;EAHvB,KAAA,SAAA;EACA,KAAA,OAAA;EAGT,KAAK,OAAO;CACd;AACF;AAwBA,MAAM,iBAAqC,UAAU;CACnD,MAAM,WAAW,MAAM,OAAO,KAAK;CAEnC,IAAI,oBAAoB,aAEtB,SAAY,SAAS,QAAQ,SAAS,QAAkB;CAG1D,IAAI,oBAAoB,UAEtB,MAAS,SAAS,QAA0C,SAAS,IAAW;CAGlF,IAAI,oBAAoB,kBAKtB,OAAOA,KAAQ,SAAS,QAAQ,SAAS,IAAI;CAG/C,MAAM,iBAAiB,KAAK;AAC9B;;;;;;;;;;;;;AAgBA,MAAa,YAAY;;;;;;;;;;CAUvB,WACE,QACA,aACsC,OAAO,KAAK,IAAI,YAAY,QAAQ,QAAQ,CAAC;;;;;;;;;;;CAYrF,QACE,QACA,SACmC,OAAO,KAAK,IAAI,SAAS,QAAQ,IAAI,CAAC;;;;;;;;;;CAW3E,OACE,QACA,SAC8C,OAAO,KAAK,IAAI,iBAAiB,QAAQ,IAAI,CAAC;;;;;;;;;CAU9F,WAAW;;;;;;;;;;;;;;;;;;;;;;;;CAyBX,iBACG,gBACA,UAAU;EACT,MAAM,WAAW,MAAM,OAAO,KAAK;EACnC,KAAK,MAAM,WAAW,YACpB,QAAQ,QAAQ;EAElB,OAAO,cAAc,KAAK;CAC5B;AACJ"}
@@ -0,0 +1,105 @@
1
+ import { Cause, Stream } from "effect";
2
+
3
+ //#region src/realtime.d.ts
4
+ declare namespace realtime_d_exports {
5
+ export { EventSourceConstructor, EventSourceError, FromEventSourceOptions, FromWebSocketOptions, SseMessage, WebSocketConstructor, WebSocketError, fromEventSource, fromWebSocket };
6
+ }
7
+ /** A decoded Server-Sent Event. */
8
+ interface SseMessage {
9
+ /** The event name (`"message"` for unnamed events). */
10
+ readonly event: string;
11
+ /** The event payload (the joined `data:` lines). */
12
+ readonly data: string;
13
+ /** The last event id seen, or `""` if none. */
14
+ readonly lastEventId: string;
15
+ }
16
+ declare const EventSourceError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => Cause.YieldableError & {
17
+ readonly _tag: "EventSourceError";
18
+ } & Readonly<A>;
19
+ /** Raised when an `EventSource` connection fails fatally (its `readyState` is `CLOSED`). */
20
+ declare class EventSourceError extends EventSourceError_base<{
21
+ readonly url: string;
22
+ }> {}
23
+ /** The subset of the `EventSource` constructor this module relies on. */
24
+ type EventSourceConstructor = new (url: string, eventSourceInitDict?: {
25
+ withCredentials?: boolean;
26
+ }) => EventSource;
27
+ interface FromEventSourceOptions {
28
+ /** Send credentials (cookies) with the request. Defaults to `false`. */
29
+ readonly withCredentials?: boolean;
30
+ /**
31
+ * Named events to listen for in addition to the default unnamed `message`
32
+ * event. An `Endpoint.fromStream({ event: "tick" })` server needs
33
+ * `events: ["tick"]` here.
34
+ */
35
+ readonly events?: ReadonlyArray<string>;
36
+ /** Buffer size for the backing queue. Defaults to 16. */
37
+ readonly bufferSize?: number;
38
+ /** Backpressure strategy when the buffer is full. Defaults to `"sliding"`. */
39
+ readonly strategy?: "sliding" | "dropping" | "suspend";
40
+ /** Override the `EventSource` constructor (Node polyfill, tests). */
41
+ readonly implementation?: EventSourceConstructor;
42
+ }
43
+ /**
44
+ * Consume a Server-Sent-Events endpoint as a `Stream` of {@link SseMessage}.
45
+ *
46
+ * The browser's `EventSource` reconnects automatically after transient drops,
47
+ * so the stream stays live across reconnects; it fails with
48
+ * {@link EventSourceError} only when the connection closes fatally.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { Realtime } from "@thomasfosterau/effect-sveltekit/client";
53
+ * import { Effect, Stream } from "effect";
54
+ *
55
+ * const program = Realtime.fromEventSource("/events", { events: ["tick"] }).pipe(
56
+ * Stream.runForEach((msg) => Effect.log(`${msg.event}: ${msg.data}`)),
57
+ * );
58
+ * ```
59
+ */
60
+ declare const fromEventSource: (url: string, options?: FromEventSourceOptions) => Stream.Stream<SseMessage, EventSourceError>;
61
+ declare const WebSocketError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => Cause.YieldableError & {
62
+ readonly _tag: "WebSocketError";
63
+ } & Readonly<A>;
64
+ /** Raised when a `WebSocket` errors or closes uncleanly. */
65
+ declare class WebSocketError extends WebSocketError_base<{
66
+ readonly url: string;
67
+ readonly code?: number;
68
+ readonly reason?: string;
69
+ }> {}
70
+ /** The subset of the `WebSocket` constructor this module relies on. */
71
+ type WebSocketConstructor = new (url: string, protocols?: string | ReadonlyArray<string>) => WebSocket;
72
+ interface FromWebSocketOptions {
73
+ /** Sub-protocols passed to the `WebSocket` constructor. */
74
+ readonly protocols?: string | ReadonlyArray<string>;
75
+ /** `arraybuffer` or `blob` for binary frames. Defaults to the platform default. */
76
+ readonly binaryType?: BinaryType;
77
+ /** Buffer size for the backing queue. Defaults to 16. */
78
+ readonly bufferSize?: number;
79
+ /** Backpressure strategy when the buffer is full. Defaults to `"suspend"`. */
80
+ readonly strategy?: "sliding" | "dropping" | "suspend";
81
+ /** Override the `WebSocket` constructor (Node polyfill, tests). */
82
+ readonly implementation?: WebSocketConstructor;
83
+ }
84
+ /**
85
+ * Consume a `WebSocket` as a `Stream` of its message payloads
86
+ * (`string | ArrayBuffer | Blob`, depending on `binaryType`).
87
+ *
88
+ * The stream ends when the socket closes cleanly and fails with
89
+ * {@link WebSocketError} on an error or unclean close. The socket is closed when
90
+ * the stream's scope closes.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * import { Realtime } from "@thomasfosterau/effect-sveltekit/client";
95
+ * import { Effect, Stream } from "effect";
96
+ *
97
+ * const program = Realtime.fromWebSocket("wss://example.com/ws").pipe(
98
+ * Stream.runForEach((data) => Effect.log(String(data))),
99
+ * );
100
+ * ```
101
+ */
102
+ declare const fromWebSocket: (url: string, options?: FromWebSocketOptions) => Stream.Stream<string | ArrayBuffer | Blob, WebSocketError>;
103
+ //#endregion
104
+ export { realtime_d_exports };
105
+ //# sourceMappingURL=realtime.d.ts.map
@@ -0,0 +1,111 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
+ import { Cause, Data, Effect, Queue, Stream } from "effect";
3
+ //#region src/realtime.ts
4
+ /**
5
+ * Client-side realtime bridges: consume a Server-Sent-Events endpoint or a
6
+ * WebSocket as an Effect `Stream`. The counterpart to `Endpoint.fromStream`
7
+ * (server) on the `@thomasfosterau/effect-sveltekit/client` `Realtime` namespace.
8
+ *
9
+ * These use the browser globals `EventSource` / `WebSocket`; both can be
10
+ * overridden (e.g. a Node polyfill, or a fake in tests) via the `implementation`
11
+ * option. The source is opened when the stream is run and closed when its scope
12
+ * closes, so a `Stream.take` / component teardown tears down the connection.
13
+ */
14
+ var realtime_exports = /* @__PURE__ */ __exportAll({
15
+ EventSourceError: () => EventSourceError,
16
+ WebSocketError: () => WebSocketError,
17
+ fromEventSource: () => fromEventSource,
18
+ fromWebSocket: () => fromWebSocket
19
+ });
20
+ /** Raised when an `EventSource` connection fails fatally (its `readyState` is `CLOSED`). */
21
+ var EventSourceError = class extends Data.TaggedError("EventSourceError") {};
22
+ /**
23
+ * Consume a Server-Sent-Events endpoint as a `Stream` of {@link SseMessage}.
24
+ *
25
+ * The browser's `EventSource` reconnects automatically after transient drops,
26
+ * so the stream stays live across reconnects; it fails with
27
+ * {@link EventSourceError} only when the connection closes fatally.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import { Realtime } from "@thomasfosterau/effect-sveltekit/client";
32
+ * import { Effect, Stream } from "effect";
33
+ *
34
+ * const program = Realtime.fromEventSource("/events", { events: ["tick"] }).pipe(
35
+ * Stream.runForEach((msg) => Effect.log(`${msg.event}: ${msg.data}`)),
36
+ * );
37
+ * ```
38
+ */
39
+ const fromEventSource = (url, options) => {
40
+ const Impl = options?.implementation ?? globalThis.EventSource;
41
+ return Stream.callback((queue) => Effect.sync(() => {
42
+ const source = options?.withCredentials === void 0 ? new Impl(url) : new Impl(url, { withCredentials: options.withCredentials });
43
+ const onMessage = (e) => {
44
+ Queue.offerUnsafe(queue, {
45
+ event: e.type,
46
+ data: e.data,
47
+ lastEventId: e.lastEventId
48
+ });
49
+ };
50
+ source.addEventListener("message", onMessage);
51
+ for (const name of options?.events ?? []) source.addEventListener(name, onMessage);
52
+ source.addEventListener("error", () => {
53
+ if (source.readyState === 2) Queue.failCauseUnsafe(queue, Cause.fail(new EventSourceError({ url })));
54
+ });
55
+ return Effect.sync(() => source.close());
56
+ }), {
57
+ bufferSize: options?.bufferSize ?? 16,
58
+ strategy: options?.strategy ?? "sliding"
59
+ });
60
+ };
61
+ /** Raised when a `WebSocket` errors or closes uncleanly. */
62
+ var WebSocketError = class extends Data.TaggedError("WebSocketError") {};
63
+ /**
64
+ * Consume a `WebSocket` as a `Stream` of its message payloads
65
+ * (`string | ArrayBuffer | Blob`, depending on `binaryType`).
66
+ *
67
+ * The stream ends when the socket closes cleanly and fails with
68
+ * {@link WebSocketError} on an error or unclean close. The socket is closed when
69
+ * the stream's scope closes.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * import { Realtime } from "@thomasfosterau/effect-sveltekit/client";
74
+ * import { Effect, Stream } from "effect";
75
+ *
76
+ * const program = Realtime.fromWebSocket("wss://example.com/ws").pipe(
77
+ * Stream.runForEach((data) => Effect.log(String(data))),
78
+ * );
79
+ * ```
80
+ */
81
+ const fromWebSocket = (url, options) => {
82
+ const Impl = options?.implementation ?? globalThis.WebSocket;
83
+ return Stream.callback((queue) => Effect.sync(() => {
84
+ const socket = new Impl(url, options?.protocols);
85
+ if (options?.binaryType !== void 0) socket.binaryType = options.binaryType;
86
+ socket.addEventListener("message", (e) => {
87
+ Queue.offerUnsafe(queue, e.data);
88
+ });
89
+ socket.addEventListener("error", () => {
90
+ Queue.failCauseUnsafe(queue, Cause.fail(new WebSocketError({ url })));
91
+ });
92
+ socket.addEventListener("close", (e) => {
93
+ if (e.wasClean) Queue.endUnsafe(queue);
94
+ else Queue.failCauseUnsafe(queue, Cause.fail(new WebSocketError({
95
+ url,
96
+ code: e.code,
97
+ reason: e.reason
98
+ })));
99
+ });
100
+ return Effect.sync(() => {
101
+ if (socket.readyState === 0 || socket.readyState === 1) socket.close();
102
+ });
103
+ }), {
104
+ bufferSize: options?.bufferSize ?? 16,
105
+ strategy: options?.strategy ?? "suspend"
106
+ });
107
+ };
108
+ //#endregion
109
+ export { realtime_exports };
110
+
111
+ //# sourceMappingURL=realtime.js.map