@thomasfosterau/effect-sveltekit 0.3.0 → 0.4.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.
- package/README.md +3 -3
- package/dist/Hooks.d.ts +4 -5
- package/dist/Hooks.js +3 -1
- package/dist/Hooks.js.map +1 -1
- package/dist/Navigation.d.ts +1 -3
- package/dist/Navigation.js +1 -1
- package/dist/Remote.d.ts +1 -3
- package/dist/Remote.js +1 -1
- package/dist/Remote.js.map +1 -1
- package/dist/State.d.ts +1 -3
- package/dist/State.js +1 -1
- package/dist/adapter/assets.d.ts +7 -5
- package/dist/adapter/assets.js +3 -3
- package/dist/adapter/assets.js.map +1 -1
- package/dist/adapter/handler.d.ts +4 -5
- package/dist/adapter/handler.js +12 -12
- package/dist/adapter/handler.js.map +1 -1
- package/dist/adapter/index.d.ts +1 -2
- package/dist/adapter/index.js +3 -3
- package/dist/adapter/index.js.map +1 -1
- package/dist/adapter/serve.d.ts +0 -1
- package/dist/adapter/serve.js +4 -4
- package/dist/adapter/serve.js.map +1 -1
- package/dist/api-client.d.ts +5 -6
- package/dist/api-client.js +2 -2
- package/dist/api-client.js.map +1 -1
- package/dist/app-runtime.d.ts +1 -2
- package/dist/app-runtime.js +3 -2
- package/dist/app-runtime.js.map +1 -1
- package/dist/client/Hooks.d.ts +1 -3
- package/dist/client/Hooks.js +1 -1
- package/dist/config.d.ts +2 -3
- package/dist/config.js +5 -5
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +88 -0
- package/dist/errors.js +126 -0
- package/dist/errors.js.map +1 -0
- package/dist/forms.d.ts +2 -3
- package/dist/forms.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/internal/app-state.d.ts +1 -2
- package/dist/internal/app-state.js +1 -1
- package/dist/internal/auto.d.ts +0 -1
- package/dist/internal/auto.js.map +1 -1
- package/dist/internal/enhance.d.ts +0 -1
- package/dist/internal/enhance.js.map +1 -1
- package/dist/internal/error.js +1 -1
- package/dist/internal/remote-run.d.ts +2 -2
- package/dist/internal/remote-run.js.map +1 -1
- package/dist/internal/remote-validators.js +1 -1
- package/dist/internal/route-params.d.ts +0 -1
- package/dist/kit.d.ts +2 -3
- package/dist/realtime.d.ts +3 -5
- package/dist/realtime.js +1 -1
- package/dist/realtime.js.map +1 -1
- package/dist/{remote.server.d.ts → remote-functions.d.ts} +4 -5
- package/dist/{remote.server.js → remote-functions.js} +4 -4
- package/dist/remote-functions.js.map +1 -0
- package/dist/remote-reactive.d.ts +1 -2
- package/dist/remote-reactive.js +1 -1
- package/dist/remote-reactive.js.map +1 -1
- package/dist/request-parse.d.ts +1 -2
- package/dist/request-parse.js.map +1 -1
- package/dist/request-runtime.d.ts +0 -1
- package/dist/request-runtime.js.map +1 -1
- package/dist/request-services.d.ts +0 -1
- package/dist/request-services.js.map +1 -1
- package/dist/response-headers.d.ts +24 -0
- package/dist/response-headers.js +35 -0
- package/dist/response-headers.js.map +1 -0
- package/dist/runtime.d.ts +2 -3
- package/dist/runtime.js +3 -3
- package/dist/runtime.js.map +1 -1
- package/dist/server/Endpoint.d.ts +18 -15
- package/dist/server/Endpoint.js +10 -9
- package/dist/server/Endpoint.js.map +1 -1
- package/dist/server/Hooks.d.ts +1 -3
- package/dist/server/Hooks.js +1 -1
- package/dist/server/Hooks.js.map +1 -1
- package/dist/server.d.ts +6 -5
- package/dist/server.js +6 -4
- package/dist/server.js.map +1 -1
- package/dist/streamed-load.d.ts +0 -1
- package/dist/streamed-load.js.map +1 -1
- package/dist/testing.d.ts +0 -1
- package/dist/testing.js.map +1 -1
- package/dist/transport.d.ts +0 -1
- package/dist/universal.d.ts +3 -4
- package/dist/universal.js +3 -3
- package/dist/universal.js.map +1 -1
- package/dist/vite.d.ts +0 -1
- package/dist/vite.js +2 -2
- package/dist/vite.js.map +1 -1
- package/package.json +8 -5
- package/dist/remote.server.js.map +0 -1
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":["runtime"],"sources":["../src/server.ts"],"sourcesContent":["import type { Action, Actions, RequestEvent, ServerLoad } from \"@sveltejs/kit\";\nimport { Effect, Exit, Cause } from \"effect\";\nimport { isHttpError, isRedirect } from \"@sveltejs/kit\";\nimport type { SvelteKitServices } from \"./app-runtime.js\";\nimport type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport { defaultServerRuntime as runtime } from \"./internal/default-runtime.js\";\n\nexport type { SvelteKitServices };\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit server load function.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with {@link makeServerRuntime} and use its `serverLoad`\n * instead.\n *\n * @example\n * ```ts\n * import { serverLoad } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return { data: yield* response.json };\n * })\n * );\n * ```\n */\nexport function serverLoad<\n Params extends SvelteRouteParams = SvelteRouteParams,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, SvelteKitServices>,\n): ServerLoad<Params, ParentData, OutputData, RouteId> {\n return runtime().serverLoad(fn);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit action handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { action } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = {\n * default: action((event) =>\n * Effect.gen(function* () {\n * const formData = yield* Effect.promise(() => event.request.formData());\n * // Process form data\n * return { success: true };\n * })\n * )\n * };\n * ```\n */\nexport function action<\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, SvelteKitServices>,\n): Action<Params, OutputData, RouteId> {\n return runtime().action(fn);\n}\n\n/**\n * Creates SvelteKit actions from an object of Effect-based action handlers.\n * Each action will have access to platform services via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { actions } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = actions({\n * create: (event) => Effect.succeed({ created: true }),\n * delete: (event) => Effect.succeed({ deleted: true })\n * });\n * ```\n */\nexport function actions<\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n actionsMap: Record<\n string,\n (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, SvelteKitServices>\n >,\n): Actions<Params, OutputData, RouteId> {\n return runtime().actions(actionsMap);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit API endpoint handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit. For any other failure, the error is logged\n * server-side and a generic error message is returned to clients. For\n * development/debugging, you can pass options to expose full error details.\n *\n * @example\n * ```ts\n * import { endpoint } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import { json } from '@sveltejs/kit';\n * import * as HttpClient from 'effect/unstable/http/HttpClient';\n *\n * export const GET = endpoint((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return json(yield* response.json);\n * })\n * );\n * ```\n */\nexport function endpoint<\n Params extends SvelteRouteParams = SvelteRouteParams,\n RouteId extends string | null = any,\n>(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, SvelteKitServices>,\n options?: { exposeErrors?: boolean },\n): (event: RequestEvent<Params, RouteId>) => Promise<Response> {\n const { exposeErrors = false } = options ?? {};\n\n return async (event) => {\n const exit = await runtime().runPromiseExit(event as RequestEvent, fn(event));\n\n return Exit.match(exit, {\n onFailure: (cause) => {\n // Let SvelteKit handle its own control flow objects.\n const squashed = Cause.squash(cause);\n if (isRedirect(squashed) || isHttpError(squashed)) {\n throw squashed;\n }\n\n // Always log the full error server-side for debugging\n console.error(\"Endpoint error:\", Cause.pretty(cause));\n\n // Return generic error message to client unless explicitly enabled\n const clientMessage = exposeErrors ? Cause.pretty(cause) : \"Internal server error\";\n\n return new Response(JSON.stringify({ error: clientMessage }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n },\n onSuccess: (value) => value,\n });\n };\n}\n\n/**\n * Effect wrappers for the server-side hooks (`hooks.server.ts`): the `Hooks`\n * namespace exposes `handle`, `handleFetch`, `handleError`, and `init`.\n */\nexport * as Hooks from \"./server/Hooks.js\";\n\n/**\n * Create SvelteKit `+server.ts` endpoints backed by Effect: the `Endpoint`\n * namespace exposes `fromApi` (an `HttpApi`), `fromRouter` (an `HttpRouter`),\n * and `fromHttpEffect` (a single HTTP route effect).\n */\nexport * as Endpoint from \"./server/Endpoint.js\";\n\n/**\n * App-wide runtime: define your application's Effect services once and share\n * them across every request.\n */\nexport {\n makeServerRuntime,\n type ServerRuntime,\n type ServerRuntimeOptions,\n type RequestRuntime as ScopedRequestRuntime,\n} from \"./app-runtime.js\";\n\n/**\n * Export the SvelteKit runtime utilities for advanced use cases.\n */\nexport { SvelteKitRuntime, runWithSvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nexport { RemoteFunction } from \"./remote.server.js\";\n\n/**\n * A general, Effect-idiomatic seam for *how the per-request execution context is\n * obtained*. {@link RequestRuntime} constructs a {@link RequestRuntimeProvider}\n * (from a consumer-owned runtime on `event.locals`, a package-built\n * per-request runtime, a build-once `ManagedRuntime`, or a `Layer`), and\n * {@link makeRuntimeAdapter} binds the `serverLoad` / `action` / `actions` /\n * `endpoint` wrappers to it — so the handler effect's services `R` are your own\n * application catalogue, not the fixed {@link SvelteKitServices}. A\n * {@link RuntimeAdapterOptions.translateError} maps a failed `Cause` to\n * SvelteKit control flow at the boundary.\n */\nexport {\n RequestRuntime,\n makeRuntimeAdapter,\n type RequestRuntimeProvider,\n type RequestRuntimeRunners,\n type RuntimeAdapter,\n type RuntimeAdapterOptions,\n type AdapterEndpointOptions,\n type FromLayerPerRequestOptions,\n type ErrorTranslator,\n} from \"./request-runtime.js\";\n\n/**\n * SvelteKit `$env` ⇄ Effect `Config` (see {@link sveltekitConfig}). The\n * Effect Schema ⇄ SvelteKit `transport` hook lives on the universal `Hooks`\n * namespace (`Hooks.transport`) in `@thomasfosterau/effect-sveltekit`.\n */\nexport { sveltekitConfig, sveltekitConfigProvider } from \"./config.js\";\n\n/**\n * Schema-driven request parsing. Each helper reads a part of the current\n * request (route params, search params, JSON body, or form data), validates it\n * against an Effect `Schema` or Standard Schema V1 validator, and returns the\n * typed output. A validation failure surfaces as {@link ParseError}.\n *\n * All helpers require `SvelteKitRequestEvent` in the environment and compose\n * naturally inside `serverLoad` / `action` / `endpoint` handlers.\n */\nexport {\n decodeParams,\n decodeSearchParams,\n decodeJson,\n decodeFormData,\n ParseError,\n type ParseIssue,\n} from \"./request-parse.js\";\n\n/**\n * Streamed load helper. {@link defer} captures the current service context and\n * runs a slow Effect in the background, returning the `Promise` so SvelteKit\n * can stream the result to the browser while sending the initial HTML\n * immediately. Use inside `serverLoad` alongside eagerly-computed fields.\n */\nexport { defer } from \"./streamed-load.js\";\n\n/**\n * Effect-native SvelteKit control-flow helpers and the default boundary\n * translator.\n *\n * - `SvelteKit.redirect` / `SvelteKit.error` / `SvelteKit.fail` — yield typed\n * Effect failures instead of throwing SvelteKit control-flow objects.\n * - `SvelteKit.translate` — the default `translateError` wired into every\n * server wrapper; maps the Kit error types to `redirect()` / `error()` /\n * `fail()` at the request boundary.\n * - `SvelteKit.makeTranslator([...cases])` — compose `translate` with your own\n * domain-error matchers (consumer cases run before the Kit base cases).\n * - `KitRedirect`, `KitError`, `KitActionFailure` — the tagged error classes\n * for use with `Effect.catchTag`, `instanceof`, etc.\n * - `KitTranslatorCase` — the argument type for `makeTranslator`.\n */\nexport {\n SvelteKit,\n KitRedirect,\n KitError,\n KitActionFailure,\n type KitTranslatorCase,\n type KitErrorTranslator,\n} from \"./kit.js\";\n\n/**\n * Effect-native services for the three per-request surfaces of a SvelteKit\n * `RequestEvent`: cookies, response headers, and locals.\n *\n * Each is a tagged `Context.Service` that is provided automatically in every\n * server handler effect (part of {@link SvelteKitServices}). Use\n * `yield* SvelteKitCookies` / `yield* SvelteKitResponseHeaders` /\n * `yield* SvelteKitLocals` to access them; stub them with `Layer.succeed` in\n * tests without a real `RequestEvent`.\n */\nexport {\n SvelteKitCookies,\n SvelteKitResponseHeaders,\n SvelteKitLocals,\n requestServicesLayer,\n type SvelteKitCookiesInterface,\n type SvelteKitResponseHeadersInterface,\n} from \"./request-services.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,WAMd,IAGqD;CACrD,OAAOA,qBAAQ,EAAE,WAAW,EAAE;AAChC;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,OAKd,IAGqC;CACrC,OAAOA,qBAAQ,EAAE,OAAO,EAAE;AAC5B;;;;;;;;;;;;;;;;AAiBA,SAAgB,QAKd,YAIsC;CACtC,OAAOA,qBAAQ,EAAE,QAAQ,UAAU;AACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,SAId,IACA,SAC6D;CAC7D,MAAM,EAAE,eAAe,UAAU,WAAW,CAAC;CAE7C,OAAO,OAAO,UAAU;EACtB,MAAM,OAAO,MAAMA,qBAAQ,EAAE,eAAe,OAAuB,GAAG,KAAK,CAAC;EAE5E,OAAO,KAAK,MAAM,MAAM;GACtB,YAAY,UAAU;IAEpB,MAAM,WAAW,MAAM,OAAO,KAAK;IACnC,IAAI,WAAW,QAAQ,KAAK,YAAY,QAAQ,GAC9C,MAAM;IAIR,QAAQ,MAAM,mBAAmB,MAAM,OAAO,KAAK,CAAC;IAGpD,MAAM,gBAAgB,eAAe,MAAM,OAAO,KAAK,IAAI;IAE3D,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,cAAc,CAAC,GAAG;KAC5D,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;IAChD,CAAC;GACH;GACA,YAAY,UAAU;EACxB,CAAC;CACH;AACF"}
|
|
1
|
+
{"version":3,"file":"server.js","names":["runtime"],"sources":["../src/server.ts"],"sourcesContent":["import type { Action, Actions, RequestEvent, ServerLoad } from \"@sveltejs/kit\";\nimport { Effect, Exit, Cause } from \"effect\";\nimport { isHttpError, isRedirect } from \"@sveltejs/kit\";\nimport type { SvelteKitServices } from \"./app-runtime.js\";\nimport type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport { defaultServerRuntime as runtime } from \"./internal/default-runtime.js\";\n\nexport type { SvelteKitServices };\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit server load function.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with {@link makeServerRuntime} and use its `serverLoad`\n * instead.\n *\n * @example\n * ```ts\n * import { serverLoad } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import * as HttpClient from 'effect/http/HttpClient';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return { data: yield* response.json };\n * })\n * );\n * ```\n */\nexport function serverLoad<\n Params extends SvelteRouteParams = SvelteRouteParams,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, SvelteKitServices>,\n): ServerLoad<Params, ParentData, OutputData, RouteId> {\n return runtime().serverLoad(fn);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit action handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { action } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = {\n * default: action((event) =>\n * Effect.gen(function* () {\n * const formData = yield* Effect.promise(() => event.request.formData());\n * // Process form data\n * return { success: true };\n * })\n * )\n * };\n * ```\n */\nexport function action<\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, SvelteKitServices>,\n): Action<Params, OutputData, RouteId> {\n return runtime().action(fn);\n}\n\n/**\n * Creates SvelteKit actions from an object of Effect-based action handlers.\n * Each action will have access to platform services via the SvelteKit runtime.\n *\n * @example\n * ```ts\n * import { actions } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const actions = actions({\n * create: (event) => Effect.succeed({ created: true }),\n * delete: (event) => Effect.succeed({ deleted: true })\n * });\n * ```\n */\nexport function actions<\n Params extends SvelteRouteParams = SvelteRouteParams,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n>(\n actionsMap: Record<\n string,\n (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, SvelteKitServices>\n >,\n): Actions<Params, OutputData, RouteId> {\n return runtime().actions(actionsMap);\n}\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit API endpoint handler.\n * The effect has access to platform services (HttpClient, the RequestEvent, etc.)\n * via the SvelteKit runtime.\n *\n * SvelteKit control flow (`redirect()`, `error()`) raised inside the effect is\n * passed through to SvelteKit. For any other failure, the error is logged\n * server-side and a generic error message is returned to clients. For\n * development/debugging, you can pass options to expose full error details.\n *\n * @example\n * ```ts\n * import { endpoint } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n * import { json } from '@sveltejs/kit';\n * import * as HttpClient from 'effect/http/HttpClient';\n *\n * export const GET = endpoint((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(\"https://api.example.com/data\");\n * return json(yield* response.json);\n * })\n * );\n * ```\n */\nexport function endpoint<\n Params extends SvelteRouteParams = SvelteRouteParams,\n RouteId extends string | null = any,\n>(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, SvelteKitServices>,\n options?: { exposeErrors?: boolean },\n): (event: RequestEvent<Params, RouteId>) => Promise<Response> {\n const { exposeErrors = false } = options ?? {};\n\n return async (event) => {\n const exit = await runtime().runPromiseExit(event as RequestEvent, fn(event));\n\n return Exit.match(exit, {\n onFailure: (cause) => {\n // Let SvelteKit handle its own control flow objects.\n const squashed = Cause.squash(cause);\n if (isRedirect(squashed) || isHttpError(squashed)) {\n throw squashed;\n }\n\n // Always log the full error server-side for debugging\n console.error(\"Endpoint error:\", Cause.pretty(cause));\n\n // Return generic error message to client unless explicitly enabled\n const clientMessage = exposeErrors ? Cause.pretty(cause) : \"Internal server error\";\n\n return new Response(JSON.stringify({ error: clientMessage }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n },\n onSuccess: (value) => value,\n });\n };\n}\n\n/**\n * Effect wrappers for the server-side hooks (`hooks.server.ts`): the `Hooks`\n * namespace exposes `handle`, `handleFetch`, `handleError`, and `init`.\n */\nexport * as Hooks from \"./server/Hooks.js\";\n\n/**\n * Create SvelteKit `+server.ts` endpoints backed by Effect: the `Endpoint`\n * namespace exposes `fromApi` (an `HttpApi`), `fromRouter` (an `HttpRouter`),\n * and `fromHttpEffect` (a single HTTP route effect).\n */\nexport * as Endpoint from \"./server/Endpoint.js\";\n\n/**\n * App-wide runtime: define your application's Effect services once and share\n * them across every request.\n */\nexport {\n makeServerRuntime,\n type ServerRuntime,\n type ServerRuntimeOptions,\n type RequestRuntime as ScopedRequestRuntime,\n} from \"./app-runtime.js\";\n\n/**\n * Export the SvelteKit runtime utilities for advanced use cases.\n */\nexport { SvelteKitRuntime, runWithSvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nexport { RemoteFunction } from \"./remote-functions.js\";\n\n/**\n * A general, Effect-idiomatic seam for *how the per-request execution context is\n * obtained*. {@link RequestRuntime} constructs a {@link RequestRuntimeProvider}\n * (from a consumer-owned runtime on `event.locals`, a package-built\n * per-request runtime, a build-once `ManagedRuntime`, or a `Layer`), and\n * {@link makeRuntimeAdapter} binds the `serverLoad` / `action` / `actions` /\n * `endpoint` wrappers to it — so the handler effect's services `R` are your own\n * application catalogue, not the fixed {@link SvelteKitServices}. A\n * {@link RuntimeAdapterOptions.translateError} maps a failed `Cause` to\n * SvelteKit control flow at the boundary.\n */\nexport {\n RequestRuntime,\n makeRuntimeAdapter,\n type RequestRuntimeProvider,\n type RequestRuntimeRunners,\n type RuntimeAdapter,\n type RuntimeAdapterOptions,\n type AdapterEndpointOptions,\n type FromLayerPerRequestOptions,\n type ErrorTranslator,\n} from \"./request-runtime.js\";\n\n/**\n * SvelteKit `$env` ⇄ Effect `Config` (see {@link sveltekitConfig}). The\n * Effect Schema ⇄ SvelteKit `transport` hook lives on the universal `Hooks`\n * namespace (`Hooks.transport`) in `@thomasfosterau/effect-sveltekit`.\n */\nexport { sveltekitConfig, sveltekitConfigProvider } from \"./config.js\";\n\n/**\n * Schema-driven request parsing. Each helper reads a part of the current\n * request (route params, search params, JSON body, or form data), validates it\n * against an Effect `Schema` or Standard Schema V1 validator, and returns the\n * typed output. A validation failure surfaces as {@link ParseError}.\n *\n * All helpers require `SvelteKitRequestEvent` in the environment and compose\n * naturally inside `serverLoad` / `action` / `endpoint` handlers.\n */\nexport {\n decodeParams,\n decodeSearchParams,\n decodeJson,\n decodeFormData,\n ParseError,\n type ParseIssue,\n} from \"./request-parse.js\";\n\n/**\n * Streamed load helper. {@link defer} captures the current service context and\n * runs a slow Effect in the background, returning the `Promise` so SvelteKit\n * can stream the result to the browser while sending the initial HTML\n * immediately. Use inside `serverLoad` alongside eagerly-computed fields.\n */\nexport { defer } from \"./streamed-load.js\";\n\n/**\n * Effect-native SvelteKit control-flow helpers and the default boundary\n * translator.\n *\n * - `SvelteKit.redirect` / `SvelteKit.error` / `SvelteKit.fail` — yield typed\n * Effect failures instead of throwing SvelteKit control-flow objects.\n * - `SvelteKit.translate` — the default `translateError` wired into every\n * server wrapper; maps the Kit error types to `redirect()` / `error()` /\n * `fail()` at the request boundary.\n * - `SvelteKit.makeTranslator([...cases])` — compose `translate` with your own\n * domain-error matchers (consumer cases run before the Kit base cases).\n * - `KitRedirect`, `KitError`, `KitActionFailure` — the tagged error classes\n * for use with `Effect.catchTag`, `instanceof`, etc.\n * - `KitTranslatorCase` — the argument type for `makeTranslator`.\n */\nexport {\n SvelteKit,\n KitRedirect,\n KitError,\n KitActionFailure,\n type KitTranslatorCase,\n type KitErrorTranslator,\n} from \"./kit.js\";\n\n/**\n * A `translateError` built from a tag-keyed catalogue:\n * `Errors.toSvelteKit({ NotFound: 404, Forbidden: 403, Redirect: \"redirect\", HttpError: \"status\" })`.\n * Pass it to `makeRuntimeAdapter` / `RemoteFunction.withRuntime`; see\n * {@link Errors.toSvelteKit}.\n */\nexport * as Errors from \"./errors.js\";\n\n/** The response-headers buffer, re-exported from the universal entry for server-only modules. */\nexport { ResponseHeaders } from \"./response-headers.js\";\n\n/**\n * Effect-native services for the three per-request surfaces of a SvelteKit\n * `RequestEvent`: cookies, response headers, and locals.\n *\n * Each is a tagged `Context.Service` that is provided automatically in every\n * server handler effect (part of {@link SvelteKitServices}). Use\n * `yield* SvelteKitCookies` / `yield* SvelteKitResponseHeaders` /\n * `yield* SvelteKitLocals` to access them; stub them with `Layer.succeed` in\n * tests without a real `RequestEvent`.\n */\nexport {\n SvelteKitCookies,\n SvelteKitResponseHeaders,\n SvelteKitLocals,\n requestServicesLayer,\n type SvelteKitCookiesInterface,\n type SvelteKitResponseHeadersInterface,\n} from \"./request-services.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,WAMd,IAGqD;CACrD,OAAOA,qBAAQ,CAAC,CAAC,WAAW,EAAE;AAChC;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,OAKd,IAGqC;CACrC,OAAOA,qBAAQ,CAAC,CAAC,OAAO,EAAE;AAC5B;;;;;;;;;;;;;;;;AAiBA,SAAgB,QAKd,YAIsC;CACtC,OAAOA,qBAAQ,CAAC,CAAC,QAAQ,UAAU;AACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,SAId,IACA,SAC6D;CAC7D,MAAM,EAAE,eAAe,UAAU,WAAW,CAAC;CAE7C,OAAO,OAAO,UAAU;EACtB,MAAM,OAAO,MAAMA,qBAAQ,CAAC,CAAC,eAAe,OAAuB,GAAG,KAAK,CAAC;EAE5E,OAAO,KAAK,MAAM,MAAM;GACtB,YAAY,UAAU;IAEpB,MAAM,WAAW,MAAM,OAAO,KAAK;IACnC,IAAI,WAAW,QAAQ,KAAK,YAAY,QAAQ,GAC9C,MAAM;IAIR,QAAQ,MAAM,mBAAmB,MAAM,OAAO,KAAK,CAAC;IAGpD,MAAM,gBAAgB,eAAe,MAAM,OAAO,KAAK,IAAI;IAE3D,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,cAAc,CAAC,GAAG;KAC5D,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;IAChD,CAAC;GACH;GACA,YAAY,UAAU;EACxB,CAAC;CACH;AACF"}
|
package/dist/streamed-load.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamed-load.js","names":[],"sources":["../src/streamed-load.ts"],"sourcesContent":["/**\n * Helpers for SvelteKit's streamed (deferred-promise) `serverLoad` data.\n *\n * SvelteKit can stream fields of a `load` return value to the browser as they\n * resolve when you return a `Promise` alongside eagerly-computed fields. This\n * module provides {@link defer} — an Effect that captures the current service\n * context and launches the slow effect in the background, handing SvelteKit\n * the resulting `Promise`.\n *\n * @example\n * ```ts\n * import { serverLoad, defer } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * // Eager: resolved before the initial HTML is sent.\n * const title = yield* fetchTitle(event.params.id);\n *\n * // Deferred: SvelteKit streams this to the client as it resolves.\n * const commentsPromise = yield* defer(fetchComments(event.params.id));\n *\n * return { title, comments: commentsPromise };\n * }),\n * );\n * ```\n *\n * In `+page.svelte`, consume the deferred field with `{#await}`:\n *\n * ```svelte\n * <script>\n * let { data } = $props();\n * </script>\n *\n * <h1>{data.title}</h1>\n *\n * {#await data.comments}\n * <p>Loading comments…</p>\n * {:then comments}\n * {#each comments as c}<p>{c.body}</p>{/each}\n * {:catch}\n * <p>Failed to load comments.</p>\n * {/await}\n * ```\n *\n * **Error handling.** If the deferred Effect fails, the promise rejects.\n * SvelteKit surfaces the rejection in the `{:catch}` block of `{#await}` in\n * the component — it does *not* throw the page into its error boundary. To\n * provide a fallback value instead, apply `Effect.orElse` / `Effect.catchAll`\n * before passing the effect to `defer`.\n */\n\nimport { Effect } from \"effect\";\n\n/**\n * Capture the current service context and run `effect` in the background,\n * returning the `Promise` immediately so SvelteKit can stream the result.\n *\n * The deferred effect runs with exactly the same services as the parent\n * `serverLoad` handler — you can `yield*` the same application services.\n *\n * @param effect - The slow Effect to run in the background. Failures cause the\n * returned Promise to reject; wrap with `Effect.orElse` for a fallback.\n *\n * @example\n * ```ts\n * const commentsPromise = yield* defer(fetchComments(id));\n * return { title, comments: commentsPromise };\n * ```\n */\nexport const defer = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<Promise<A>, never, R> =>\n Effect.map(Effect.context<R>(), (ctx) => Effect.runPromiseWith(ctx)(effect));\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,MAAa,SACX,WAEA,OAAO,IAAI,OAAO,QAAW,IAAI,QAAQ,OAAO,eAAe,GAAG,
|
|
1
|
+
{"version":3,"file":"streamed-load.js","names":[],"sources":["../src/streamed-load.ts"],"sourcesContent":["/**\n * Helpers for SvelteKit's streamed (deferred-promise) `serverLoad` data.\n *\n * SvelteKit can stream fields of a `load` return value to the browser as they\n * resolve when you return a `Promise` alongside eagerly-computed fields. This\n * module provides {@link defer} — an Effect that captures the current service\n * context and launches the slow effect in the background, handing SvelteKit\n * the resulting `Promise`.\n *\n * @example\n * ```ts\n * import { serverLoad, defer } from '@thomasfosterau/effect-sveltekit/server';\n * import { Effect } from 'effect';\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * // Eager: resolved before the initial HTML is sent.\n * const title = yield* fetchTitle(event.params.id);\n *\n * // Deferred: SvelteKit streams this to the client as it resolves.\n * const commentsPromise = yield* defer(fetchComments(event.params.id));\n *\n * return { title, comments: commentsPromise };\n * }),\n * );\n * ```\n *\n * In `+page.svelte`, consume the deferred field with `{#await}`:\n *\n * ```svelte\n * <script>\n * let { data } = $props();\n * </script>\n *\n * <h1>{data.title}</h1>\n *\n * {#await data.comments}\n * <p>Loading comments…</p>\n * {:then comments}\n * {#each comments as c}<p>{c.body}</p>{/each}\n * {:catch}\n * <p>Failed to load comments.</p>\n * {/await}\n * ```\n *\n * **Error handling.** If the deferred Effect fails, the promise rejects.\n * SvelteKit surfaces the rejection in the `{:catch}` block of `{#await}` in\n * the component — it does *not* throw the page into its error boundary. To\n * provide a fallback value instead, apply `Effect.orElse` / `Effect.catchAll`\n * before passing the effect to `defer`.\n */\n\nimport { Effect } from \"effect\";\n\n/**\n * Capture the current service context and run `effect` in the background,\n * returning the `Promise` immediately so SvelteKit can stream the result.\n *\n * The deferred effect runs with exactly the same services as the parent\n * `serverLoad` handler — you can `yield*` the same application services.\n *\n * @param effect - The slow Effect to run in the background. Failures cause the\n * returned Promise to reject; wrap with `Effect.orElse` for a fallback.\n *\n * @example\n * ```ts\n * const commentsPromise = yield* defer(fetchComments(id));\n * return { title, comments: commentsPromise };\n * ```\n */\nexport const defer = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.Effect<Promise<A>, never, R> =>\n Effect.map(Effect.context<R>(), (ctx) => Effect.runPromiseWith(ctx)(effect));\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,MAAa,SACX,WAEA,OAAO,IAAI,OAAO,QAAW,IAAI,QAAQ,OAAO,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC"}
|
package/dist/testing.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { ErrorTranslator } from "./request-runtime.js";
|
|
|
2
2
|
import { FormErrorMapper, RunHelpers } from "./internal/remote-run.js";
|
|
3
3
|
import { Effect, Layer } from "effect";
|
|
4
4
|
import { RequestEvent } from "@sveltejs/kit";
|
|
5
|
-
|
|
6
5
|
//#region src/testing.d.ts
|
|
7
6
|
/** Options for {@link TestRequestEvent}. */
|
|
8
7
|
interface TestRequestEventOptions {
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","names":[],"sources":["../src/testing.ts"],"sourcesContent":["/**\n * Framework-agnostic test utilities for `@thomasfosterau/effect-sveltekit`.\n *\n * Exports a minimal {@link TestRequestEvent} constructor and plain-async\n * runner helpers ({@link runServerLoad}, {@link runAction},\n * {@link runEndpoint}, {@link runRemote}) so Effect-based SvelteKit handlers\n * can be unit-tested without mocking the SvelteKit module graph or installing\n * `$app/server`.\n *\n * Ships under the `./testing` subpath of `@thomasfosterau/effect-sveltekit`.\n * It has no coupling to Vitest, Jest, or any other test framework — import\n * it from whichever test runner you use.\n *\n * @example\n * ```ts\n * import {\n * TestRequestEvent,\n * runServerLoad,\n * runAction,\n * runRemote,\n * } from \"@thomasfosterau/effect-sveltekit/testing\";\n * import { Effect } from \"effect\";\n *\n * const event = TestRequestEvent({ params: { id: \"1\" }, locals: { user } });\n *\n * // Test a server load:\n * const data = await runServerLoad(\n * (event) =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return { item: yield* db.findById(event.params.id) };\n * }),\n * event,\n * { layer: TestDatabase.layer },\n * );\n *\n * // Test a remote function handler directly:\n * const { run } = runRemote(event, { layer: TestLayer });\n * const items = await run(listItems());\n * ```\n */\n\nimport type { RequestEvent } from \"@sveltejs/kit\";\nimport { Effect, Layer } from \"effect\";\nimport {\n makeRuntimeAdapter,\n RequestRuntime,\n type ErrorTranslator,\n type RequestRuntimeProvider,\n} from \"./request-runtime.js\";\nimport { SvelteKit } from \"./kit.js\";\nimport { makeRunHelpers, type FormErrorMapper, type RunHelpers } from \"./internal/remote-run.js\";\n\nexport type { RunHelpers, FormErrorMapper } from \"./internal/remote-run.js\";\n\n// ─── TestCookies ─────────────────────────────────────────────────────────────\n\n/** Map-backed Cookies implementation for use in {@link TestRequestEvent}. */\nclass TestCookies {\n private readonly store: Map<string, string>;\n\n constructor(initial: Record<string, string> = {}) {\n this.store = new Map(Object.entries(initial));\n }\n\n get(name: string, _opts?: unknown): string | undefined {\n return this.store.get(name);\n }\n\n getAll(_opts?: unknown): Array<{ name: string; value: string }> {\n return [...this.store.entries()].map(([name, value]) => ({ name, value }));\n }\n\n set(name: string, value: string, _opts?: unknown): void {\n this.store.set(name, value);\n }\n\n delete(name: string, _opts?: unknown): void {\n this.store.delete(name);\n }\n\n serialize(name: string, value: string, _opts?: unknown): string {\n return `${name}=${encodeURIComponent(value)}`;\n }\n}\n\n// ─── TestRequestEvent ─────────────────────────────────────────────────────────\n\n/** Options for {@link TestRequestEvent}. */\nexport interface TestRequestEventOptions {\n /**\n * The request URL. Accepts a `string` or a `URL` instance.\n * Defaults to `\"http://localhost/\"`.\n */\n readonly url?: string | URL;\n /** The HTTP method. Defaults to `\"GET\"`. */\n readonly method?: string;\n /** Additional HTTP headers added to the underlying `Request`. */\n readonly headers?: HeadersInit;\n /** The request body. Only usable with methods that accept a body. */\n readonly body?: BodyInit | null;\n /**\n * Route params (e.g. `{ id: \"123\" }` for a route like `/items/[id]`).\n * Defaults to `{}`.\n */\n readonly params?: Record<string, string>;\n /**\n * Initial `event.locals` value. Defaults to `{}`.\n * Cast to `App.Locals` — extend it with the properties your handlers need.\n */\n readonly locals?: Record<string, unknown>;\n /**\n * Pre-seeded cookies as `{ name: value }` pairs.\n * The backing store supports `get`, `getAll`, `set`, `delete`, and\n * `serialize`; the object is cast to SvelteKit's `Cookies` interface.\n */\n readonly cookies?: Record<string, string>;\n /**\n * The `event.platform` value, if any.\n * Pass a partial platform object matching the environment the handler targets.\n */\n readonly platform?: unknown;\n /**\n * The `fetch` implementation used by `event.fetch`.\n * Defaults to `globalThis.fetch`. Pass a custom function to intercept or\n * stub network calls made by the handler.\n */\n readonly fetch?: typeof globalThis.fetch;\n}\n\n/**\n * Create a minimal {@link RequestEvent} for use in unit tests.\n *\n * All fields are optional — omit what the handler under test doesn't inspect.\n * Cookies are backed by an in-memory `Map`; mutations (`set`, `delete`) are\n * reflected in subsequent `get`/`getAll` calls on the same event.\n *\n * @example\n * ```ts\n * const event = TestRequestEvent({\n * url: \"http://localhost/items/42\",\n * params: { id: \"42\" },\n * locals: { user: { id: \"u1\", role: \"admin\" } },\n * cookies: { session: \"tok_test\" },\n * });\n * ```\n */\nexport function TestRequestEvent(opts: TestRequestEventOptions = {}): RequestEvent {\n const rawUrl = opts.url instanceof URL ? opts.url.toString() : (opts.url ?? \"http://localhost/\");\n const url = new URL(rawUrl);\n\n const init: RequestInit = { method: opts.method ?? \"GET\" };\n if (opts.headers !== undefined) init.headers = opts.headers;\n if (opts.body !== undefined) init.body = opts.body;\n\n const request = new Request(url.toString(), init);\n const cookies = new TestCookies(opts.cookies ?? {});\n const fetchFn = opts.fetch ?? globalThis.fetch;\n\n return {\n request,\n url,\n params: (opts.params ?? {}) as any,\n route: { id: null },\n fetch: fetchFn,\n cookies: cookies as any,\n locals: (opts.locals ?? {}) as any,\n platform: opts.platform as any,\n isDataRequest: false,\n isSubRequest: false,\n setHeaders: () => {},\n getClientAddress: () => \"127.0.0.1\",\n } as unknown as RequestEvent;\n}\n\n// ─── Options ──────────────────────────────────────────────────────────────────\n\n/**\n * Options shared by {@link runServerLoad}, {@link runAction}, and\n * {@link runEndpoint}.\n */\nexport interface TestRunOptions<R> {\n /**\n * The `Layer` providing the handler effect's required services `R`.\n * Omit when the handler has no service requirements (`R` is `never`).\n */\n readonly layer?: Layer.Layer<R>;\n /**\n * Translate a failed `Cause` at the boundary. Defaults to\n * {@link SvelteKit.translate} (Kit control-flow through; anything else\n * becomes an `EffectCauseError`).\n */\n readonly translateError?: ErrorTranslator;\n}\n\n/**\n * Options for {@link runRemote}.\n */\nexport interface RunRemoteOptions<R> {\n /** The `Layer` providing the remote handler's service requirements. */\n readonly layer?: Layer.Layer<R>;\n /**\n * Translate a failed `Cause` at the boundary. Defaults to\n * {@link SvelteKit.translate}.\n */\n readonly translateError?: ErrorTranslator;\n /**\n * Map a remote form handler's failure to SvelteKit field issues. Receives\n * the squashed handler failure and the `{ issue, data }` context. When\n * provided it is called instead of `translateError` on the form error path.\n */\n readonly formError?: FormErrorMapper;\n}\n\n// ─── Internal ─────────────────────────────────────────────────────────────────\n\nconst makeAdapter = <R>(opts: TestRunOptions<R> | undefined) =>\n makeRuntimeAdapter(RequestRuntime.fromLayer((opts?.layer ?? Layer.empty) as Layer.Layer<R>), {\n translateError: opts?.translateError ?? SvelteKit.translate,\n });\n\n// ─── runServerLoad ────────────────────────────────────────────────────────────\n\n/**\n * Run an Effect-based server `load` function under test.\n *\n * Wraps `fn` with `makeRuntimeAdapter` using the provided `opts.layer` (or\n * `Layer.empty` when omitted) and calls the resulting `ServerLoad` with\n * `event`. Errors are translated by `opts.translateError` (defaults to\n * {@link SvelteKit.translate}).\n *\n * @example\n * ```ts\n * const data = await runServerLoad(\n * (event) => Effect.gen(function* () {\n * const db = yield* Database;\n * return { items: yield* db.list() };\n * }),\n * TestRequestEvent(),\n * { layer: TestDatabase.layer },\n * );\n * expect(data.items).toHaveLength(0);\n * ```\n */\nexport const runServerLoad = <R, A extends Record<string, any> | void = void>(\n fn: (event: RequestEvent) => Effect.Effect<A, unknown, R>,\n event: RequestEvent,\n opts?: TestRunOptions<R>,\n): Promise<A> => (makeAdapter(opts).serverLoad(fn) as (e: RequestEvent) => Promise<A>)(event);\n\n// ─── runAction ───────────────────────────────────────────────────────────────\n\n/**\n * Run an Effect-based form action handler under test.\n *\n * @example\n * ```ts\n * const result = await runAction(\n * (_event) => Effect.succeed({ success: true }),\n * TestRequestEvent({ method: \"POST\" }),\n * );\n * expect((result as any).success).toBe(true);\n * ```\n */\nexport const runAction = <R, A extends Record<string, any> | void = void>(\n fn: (event: RequestEvent) => Effect.Effect<A, unknown, R>,\n event: RequestEvent,\n opts?: TestRunOptions<R>,\n): Promise<A> => (makeAdapter(opts).action(fn) as (e: RequestEvent) => Promise<A>)(event);\n\n// ─── runEndpoint ──────────────────────────────────────────────────────────────\n\n/**\n * Run an Effect-based `+server.ts` endpoint handler under test.\n *\n * @example\n * ```ts\n * const response = await runEndpoint(\n * (_event) => Effect.succeed(new Response(JSON.stringify({ ok: true }))),\n * TestRequestEvent({ url: \"http://localhost/api/ping\" }),\n * );\n * expect(response.status).toBe(200);\n * ```\n */\nexport const runEndpoint = <R>(\n fn: (event: RequestEvent) => Effect.Effect<Response, unknown, R>,\n event: RequestEvent,\n opts?: TestRunOptions<R>,\n): Promise<Response> => Promise.resolve(makeAdapter(opts).endpoint(fn)(event as any));\n\n// ─── runRemote ────────────────────────────────────────────────────────────────\n\n/**\n * Build a `{ run, runForm }` helper pair for directly invoking remote-function\n * Effect handlers under test.\n *\n * Unlike the production `query` / `command` / `form` helpers, which resolve the\n * request event via `$app/server.getRequestEvent()`, this resolves it from the\n * `event` argument you supply — no SvelteKit module-graph mocking required.\n *\n * Use `run` for `query` / `command` handlers and `runForm` for `form` handlers\n * that need the `issue` / `data` context.\n *\n * @example\n * ```ts\n * import { TestRequestEvent, runRemote } from \"@thomasfosterau/effect-sveltekit/testing\";\n *\n * const event = TestRequestEvent();\n * const { run } = runRemote(event, { layer: TestDatabase.layer });\n *\n * // Invoke the underlying Effect directly — no AppServer needed:\n * const items = await run(listItemsEffect());\n * expect(items).toEqual([]);\n * ```\n */\nexport const runRemote = <R = never>(\n event: RequestEvent,\n opts?: RunRemoteOptions<R>,\n): RunHelpers => {\n const translate = opts?.translateError ?? SvelteKit.translate;\n const layer = (opts?.layer ?? Layer.empty) as Layer.Layer<R>;\n const provider: RequestRuntimeProvider<R> = RequestRuntime.fromLayer(layer);\n return makeRunHelpers(\n () => ({ runExit: (effect) => provider.run(event).runPromiseExit(effect) }),\n translate,\n opts?.formError,\n );\n};\n"],"mappings":";;;;;;AA0DA,IAAM,cAAN,MAAkB;CAChB;CAEA,YAAY,UAAkC,CAAC,GAAG;EAChD,KAAK,QAAQ,IAAI,IAAI,OAAO,QAAQ,OAAO,CAAC;CAC9C;CAEA,IAAI,MAAc,OAAqC;EACrD,OAAO,KAAK,MAAM,IAAI,IAAI;CAC5B;CAEA,OAAO,OAAyD;EAC9D,OAAO,CAAC,GAAG,KAAK,MAAM,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,YAAY;GAAE;GAAM;EAAM,EAAE;CAC3E;CAEA,IAAI,MAAc,OAAe,OAAuB;EACtD,KAAK,MAAM,IAAI,MAAM,KAAK;CAC5B;CAEA,OAAO,MAAc,OAAuB;EAC1C,KAAK,MAAM,OAAO,IAAI;CACxB;CAEA,UAAU,MAAc,OAAe,OAAyB;EAC9D,OAAO,GAAG,KAAK,GAAG,mBAAmB,KAAK;CAC5C;AACF;;;;;;;;;;;;;;;;;;AA+DA,SAAgB,iBAAiB,OAAgC,CAAC,GAAiB;CACjF,MAAM,SAAS,KAAK,eAAe,MAAM,KAAK,IAAI,SAAS,IAAK,KAAK,OAAO;CAC5E,MAAM,MAAM,IAAI,IAAI,MAAM;CAE1B,MAAM,OAAoB,EAAE,QAAQ,KAAK,UAAU,MAAM;CACzD,IAAI,KAAK,YAAY,KAAA,GAAW,KAAK,UAAU,KAAK;CACpD,IAAI,KAAK,SAAS,KAAA,GAAW,KAAK,OAAO,KAAK;CAE9C,MAAM,UAAU,IAAI,QAAQ,IAAI,SAAS,GAAG,IAAI;CAChD,MAAM,UAAU,IAAI,YAAY,KAAK,WAAW,CAAC,CAAC;CAClD,MAAM,UAAU,KAAK,SAAS,WAAW;CAEzC,OAAO;EACL;EACA;EACA,QAAS,KAAK,UAAU,CAAC;EACzB,OAAO,EAAE,IAAI,KAAK;EAClB,OAAO;EACE;EACT,QAAS,KAAK,UAAU,CAAC;EACzB,UAAU,KAAK;EACf,eAAe;EACf,cAAc;EACd,kBAAkB,CAAC;EACnB,wBAAwB;CAC1B;AACF;AA2CA,MAAM,eAAkB,SACtB,mBAAmB,eAAe,UAAW,MAAM,SAAS,MAAM,KAAwB,GAAG,EAC3F,gBAAgB,MAAM,kBAAkB,UAAU,UACpD,CAAC;;;;;;;;;;;;;;;;;;;;;;AAyBH,MAAa,iBACX,IACA,OACA,SACgB,YAAY,IAAI,EAAE,WAAW,EAAE,EAAsC,KAAK;;;;;;;;;;;;;AAgB5F,MAAa,aACX,IACA,OACA,SACgB,YAAY,IAAI,EAAE,OAAO,EAAE,EAAsC,KAAK;;;;;;;;;;;;;AAgBxF,MAAa,eACX,IACA,OACA,SACsB,QAAQ,QAAQ,YAAY,IAAI,EAAE,SAAS,EAAE,EAAE,KAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;AA2BpF,MAAa,aACX,OACA,SACe;CACf,MAAM,YAAY,MAAM,kBAAkB,UAAU;CACpD,MAAM,QAAS,MAAM,SAAS,MAAM;CACpC,MAAM,WAAsC,eAAe,UAAU,KAAK;CAC1E,OAAO,sBACE,EAAE,UAAU,WAAW,SAAS,IAAI,KAAK,EAAE,eAAe,MAAM,EAAE,IACzE,WACA,MAAM,SACR;AACF"}
|
|
1
|
+
{"version":3,"file":"testing.js","names":[],"sources":["../src/testing.ts"],"sourcesContent":["/**\n * Framework-agnostic test utilities for `@thomasfosterau/effect-sveltekit`.\n *\n * Exports a minimal {@link TestRequestEvent} constructor and plain-async\n * runner helpers ({@link runServerLoad}, {@link runAction},\n * {@link runEndpoint}, {@link runRemote}) so Effect-based SvelteKit handlers\n * can be unit-tested without mocking the SvelteKit module graph or installing\n * `$app/server`.\n *\n * Ships under the `./testing` subpath of `@thomasfosterau/effect-sveltekit`.\n * It has no coupling to Vitest, Jest, or any other test framework — import\n * it from whichever test runner you use.\n *\n * @example\n * ```ts\n * import {\n * TestRequestEvent,\n * runServerLoad,\n * runAction,\n * runRemote,\n * } from \"@thomasfosterau/effect-sveltekit/testing\";\n * import { Effect } from \"effect\";\n *\n * const event = TestRequestEvent({ params: { id: \"1\" }, locals: { user } });\n *\n * // Test a server load:\n * const data = await runServerLoad(\n * (event) =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return { item: yield* db.findById(event.params.id) };\n * }),\n * event,\n * { layer: TestDatabase.layer },\n * );\n *\n * // Test a remote function handler directly:\n * const { run } = runRemote(event, { layer: TestLayer });\n * const items = await run(listItems());\n * ```\n */\n\nimport type { RequestEvent } from \"@sveltejs/kit\";\nimport { Effect, Layer } from \"effect\";\nimport {\n makeRuntimeAdapter,\n RequestRuntime,\n type ErrorTranslator,\n type RequestRuntimeProvider,\n} from \"./request-runtime.js\";\nimport { SvelteKit } from \"./kit.js\";\nimport { makeRunHelpers, type FormErrorMapper, type RunHelpers } from \"./internal/remote-run.js\";\n\nexport type { RunHelpers, FormErrorMapper } from \"./internal/remote-run.js\";\n\n// ─── TestCookies ─────────────────────────────────────────────────────────────\n\n/** Map-backed Cookies implementation for use in {@link TestRequestEvent}. */\nclass TestCookies {\n private readonly store: Map<string, string>;\n\n constructor(initial: Record<string, string> = {}) {\n this.store = new Map(Object.entries(initial));\n }\n\n get(name: string, _opts?: unknown): string | undefined {\n return this.store.get(name);\n }\n\n getAll(_opts?: unknown): Array<{ name: string; value: string }> {\n return [...this.store.entries()].map(([name, value]) => ({ name, value }));\n }\n\n set(name: string, value: string, _opts?: unknown): void {\n this.store.set(name, value);\n }\n\n delete(name: string, _opts?: unknown): void {\n this.store.delete(name);\n }\n\n serialize(name: string, value: string, _opts?: unknown): string {\n return `${name}=${encodeURIComponent(value)}`;\n }\n}\n\n// ─── TestRequestEvent ─────────────────────────────────────────────────────────\n\n/** Options for {@link TestRequestEvent}. */\nexport interface TestRequestEventOptions {\n /**\n * The request URL. Accepts a `string` or a `URL` instance.\n * Defaults to `\"http://localhost/\"`.\n */\n readonly url?: string | URL;\n /** The HTTP method. Defaults to `\"GET\"`. */\n readonly method?: string;\n /** Additional HTTP headers added to the underlying `Request`. */\n readonly headers?: HeadersInit;\n /** The request body. Only usable with methods that accept a body. */\n readonly body?: BodyInit | null;\n /**\n * Route params (e.g. `{ id: \"123\" }` for a route like `/items/[id]`).\n * Defaults to `{}`.\n */\n readonly params?: Record<string, string>;\n /**\n * Initial `event.locals` value. Defaults to `{}`.\n * Cast to `App.Locals` — extend it with the properties your handlers need.\n */\n readonly locals?: Record<string, unknown>;\n /**\n * Pre-seeded cookies as `{ name: value }` pairs.\n * The backing store supports `get`, `getAll`, `set`, `delete`, and\n * `serialize`; the object is cast to SvelteKit's `Cookies` interface.\n */\n readonly cookies?: Record<string, string>;\n /**\n * The `event.platform` value, if any.\n * Pass a partial platform object matching the environment the handler targets.\n */\n readonly platform?: unknown;\n /**\n * The `fetch` implementation used by `event.fetch`.\n * Defaults to `globalThis.fetch`. Pass a custom function to intercept or\n * stub network calls made by the handler.\n */\n readonly fetch?: typeof globalThis.fetch;\n}\n\n/**\n * Create a minimal {@link RequestEvent} for use in unit tests.\n *\n * All fields are optional — omit what the handler under test doesn't inspect.\n * Cookies are backed by an in-memory `Map`; mutations (`set`, `delete`) are\n * reflected in subsequent `get`/`getAll` calls on the same event.\n *\n * @example\n * ```ts\n * const event = TestRequestEvent({\n * url: \"http://localhost/items/42\",\n * params: { id: \"42\" },\n * locals: { user: { id: \"u1\", role: \"admin\" } },\n * cookies: { session: \"tok_test\" },\n * });\n * ```\n */\nexport function TestRequestEvent(opts: TestRequestEventOptions = {}): RequestEvent {\n const rawUrl = opts.url instanceof URL ? opts.url.toString() : (opts.url ?? \"http://localhost/\");\n const url = new URL(rawUrl);\n\n const init: RequestInit = { method: opts.method ?? \"GET\" };\n if (opts.headers !== undefined) init.headers = opts.headers;\n if (opts.body !== undefined) init.body = opts.body;\n\n const request = new Request(url.toString(), init);\n const cookies = new TestCookies(opts.cookies ?? {});\n const fetchFn = opts.fetch ?? globalThis.fetch;\n\n return {\n request,\n url,\n params: (opts.params ?? {}) as any,\n route: { id: null },\n fetch: fetchFn,\n cookies: cookies as any,\n locals: (opts.locals ?? {}) as any,\n platform: opts.platform as any,\n isDataRequest: false,\n isSubRequest: false,\n setHeaders: () => {},\n getClientAddress: () => \"127.0.0.1\",\n } as unknown as RequestEvent;\n}\n\n// ─── Options ──────────────────────────────────────────────────────────────────\n\n/**\n * Options shared by {@link runServerLoad}, {@link runAction}, and\n * {@link runEndpoint}.\n */\nexport interface TestRunOptions<R> {\n /**\n * The `Layer` providing the handler effect's required services `R`.\n * Omit when the handler has no service requirements (`R` is `never`).\n */\n readonly layer?: Layer.Layer<R>;\n /**\n * Translate a failed `Cause` at the boundary. Defaults to\n * {@link SvelteKit.translate} (Kit control-flow through; anything else\n * becomes an `EffectCauseError`).\n */\n readonly translateError?: ErrorTranslator;\n}\n\n/**\n * Options for {@link runRemote}.\n */\nexport interface RunRemoteOptions<R> {\n /** The `Layer` providing the remote handler's service requirements. */\n readonly layer?: Layer.Layer<R>;\n /**\n * Translate a failed `Cause` at the boundary. Defaults to\n * {@link SvelteKit.translate}.\n */\n readonly translateError?: ErrorTranslator;\n /**\n * Map a remote form handler's failure to SvelteKit field issues. Receives\n * the squashed handler failure and the `{ issue, data }` context. When\n * provided it is called instead of `translateError` on the form error path.\n */\n readonly formError?: FormErrorMapper;\n}\n\n// ─── Internal ─────────────────────────────────────────────────────────────────\n\nconst makeAdapter = <R>(opts: TestRunOptions<R> | undefined) =>\n makeRuntimeAdapter(RequestRuntime.fromLayer((opts?.layer ?? Layer.empty) as Layer.Layer<R>), {\n translateError: opts?.translateError ?? SvelteKit.translate,\n });\n\n// ─── runServerLoad ────────────────────────────────────────────────────────────\n\n/**\n * Run an Effect-based server `load` function under test.\n *\n * Wraps `fn` with `makeRuntimeAdapter` using the provided `opts.layer` (or\n * `Layer.empty` when omitted) and calls the resulting `ServerLoad` with\n * `event`. Errors are translated by `opts.translateError` (defaults to\n * {@link SvelteKit.translate}).\n *\n * @example\n * ```ts\n * const data = await runServerLoad(\n * (event) => Effect.gen(function* () {\n * const db = yield* Database;\n * return { items: yield* db.list() };\n * }),\n * TestRequestEvent(),\n * { layer: TestDatabase.layer },\n * );\n * expect(data.items).toHaveLength(0);\n * ```\n */\nexport const runServerLoad = <R, A extends Record<string, any> | void = void>(\n fn: (event: RequestEvent) => Effect.Effect<A, unknown, R>,\n event: RequestEvent,\n opts?: TestRunOptions<R>,\n): Promise<A> => (makeAdapter(opts).serverLoad(fn) as (e: RequestEvent) => Promise<A>)(event);\n\n// ─── runAction ───────────────────────────────────────────────────────────────\n\n/**\n * Run an Effect-based form action handler under test.\n *\n * @example\n * ```ts\n * const result = await runAction(\n * (_event) => Effect.succeed({ success: true }),\n * TestRequestEvent({ method: \"POST\" }),\n * );\n * expect((result as any).success).toBe(true);\n * ```\n */\nexport const runAction = <R, A extends Record<string, any> | void = void>(\n fn: (event: RequestEvent) => Effect.Effect<A, unknown, R>,\n event: RequestEvent,\n opts?: TestRunOptions<R>,\n): Promise<A> => (makeAdapter(opts).action(fn) as (e: RequestEvent) => Promise<A>)(event);\n\n// ─── runEndpoint ──────────────────────────────────────────────────────────────\n\n/**\n * Run an Effect-based `+server.ts` endpoint handler under test.\n *\n * @example\n * ```ts\n * const response = await runEndpoint(\n * (_event) => Effect.succeed(new Response(JSON.stringify({ ok: true }))),\n * TestRequestEvent({ url: \"http://localhost/api/ping\" }),\n * );\n * expect(response.status).toBe(200);\n * ```\n */\nexport const runEndpoint = <R>(\n fn: (event: RequestEvent) => Effect.Effect<Response, unknown, R>,\n event: RequestEvent,\n opts?: TestRunOptions<R>,\n): Promise<Response> => Promise.resolve(makeAdapter(opts).endpoint(fn)(event as any));\n\n// ─── runRemote ────────────────────────────────────────────────────────────────\n\n/**\n * Build a `{ run, runForm }` helper pair for directly invoking remote-function\n * Effect handlers under test.\n *\n * Unlike the production `query` / `command` / `form` helpers, which resolve the\n * request event via `$app/server.getRequestEvent()`, this resolves it from the\n * `event` argument you supply — no SvelteKit module-graph mocking required.\n *\n * Use `run` for `query` / `command` handlers and `runForm` for `form` handlers\n * that need the `issue` / `data` context.\n *\n * @example\n * ```ts\n * import { TestRequestEvent, runRemote } from \"@thomasfosterau/effect-sveltekit/testing\";\n *\n * const event = TestRequestEvent();\n * const { run } = runRemote(event, { layer: TestDatabase.layer });\n *\n * // Invoke the underlying Effect directly — no AppServer needed:\n * const items = await run(listItemsEffect());\n * expect(items).toEqual([]);\n * ```\n */\nexport const runRemote = <R = never>(\n event: RequestEvent,\n opts?: RunRemoteOptions<R>,\n): RunHelpers => {\n const translate = opts?.translateError ?? SvelteKit.translate;\n const layer = (opts?.layer ?? Layer.empty) as Layer.Layer<R>;\n const provider: RequestRuntimeProvider<R> = RequestRuntime.fromLayer(layer);\n return makeRunHelpers(\n () => ({ runExit: (effect) => provider.run(event).runPromiseExit(effect) }),\n translate,\n opts?.formError,\n );\n};\n"],"mappings":";;;;;;AA0DA,IAAM,cAAN,MAAkB;CAChB;CAEA,YAAY,UAAkC,CAAC,GAAG;EAChD,KAAK,QAAQ,IAAI,IAAI,OAAO,QAAQ,OAAO,CAAC;CAC9C;CAEA,IAAI,MAAc,OAAqC;EACrD,OAAO,KAAK,MAAM,IAAI,IAAI;CAC5B;CAEA,OAAO,OAAyD;EAC9D,OAAO,CAAC,GAAG,KAAK,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY;GAAE;GAAM;EAAM,EAAE;CAC3E;CAEA,IAAI,MAAc,OAAe,OAAuB;EACtD,KAAK,MAAM,IAAI,MAAM,KAAK;CAC5B;CAEA,OAAO,MAAc,OAAuB;EAC1C,KAAK,MAAM,OAAO,IAAI;CACxB;CAEA,UAAU,MAAc,OAAe,OAAyB;EAC9D,OAAO,GAAG,KAAK,GAAG,mBAAmB,KAAK;CAC5C;AACF;;;;;;;;;;;;;;;;;;AA+DA,SAAgB,iBAAiB,OAAgC,CAAC,GAAiB;CACjF,MAAM,SAAS,KAAK,eAAe,MAAM,KAAK,IAAI,SAAS,IAAK,KAAK,OAAO;CAC5E,MAAM,MAAM,IAAI,IAAI,MAAM;CAE1B,MAAM,OAAoB,EAAE,QAAQ,KAAK,UAAU,MAAM;CACzD,IAAI,KAAK,YAAY,KAAA,GAAW,KAAK,UAAU,KAAK;CACpD,IAAI,KAAK,SAAS,KAAA,GAAW,KAAK,OAAO,KAAK;CAE9C,MAAM,UAAU,IAAI,QAAQ,IAAI,SAAS,GAAG,IAAI;CAChD,MAAM,UAAU,IAAI,YAAY,KAAK,WAAW,CAAC,CAAC;CAClD,MAAM,UAAU,KAAK,SAAS,WAAW;CAEzC,OAAO;EACL;EACA;EACA,QAAS,KAAK,UAAU,CAAC;EACzB,OAAO,EAAE,IAAI,KAAK;EAClB,OAAO;EACE;EACT,QAAS,KAAK,UAAU,CAAC;EACzB,UAAU,KAAK;EACf,eAAe;EACf,cAAc;EACd,kBAAkB,CAAC;EACnB,wBAAwB;CAC1B;AACF;AA2CA,MAAM,eAAkB,SACtB,mBAAmB,eAAe,UAAW,MAAM,SAAS,MAAM,KAAwB,GAAG,EAC3F,gBAAgB,MAAM,kBAAkB,UAAU,UACpD,CAAC;;;;;;;;;;;;;;;;;;;;;;AAyBH,MAAa,iBACX,IACA,OACA,SACgB,YAAY,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,CAAqC,KAAK;;;;;;;;;;;;;AAgB5F,MAAa,aACX,IACA,OACA,SACgB,YAAY,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAqC,KAAK;;;;;;;;;;;;;AAgBxF,MAAa,eACX,IACA,OACA,SACsB,QAAQ,QAAQ,YAAY,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,KAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;AA2BpF,MAAa,aACX,OACA,SACe;CACf,MAAM,YAAY,MAAM,kBAAkB,UAAU;CACpD,MAAM,QAAS,MAAM,SAAS,MAAM;CACpC,MAAM,WAAsC,eAAe,UAAU,KAAK;CAC1E,OAAO,sBACE,EAAE,UAAU,WAAW,SAAS,IAAI,KAAK,CAAC,CAAC,eAAe,MAAM,EAAE,IACzE,WACA,MAAM,SACR;AACF"}
|
package/dist/transport.d.ts
CHANGED
package/dist/universal.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { SvelteRouteParams } from "./internal/route-params.js";
|
|
2
2
|
import { UniversalRuntime, makeUniversalRuntime } from "./app-runtime.js";
|
|
3
3
|
import { Effect } from "effect";
|
|
4
|
-
import * as HttpClient from "effect/
|
|
4
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
5
5
|
import { Load } from "@sveltejs/kit";
|
|
6
|
-
|
|
7
6
|
//#region src/universal.d.ts
|
|
8
7
|
/**
|
|
9
8
|
* Wraps an Effect-based function to work as a SvelteKit universal load function.
|
|
@@ -18,7 +17,7 @@ import { Load } from "@sveltejs/kit";
|
|
|
18
17
|
* @example
|
|
19
18
|
* ```ts
|
|
20
19
|
* import { load as effectLoad } from '@thomasfosterau/effect-sveltekit';
|
|
21
|
-
* import * as HttpClient from 'effect/
|
|
20
|
+
* import * as HttpClient from 'effect/http/HttpClient';
|
|
22
21
|
* import { Effect } from 'effect';
|
|
23
22
|
*
|
|
24
23
|
* export const load = effectLoad((event) =>
|
|
@@ -32,5 +31,5 @@ import { Load } from "@sveltejs/kit";
|
|
|
32
31
|
*/
|
|
33
32
|
declare function load<Params extends SvelteRouteParams = SvelteRouteParams, InputData extends Record<string, unknown> | null = any, ParentData extends Record<string, unknown> = any, OutputData extends Record<string, unknown> | void = any, RouteId extends string | null = any>(fn: (event: Parameters<Load<Params, InputData, ParentData, OutputData, RouteId>>[0]) => Effect.Effect<OutputData, unknown, HttpClient.HttpClient>): Load<Params, InputData, ParentData, OutputData, RouteId>;
|
|
34
33
|
//#endregion
|
|
35
|
-
export { load };
|
|
34
|
+
export { type UniversalRuntime, load, makeUniversalRuntime };
|
|
36
35
|
//# sourceMappingURL=universal.d.ts.map
|
package/dist/universal.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./app-runtime.js";
|
|
1
|
+
import { makeUniversalRuntime } from "./app-runtime.js";
|
|
2
2
|
import { defaultUniversalRuntime } from "./internal/default-runtime.js";
|
|
3
3
|
import "effect";
|
|
4
4
|
//#region src/universal.ts
|
|
@@ -15,7 +15,7 @@ import "effect";
|
|
|
15
15
|
* @example
|
|
16
16
|
* ```ts
|
|
17
17
|
* import { load as effectLoad } from '@thomasfosterau/effect-sveltekit';
|
|
18
|
-
* import * as HttpClient from 'effect/
|
|
18
|
+
* import * as HttpClient from 'effect/http/HttpClient';
|
|
19
19
|
* import { Effect } from 'effect';
|
|
20
20
|
*
|
|
21
21
|
* export const load = effectLoad((event) =>
|
|
@@ -31,6 +31,6 @@ function load(fn) {
|
|
|
31
31
|
return defaultUniversalRuntime().load(fn);
|
|
32
32
|
}
|
|
33
33
|
//#endregion
|
|
34
|
-
export { load };
|
|
34
|
+
export { load, makeUniversalRuntime };
|
|
35
35
|
|
|
36
36
|
//# sourceMappingURL=universal.js.map
|
package/dist/universal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"universal.js","names":["runtime"],"sources":["../src/universal.ts"],"sourcesContent":["import type { Load } from \"@sveltejs/kit\";\nimport { Effect } from \"effect\";\nimport type * as HttpClient from \"effect/
|
|
1
|
+
{"version":3,"file":"universal.js","names":["runtime"],"sources":["../src/universal.ts"],"sourcesContent":["import type { Load } from \"@sveltejs/kit\";\nimport { Effect } from \"effect\";\nimport type * as HttpClient from \"effect/http/HttpClient\";\nimport type { SvelteRouteParams } from \"./internal/route-params.js\";\nimport { defaultUniversalRuntime as runtime } from \"./internal/default-runtime.js\";\n\n/**\n * Wraps an Effect-based function to work as a SvelteKit universal load function.\n *\n * The effect has access to `HttpClient` on both the server and the client:\n * it is backed by the load event's `fetch`, so relative URLs and SvelteKit's\n * fetch behaviours (deduplication, cookies forwarding) work as expected.\n *\n * To give effects access to your own application services, create an\n * app-wide runtime with `makeUniversalRuntime` and use its `load` instead.\n *\n * @example\n * ```ts\n * import { load as effectLoad } from '@thomasfosterau/effect-sveltekit';\n * import * as HttpClient from 'effect/http/HttpClient';\n * import { Effect } from 'effect';\n *\n * export const load = effectLoad((event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * const response = yield* client.get(`/api/items/${event.params.id}`);\n * return { item: yield* response.json };\n * })\n * );\n * ```\n */\nexport function load<\n Params extends SvelteRouteParams = SvelteRouteParams,\n InputData extends Record<string, unknown> | null = any,\n ParentData extends Record<string, unknown> = any,\n OutputData extends Record<string, unknown> | void = any,\n RouteId extends string | null = any,\n>(\n fn: (\n event: Parameters<Load<Params, InputData, ParentData, OutputData, RouteId>>[0],\n ) => Effect.Effect<OutputData, unknown, HttpClient.HttpClient>,\n): Load<Params, InputData, ParentData, OutputData, RouteId> {\n return runtime().load(fn);\n}\n\n/**\n * App-wide runtime: define your application's (universal) Effect services\n * once and share them across every `load` invocation.\n */\nexport { makeUniversalRuntime, type UniversalRuntime } from \"./app-runtime.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,KAOd,IAG0D;CAC1D,OAAOA,wBAAQ,CAAC,CAAC,KAAK,EAAE;AAC1B"}
|
package/dist/vite.d.ts
CHANGED
package/dist/vite.js
CHANGED
|
@@ -17,7 +17,7 @@ import MagicString from "magic-string";
|
|
|
17
17
|
* ```ts
|
|
18
18
|
* // +page.server.ts
|
|
19
19
|
* import { Effect } from "effect";
|
|
20
|
-
* import * as HttpClient from "effect/
|
|
20
|
+
* import * as HttpClient from "effect/http/HttpClient";
|
|
21
21
|
*
|
|
22
22
|
* export const load = (event) =>
|
|
23
23
|
* Effect.gen(function* () {
|
|
@@ -35,7 +35,7 @@ const INTERNAL_IMPORT = "@thomasfosterau/effect-sveltekit/internal/auto";
|
|
|
35
35
|
const ORIG_PREFIX = "__effect_kit_orig_";
|
|
36
36
|
const RUNTIME_IDENT = "__effect_kit_runtime";
|
|
37
37
|
/** File kinds whose exports only ever run on the server. */
|
|
38
|
-
const serverKinds = new Set([
|
|
38
|
+
const serverKinds = /* @__PURE__ */ new Set([
|
|
39
39
|
"pageServer",
|
|
40
40
|
"layoutServer",
|
|
41
41
|
"server",
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["/**\n * Vite plugin that lets you write SvelteKit route exports as Effects (or\n * functions returning Effects) without manually wrapping them.\n *\n * ```ts\n * // vite.config.ts\n * import { sveltekit } from \"@sveltejs/kit/vite\";\n * import { effectKit } from \"@thomasfosterau/effect-sveltekit/vite\";\n *\n * export default {\n * plugins: [effectKit(), sveltekit()],\n * };\n * ```\n *\n * ```ts\n * // +page.server.ts\n * import { Effect } from \"effect\";\n * import * as HttpClient from \"effect/unstable/http/HttpClient\";\n *\n * export const load = (event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * return { hello: \"world\" };\n * });\n * ```\n *\n * Under the hood the plugin rewrites the export to call the same wrappers\n * that the explicit API (`serverLoad`, `actions`, `handle`, `endpoint`, ...)\n * uses. Plain functions that return promises or values pass through\n * unchanged, so the plugin is safe to enable on existing projects.\n */\n\nimport MagicString from \"magic-string\";\nimport type { Plugin } from \"vite\";\n\nconst INTERNAL_IMPORT = \"@thomasfosterau/effect-sveltekit/internal/auto\";\nconst ORIG_PREFIX = \"__effect_kit_orig_\";\nconst RUNTIME_IDENT = \"__effect_kit_runtime\";\n\nexport type EffectKitFileKind =\n | \"pageServer\"\n | \"page\"\n | \"layoutServer\"\n | \"layout\"\n | \"server\"\n | \"hooks\"\n | \"hooksServer\"\n | \"hooksClient\";\n\n/** File kinds whose exports only ever run on the server. */\nconst serverKinds: ReadonlySet<EffectKitFileKind> = new Set([\n \"pageServer\",\n \"layoutServer\",\n \"server\",\n \"hooksServer\",\n]);\n\nconst wrapperByKind: Record<EffectKitFileKind, Record<string, string>> = {\n pageServer: { load: \"wrapServerLoad\", actions: \"wrapActions\" },\n page: { load: \"wrapLoad\" },\n layoutServer: { load: \"wrapServerLoad\" },\n layout: { load: \"wrapLoad\" },\n server: {\n GET: \"wrapEndpoint\",\n POST: \"wrapEndpoint\",\n PUT: \"wrapEndpoint\",\n PATCH: \"wrapEndpoint\",\n DELETE: \"wrapEndpoint\",\n OPTIONS: \"wrapEndpoint\",\n HEAD: \"wrapEndpoint\",\n fallback: \"wrapEndpoint\",\n },\n hooksServer: {\n handle: \"wrapHandle\",\n handleFetch: \"wrapHandleFetch\",\n handleError: \"wrapHandleError\",\n init: \"wrapInit\",\n },\n hooksClient: {\n handleError: \"wrapHandleError\",\n init: \"wrapInit\",\n },\n // Universal `src/hooks.ts`: only `reroute` is function-shaped (the `transport`\n // export is plain data and is intentionally left untouched).\n hooks: {\n reroute: \"wrapReroute\",\n },\n};\n\nfunction getFileKind(id: string): EffectKitFileKind | null {\n const path = id.split(\"?\")[0]!.replace(/\\\\/g, \"/\");\n if (/\\/\\+page\\.server\\.(?:ts|js|mts|mjs)$/.test(path)) return \"pageServer\";\n if (/\\/\\+page\\.(?:ts|js|mts|mjs)$/.test(path)) return \"page\";\n if (/\\/\\+layout\\.server\\.(?:ts|js|mts|mjs)$/.test(path)) return \"layoutServer\";\n if (/\\/\\+layout\\.(?:ts|js|mts|mjs)$/.test(path)) return \"layout\";\n if (/\\/\\+server\\.(?:ts|js|mts|mjs)$/.test(path)) return \"server\";\n if (/\\/hooks\\.server\\.(?:ts|js|mts|mjs)$/.test(path)) return \"hooksServer\";\n if (/\\/hooks\\.client\\.(?:ts|js|mts|mjs)$/.test(path)) return \"hooksClient\";\n // Plain `hooks.ts` is the universal hooks file; checked last so the\n // `.server.`/`.client.` variants above take precedence.\n if (/\\/hooks\\.(?:ts|js|mts|mjs)$/.test(path)) return \"hooks\";\n return null;\n}\n\n/**\n * Returns a copy of `source` with line/block comments and string/template\n * contents replaced by spaces (newlines preserved). Byte offsets in the\n * returned string line up with the original, so regex scans don't trip on\n * tokens inside comments or strings.\n */\nfunction maskCommentsAndStrings(source: string): string {\n const out: string[] = [];\n let i = 0;\n const len = source.length;\n\n while (i < len) {\n const c = source[i]!;\n const next = source[i + 1];\n\n if (c === \"/\" && next === \"/\") {\n out.push(\" \");\n i += 2;\n while (i < len && source[i] !== \"\\n\") {\n out.push(\" \");\n i++;\n }\n continue;\n }\n\n if (c === \"/\" && next === \"*\") {\n out.push(\" \");\n i += 2;\n while (i < len && !(source[i] === \"*\" && source[i + 1] === \"/\")) {\n out.push(source[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n if (i < len) {\n out.push(\" \");\n i += 2;\n }\n continue;\n }\n\n if (c === '\"' || c === \"'\") {\n const quote = c;\n out.push(quote);\n i++;\n while (i < len && source[i] !== quote) {\n if (source[i] === \"\\\\\" && i + 1 < len) {\n out.push(\" \");\n i += 2;\n continue;\n }\n out.push(source[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n if (i < len) {\n out.push(quote);\n i++;\n }\n continue;\n }\n\n if (c === \"`\") {\n out.push(\"`\");\n i++;\n while (i < len && source[i] !== \"`\") {\n if (source[i] === \"\\\\\" && i + 1 < len) {\n out.push(\" \");\n i += 2;\n continue;\n }\n if (source[i] === \"$\" && source[i + 1] === \"{\") {\n // Preserve template expression so nested strings are masked by\n // the outer loop.\n out.push(\"${\");\n i += 2;\n let depth = 1;\n while (i < len && depth > 0) {\n out.push(source[i]!);\n if (source[i] === \"{\") depth++;\n else if (source[i] === \"}\") depth--;\n i++;\n }\n continue;\n }\n out.push(source[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n if (i < len) {\n out.push(\"`\");\n i++;\n }\n continue;\n }\n\n out.push(c);\n i++;\n }\n\n return out.join(\"\");\n}\n\ninterface ExportMatch {\n name: string;\n start: number;\n end: number;\n replacement: string;\n}\n\n/**\n * Find top-level `export const|let|var|function NAME` declarations whose\n * NAME is in `targetNames`. Returns the slice of the original source to\n * replace, along with a renamed declaration to put in its place.\n *\n * `export { NAME }`, `export default`, and destructuring forms are not\n * supported — users who need them should use the explicit wrappers.\n */\nfunction findExports(masked: string, targetNames: ReadonlySet<string>): ExportMatch[] {\n const matches: ExportMatch[] = [];\n const pattern =\n /^[ \\t]*(export)[ \\t]+(?:async[ \\t]+)?(?:const|let|var|function)[ \\t]+([A-Za-z_$][\\w$]*)/gm;\n\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(masked))) {\n const [full, , name] = m;\n if (!targetNames.has(name!)) continue;\n\n const exportStart = m.index + full.indexOf(\"export\");\n const nameEnd = m.index + full.length;\n const renamed = `${ORIG_PREFIX}${name}`;\n\n const slice = masked.slice(exportStart, nameEnd);\n const replacement = slice\n .replace(/^export[ \\t]+/, \"\")\n .replace(new RegExp(`\\\\b${name}$`), renamed);\n\n matches.push({ name: name!, start: exportStart, end: nameEnd, replacement });\n }\n\n return matches;\n}\n\nexport interface EffectKitPluginOptions {\n /**\n * Restrict transformation to a subset of SvelteKit file kinds.\n * Defaults to all standard route files.\n */\n include?: ReadonlyArray<EffectKitFileKind>;\n\n /**\n * Import specifier of a server-only module whose `runtime` (or default)\n * export is an app-wide runtime created with `makeServerRuntime`.\n *\n * When set, Effects returned from server route files (`+page.server.ts`,\n * `+layout.server.ts`, `+server.ts`, `hooks.server.ts`) have access to the\n * services of that runtime's layer.\n *\n * @example \"$lib/server/runtime\"\n */\n serverRuntime?: string;\n\n /**\n * Import specifier of a universal module whose `runtime` (or default)\n * export is an app-wide runtime created with `makeUniversalRuntime`.\n *\n * When set, Effects returned from universal route files (`+page.ts`,\n * `+layout.ts`, `hooks.client.ts`) have access to the services of that\n * runtime's layer. The module must be safe to import in the browser.\n *\n * @example \"$lib/runtime\"\n */\n clientRuntime?: string;\n}\n\n/**\n * Vite plugin that rewrites SvelteKit route exports so that Effects, or\n * functions returning Effects, are accepted wherever SvelteKit expects a\n * function-shaped export.\n */\nexport function effectKit(options: EffectKitPluginOptions = {}): Plugin {\n const include = options.include ? new Set(options.include) : null;\n\n return {\n name: \"effect-kit\",\n enforce: \"pre\",\n transform(code, id) {\n const kind = getFileKind(id);\n if (!kind) return null;\n if (include && !include.has(kind)) return null;\n\n const wrappers = wrapperByKind[kind];\n const targetNames = new Set(Object.keys(wrappers));\n\n const masked = maskCommentsAndStrings(code);\n const found = findExports(masked, targetNames);\n if (found.length === 0) return null;\n\n const s = new MagicString(code);\n\n for (const match of found) {\n s.overwrite(match.start, match.end, match.replacement);\n }\n\n const wrapperImports = Array.from(new Set(found.map((m) => wrappers[m.name]!)));\n\n // Server-only files get the server runtime module; universal/client\n // files get the client runtime module (it must be browser-safe).\n const runtimeSpecifier = serverKinds.has(kind)\n ? options.serverRuntime\n : options.clientRuntime;\n\n const imports =\n `\\nimport { ${wrapperImports.join(\", \")} } from ${JSON.stringify(INTERNAL_IMPORT)};\\n` +\n (runtimeSpecifier\n ? `import * as ${RUNTIME_IDENT} from ${JSON.stringify(runtimeSpecifier)};\\n`\n : \"\");\n const runtimeArg = runtimeSpecifier ? `, ${RUNTIME_IDENT}` : \"\";\n const exports = found\n .map(\n (m) =>\n `export const ${m.name} = ${wrappers[m.name]}(${ORIG_PREFIX}${m.name}${runtimeArg});`,\n )\n .join(\"\\n\");\n\n s.append(`\\n${imports}${exports}\\n`);\n\n return {\n code: s.toString(),\n map: s.generateMap({ hires: \"boundary\", source: id }),\n };\n },\n };\n}\n\nexport default effectKit;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["/**\n * Vite plugin that lets you write SvelteKit route exports as Effects (or\n * functions returning Effects) without manually wrapping them.\n *\n * ```ts\n * // vite.config.ts\n * import { sveltekit } from \"@sveltejs/kit/vite\";\n * import { effectKit } from \"@thomasfosterau/effect-sveltekit/vite\";\n *\n * export default {\n * plugins: [effectKit(), sveltekit()],\n * };\n * ```\n *\n * ```ts\n * // +page.server.ts\n * import { Effect } from \"effect\";\n * import * as HttpClient from \"effect/http/HttpClient\";\n *\n * export const load = (event) =>\n * Effect.gen(function* () {\n * const client = yield* HttpClient.HttpClient;\n * return { hello: \"world\" };\n * });\n * ```\n *\n * Under the hood the plugin rewrites the export to call the same wrappers\n * that the explicit API (`serverLoad`, `actions`, `handle`, `endpoint`, ...)\n * uses. Plain functions that return promises or values pass through\n * unchanged, so the plugin is safe to enable on existing projects.\n */\n\nimport MagicString from \"magic-string\";\nimport type { Plugin } from \"vite\";\n\nconst INTERNAL_IMPORT = \"@thomasfosterau/effect-sveltekit/internal/auto\";\nconst ORIG_PREFIX = \"__effect_kit_orig_\";\nconst RUNTIME_IDENT = \"__effect_kit_runtime\";\n\nexport type EffectKitFileKind =\n | \"pageServer\"\n | \"page\"\n | \"layoutServer\"\n | \"layout\"\n | \"server\"\n | \"hooks\"\n | \"hooksServer\"\n | \"hooksClient\";\n\n/** File kinds whose exports only ever run on the server. */\nconst serverKinds: ReadonlySet<EffectKitFileKind> = new Set([\n \"pageServer\",\n \"layoutServer\",\n \"server\",\n \"hooksServer\",\n]);\n\nconst wrapperByKind: Record<EffectKitFileKind, Record<string, string>> = {\n pageServer: { load: \"wrapServerLoad\", actions: \"wrapActions\" },\n page: { load: \"wrapLoad\" },\n layoutServer: { load: \"wrapServerLoad\" },\n layout: { load: \"wrapLoad\" },\n server: {\n GET: \"wrapEndpoint\",\n POST: \"wrapEndpoint\",\n PUT: \"wrapEndpoint\",\n PATCH: \"wrapEndpoint\",\n DELETE: \"wrapEndpoint\",\n OPTIONS: \"wrapEndpoint\",\n HEAD: \"wrapEndpoint\",\n fallback: \"wrapEndpoint\",\n },\n hooksServer: {\n handle: \"wrapHandle\",\n handleFetch: \"wrapHandleFetch\",\n handleError: \"wrapHandleError\",\n init: \"wrapInit\",\n },\n hooksClient: {\n handleError: \"wrapHandleError\",\n init: \"wrapInit\",\n },\n // Universal `src/hooks.ts`: only `reroute` is function-shaped (the `transport`\n // export is plain data and is intentionally left untouched).\n hooks: {\n reroute: \"wrapReroute\",\n },\n};\n\nfunction getFileKind(id: string): EffectKitFileKind | null {\n const path = id.split(\"?\")[0]!.replace(/\\\\/g, \"/\");\n if (/\\/\\+page\\.server\\.(?:ts|js|mts|mjs)$/.test(path)) return \"pageServer\";\n if (/\\/\\+page\\.(?:ts|js|mts|mjs)$/.test(path)) return \"page\";\n if (/\\/\\+layout\\.server\\.(?:ts|js|mts|mjs)$/.test(path)) return \"layoutServer\";\n if (/\\/\\+layout\\.(?:ts|js|mts|mjs)$/.test(path)) return \"layout\";\n if (/\\/\\+server\\.(?:ts|js|mts|mjs)$/.test(path)) return \"server\";\n if (/\\/hooks\\.server\\.(?:ts|js|mts|mjs)$/.test(path)) return \"hooksServer\";\n if (/\\/hooks\\.client\\.(?:ts|js|mts|mjs)$/.test(path)) return \"hooksClient\";\n // Plain `hooks.ts` is the universal hooks file; checked last so the\n // `.server.`/`.client.` variants above take precedence.\n if (/\\/hooks\\.(?:ts|js|mts|mjs)$/.test(path)) return \"hooks\";\n return null;\n}\n\n/**\n * Returns a copy of `source` with line/block comments and string/template\n * contents replaced by spaces (newlines preserved). Byte offsets in the\n * returned string line up with the original, so regex scans don't trip on\n * tokens inside comments or strings.\n */\nfunction maskCommentsAndStrings(source: string): string {\n const out: string[] = [];\n let i = 0;\n const len = source.length;\n\n while (i < len) {\n const c = source[i]!;\n const next = source[i + 1];\n\n if (c === \"/\" && next === \"/\") {\n out.push(\" \");\n i += 2;\n while (i < len && source[i] !== \"\\n\") {\n out.push(\" \");\n i++;\n }\n continue;\n }\n\n if (c === \"/\" && next === \"*\") {\n out.push(\" \");\n i += 2;\n while (i < len && !(source[i] === \"*\" && source[i + 1] === \"/\")) {\n out.push(source[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n if (i < len) {\n out.push(\" \");\n i += 2;\n }\n continue;\n }\n\n if (c === '\"' || c === \"'\") {\n const quote = c;\n out.push(quote);\n i++;\n while (i < len && source[i] !== quote) {\n if (source[i] === \"\\\\\" && i + 1 < len) {\n out.push(\" \");\n i += 2;\n continue;\n }\n out.push(source[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n if (i < len) {\n out.push(quote);\n i++;\n }\n continue;\n }\n\n if (c === \"`\") {\n out.push(\"`\");\n i++;\n while (i < len && source[i] !== \"`\") {\n if (source[i] === \"\\\\\" && i + 1 < len) {\n out.push(\" \");\n i += 2;\n continue;\n }\n if (source[i] === \"$\" && source[i + 1] === \"{\") {\n // Preserve template expression so nested strings are masked by\n // the outer loop.\n out.push(\"${\");\n i += 2;\n let depth = 1;\n while (i < len && depth > 0) {\n out.push(source[i]!);\n if (source[i] === \"{\") depth++;\n else if (source[i] === \"}\") depth--;\n i++;\n }\n continue;\n }\n out.push(source[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n if (i < len) {\n out.push(\"`\");\n i++;\n }\n continue;\n }\n\n out.push(c);\n i++;\n }\n\n return out.join(\"\");\n}\n\ninterface ExportMatch {\n name: string;\n start: number;\n end: number;\n replacement: string;\n}\n\n/**\n * Find top-level `export const|let|var|function NAME` declarations whose\n * NAME is in `targetNames`. Returns the slice of the original source to\n * replace, along with a renamed declaration to put in its place.\n *\n * `export { NAME }`, `export default`, and destructuring forms are not\n * supported — users who need them should use the explicit wrappers.\n */\nfunction findExports(masked: string, targetNames: ReadonlySet<string>): ExportMatch[] {\n const matches: ExportMatch[] = [];\n const pattern =\n /^[ \\t]*(export)[ \\t]+(?:async[ \\t]+)?(?:const|let|var|function)[ \\t]+([A-Za-z_$][\\w$]*)/gm;\n\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(masked))) {\n const [full, , name] = m;\n if (!targetNames.has(name!)) continue;\n\n const exportStart = m.index + full.indexOf(\"export\");\n const nameEnd = m.index + full.length;\n const renamed = `${ORIG_PREFIX}${name}`;\n\n const slice = masked.slice(exportStart, nameEnd);\n const replacement = slice\n .replace(/^export[ \\t]+/, \"\")\n .replace(new RegExp(`\\\\b${name}$`), renamed);\n\n matches.push({ name: name!, start: exportStart, end: nameEnd, replacement });\n }\n\n return matches;\n}\n\nexport interface EffectKitPluginOptions {\n /**\n * Restrict transformation to a subset of SvelteKit file kinds.\n * Defaults to all standard route files.\n */\n include?: ReadonlyArray<EffectKitFileKind>;\n\n /**\n * Import specifier of a server-only module whose `runtime` (or default)\n * export is an app-wide runtime created with `makeServerRuntime`.\n *\n * When set, Effects returned from server route files (`+page.server.ts`,\n * `+layout.server.ts`, `+server.ts`, `hooks.server.ts`) have access to the\n * services of that runtime's layer.\n *\n * @example \"$lib/server/runtime\"\n */\n serverRuntime?: string;\n\n /**\n * Import specifier of a universal module whose `runtime` (or default)\n * export is an app-wide runtime created with `makeUniversalRuntime`.\n *\n * When set, Effects returned from universal route files (`+page.ts`,\n * `+layout.ts`, `hooks.client.ts`) have access to the services of that\n * runtime's layer. The module must be safe to import in the browser.\n *\n * @example \"$lib/runtime\"\n */\n clientRuntime?: string;\n}\n\n/**\n * Vite plugin that rewrites SvelteKit route exports so that Effects, or\n * functions returning Effects, are accepted wherever SvelteKit expects a\n * function-shaped export.\n */\nexport function effectKit(options: EffectKitPluginOptions = {}): Plugin {\n const include = options.include ? new Set(options.include) : null;\n\n return {\n name: \"effect-kit\",\n enforce: \"pre\",\n transform(code, id) {\n const kind = getFileKind(id);\n if (!kind) return null;\n if (include && !include.has(kind)) return null;\n\n const wrappers = wrapperByKind[kind];\n const targetNames = new Set(Object.keys(wrappers));\n\n const masked = maskCommentsAndStrings(code);\n const found = findExports(masked, targetNames);\n if (found.length === 0) return null;\n\n const s = new MagicString(code);\n\n for (const match of found) {\n s.overwrite(match.start, match.end, match.replacement);\n }\n\n const wrapperImports = Array.from(new Set(found.map((m) => wrappers[m.name]!)));\n\n // Server-only files get the server runtime module; universal/client\n // files get the client runtime module (it must be browser-safe).\n const runtimeSpecifier = serverKinds.has(kind)\n ? options.serverRuntime\n : options.clientRuntime;\n\n const imports =\n `\\nimport { ${wrapperImports.join(\", \")} } from ${JSON.stringify(INTERNAL_IMPORT)};\\n` +\n (runtimeSpecifier\n ? `import * as ${RUNTIME_IDENT} from ${JSON.stringify(runtimeSpecifier)};\\n`\n : \"\");\n const runtimeArg = runtimeSpecifier ? `, ${RUNTIME_IDENT}` : \"\";\n const exports = found\n .map(\n (m) =>\n `export const ${m.name} = ${wrappers[m.name]}(${ORIG_PREFIX}${m.name}${runtimeArg});`,\n )\n .join(\"\\n\");\n\n s.append(`\\n${imports}${exports}\\n`);\n\n return {\n code: s.toString(),\n map: s.generateMap({ hires: \"boundary\", source: id }),\n };\n },\n };\n}\n\nexport default effectKit;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thomasfosterau/effect-sveltekit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Effect integration for SvelteKit: load/action/endpoint wrappers, remote functions, an effectKit() Vite plugin, and an Effect HTTP deployment adapter",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect",
|
|
@@ -73,13 +73,16 @@
|
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"@standard-schema/spec": "^1.1.0",
|
|
76
|
-
"magic-string": "^
|
|
77
|
-
"@thomasfosterau/effect-svelte": "^0.
|
|
76
|
+
"magic-string": "^1.4.2",
|
|
77
|
+
"@thomasfosterau/effect-svelte": "^0.4.0"
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"typescript": "npm:@typescript/typescript6@6.0.2"
|
|
78
81
|
},
|
|
79
82
|
"peerDependencies": {
|
|
80
|
-
"@effect/platform-node": "
|
|
83
|
+
"@effect/platform-node": ">=4.0.0-rc.118",
|
|
81
84
|
"@sveltejs/kit": "^2.0.0 || ^3.0.0-next.0",
|
|
82
|
-
"effect": "
|
|
85
|
+
"effect": ">=4.0.0-rc.118",
|
|
83
86
|
"svelte": "^5.40.0",
|
|
84
87
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
85
88
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"remote.server.js","names":["isBareHandlerValue"],"sources":["../src/remote.server.ts"],"sourcesContent":["import * 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/unstable/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\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\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\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\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\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\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\nexport const query: QueryNamespace<SvelteKitEffectEnv> = makeQueryNamespace(defaultHelpers.run);\nexport const queryBatch: QueryBatchFn<SvelteKitEffectEnv> = query.batch;\nexport const queryLive: QueryLiveFn<SvelteKitEffectEnv> = query.live;\nexport const form: FormFn<SvelteKitEffectEnv> = makeForm(defaultHelpers);\nexport const command: CommandFn<SvelteKitEffectEnv> = makeCommand(defaultHelpers.run);\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\nexport const RemoteFunction = {\n query,\n form,\n command,\n prerender,\n requested,\n withRuntime,\n};\n"],"mappings":"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"}
|