@thomasfosterau/effect-sveltekit 0.4.1 → 0.6.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 (66) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +183 -85
  3. package/dist/Hooks.d.ts +1 -1
  4. package/dist/Hooks.js.map +1 -1
  5. package/dist/Navigation.d.ts +83 -19
  6. package/dist/Navigation.js +93 -20
  7. package/dist/Navigation.js.map +1 -1
  8. package/dist/Remote.d.ts +37 -19
  9. package/dist/Remote.js +20 -7
  10. package/dist/Remote.js.map +1 -1
  11. package/dist/State.d.ts +77 -8
  12. package/dist/State.js +116 -27
  13. package/dist/State.js.map +1 -1
  14. package/dist/adapter/handler.d.ts +14 -12
  15. package/dist/adapter/handler.js +5 -7
  16. package/dist/adapter/handler.js.map +1 -1
  17. package/dist/adapter/index.js +25 -21
  18. package/dist/adapter/index.js.map +1 -1
  19. package/dist/api-client.js +1 -1
  20. package/dist/api-client.js.map +1 -1
  21. package/dist/app-runtime.d.ts +7 -6
  22. package/dist/app-runtime.js +5 -5
  23. package/dist/app-runtime.js.map +1 -1
  24. package/dist/client/Hooks.d.ts +1 -1
  25. package/dist/client/Hooks.js.map +1 -1
  26. package/dist/errors.js +3 -2
  27. package/dist/errors.js.map +1 -1
  28. package/dist/forms.d.ts +72 -9
  29. package/dist/forms.js +66 -5
  30. package/dist/forms.js.map +1 -1
  31. package/dist/internal/app-state.d.ts +38 -3
  32. package/dist/internal/app-state.js +58 -15
  33. package/dist/internal/app-state.js.map +1 -1
  34. package/dist/internal/auto.d.ts +2 -1
  35. package/dist/internal/auto.js.map +1 -1
  36. package/dist/internal/enhance.d.ts +107 -9
  37. package/dist/internal/enhance.js +30 -6
  38. package/dist/internal/enhance.js.map +1 -1
  39. package/dist/internal/error.js +23 -2
  40. package/dist/internal/error.js.map +1 -1
  41. package/dist/internal/remote-run.d.ts +3 -3
  42. package/dist/internal/remote-run.js.map +1 -1
  43. package/dist/internal/watch.svelte.js +33 -0
  44. package/dist/internal/watch.svelte.js.map +1 -0
  45. package/dist/kit.js +3 -3
  46. package/dist/kit.js.map +1 -1
  47. package/dist/realtime.d.ts +28 -7
  48. package/dist/realtime.js +68 -43
  49. package/dist/realtime.js.map +1 -1
  50. package/dist/remote-functions.d.ts +4 -4
  51. package/dist/remote-functions.js.map +1 -1
  52. package/dist/remote-reactive.d.ts +1 -1
  53. package/dist/remote-reactive.js.map +1 -1
  54. package/dist/response-headers.d.ts +1 -1
  55. package/dist/response-headers.js.map +1 -1
  56. package/dist/server/Endpoint.d.ts +2 -2
  57. package/dist/server/Endpoint.js +2 -2
  58. package/dist/server/Endpoint.js.map +1 -1
  59. package/dist/server/Hooks.d.ts +1 -1
  60. package/dist/server/Hooks.js.map +1 -1
  61. package/dist/transport.d.ts +1 -1
  62. package/dist/transport.js.map +1 -1
  63. package/dist/vite.d.ts +2 -2
  64. package/dist/vite.js.map +1 -1
  65. package/package.json +10 -8
  66. package/LICENSE +0 -21
package/dist/kit.js.map CHANGED
@@ -1 +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"}
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 { fail as kitFail, redirect as kitRedirect } from \"@sveltejs/kit\";\nimport { Cause, Effect } from \"effect\";\nimport { causeToThrowable, throwKitError } 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 // throwKitError() is typed as never: it always throws Kit's HttpError.\n throwKitError(squashed.status, squashed.body);\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,cAAc,SAAS,QAAQ,SAAS,IAAI;CAG9C,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"}
@@ -14,9 +14,16 @@ interface SseMessage {
14
14
  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 & {
15
15
  readonly _tag: "EventSourceError";
16
16
  } & Readonly<A>;
17
- /** Raised when an `EventSource` connection fails fatally (its `readyState` is `CLOSED`). */
17
+ /**
18
+ * Raised when an `EventSource` cannot be opened (its constructor throws, e.g.
19
+ * for an invalid URL; `cause` holds the thrown value) or its connection fails
20
+ * fatally (its `readyState` is `CLOSED`; `cause` is absent).
21
+ */
18
22
  declare class EventSourceError extends EventSourceError_base<{
23
+ /** The URL the `EventSource` was opened with. */
19
24
  readonly url: string;
25
+ /** What the `EventSource` constructor threw, when that is why the stream failed. */
26
+ readonly cause?: unknown;
20
27
  }> {}
21
28
  /** The subset of the `EventSource` constructor this module relies on. */
22
29
  type EventSourceConstructor = new (url: string, eventSourceInitDict?: {
@@ -43,8 +50,11 @@ interface FromEventSourceOptions {
43
50
  * Consume a Server-Sent-Events endpoint as a `Stream` of {@link SseMessage}.
44
51
  *
45
52
  * The browser's `EventSource` reconnects automatically after transient drops,
46
- * so the stream stays live across reconnects; it fails with
47
- * {@link EventSourceError} only when the connection closes fatally.
53
+ * so the stream stays live across reconnects. It fails with
54
+ * {@link EventSourceError} when the connection closes fatally, or, with the
55
+ * thrown value as its `cause`, when the `EventSource` constructor throws (e.g.
56
+ * a `SyntaxError` for an invalid URL). The source is closed when the stream's
57
+ * scope closes; one that failed to construct has nothing to close.
48
58
  *
49
59
  * @example
50
60
  * ```ts
@@ -60,11 +70,20 @@ declare const fromEventSource: (url: string, options?: FromEventSourceOptions) =
60
70
  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 & {
61
71
  readonly _tag: "WebSocketError";
62
72
  } & Readonly<A>;
63
- /** Raised when a `WebSocket` errors or closes uncleanly. */
73
+ /**
74
+ * Raised when a `WebSocket` cannot be opened (its constructor throws, e.g. for
75
+ * an invalid URL or protocol list; `cause` holds the thrown value), errors, or
76
+ * closes uncleanly (`code` and `reason` come from the close event).
77
+ */
64
78
  declare class WebSocketError extends WebSocketError_base<{
79
+ /** The URL the `WebSocket` was opened with. */
65
80
  readonly url: string;
81
+ /** The close code, when an unclean close is why the stream failed. */
66
82
  readonly code?: number;
83
+ /** The close reason, when an unclean close is why the stream failed. */
67
84
  readonly reason?: string;
85
+ /** What the `WebSocket` constructor threw, when that is why the stream failed. */
86
+ readonly cause?: unknown;
68
87
  }> {}
69
88
  /** The subset of the `WebSocket` constructor this module relies on. */
70
89
  type WebSocketConstructor = new (url: string, protocols?: string | ReadonlyArray<string>) => WebSocket;
@@ -85,9 +104,11 @@ interface FromWebSocketOptions {
85
104
  * Consume a `WebSocket` as a `Stream` of its message payloads
86
105
  * (`string | ArrayBuffer | Blob`, depending on `binaryType`).
87
106
  *
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.
107
+ * The stream ends when the socket closes cleanly. It fails with
108
+ * {@link WebSocketError} on an error or unclean close, or, with the thrown
109
+ * value as its `cause`, when the `WebSocket` constructor throws (e.g. a
110
+ * `SyntaxError` for an invalid URL or protocol list). The socket is closed when
111
+ * the stream's scope closes; one that failed to construct has nothing to close.
91
112
  *
92
113
  * @example
93
114
  * ```ts
package/dist/realtime.js CHANGED
@@ -17,14 +17,21 @@ var realtime_exports = /* @__PURE__ */ __exportAll({
17
17
  fromEventSource: () => fromEventSource,
18
18
  fromWebSocket: () => fromWebSocket
19
19
  });
20
- /** Raised when an `EventSource` connection fails fatally (its `readyState` is `CLOSED`). */
20
+ /**
21
+ * Raised when an `EventSource` cannot be opened (its constructor throws, e.g.
22
+ * for an invalid URL; `cause` holds the thrown value) or its connection fails
23
+ * fatally (its `readyState` is `CLOSED`; `cause` is absent).
24
+ */
21
25
  var EventSourceError = class extends Data.TaggedError("EventSourceError") {};
22
26
  /**
23
27
  * Consume a Server-Sent-Events endpoint as a `Stream` of {@link SseMessage}.
24
28
  *
25
29
  * 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.
30
+ * so the stream stays live across reconnects. It fails with
31
+ * {@link EventSourceError} when the connection closes fatally, or, with the
32
+ * thrown value as its `cause`, when the `EventSource` constructor throws (e.g.
33
+ * a `SyntaxError` for an invalid URL). The source is closed when the stream's
34
+ * scope closes; one that failed to construct has nothing to close.
28
35
  *
29
36
  * @example
30
37
  * ```ts
@@ -38,35 +45,47 @@ var EventSourceError = class extends Data.TaggedError("EventSourceError") {};
38
45
  */
39
46
  const fromEventSource = (url, options) => {
40
47
  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
+ return Stream.callback((queue) => Effect.acquireRelease(Effect.try({
49
+ try: () => {
50
+ const source = options?.withCredentials === void 0 ? new Impl(url) : new Impl(url, { withCredentials: options.withCredentials });
51
+ const onMessage = (e) => {
52
+ Queue.offerUnsafe(queue, {
53
+ event: e.type,
54
+ data: e.data,
55
+ lastEventId: e.lastEventId
56
+ });
57
+ };
58
+ source.addEventListener("message", onMessage);
59
+ for (const name of options?.events ?? []) source.addEventListener(name, onMessage);
60
+ source.addEventListener("error", () => {
61
+ if (source.readyState === 2) Queue.failCauseUnsafe(queue, Cause.fail(new EventSourceError({ url })));
48
62
  });
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
- }), {
63
+ return source;
64
+ },
65
+ catch: (cause) => new EventSourceError({
66
+ url,
67
+ cause
68
+ })
69
+ }), (source) => Effect.sync(() => source.close())), {
57
70
  bufferSize: options?.bufferSize ?? 16,
58
71
  strategy: options?.strategy ?? "sliding"
59
72
  });
60
73
  };
61
- /** Raised when a `WebSocket` errors or closes uncleanly. */
74
+ /**
75
+ * Raised when a `WebSocket` cannot be opened (its constructor throws, e.g. for
76
+ * an invalid URL or protocol list; `cause` holds the thrown value), errors, or
77
+ * closes uncleanly (`code` and `reason` come from the close event).
78
+ */
62
79
  var WebSocketError = class extends Data.TaggedError("WebSocketError") {};
63
80
  /**
64
81
  * Consume a `WebSocket` as a `Stream` of its message payloads
65
82
  * (`string | ArrayBuffer | Blob`, depending on `binaryType`).
66
83
  *
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.
84
+ * The stream ends when the socket closes cleanly. It fails with
85
+ * {@link WebSocketError} on an error or unclean close, or, with the thrown
86
+ * value as its `cause`, when the `WebSocket` constructor throws (e.g. a
87
+ * `SyntaxError` for an invalid URL or protocol list). The socket is closed when
88
+ * the stream's scope closes; one that failed to construct has nothing to close.
70
89
  *
71
90
  * @example
72
91
  * ```ts
@@ -80,27 +99,33 @@ var WebSocketError = class extends Data.TaggedError("WebSocketError") {};
80
99
  */
81
100
  const fromWebSocket = (url, options) => {
82
101
  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
- }), {
102
+ return Stream.callback((queue) => Effect.acquireRelease(Effect.try({
103
+ try: () => {
104
+ const socket = new Impl(url, options?.protocols);
105
+ if (options?.binaryType !== void 0) socket.binaryType = options.binaryType;
106
+ socket.addEventListener("message", (e) => {
107
+ Queue.offerUnsafe(queue, e.data);
108
+ });
109
+ socket.addEventListener("error", () => {
110
+ Queue.failCauseUnsafe(queue, Cause.fail(new WebSocketError({ url })));
111
+ });
112
+ socket.addEventListener("close", (e) => {
113
+ if (e.wasClean) Queue.endUnsafe(queue);
114
+ else Queue.failCauseUnsafe(queue, Cause.fail(new WebSocketError({
115
+ url,
116
+ code: e.code,
117
+ reason: e.reason
118
+ })));
119
+ });
120
+ return socket;
121
+ },
122
+ catch: (cause) => new WebSocketError({
123
+ url,
124
+ cause
125
+ })
126
+ }), (socket) => Effect.sync(() => {
127
+ if (socket.readyState === 0 || socket.readyState === 1) socket.close();
128
+ })), {
104
129
  bufferSize: options?.bufferSize ?? 16,
105
130
  strategy: options?.strategy ?? "suspend"
106
131
  });
@@ -1 +1 @@
1
- {"version":3,"file":"realtime.js","names":[],"sources":["../src/realtime.ts"],"sourcesContent":["/**\n * Client-side realtime bridges: consume a Server-Sent-Events endpoint or a\n * WebSocket as an Effect `Stream`. The counterpart to `Endpoint.fromStream`\n * (server) on the `@thomasfosterau/effect-sveltekit/client` `Realtime` namespace.\n *\n * These use the browser globals `EventSource` / `WebSocket`; both can be\n * overridden (e.g. a Node polyfill, or a fake in tests) via the `implementation`\n * option. The source is opened when the stream is run and closed when its scope\n * closes, so a `Stream.take` / component teardown tears down the connection.\n */\n\nimport { Cause, Data, Effect, Queue, Stream } from \"effect\";\n\n// ---------------------------------------------------------------------------\n// EventSource\n// ---------------------------------------------------------------------------\n\n/** A decoded Server-Sent Event. */\nexport interface SseMessage {\n /** The event name (`\"message\"` for unnamed events). */\n readonly event: string;\n /** The event payload (the joined `data:` lines). */\n readonly data: string;\n /** The last event id seen, or `\"\"` if none. */\n readonly lastEventId: string;\n}\n\n/** Raised when an `EventSource` connection fails fatally (its `readyState` is `CLOSED`). */\nexport class EventSourceError extends Data.TaggedError(\"EventSourceError\")<{\n readonly url: string;\n}> {}\n\n/** The subset of the `EventSource` constructor this module relies on. */\nexport type EventSourceConstructor = new (\n url: string,\n eventSourceInitDict?: { withCredentials?: boolean },\n) => EventSource;\n\n/** Options for {@link fromEventSource}. */\nexport interface FromEventSourceOptions {\n /** Send credentials (cookies) with the request. Defaults to `false`. */\n readonly withCredentials?: boolean;\n /**\n * Named events to listen for in addition to the default unnamed `message`\n * event. An `Endpoint.fromStream({ event: \"tick\" })` server needs\n * `events: [\"tick\"]` here.\n */\n readonly events?: ReadonlyArray<string>;\n /** Buffer size for the backing queue. Defaults to 16. */\n readonly bufferSize?: number;\n /** Backpressure strategy when the buffer is full. Defaults to `\"sliding\"`. */\n readonly strategy?: \"sliding\" | \"dropping\" | \"suspend\";\n /** Override the `EventSource` constructor (Node polyfill, tests). */\n readonly implementation?: EventSourceConstructor;\n}\n\n/**\n * Consume a Server-Sent-Events endpoint as a `Stream` of {@link SseMessage}.\n *\n * The browser's `EventSource` reconnects automatically after transient drops,\n * so the stream stays live across reconnects; it fails with\n * {@link EventSourceError} only when the connection closes fatally.\n *\n * @example\n * ```ts\n * import { Realtime } from \"@thomasfosterau/effect-sveltekit/client\";\n * import { Effect, Stream } from \"effect\";\n *\n * const program = Realtime.fromEventSource(\"/events\", { events: [\"tick\"] }).pipe(\n * Stream.runForEach((msg) => Effect.log(`${msg.event}: ${msg.data}`)),\n * );\n * ```\n */\nexport const fromEventSource = (\n url: string,\n options?: FromEventSourceOptions,\n): Stream.Stream<SseMessage, EventSourceError> => {\n const Impl = options?.implementation ?? (globalThis.EventSource as EventSourceConstructor);\n return Stream.callback<SseMessage, EventSourceError>(\n (queue) =>\n Effect.sync(() => {\n const source =\n options?.withCredentials === undefined\n ? new Impl(url)\n : new Impl(url, { withCredentials: options.withCredentials });\n\n const onMessage = (e: MessageEvent): void => {\n Queue.offerUnsafe(queue, { event: e.type, data: e.data, lastEventId: e.lastEventId });\n };\n\n source.addEventListener(\"message\", onMessage);\n for (const name of options?.events ?? []) source.addEventListener(name, onMessage);\n\n source.addEventListener(\"error\", () => {\n // EventSource reconnects on its own unless it has closed for good.\n if (source.readyState === 2 /* CLOSED */) {\n Queue.failCauseUnsafe(queue, Cause.fail(new EventSourceError({ url })));\n }\n });\n\n return Effect.sync(() => source.close());\n }),\n { bufferSize: options?.bufferSize ?? 16, strategy: options?.strategy ?? \"sliding\" },\n );\n};\n\n// ---------------------------------------------------------------------------\n// WebSocket\n// ---------------------------------------------------------------------------\n\n/** Raised when a `WebSocket` errors or closes uncleanly. */\nexport class WebSocketError extends Data.TaggedError(\"WebSocketError\")<{\n readonly url: string;\n readonly code?: number;\n readonly reason?: string;\n}> {}\n\n/** The subset of the `WebSocket` constructor this module relies on. */\nexport type WebSocketConstructor = new (\n url: string,\n protocols?: string | ReadonlyArray<string>,\n) => WebSocket;\n\n/** Options for {@link fromWebSocket}. */\nexport interface FromWebSocketOptions {\n /** Sub-protocols passed to the `WebSocket` constructor. */\n readonly protocols?: string | ReadonlyArray<string>;\n /** `arraybuffer` or `blob` for binary frames. Defaults to the platform default. */\n readonly binaryType?: BinaryType;\n /** Buffer size for the backing queue. Defaults to 16. */\n readonly bufferSize?: number;\n /** Backpressure strategy when the buffer is full. Defaults to `\"suspend\"`. */\n readonly strategy?: \"sliding\" | \"dropping\" | \"suspend\";\n /** Override the `WebSocket` constructor (Node polyfill, tests). */\n readonly implementation?: WebSocketConstructor;\n}\n\n/**\n * Consume a `WebSocket` as a `Stream` of its message payloads\n * (`string | ArrayBuffer | Blob`, depending on `binaryType`).\n *\n * The stream ends when the socket closes cleanly and fails with\n * {@link WebSocketError} on an error or unclean close. The socket is closed when\n * the stream's scope closes.\n *\n * @example\n * ```ts\n * import { Realtime } from \"@thomasfosterau/effect-sveltekit/client\";\n * import { Effect, Stream } from \"effect\";\n *\n * const program = Realtime.fromWebSocket(\"wss://example.com/ws\").pipe(\n * Stream.runForEach((data) => Effect.log(String(data))),\n * );\n * ```\n */\nexport const fromWebSocket = (\n url: string,\n options?: FromWebSocketOptions,\n): Stream.Stream<string | ArrayBuffer | Blob, WebSocketError> => {\n const Impl = options?.implementation ?? (globalThis.WebSocket as WebSocketConstructor);\n return Stream.callback<string | ArrayBuffer | Blob, WebSocketError>(\n (queue) =>\n Effect.sync(() => {\n const socket = new Impl(url, options?.protocols);\n if (options?.binaryType !== undefined) socket.binaryType = options.binaryType;\n\n socket.addEventListener(\"message\", (e: MessageEvent) => {\n Queue.offerUnsafe(queue, e.data);\n });\n\n socket.addEventListener(\"error\", () => {\n Queue.failCauseUnsafe(queue, Cause.fail(new WebSocketError({ url })));\n });\n\n socket.addEventListener(\"close\", (e: CloseEvent) => {\n if (e.wasClean) {\n Queue.endUnsafe(queue);\n } else {\n Queue.failCauseUnsafe(\n queue,\n Cause.fail(new WebSocketError({ url, code: e.code, reason: e.reason })),\n );\n }\n });\n\n return Effect.sync(() => {\n // Only close a socket that is still connecting/open.\n if (socket.readyState === 0 || socket.readyState === 1) socket.close();\n });\n }),\n { bufferSize: options?.bufferSize ?? 16, strategy: options?.strategy ?? \"suspend\" },\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4BA,IAAa,mBAAb,cAAsC,KAAK,YAAY,kBAAkB,CAAC,CAEvE,CAAC;;;;;;;;;;;;;;;;;;AA2CJ,MAAa,mBACX,KACA,YACgD;CAChD,MAAM,OAAO,SAAS,kBAAmB,WAAW;CACpD,OAAO,OAAO,UACX,UACC,OAAO,WAAW;EAChB,MAAM,SACJ,SAAS,oBAAoB,KAAA,IACzB,IAAI,KAAK,GAAG,IACZ,IAAI,KAAK,KAAK,EAAE,iBAAiB,QAAQ,gBAAgB,CAAC;EAEhE,MAAM,aAAa,MAA0B;GAC3C,MAAM,YAAY,OAAO;IAAE,OAAO,EAAE;IAAM,MAAM,EAAE;IAAM,aAAa,EAAE;GAAY,CAAC;EACtF;EAEA,OAAO,iBAAiB,WAAW,SAAS;EAC5C,KAAK,MAAM,QAAQ,SAAS,UAAU,CAAC,GAAG,OAAO,iBAAiB,MAAM,SAAS;EAEjF,OAAO,iBAAiB,eAAe;GAErC,IAAI,OAAO,eAAe,GACxB,MAAM,gBAAgB,OAAO,MAAM,KAAK,IAAI,iBAAiB,EAAE,IAAI,CAAC,CAAC,CAAC;EAE1E,CAAC;EAED,OAAO,OAAO,WAAW,OAAO,MAAM,CAAC;CACzC,CAAC,GACH;EAAE,YAAY,SAAS,cAAc;EAAI,UAAU,SAAS,YAAY;CAAU,CACpF;AACF;;AAOA,IAAa,iBAAb,cAAoC,KAAK,YAAY,gBAAgB,CAAC,CAInE,CAAC;;;;;;;;;;;;;;;;;;;AAwCJ,MAAa,iBACX,KACA,YAC+D;CAC/D,MAAM,OAAO,SAAS,kBAAmB,WAAW;CACpD,OAAO,OAAO,UACX,UACC,OAAO,WAAW;EAChB,MAAM,SAAS,IAAI,KAAK,KAAK,SAAS,SAAS;EAC/C,IAAI,SAAS,eAAe,KAAA,GAAW,OAAO,aAAa,QAAQ;EAEnE,OAAO,iBAAiB,YAAY,MAAoB;GACtD,MAAM,YAAY,OAAO,EAAE,IAAI;EACjC,CAAC;EAED,OAAO,iBAAiB,eAAe;GACrC,MAAM,gBAAgB,OAAO,MAAM,KAAK,IAAI,eAAe,EAAE,IAAI,CAAC,CAAC,CAAC;EACtE,CAAC;EAED,OAAO,iBAAiB,UAAU,MAAkB;GAClD,IAAI,EAAE,UACJ,MAAM,UAAU,KAAK;QAErB,MAAM,gBACJ,OACA,MAAM,KAAK,IAAI,eAAe;IAAE;IAAK,MAAM,EAAE;IAAM,QAAQ,EAAE;GAAO,CAAC,CAAC,CACxE;EAEJ,CAAC;EAED,OAAO,OAAO,WAAW;GAEvB,IAAI,OAAO,eAAe,KAAK,OAAO,eAAe,GAAG,OAAO,MAAM;EACvE,CAAC;CACH,CAAC,GACH;EAAE,YAAY,SAAS,cAAc;EAAI,UAAU,SAAS,YAAY;CAAU,CACpF;AACF"}
1
+ {"version":3,"file":"realtime.js","names":[],"sources":["../src/realtime.ts"],"sourcesContent":["/**\n * Client-side realtime bridges: consume a Server-Sent-Events endpoint or a\n * WebSocket as an Effect `Stream`. The counterpart to `Endpoint.fromStream`\n * (server) on the `@thomasfosterau/effect-sveltekit/client` `Realtime` namespace.\n *\n * These use the browser globals `EventSource` / `WebSocket`; both can be\n * overridden (e.g. a Node polyfill, or a fake in tests) via the `implementation`\n * option. The source is opened when the stream is run and closed when its scope\n * closes, so a `Stream.take` / component teardown tears down the connection.\n */\n\nimport { Cause, Data, Effect, Queue, Stream } from \"effect\";\n\n// ---------------------------------------------------------------------------\n// EventSource\n// ---------------------------------------------------------------------------\n\n/** A decoded Server-Sent Event. */\nexport interface SseMessage {\n /** The event name (`\"message\"` for unnamed events). */\n readonly event: string;\n /** The event payload (the joined `data:` lines). */\n readonly data: string;\n /** The last event id seen, or `\"\"` if none. */\n readonly lastEventId: string;\n}\n\n/**\n * Raised when an `EventSource` cannot be opened (its constructor throws, e.g.\n * for an invalid URL; `cause` holds the thrown value) or its connection fails\n * fatally (its `readyState` is `CLOSED`; `cause` is absent).\n */\nexport class EventSourceError extends Data.TaggedError(\"EventSourceError\")<{\n /** The URL the `EventSource` was opened with. */\n readonly url: string;\n /** What the `EventSource` constructor threw, when that is why the stream failed. */\n readonly cause?: unknown;\n}> {}\n\n/** The subset of the `EventSource` constructor this module relies on. */\nexport type EventSourceConstructor = new (\n url: string,\n eventSourceInitDict?: { withCredentials?: boolean },\n) => EventSource;\n\n/** Options for {@link fromEventSource}. */\nexport interface FromEventSourceOptions {\n /** Send credentials (cookies) with the request. Defaults to `false`. */\n readonly withCredentials?: boolean;\n /**\n * Named events to listen for in addition to the default unnamed `message`\n * event. An `Endpoint.fromStream({ event: \"tick\" })` server needs\n * `events: [\"tick\"]` here.\n */\n readonly events?: ReadonlyArray<string>;\n /** Buffer size for the backing queue. Defaults to 16. */\n readonly bufferSize?: number;\n /** Backpressure strategy when the buffer is full. Defaults to `\"sliding\"`. */\n readonly strategy?: \"sliding\" | \"dropping\" | \"suspend\";\n /** Override the `EventSource` constructor (Node polyfill, tests). */\n readonly implementation?: EventSourceConstructor;\n}\n\n/**\n * Consume a Server-Sent-Events endpoint as a `Stream` of {@link SseMessage}.\n *\n * The browser's `EventSource` reconnects automatically after transient drops,\n * so the stream stays live across reconnects. It fails with\n * {@link EventSourceError} when the connection closes fatally, or, with the\n * thrown value as its `cause`, when the `EventSource` constructor throws (e.g.\n * a `SyntaxError` for an invalid URL). The source is closed when the stream's\n * scope closes; one that failed to construct has nothing to close.\n *\n * @example\n * ```ts\n * import { Realtime } from \"@thomasfosterau/effect-sveltekit/client\";\n * import { Effect, Stream } from \"effect\";\n *\n * const program = Realtime.fromEventSource(\"/events\", { events: [\"tick\"] }).pipe(\n * Stream.runForEach((msg) => Effect.log(`${msg.event}: ${msg.data}`)),\n * );\n * ```\n */\nexport const fromEventSource = (\n url: string,\n options?: FromEventSourceOptions,\n): Stream.Stream<SseMessage, EventSourceError> => {\n const Impl = options?.implementation ?? (globalThis.EventSource as EventSourceConstructor);\n // Effect v4's `Stream.callback` runs the returned Effect as the stream's\n // setup, inside the stream's scope; it is not a cleanup. Open the source\n // with `acquireRelease` so it is closed when that scope closes.\n return Stream.callback<SseMessage, EventSourceError>(\n (queue) =>\n Effect.acquireRelease(\n // Construct and attach the listeners in one synchronous step, so no\n // event can be dispatched in between; a throw from either (in practice\n // the constructor) becomes the typed error. A failed acquire registers\n // no release: a source that failed to construct has nothing to close.\n Effect.try({\n try: () => {\n const source =\n options?.withCredentials === undefined\n ? new Impl(url)\n : new Impl(url, { withCredentials: options.withCredentials });\n\n const onMessage = (e: MessageEvent): void => {\n Queue.offerUnsafe(queue, { event: e.type, data: e.data, lastEventId: e.lastEventId });\n };\n\n source.addEventListener(\"message\", onMessage);\n for (const name of options?.events ?? []) source.addEventListener(name, onMessage);\n\n source.addEventListener(\"error\", () => {\n // EventSource reconnects on its own unless it has closed for good.\n if (source.readyState === 2 /* CLOSED */) {\n Queue.failCauseUnsafe(queue, Cause.fail(new EventSourceError({ url })));\n }\n });\n\n return source;\n },\n catch: (cause) => new EventSourceError({ url, cause }),\n }),\n (source) => Effect.sync(() => source.close()),\n ),\n { bufferSize: options?.bufferSize ?? 16, strategy: options?.strategy ?? \"sliding\" },\n );\n};\n\n// ---------------------------------------------------------------------------\n// WebSocket\n// ---------------------------------------------------------------------------\n\n/**\n * Raised when a `WebSocket` cannot be opened (its constructor throws, e.g. for\n * an invalid URL or protocol list; `cause` holds the thrown value), errors, or\n * closes uncleanly (`code` and `reason` come from the close event).\n */\nexport class WebSocketError extends Data.TaggedError(\"WebSocketError\")<{\n /** The URL the `WebSocket` was opened with. */\n readonly url: string;\n /** The close code, when an unclean close is why the stream failed. */\n readonly code?: number;\n /** The close reason, when an unclean close is why the stream failed. */\n readonly reason?: string;\n /** What the `WebSocket` constructor threw, when that is why the stream failed. */\n readonly cause?: unknown;\n}> {}\n\n/** The subset of the `WebSocket` constructor this module relies on. */\nexport type WebSocketConstructor = new (\n url: string,\n protocols?: string | ReadonlyArray<string>,\n) => WebSocket;\n\n/** Options for {@link fromWebSocket}. */\nexport interface FromWebSocketOptions {\n /** Sub-protocols passed to the `WebSocket` constructor. */\n readonly protocols?: string | ReadonlyArray<string>;\n /** `arraybuffer` or `blob` for binary frames. Defaults to the platform default. */\n readonly binaryType?: BinaryType;\n /** Buffer size for the backing queue. Defaults to 16. */\n readonly bufferSize?: number;\n /** Backpressure strategy when the buffer is full. Defaults to `\"suspend\"`. */\n readonly strategy?: \"sliding\" | \"dropping\" | \"suspend\";\n /** Override the `WebSocket` constructor (Node polyfill, tests). */\n readonly implementation?: WebSocketConstructor;\n}\n\n/**\n * Consume a `WebSocket` as a `Stream` of its message payloads\n * (`string | ArrayBuffer | Blob`, depending on `binaryType`).\n *\n * The stream ends when the socket closes cleanly. It fails with\n * {@link WebSocketError} on an error or unclean close, or, with the thrown\n * value as its `cause`, when the `WebSocket` constructor throws (e.g. a\n * `SyntaxError` for an invalid URL or protocol list). The socket is closed when\n * the stream's scope closes; one that failed to construct has nothing to close.\n *\n * @example\n * ```ts\n * import { Realtime } from \"@thomasfosterau/effect-sveltekit/client\";\n * import { Effect, Stream } from \"effect\";\n *\n * const program = Realtime.fromWebSocket(\"wss://example.com/ws\").pipe(\n * Stream.runForEach((data) => Effect.log(String(data))),\n * );\n * ```\n */\nexport const fromWebSocket = (\n url: string,\n options?: FromWebSocketOptions,\n): Stream.Stream<string | ArrayBuffer | Blob, WebSocketError> => {\n const Impl = options?.implementation ?? (globalThis.WebSocket as WebSocketConstructor);\n return Stream.callback<string | ArrayBuffer | Blob, WebSocketError>(\n (queue) =>\n Effect.acquireRelease(\n // See `fromEventSource`: construct and attach the listeners in one\n // synchronous step, mapping a throw to the typed error; a failed\n // acquire registers no release.\n Effect.try({\n try: () => {\n const socket = new Impl(url, options?.protocols);\n if (options?.binaryType !== undefined) socket.binaryType = options.binaryType;\n\n socket.addEventListener(\"message\", (e: MessageEvent) => {\n Queue.offerUnsafe(queue, e.data);\n });\n\n socket.addEventListener(\"error\", () => {\n Queue.failCauseUnsafe(queue, Cause.fail(new WebSocketError({ url })));\n });\n\n socket.addEventListener(\"close\", (e: CloseEvent) => {\n if (e.wasClean) {\n Queue.endUnsafe(queue);\n } else {\n Queue.failCauseUnsafe(\n queue,\n Cause.fail(new WebSocketError({ url, code: e.code, reason: e.reason })),\n );\n }\n });\n\n return socket;\n },\n catch: (cause) => new WebSocketError({ url, cause }),\n }),\n (socket) =>\n Effect.sync(() => {\n // Only close a socket that is still connecting/open.\n if (socket.readyState === 0 || socket.readyState === 1) socket.close();\n }),\n ),\n { bufferSize: options?.bufferSize ?? 16, strategy: options?.strategy ?? \"suspend\" },\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,mBAAb,cAAsC,KAAK,YAAY,kBAAkB,CAAC,CAKvE,CAAC;;;;;;;;;;;;;;;;;;;;;AA8CJ,MAAa,mBACX,KACA,YACgD;CAChD,MAAM,OAAO,SAAS,kBAAmB,WAAW;CAIpD,OAAO,OAAO,UACX,UACC,OAAO,eAKL,OAAO,IAAI;EACT,WAAW;GACT,MAAM,SACJ,SAAS,oBAAoB,KAAA,IACzB,IAAI,KAAK,GAAG,IACZ,IAAI,KAAK,KAAK,EAAE,iBAAiB,QAAQ,gBAAgB,CAAC;GAEhE,MAAM,aAAa,MAA0B;IAC3C,MAAM,YAAY,OAAO;KAAE,OAAO,EAAE;KAAM,MAAM,EAAE;KAAM,aAAa,EAAE;IAAY,CAAC;GACtF;GAEA,OAAO,iBAAiB,WAAW,SAAS;GAC5C,KAAK,MAAM,QAAQ,SAAS,UAAU,CAAC,GAAG,OAAO,iBAAiB,MAAM,SAAS;GAEjF,OAAO,iBAAiB,eAAe;IAErC,IAAI,OAAO,eAAe,GACxB,MAAM,gBAAgB,OAAO,MAAM,KAAK,IAAI,iBAAiB,EAAE,IAAI,CAAC,CAAC,CAAC;GAE1E,CAAC;GAED,OAAO;EACT;EACA,QAAQ,UAAU,IAAI,iBAAiB;GAAE;GAAK;EAAM,CAAC;CACvD,CAAC,IACA,WAAW,OAAO,WAAW,OAAO,MAAM,CAAC,CAC9C,GACF;EAAE,YAAY,SAAS,cAAc;EAAI,UAAU,SAAS,YAAY;CAAU,CACpF;AACF;;;;;;AAWA,IAAa,iBAAb,cAAoC,KAAK,YAAY,gBAAgB,CAAC,CASnE,CAAC;;;;;;;;;;;;;;;;;;;;;AA0CJ,MAAa,iBACX,KACA,YAC+D;CAC/D,MAAM,OAAO,SAAS,kBAAmB,WAAW;CACpD,OAAO,OAAO,UACX,UACC,OAAO,eAIL,OAAO,IAAI;EACT,WAAW;GACT,MAAM,SAAS,IAAI,KAAK,KAAK,SAAS,SAAS;GAC/C,IAAI,SAAS,eAAe,KAAA,GAAW,OAAO,aAAa,QAAQ;GAEnE,OAAO,iBAAiB,YAAY,MAAoB;IACtD,MAAM,YAAY,OAAO,EAAE,IAAI;GACjC,CAAC;GAED,OAAO,iBAAiB,eAAe;IACrC,MAAM,gBAAgB,OAAO,MAAM,KAAK,IAAI,eAAe,EAAE,IAAI,CAAC,CAAC,CAAC;GACtE,CAAC;GAED,OAAO,iBAAiB,UAAU,MAAkB;IAClD,IAAI,EAAE,UACJ,MAAM,UAAU,KAAK;SAErB,MAAM,gBACJ,OACA,MAAM,KAAK,IAAI,eAAe;KAAE;KAAK,MAAM,EAAE;KAAM,QAAQ,EAAE;IAAO,CAAC,CAAC,CACxE;GAEJ,CAAC;GAED,OAAO;EACT;EACA,QAAQ,UAAU,IAAI,eAAe;GAAE;GAAK;EAAM,CAAC;CACrD,CAAC,IACA,WACC,OAAO,WAAW;EAEhB,IAAI,OAAO,eAAe,KAAK,OAAO,eAAe,GAAG,OAAO,MAAM;CACvE,CAAC,CACL,GACF;EAAE,YAAY,SAAS,cAAc;EAAI,UAAU,SAAS,YAAY;CAAU,CACpF;AACF"}
@@ -3,7 +3,7 @@ import { ErrorTranslator, RequestRuntimeProvider } from "./request-runtime.js";
3
3
  import { FormErrorMapper } from "./internal/remote-run.js";
4
4
  import { Effect, Schema } from "effect";
5
5
  import * as HttpClient from "effect/http/HttpClient";
6
- import { InvalidField, RemoteCommand, RemoteForm, RemoteFormInput, RemotePrerenderFunction, RemoteQueryFunction } from "@sveltejs/kit";
6
+ import { RemoteCommand, RemoteForm, RemoteFormInput, RemoteFormInvalidField, RemotePrerenderFunction, RemoteQueryFunction } from "$app/server";
7
7
  import { StandardSchemaV1 } from "@standard-schema/spec";
8
8
  //#region src/remote-functions.d.ts
9
9
  type SvelteKitEffectEnv = HttpClient.HttpClient | SvelteKitRequestEvent;
@@ -29,9 +29,9 @@ interface QueryLiveFn<Env> {
29
29
  /** The public shape of the `form` helper, parameterised by its handler env. */
30
30
  interface FormFn<Env> {
31
31
  <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteForm<void, Output>;
32
- <Input, Output>(validate: "unchecked", fn: (data: Input, issue: InvalidField<Input & RemoteFormInput>) => Effect.Effect<Output, unknown, Env>): RemoteForm<Input & RemoteFormInput, Output>;
33
- <Sch extends StandardSchemaV1, Output>(validate: Sch, fn: (data: StandardSchemaV1.InferOutput<Sch>, issue: InvalidField<StandardSchemaV1.InferInput<Sch> & RemoteFormInput>) => Effect.Effect<Output, unknown, Env>): RemoteForm<StandardSchemaV1.InferInput<Sch> & RemoteFormInput, Output>;
34
- <S extends Schema.Top, Output>(validate: S, fn: (data: S["Type"], issue: InvalidField<S["Encoded"] & RemoteFormInput>) => Effect.Effect<Output, unknown, Env>): RemoteForm<S["Encoded"] & RemoteFormInput, Output>;
32
+ <Input, Output>(validate: "unchecked", fn: (data: Input, issue: RemoteFormInvalidField<Input & RemoteFormInput>) => Effect.Effect<Output, unknown, Env>): RemoteForm<Input & RemoteFormInput, Output>;
33
+ <Sch extends StandardSchemaV1, Output>(validate: Sch, fn: (data: StandardSchemaV1.InferOutput<Sch>, issue: RemoteFormInvalidField<StandardSchemaV1.InferInput<Sch> & RemoteFormInput>) => Effect.Effect<Output, unknown, Env>): RemoteForm<StandardSchemaV1.InferInput<Sch> & RemoteFormInput, Output>;
34
+ <S extends Schema.Top, Output>(validate: S, fn: (data: S["Type"], issue: RemoteFormInvalidField<S["Encoded"] & RemoteFormInput>) => Effect.Effect<Output, unknown, Env>): RemoteForm<S["Encoded"] & RemoteFormInput, Output>;
35
35
  }
36
36
  /** The public shape of the `command` helper, parameterised by its handler env. */
37
37
  interface CommandFn<Env> {
@@ -1 +1 @@
1
- {"version":3,"file":"remote-functions.js","names":["isBareHandlerValue"],"sources":["../src/remote-functions.ts"],"sourcesContent":["// NOTE: This module builds the `query` / `form` / `command` / `prerender`\n// *factories* that consumers call inside their own `*.remote.ts` files — it is\n// NOT itself a SvelteKit remote module, and its exports (e.g. `RemoteFunction`)\n// are deliberately not remote functions. Its filename must therefore never match\n// SvelteKit's remote-module matcher `/[/.]remote(\\.[^/]+)+$/`\n// (kit `src/exports/vite/utils.js`), or kit's build-time validation will reject\n// the built file with \"all exports from this file must be remote functions\".\n// That is why this is `remote-functions.ts` and never `remote.server.ts` /\n// `*.remote.ts`. See `tests/remote-module-naming.test.ts` for the guard.\nimport * as AppServer from \"$app/server\";\nimport type {\n RemoteCommand,\n RemoteForm,\n RemoteFormInput,\n RemotePrerenderFunction,\n RemoteQueryFunction,\n InvalidField,\n} from \"@sveltejs/kit\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport * as HttpClient from \"effect/http/HttpClient\";\nimport { Effect, type Schema } from \"effect\";\nimport { SvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nimport {\n isBareHandler as isBareHandlerValue,\n toStandardValidator,\n} from \"./internal/remote-validators.js\";\nimport {\n defaultTranslate,\n makeRunHelpers,\n type FormErrorMapper,\n type RunHelpers,\n type Runner,\n} from \"./internal/remote-run.js\";\nimport type { ErrorTranslator, RequestRuntimeProvider } from \"./request-runtime.js\";\n\nexport type { FormErrorMapper } from \"./internal/remote-run.js\";\n\ntype SvelteKitEffectEnv = HttpClient.HttpClient | SvelteKitRequestEvent;\n\n/** The default helpers: a fresh SvelteKit per-request runtime, passthrough translation. */\nconst defaultHelpers: RunHelpers = makeRunHelpers(() => {\n const event = AppServer.getRequestEvent();\n return { runExit: (effect) => SvelteKitRuntime.forRequest(event).runPromiseExit(effect) };\n}, defaultTranslate);\n\n/** Build helpers from a {@link RequestRuntimeProvider}, translator, and form-error mapper. */\nconst helpersFromProvider = (\n provider: RequestRuntimeProvider<any, any>,\n translate: ErrorTranslator,\n formError?: FormErrorMapper,\n): RunHelpers =>\n makeRunHelpers(\n () => {\n const event = AppServer.getRequestEvent();\n return {\n runExit: (effect) => provider.run(event).runPromiseExit(effect),\n dispose: () => provider.disposeAfterResponse?.(event),\n };\n },\n translate,\n formError,\n );\n\nfunction unavailable(name: string): never {\n throw new Error(\n `${name} is not available in the installed SvelteKit version. Update @sveltejs/kit to use this API.`,\n );\n}\n\ntype EffectHandler<Env> = (arg: unknown) => Effect.Effect<unknown, unknown, Env>;\ntype EffectFormHandler<Env> = (\n data: unknown,\n issue: unknown,\n) => Effect.Effect<unknown, unknown, Env>;\ntype EffectVoidHandler<Env> = () => Effect.Effect<unknown, unknown, Env>;\n\n/** True when the argument is the no-validation handler overload (typed narrowing). */\nconst isBareHandler = <Env>(value: unknown): value is EffectVoidHandler<Env> =>\n isBareHandlerValue(value);\n\n/** The public shape of the `query` helper, parameterised by its handler env. */\nexport interface QueryFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQuery = <Env>(run: Runner): QueryFn<Env> =>\n ((arg1: unknown, arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, Env>): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.query(() => run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"query requires a handler function when a schema is provided\");\n }\n\n return AppServer.query(toStandardValidator(arg1) as \"unchecked\", (arg: unknown) =>\n run(arg2(arg)),\n );\n }) as QueryFn<Env>;\n\n/** The public shape of `query.batch`, parameterised by its handler env. */\nexport interface QueryBatchFn<Env> {\n <Input, Output>(\n validate: \"unchecked\",\n fn: (args: Input[]) => Effect.Effect<(arg: Input, idx: number) => Output, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (\n args: StandardSchemaV1.InferOutput<Sch>[],\n ) => Effect.Effect<\n (arg: StandardSchemaV1.InferOutput<Sch>, idx: number) => Output,\n unknown,\n Env\n >,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (args: S[\"Type\"][]) => Effect.Effect<(arg: S[\"Type\"], idx: number) => Output, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQueryBatch = <Env>(run: Runner): QueryBatchFn<Env> =>\n ((\n validate: unknown,\n fn: (args: unknown[]) => Effect.Effect<(arg: unknown, idx: number) => unknown, unknown, Env>,\n ): any =>\n AppServer.query.batch(toStandardValidator(validate) as \"unchecked\", (args: unknown[]) =>\n run(fn(args)),\n )) as QueryBatchFn<Env>;\n\ntype QueryLiveFactory = (\n validate: unknown,\n fn: (arg: unknown) => Promise<AsyncIterable<unknown>> | AsyncIterable<unknown>,\n) => unknown;\n\ntype QueryWithCompat = typeof AppServer.query & {\n live?: QueryLiveFactory;\n};\n\ntype AppServerWithCompat = typeof AppServer & {\n requested?: (...args: unknown[]) => unknown;\n};\n\nconst appServerCompat = AppServer as AppServerWithCompat;\nconst queryCompat = AppServer.query as QueryWithCompat;\n\nfunction getQueryLive(): QueryLiveFactory {\n const live = queryCompat.live;\n\n if (typeof live !== \"function\") {\n unavailable(\"query.live\");\n }\n\n return live;\n}\n\nfunction getRequested(): (...args: unknown[]) => unknown {\n const requestedFn = appServerCompat.requested;\n\n if (typeof requestedFn !== \"function\") {\n unavailable(\"requested\");\n }\n\n return requestedFn;\n}\n\n/** The public shape of `query.live`, parameterised by its handler env. */\nexport interface QueryLiveFn<Env> {\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (\n arg: StandardSchemaV1.InferOutput<Sch>,\n ) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQueryLive = <Env>(run: Runner): QueryLiveFn<Env> =>\n ((\n validate: unknown,\n fn: (arg: unknown) => Effect.Effect<AsyncIterable<unknown>, unknown, Env>,\n ): any => {\n const live = getQueryLive();\n return live(toStandardValidator(validate), (arg) => run(fn(arg)));\n }) as QueryLiveFn<Env>;\n\n/** The public shape of the `form` helper, parameterised by its handler env. */\nexport interface FormFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteForm<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (\n data: Input,\n issue: InvalidField<Input & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<Input & RemoteFormInput, Output>;\n <Sch extends StandardSchemaV1, Output>(\n validate: Sch,\n fn: (\n data: StandardSchemaV1.InferOutput<Sch>,\n issue: InvalidField<StandardSchemaV1.InferInput<Sch> & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<StandardSchemaV1.InferInput<Sch> & RemoteFormInput, Output>;\n <S extends Schema.Top, Output>(\n validate: S,\n fn: (\n data: S[\"Type\"],\n issue: InvalidField<S[\"Encoded\"] & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<S[\"Encoded\"] & RemoteFormInput, Output>;\n}\n\nconst makeForm = <Env>(helpers: RunHelpers): FormFn<Env> =>\n ((arg1: unknown, arg2?: unknown): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.form(() => helpers.run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"form requires a handler function when a schema is provided\");\n }\n\n return AppServer.form(\n toStandardValidator(arg1) as \"unchecked\",\n (data: unknown, issue: unknown) =>\n helpers.runForm((arg2 as EffectFormHandler<Env>)(data, issue), {\n issue: issue as InvalidField<any>,\n data,\n }),\n );\n }) as FormFn<Env>;\n\n/** The public shape of the `command` helper, parameterised by its handler env. */\nexport interface CommandFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteCommand<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n validate: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n validate: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<S[\"Encoded\"], Output>;\n}\n\nconst makeCommand = <Env>(run: Runner): CommandFn<Env> =>\n ((arg1: unknown, arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, Env>): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.command(() => run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"command requires a handler function when a schema is provided\");\n }\n\n return AppServer.command(toStandardValidator(arg1) as \"unchecked\", (arg: unknown) =>\n run(arg2(arg)),\n );\n }) as CommandFn<Env>;\n\n/** The public shape of the `prerender` helper, parameterised by its handler env. */\nexport interface PrerenderFn<Env> {\n <Output>(\n fn: () => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<void[]> | void[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<Input[]> | Input[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () =>\n | Promise<StandardSchemaV1.InferInput<Sch>[]>\n | StandardSchemaV1.InferInput<Sch>[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<S[\"Encoded\"][]> | S[\"Encoded\"][];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<S[\"Encoded\"], Output>;\n}\n\nconst makePrerender = <Env>(run: Runner): PrerenderFn<Env> =>\n ((\n arg1: unknown,\n arg2?:\n | ((arg: unknown) => Effect.Effect<unknown, unknown, Env>)\n | {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n arg3?: {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n ): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.prerender(\n () => run(handler()),\n arg2 as { inputs?: () => Promise<void[]> | void[]; dynamic?: boolean } | undefined,\n );\n }\n\n if (typeof arg2 !== \"function\") {\n throw new Error(\"prerender requires a handler function when a schema is provided\");\n }\n\n return AppServer.prerender(\n toStandardValidator(arg1) as \"unchecked\",\n (arg: unknown) => run((arg2 as EffectHandler<Env>)(arg)),\n arg3,\n );\n }) as PrerenderFn<Env>;\n\n/**\n * SvelteKit's `requested` from `$app/server`, passed through. Throws when the\n * installed SvelteKit version does not provide it.\n */\nexport function requested<Result = unknown>(...args: unknown[]): Result {\n const requestedFn = getRequested();\n return requestedFn(...args) as Result;\n}\n\n/** A `query` with its `batch` / `live` helpers attached. */\nexport type QueryNamespace<Env> = QueryFn<Env> & {\n batch: QueryBatchFn<Env>;\n live: QueryLiveFn<Env>;\n};\n\nconst makeQueryNamespace = <Env>(run: Runner): QueryNamespace<Env> =>\n Object.assign(makeQuery<Env>(run), {\n batch: makeQueryBatch<Env>(run),\n live: makeQueryLive<Env>(run),\n }) as QueryNamespace<Env>;\n\n// ── Standalone exports (default SvelteKit per-request runtime) ──────────────\n\n/**\n * SvelteKit's remote `query` with an Effect handler. Accepts `\"unchecked\"`, a\n * Standard Schema or a bare Effect `Schema` as the validator; `query.batch`\n * and `query.live` are attached.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const query: QueryNamespace<SvelteKitEffectEnv> = makeQueryNamespace(defaultHelpers.run);\n/**\n * SvelteKit's remote `query.batch` with an Effect handler (the same function\n * as `query.batch`).\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const queryBatch: QueryBatchFn<SvelteKitEffectEnv> = query.batch;\n/**\n * SvelteKit's remote `query.live` with an Effect handler that returns an\n * `AsyncIterable` (the same function as `query.live`).\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const queryLive: QueryLiveFn<SvelteKitEffectEnv> = query.live;\n/**\n * SvelteKit's remote `form` with an Effect handler.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const form: FormFn<SvelteKitEffectEnv> = makeForm(defaultHelpers);\n/**\n * SvelteKit's remote `command` with an Effect handler.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const command: CommandFn<SvelteKitEffectEnv> = makeCommand(defaultHelpers.run);\n/**\n * SvelteKit's remote `prerender` with an Effect handler.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const prerender: PrerenderFn<SvelteKitEffectEnv> = makePrerender(defaultHelpers.run);\n\n/** The set of remote-function helpers bound to a particular runner. */\nexport interface RemoteFunctions<Env> {\n readonly query: QueryNamespace<Env>;\n readonly form: FormFn<Env>;\n readonly command: CommandFn<Env>;\n readonly prerender: PrerenderFn<Env>;\n}\n\n/**\n * Bind the remote-function helpers to a {@link RequestRuntimeProvider} (and an\n * optional boundary error translator), so their handlers run on the provider's\n * services `Env` — your own application catalogue — instead of the fixed\n * {@link SvelteKitServices}. The Standard-Schema / `\"unchecked\"` validation\n * argument and the native return types (`RemoteQueryFunction`, `RemoteForm`,\n * ...) are exactly as the standalone helpers.\n *\n * For remote forms, the failure path runs `opts.formError` when provided —\n * mapping a structured/nested error shape into SvelteKit's field issues (via the\n * `issue` helper / `invalid()`) so a domain validation failure drives\n * field-level invalidation rather than a generic failure. Otherwise the form\n * falls back to `translateError`.\n *\n * @example\n * ```ts\n * const { query, form, command } = RemoteFunction.withRuntime(\n * RequestRuntime.fromLocals((event) => event.locals.runtime),\n * {\n * translateError: toSvelteKit,\n * formError: (error, { issue }) => {\n * if (error instanceof ValidationFailure) issue.field(error.field, error.message);\n * invalid();\n * },\n * },\n * );\n *\n * export const items = query(() =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return yield* db.list();\n * }),\n * );\n * ```\n */\nexport function withRuntime<R>(\n provider: RequestRuntimeProvider<R, any>,\n opts?: { translateError?: ErrorTranslator; formError?: FormErrorMapper },\n): RemoteFunctions<R> {\n const helpers = helpersFromProvider(\n provider,\n opts?.translateError ?? defaultTranslate,\n opts?.formError,\n );\n return {\n query: makeQueryNamespace<R>(helpers.run),\n form: makeForm<R>(helpers),\n command: makeCommand<R>(helpers.run),\n prerender: makePrerender<R>(helpers.run),\n };\n}\n\n/**\n * The remote-function helpers (`query`, `form`, `command`, `prerender`,\n * `requested`) bound to the default per-request runtime; `withRuntime` binds\n * them to your own services.\n */\nexport const RemoteFunction = {\n query,\n form,\n command,\n prerender,\n requested,\n withRuntime,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"remote-functions.js","names":["isBareHandlerValue"],"sources":["../src/remote-functions.ts"],"sourcesContent":["// NOTE: This module builds the `query` / `form` / `command` / `prerender`\n// *factories* that consumers call inside their own `*.remote.ts` files — it is\n// NOT itself a SvelteKit remote module, and its exports (e.g. `RemoteFunction`)\n// are deliberately not remote functions. Its filename must therefore never match\n// SvelteKit's remote-module matcher `/[/.]remote(\\.[^/]+)+$/`\n// (kit `src/exports/vite/utils.js`), or kit's build-time validation will reject\n// the built file with \"all exports from this file must be remote functions\".\n// That is why this is `remote-functions.ts` and never `remote.server.ts` /\n// `*.remote.ts`. See `tests/remote-module-naming.test.ts` for the guard.\nimport * as AppServer from \"$app/server\";\nimport type {\n RemoteCommand,\n RemoteForm,\n RemoteFormInput,\n RemotePrerenderFunction,\n RemoteQueryFunction,\n RemoteFormInvalidField as InvalidField,\n} from \"$app/server\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport * as HttpClient from \"effect/http/HttpClient\";\nimport { Effect, type Schema } from \"effect\";\nimport { SvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nimport {\n isBareHandler as isBareHandlerValue,\n toStandardValidator,\n} from \"./internal/remote-validators.js\";\nimport {\n defaultTranslate,\n makeRunHelpers,\n type FormErrorMapper,\n type RunHelpers,\n type Runner,\n} from \"./internal/remote-run.js\";\nimport type { ErrorTranslator, RequestRuntimeProvider } from \"./request-runtime.js\";\n\nexport type { FormErrorMapper } from \"./internal/remote-run.js\";\n\ntype SvelteKitEffectEnv = HttpClient.HttpClient | SvelteKitRequestEvent;\n\n/** The default helpers: a fresh SvelteKit per-request runtime, passthrough translation. */\nconst defaultHelpers: RunHelpers = makeRunHelpers(() => {\n const event = AppServer.getRequestEvent();\n return { runExit: (effect) => SvelteKitRuntime.forRequest(event).runPromiseExit(effect) };\n}, defaultTranslate);\n\n/** Build helpers from a {@link RequestRuntimeProvider}, translator, and form-error mapper. */\nconst helpersFromProvider = (\n provider: RequestRuntimeProvider<any, any>,\n translate: ErrorTranslator,\n formError?: FormErrorMapper,\n): RunHelpers =>\n makeRunHelpers(\n () => {\n const event = AppServer.getRequestEvent();\n return {\n runExit: (effect) => provider.run(event).runPromiseExit(effect),\n dispose: () => provider.disposeAfterResponse?.(event),\n };\n },\n translate,\n formError,\n );\n\nfunction unavailable(name: string): never {\n throw new Error(\n `${name} is not available in the installed SvelteKit version. Update @sveltejs/kit to use this API.`,\n );\n}\n\ntype EffectHandler<Env> = (arg: unknown) => Effect.Effect<unknown, unknown, Env>;\ntype EffectFormHandler<Env> = (\n data: unknown,\n issue: unknown,\n) => Effect.Effect<unknown, unknown, Env>;\ntype EffectVoidHandler<Env> = () => Effect.Effect<unknown, unknown, Env>;\n\n/** True when the argument is the no-validation handler overload (typed narrowing). */\nconst isBareHandler = <Env>(value: unknown): value is EffectVoidHandler<Env> =>\n isBareHandlerValue(value);\n\n/** The public shape of the `query` helper, parameterised by its handler env. */\nexport interface QueryFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQuery = <Env>(run: Runner): QueryFn<Env> =>\n ((arg1: unknown, arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, Env>): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.query(() => run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"query requires a handler function when a schema is provided\");\n }\n\n return AppServer.query(toStandardValidator(arg1) as \"unchecked\", (arg: unknown) =>\n run(arg2(arg)),\n );\n }) as QueryFn<Env>;\n\n/** The public shape of `query.batch`, parameterised by its handler env. */\nexport interface QueryBatchFn<Env> {\n <Input, Output>(\n validate: \"unchecked\",\n fn: (args: Input[]) => Effect.Effect<(arg: Input, idx: number) => Output, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (\n args: StandardSchemaV1.InferOutput<Sch>[],\n ) => Effect.Effect<\n (arg: StandardSchemaV1.InferOutput<Sch>, idx: number) => Output,\n unknown,\n Env\n >,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (args: S[\"Type\"][]) => Effect.Effect<(arg: S[\"Type\"], idx: number) => Output, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQueryBatch = <Env>(run: Runner): QueryBatchFn<Env> =>\n ((\n validate: unknown,\n fn: (args: unknown[]) => Effect.Effect<(arg: unknown, idx: number) => unknown, unknown, Env>,\n ): any =>\n AppServer.query.batch(toStandardValidator(validate) as \"unchecked\", (args: unknown[]) =>\n run(fn(args)),\n )) as QueryBatchFn<Env>;\n\ntype QueryLiveFactory = (\n validate: unknown,\n fn: (arg: unknown) => Promise<AsyncIterable<unknown>> | AsyncIterable<unknown>,\n) => unknown;\n\ntype QueryWithCompat = typeof AppServer.query & {\n live?: QueryLiveFactory;\n};\n\ntype AppServerWithCompat = typeof AppServer & {\n requested?: (...args: unknown[]) => unknown;\n};\n\nconst appServerCompat = AppServer as AppServerWithCompat;\nconst queryCompat = AppServer.query as QueryWithCompat;\n\nfunction getQueryLive(): QueryLiveFactory {\n const live = queryCompat.live;\n\n if (typeof live !== \"function\") {\n unavailable(\"query.live\");\n }\n\n return live;\n}\n\nfunction getRequested(): (...args: unknown[]) => unknown {\n const requestedFn = appServerCompat.requested;\n\n if (typeof requestedFn !== \"function\") {\n unavailable(\"requested\");\n }\n\n return requestedFn;\n}\n\n/** The public shape of `query.live`, parameterised by its handler env. */\nexport interface QueryLiveFn<Env> {\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (\n arg: StandardSchemaV1.InferOutput<Sch>,\n ) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQueryLive = <Env>(run: Runner): QueryLiveFn<Env> =>\n ((\n validate: unknown,\n fn: (arg: unknown) => Effect.Effect<AsyncIterable<unknown>, unknown, Env>,\n ): any => {\n const live = getQueryLive();\n return live(toStandardValidator(validate), (arg) => run(fn(arg)));\n }) as QueryLiveFn<Env>;\n\n/** The public shape of the `form` helper, parameterised by its handler env. */\nexport interface FormFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteForm<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (\n data: Input,\n issue: InvalidField<Input & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<Input & RemoteFormInput, Output>;\n <Sch extends StandardSchemaV1, Output>(\n validate: Sch,\n fn: (\n data: StandardSchemaV1.InferOutput<Sch>,\n issue: InvalidField<StandardSchemaV1.InferInput<Sch> & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<StandardSchemaV1.InferInput<Sch> & RemoteFormInput, Output>;\n <S extends Schema.Top, Output>(\n validate: S,\n fn: (\n data: S[\"Type\"],\n issue: InvalidField<S[\"Encoded\"] & RemoteFormInput>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<S[\"Encoded\"] & RemoteFormInput, Output>;\n}\n\nconst makeForm = <Env>(helpers: RunHelpers): FormFn<Env> =>\n ((arg1: unknown, arg2?: unknown): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.form(() => helpers.run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"form requires a handler function when a schema is provided\");\n }\n\n return AppServer.form(\n toStandardValidator(arg1) as \"unchecked\",\n (data: unknown, issue: unknown) =>\n helpers.runForm((arg2 as EffectFormHandler<Env>)(data, issue), {\n issue: issue as InvalidField<any>,\n data,\n }),\n );\n }) as FormFn<Env>;\n\n/** The public shape of the `command` helper, parameterised by its handler env. */\nexport interface CommandFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteCommand<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n validate: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n validate: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<S[\"Encoded\"], Output>;\n}\n\nconst makeCommand = <Env>(run: Runner): CommandFn<Env> =>\n ((arg1: unknown, arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, Env>): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.command(() => run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"command requires a handler function when a schema is provided\");\n }\n\n return AppServer.command(toStandardValidator(arg1) as \"unchecked\", (arg: unknown) =>\n run(arg2(arg)),\n );\n }) as CommandFn<Env>;\n\n/** The public shape of the `prerender` helper, parameterised by its handler env. */\nexport interface PrerenderFn<Env> {\n <Output>(\n fn: () => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<void[]> | void[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<Input[]> | Input[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () =>\n | Promise<StandardSchemaV1.InferInput<Sch>[]>\n | StandardSchemaV1.InferInput<Sch>[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<S[\"Encoded\"][]> | S[\"Encoded\"][];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<S[\"Encoded\"], Output>;\n}\n\nconst makePrerender = <Env>(run: Runner): PrerenderFn<Env> =>\n ((\n arg1: unknown,\n arg2?:\n | ((arg: unknown) => Effect.Effect<unknown, unknown, Env>)\n | {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n arg3?: {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n ): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.prerender(\n () => run(handler()),\n arg2 as { inputs?: () => Promise<void[]> | void[]; dynamic?: boolean } | undefined,\n );\n }\n\n if (typeof arg2 !== \"function\") {\n throw new Error(\"prerender requires a handler function when a schema is provided\");\n }\n\n return AppServer.prerender(\n toStandardValidator(arg1) as \"unchecked\",\n (arg: unknown) => run((arg2 as EffectHandler<Env>)(arg)),\n arg3,\n );\n }) as PrerenderFn<Env>;\n\n/**\n * SvelteKit's `requested` from `$app/server`, passed through. Throws when the\n * installed SvelteKit version does not provide it.\n */\nexport function requested<Result = unknown>(...args: unknown[]): Result {\n const requestedFn = getRequested();\n return requestedFn(...args) as Result;\n}\n\n/** A `query` with its `batch` / `live` helpers attached. */\nexport type QueryNamespace<Env> = QueryFn<Env> & {\n batch: QueryBatchFn<Env>;\n live: QueryLiveFn<Env>;\n};\n\nconst makeQueryNamespace = <Env>(run: Runner): QueryNamespace<Env> =>\n Object.assign(makeQuery<Env>(run), {\n batch: makeQueryBatch<Env>(run),\n live: makeQueryLive<Env>(run),\n }) as QueryNamespace<Env>;\n\n// ── Standalone exports (default SvelteKit per-request runtime) ──────────────\n\n/**\n * SvelteKit's remote `query` with an Effect handler. Accepts `\"unchecked\"`, a\n * Standard Schema or a bare Effect `Schema` as the validator; `query.batch`\n * and `query.live` are attached.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const query: QueryNamespace<SvelteKitEffectEnv> = makeQueryNamespace(defaultHelpers.run);\n/**\n * SvelteKit's remote `query.batch` with an Effect handler (the same function\n * as `query.batch`).\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const queryBatch: QueryBatchFn<SvelteKitEffectEnv> = query.batch;\n/**\n * SvelteKit's remote `query.live` with an Effect handler that returns an\n * `AsyncIterable` (the same function as `query.live`).\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const queryLive: QueryLiveFn<SvelteKitEffectEnv> = query.live;\n/**\n * SvelteKit's remote `form` with an Effect handler.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const form: FormFn<SvelteKitEffectEnv> = makeForm(defaultHelpers);\n/**\n * SvelteKit's remote `command` with an Effect handler.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const command: CommandFn<SvelteKitEffectEnv> = makeCommand(defaultHelpers.run);\n/**\n * SvelteKit's remote `prerender` with an Effect handler.\n *\n * Handlers run on the default per-request runtime (`HttpClient` plus\n * `SvelteKitRequestEvent`); a failure that is not a SvelteKit `error()` /\n * `redirect()` becomes a 500. Use {@link withRuntime} to run on your own\n * services.\n */\nexport const prerender: PrerenderFn<SvelteKitEffectEnv> = makePrerender(defaultHelpers.run);\n\n/** The set of remote-function helpers bound to a particular runner. */\nexport interface RemoteFunctions<Env> {\n readonly query: QueryNamespace<Env>;\n readonly form: FormFn<Env>;\n readonly command: CommandFn<Env>;\n readonly prerender: PrerenderFn<Env>;\n}\n\n/**\n * Bind the remote-function helpers to a {@link RequestRuntimeProvider} (and an\n * optional boundary error translator), so their handlers run on the provider's\n * services `Env` — your own application catalogue — instead of the fixed\n * {@link SvelteKitServices}. The Standard-Schema / `\"unchecked\"` validation\n * argument and the native return types (`RemoteQueryFunction`, `RemoteForm`,\n * ...) are exactly as the standalone helpers.\n *\n * For remote forms, the failure path runs `opts.formError` when provided —\n * mapping a structured/nested error shape into SvelteKit's field issues (via the\n * `issue` helper / `invalid()`) so a domain validation failure drives\n * field-level invalidation rather than a generic failure. Otherwise the form\n * falls back to `translateError`.\n *\n * @example\n * ```ts\n * const { query, form, command } = RemoteFunction.withRuntime(\n * RequestRuntime.fromLocals((event) => event.locals.runtime),\n * {\n * translateError: toSvelteKit,\n * formError: (error, { issue }) => {\n * if (error instanceof ValidationFailure) issue.field(error.field, error.message);\n * invalid();\n * },\n * },\n * );\n *\n * export const items = query(() =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return yield* db.list();\n * }),\n * );\n * ```\n */\nexport function withRuntime<R>(\n provider: RequestRuntimeProvider<R, any>,\n opts?: { translateError?: ErrorTranslator; formError?: FormErrorMapper },\n): RemoteFunctions<R> {\n const helpers = helpersFromProvider(\n provider,\n opts?.translateError ?? defaultTranslate,\n opts?.formError,\n );\n return {\n query: makeQueryNamespace<R>(helpers.run),\n form: makeForm<R>(helpers),\n command: makeCommand<R>(helpers.run),\n prerender: makePrerender<R>(helpers.run),\n };\n}\n\n/**\n * The remote-function helpers (`query`, `form`, `command`, `prerender`,\n * `requested`) bound to the default per-request runtime; `withRuntime` binds\n * them to your own services.\n */\nexport const RemoteFunction = {\n query,\n form,\n command,\n prerender,\n requested,\n withRuntime,\n};\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { RemoteResource } from "@sveltejs/kit";
1
+ import { RemoteResource } from "$app/server";
2
2
  import * as AsyncResult from "effect/reactivity/AsyncResult";
3
3
  //#region src/remote-reactive.d.ts
4
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"remote-reactive.js","names":[],"sources":["../src/remote-reactive.ts"],"sourcesContent":["import type { RemoteResource } from \"@sveltejs/kit\";\nimport * as AsyncResult from \"effect/reactivity/AsyncResult\";\n\n/**\n * Bridges a SvelteKit remote resource (`query(...)`, `query.live(...)`,\n * `prerender(...)`) into Effect's native\n * {@link AsyncResult.AsyncResult} — the same result type the Svelte hooks in\n * `@thomasfosterau/effect-svelte` expose — so remote-function data folds into\n * the same templates and combinators (`AsyncResult.match`, `isSuccess`, ...).\n *\n * A `RemoteResource<T>` is already reactive (`current` / `loading` / `error` /\n * `ready`); reading them inside the returned `current` getter tracks them, so\n * the result updates as the resource does. The `loading` flag is surfaced as\n * `AsyncResult`'s `waiting` (stale-while-revalidate): a refresh keeps the\n * previous `Success` on screen.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { useRemoteQuery } from '@thomasfosterau/effect-sveltekit/client';\n * import { AsyncResult } from '@thomasfosterau/effect-svelte';\n * import { getTodos } from './todos.remote';\n *\n * const todos = useRemoteQuery(getTodos());\n * </script>\n *\n * {#if AsyncResult.isSuccess(todos.current)}\n * <ul>{#each todos.current.value as todo}<li>{todo.text}</li>{/each}</ul>\n * {/if}\n * ```\n */\nexport function useRemoteQuery<T>(query: RemoteResource<T>): {\n readonly current: AsyncResult.AsyncResult<T, unknown>;\n} {\n return {\n get current() {\n // Read every reactive field so the consumer's effect tracks the resource.\n const loading = query.loading;\n const error = query.error;\n const ready = query.ready;\n\n if (error !== undefined && error !== null) {\n return AsyncResult.fail<unknown, T>(error, { waiting: loading });\n }\n if (ready) {\n return AsyncResult.success<T, unknown>(query.current as T, { waiting: loading });\n }\n return AsyncResult.initial<T, unknown>(loading);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,eAAkB,OAEhC;CACA,OAAO,EACL,IAAI,UAAU;EAEZ,MAAM,UAAU,MAAM;EACtB,MAAM,QAAQ,MAAM;EACpB,MAAM,QAAQ,MAAM;EAEpB,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC,OAAO,YAAY,KAAiB,OAAO,EAAE,SAAS,QAAQ,CAAC;EAEjE,IAAI,OACF,OAAO,YAAY,QAAoB,MAAM,SAAc,EAAE,SAAS,QAAQ,CAAC;EAEjF,OAAO,YAAY,QAAoB,OAAO;CAChD,EACF;AACF"}
1
+ {"version":3,"file":"remote-reactive.js","names":[],"sources":["../src/remote-reactive.ts"],"sourcesContent":["import type { RemoteResource } from \"$app/server\";\nimport * as AsyncResult from \"effect/reactivity/AsyncResult\";\n\n/**\n * Bridges a SvelteKit remote resource (`query(...)`, `query.live(...)`,\n * `prerender(...)`) into Effect's native\n * {@link AsyncResult.AsyncResult} — the same result type the Svelte hooks in\n * `@thomasfosterau/effect-svelte` expose — so remote-function data folds into\n * the same templates and combinators (`AsyncResult.match`, `isSuccess`, ...).\n *\n * A `RemoteResource<T>` is already reactive (`current` / `loading` / `error` /\n * `ready`); reading them inside the returned `current` getter tracks them, so\n * the result updates as the resource does. The `loading` flag is surfaced as\n * `AsyncResult`'s `waiting` (stale-while-revalidate): a refresh keeps the\n * previous `Success` on screen.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { useRemoteQuery } from '@thomasfosterau/effect-sveltekit/client';\n * import { AsyncResult } from '@thomasfosterau/effect-svelte';\n * import { getTodos } from './todos.remote';\n *\n * const todos = useRemoteQuery(getTodos());\n * </script>\n *\n * {#if AsyncResult.isSuccess(todos.current)}\n * <ul>{#each todos.current.value as todo}<li>{todo.text}</li>{/each}</ul>\n * {/if}\n * ```\n */\nexport function useRemoteQuery<T>(query: RemoteResource<T>): {\n readonly current: AsyncResult.AsyncResult<T, unknown>;\n} {\n return {\n get current() {\n // Read every reactive field so the consumer's effect tracks the resource.\n const loading = query.loading;\n const error = query.error;\n const ready = query.ready;\n\n if (error !== undefined && error !== null) {\n return AsyncResult.fail<unknown, T>(error, { waiting: loading });\n }\n if (ready) {\n return AsyncResult.success<T, unknown>(query.current as T, { waiting: loading });\n }\n return AsyncResult.initial<T, unknown>(loading);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,eAAkB,OAEhC;CACA,OAAO,EACL,IAAI,UAAU;EAEZ,MAAM,UAAU,MAAM;EACtB,MAAM,QAAQ,MAAM;EACpB,MAAM,QAAQ,MAAM;EAEpB,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC,OAAO,YAAY,KAAiB,OAAO,EAAE,SAAS,QAAQ,CAAC;EAEjE,IAAI,OACF,OAAO,YAAY,QAAoB,MAAM,SAAc,EAAE,SAAS,QAAQ,CAAC;EAEjF,OAAO,YAAY,QAAoB,OAAO;CAChD,EACF;AACF"}
@@ -1,4 +1,4 @@
1
- import { Transporter } from "@sveltejs/kit";
1
+ import { Transporter } from "@sveltejs/kit/hooks";
2
2
  //#region src/response-headers.d.ts
3
3
  /**
4
4
  * A `Headers` that serialises to `{}` and knows how to copy itself onto a
@@ -1 +1 @@
1
- {"version":3,"file":"response-headers.js","names":[],"sources":["../src/response-headers.ts"],"sourcesContent":["/**\n * A per-request **response-headers buffer** that can ride through `load` data.\n *\n * SvelteKit's `setHeaders` refuses `set-cookie` and throws when the same header\n * is set twice, and components rendering during SSR cannot reach it at all. The\n * pattern the applications converged on is a mutable `Headers` buffer on\n * `event.locals`: loads (and SSR components, via `page.data`) append to it, and\n * a server `handle` copies it onto the outgoing response after `resolve()`.\n *\n * Returning that buffer from a `load` needs a `transport` entry, or SvelteKit\n * refuses to serialise it (\"Cannot stringify arbitrary non-POJOs\"). Its contents\n * are deliberately *not* round-tripped — the server hook reads the live buffer\n * after rendering, so the client only needs a fresh, empty instance.\n * {@link ResponseHeaders} is the buffer, {@link responseHeadersTransporter} the\n * transport entry, and {@link ResponseHeaders.appendTo} the copy step.\n *\n * @example\n * ```ts\n * // src/hooks.ts\n * import { Hooks } from \"@thomasfosterau/effect-sveltekit\";\n *\n * export const transport = Hooks.transport({ ResponseHeaders: Hooks.responseHeadersTransporter });\n *\n * // src/hooks.server.ts\n * import { ResponseHeaders } from \"@thomasfosterau/effect-sveltekit\";\n * import type { Handle } from \"@sveltejs/kit\";\n *\n * export const handle: Handle = async ({ event, resolve }) => {\n * event.locals.headers = new ResponseHeaders();\n * const response = await resolve(event);\n * event.locals.headers.appendTo(response.headers);\n * return response;\n * };\n *\n * // src/routes/+layout.server.ts — expose the buffer to every page\n * export const load = ({ locals }) => ({ headers: locals.headers });\n * ```\n *\n * @module\n */\n\nimport type { Transporter } from \"@sveltejs/kit\";\nimport { markerTransporter } from \"./transport.js\";\n\n/**\n * A `Headers` that serialises to `{}` and knows how to copy itself onto a\n * response. Everything else is the platform `Headers`.\n */\nexport class ResponseHeaders extends globalThis.Headers {\n /**\n * Append every buffered header to `target` (typically `response.headers`\n * after `resolve()`). Appends rather than sets, so repeated headers such as\n * `set-cookie` all survive.\n */\n appendTo(target: globalThis.Headers): void {\n for (const cookie of this.getSetCookie()) target.append(\"set-cookie\", cookie);\n this.forEach((value, key) => {\n if (key !== \"set-cookie\") target.append(key, value);\n });\n }\n\n /** Serialises to an empty object: the contents never leave the server. */\n toJSON(): Record<string, never> {\n return {};\n }\n}\n\n/**\n * The `transport` entry for {@link ResponseHeaders}: detected on the server,\n * reconstructed on the client as a fresh, empty buffer.\n */\nexport const responseHeadersTransporter: Transporter<ResponseHeaders, unknown> = markerTransporter({\n guard: (value): value is ResponseHeaders => value instanceof ResponseHeaders,\n make: () => new ResponseHeaders(),\n});\n"],"mappings":";;;;;;AAgDA,IAAa,kBAAb,cAAqC,WAAW,QAAQ;;;;;;CAMtD,SAAS,QAAkC;EACzC,KAAK,MAAM,UAAU,KAAK,aAAa,GAAG,OAAO,OAAO,cAAc,MAAM;EAC5E,KAAK,SAAS,OAAO,QAAQ;GAC3B,IAAI,QAAQ,cAAc,OAAO,OAAO,KAAK,KAAK;EACpD,CAAC;CACH;;CAGA,SAAgC;EAC9B,OAAO,CAAC;CACV;AACF;;;;;AAMA,MAAa,6BAAoE,kBAAkB;CACjG,QAAQ,UAAoC,iBAAiB;CAC7D,YAAY,IAAI,gBAAgB;AAClC,CAAC"}
1
+ {"version":3,"file":"response-headers.js","names":[],"sources":["../src/response-headers.ts"],"sourcesContent":["/**\n * A per-request **response-headers buffer** that can ride through `load` data.\n *\n * SvelteKit's `setHeaders` refuses `set-cookie` and throws when the same header\n * is set twice, and components rendering during SSR cannot reach it at all. The\n * pattern the applications converged on is a mutable `Headers` buffer on\n * `event.locals`: loads (and SSR components, via `page.data`) append to it, and\n * a server `handle` copies it onto the outgoing response after `resolve()`.\n *\n * Returning that buffer from a `load` needs a `transport` entry, or SvelteKit\n * refuses to serialise it (\"Cannot stringify arbitrary non-POJOs\"). Its contents\n * are deliberately *not* round-tripped — the server hook reads the live buffer\n * after rendering, so the client only needs a fresh, empty instance.\n * {@link ResponseHeaders} is the buffer, {@link responseHeadersTransporter} the\n * transport entry, and {@link ResponseHeaders.appendTo} the copy step.\n *\n * @example\n * ```ts\n * // src/hooks.ts\n * import { Hooks } from \"@thomasfosterau/effect-sveltekit\";\n *\n * export const transport = Hooks.transport({ ResponseHeaders: Hooks.responseHeadersTransporter });\n *\n * // src/hooks.server.ts\n * import { ResponseHeaders } from \"@thomasfosterau/effect-sveltekit\";\n * import type { Handle } from \"@sveltejs/kit/hooks\";\n *\n * export const handle: Handle = async ({ event, resolve }) => {\n * event.locals.headers = new ResponseHeaders();\n * const response = await resolve(event);\n * event.locals.headers.appendTo(response.headers);\n * return response;\n * };\n *\n * // src/routes/+layout.server.ts — expose the buffer to every page\n * export const load = ({ locals }) => ({ headers: locals.headers });\n * ```\n *\n * @module\n */\n\nimport type { Transporter } from \"@sveltejs/kit/hooks\";\nimport { markerTransporter } from \"./transport.js\";\n\n/**\n * A `Headers` that serialises to `{}` and knows how to copy itself onto a\n * response. Everything else is the platform `Headers`.\n */\nexport class ResponseHeaders extends globalThis.Headers {\n /**\n * Append every buffered header to `target` (typically `response.headers`\n * after `resolve()`). Appends rather than sets, so repeated headers such as\n * `set-cookie` all survive.\n */\n appendTo(target: globalThis.Headers): void {\n for (const cookie of this.getSetCookie()) target.append(\"set-cookie\", cookie);\n this.forEach((value, key) => {\n if (key !== \"set-cookie\") target.append(key, value);\n });\n }\n\n /** Serialises to an empty object: the contents never leave the server. */\n toJSON(): Record<string, never> {\n return {};\n }\n}\n\n/**\n * The `transport` entry for {@link ResponseHeaders}: detected on the server,\n * reconstructed on the client as a fresh, empty buffer.\n */\nexport const responseHeadersTransporter: Transporter<ResponseHeaders, unknown> = markerTransporter({\n guard: (value): value is ResponseHeaders => value instanceof ResponseHeaders,\n make: () => new ResponseHeaders(),\n});\n"],"mappings":";;;;;;AAgDA,IAAa,kBAAb,cAAqC,WAAW,QAAQ;;;;;;CAMtD,SAAS,QAAkC;EACzC,KAAK,MAAM,UAAU,KAAK,aAAa,GAAG,OAAO,OAAO,cAAc,MAAM;EAC5E,KAAK,SAAS,OAAO,QAAQ;GAC3B,IAAI,QAAQ,cAAc,OAAO,OAAO,KAAK,KAAK;EACpD,CAAC;CACH;;CAGA,SAAgC;EAC9B,OAAO,CAAC;CACV;AACF;;;;;AAMA,MAAa,6BAAoE,kBAAkB;CACjG,QAAQ,UAAoC,iBAAiB;CAC7D,YAAY,IAAI,gBAAgB;AAClC,CAAC"}
@@ -49,8 +49,8 @@ interface EndpointOptions {
49
49
  * ```ts
50
50
  * // src/routes/api/[...path]/+server.ts
51
51
  * import { Endpoint } from "@thomasfosterau/effect-sveltekit/server";
52
- * import { api } from "$lib/server/api";
53
- * import { ApiLive } from "$lib/server/handlers";
52
+ * import { api } from "#lib/server/api.ts";
53
+ * import { ApiLive } from "#lib/server/handlers.ts";
54
54
  *
55
55
  * export const fallback = Endpoint.fromApi(api, ApiLive, { prefix: "/api" });
56
56
  * ```
@@ -55,8 +55,8 @@ const apiPlatform = Layer.mergeAll(Path.layer, Etag.layerWeak, HttpPlatform.laye
55
55
  * ```ts
56
56
  * // src/routes/api/[...path]/+server.ts
57
57
  * import { Endpoint } from "@thomasfosterau/effect-sveltekit/server";
58
- * import { api } from "$lib/server/api";
59
- * import { ApiLive } from "$lib/server/handlers";
58
+ * import { api } from "#lib/server/api.ts";
59
+ * import { ApiLive } from "#lib/server/handlers.ts";
60
60
  *
61
61
  * export const fallback = Endpoint.fromApi(api, ApiLive, { prefix: "/api" });
62
62
  * ```