@thomasfosterau/effect-sveltekit 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/dist/Hooks.d.ts +33 -0
  4. package/dist/Hooks.js +34 -0
  5. package/dist/Hooks.js.map +1 -0
  6. package/dist/Navigation.d.ts +110 -0
  7. package/dist/Navigation.js +155 -0
  8. package/dist/Navigation.js.map +1 -0
  9. package/dist/Remote.d.ts +52 -0
  10. package/dist/Remote.js +67 -0
  11. package/dist/Remote.js.map +1 -0
  12. package/dist/State.d.ts +17 -0
  13. package/dist/State.js +65 -0
  14. package/dist/State.js.map +1 -0
  15. package/dist/_virtual/_rolldown/runtime.js +13 -0
  16. package/dist/adapter/handler.d.ts +67 -0
  17. package/dist/adapter/handler.js +158 -0
  18. package/dist/adapter/handler.js.map +1 -0
  19. package/dist/adapter/index.d.ts +29 -0
  20. package/dist/adapter/index.js +105 -0
  21. package/dist/adapter/index.js.map +1 -0
  22. package/dist/adapter/serve.d.ts +46 -0
  23. package/dist/adapter/serve.js +57 -0
  24. package/dist/adapter/serve.js.map +1 -0
  25. package/dist/app-runtime.d.ts +212 -0
  26. package/dist/app-runtime.js +173 -0
  27. package/dist/app-runtime.js.map +1 -0
  28. package/dist/client/Hooks.d.ts +48 -0
  29. package/dist/client/Hooks.js +61 -0
  30. package/dist/client/Hooks.js.map +1 -0
  31. package/dist/client.d.ts +6 -0
  32. package/dist/client.js +6 -0
  33. package/dist/config.d.ts +17 -0
  34. package/dist/config.js +51 -0
  35. package/dist/config.js.map +1 -0
  36. package/dist/index.d.ts +6 -0
  37. package/dist/index.js +6 -0
  38. package/dist/internal/app-state.d.ts +39 -0
  39. package/dist/internal/app-state.js +52 -0
  40. package/dist/internal/app-state.js.map +1 -0
  41. package/dist/internal/auto.d.ts +70 -0
  42. package/dist/internal/auto.js +161 -0
  43. package/dist/internal/auto.js.map +1 -0
  44. package/dist/internal/default-runtime.js +21 -0
  45. package/dist/internal/default-runtime.js.map +1 -0
  46. package/dist/internal/error.js +31 -0
  47. package/dist/internal/error.js.map +1 -0
  48. package/dist/internal/navigation-events.js +41 -0
  49. package/dist/internal/navigation-events.js.map +1 -0
  50. package/dist/remote-reactive.d.ts +38 -0
  51. package/dist/remote-reactive.js +44 -0
  52. package/dist/remote-reactive.js.map +1 -0
  53. package/dist/remote.server.d.ts +48 -0
  54. package/dist/remote.server.js +90 -0
  55. package/dist/remote.server.js.map +1 -0
  56. package/dist/runtime.d.ts +130 -0
  57. package/dist/runtime.js +143 -0
  58. package/dist/runtime.js.map +1 -0
  59. package/dist/server/Endpoint.d.ts +101 -0
  60. package/dist/server/Endpoint.js +109 -0
  61. package/dist/server/Endpoint.js.map +1 -0
  62. package/dist/server/Hooks.d.ts +97 -0
  63. package/dist/server/Hooks.js +108 -0
  64. package/dist/server/Hooks.js.map +1 -0
  65. package/dist/server.d.ts +108 -0
  66. package/dist/server.js +131 -0
  67. package/dist/server.js.map +1 -0
  68. package/dist/transport.d.ts +23 -0
  69. package/dist/transport.js +34 -0
  70. package/dist/transport.js.map +1 -0
  71. package/dist/universal.d.ts +35 -0
  72. package/dist/universal.js +36 -0
  73. package/dist/universal.js.map +1 -0
  74. package/dist/vite.d.ts +42 -0
  75. package/dist/vite.js +242 -0
  76. package/dist/vite.js.map +1 -0
  77. package/package.json +101 -0
@@ -0,0 +1,212 @@
1
+ import { SvelteKitRequestEvent } from "./runtime.js";
2
+ import { Effect, Exit, Fiber, Layer, ManagedRuntime } from "effect";
3
+ import * as HttpClient from "effect/unstable/http/HttpClient";
4
+ import { Action, Actions, Handle, HandleFetch, HandleServerError, Load, RequestEvent, ServerInit, ServerLoad } from "@sveltejs/kit";
5
+
6
+ //#region src/app-runtime.d.ts
7
+ /**
8
+ * The services provided to every wrapped effect on the server, in addition to
9
+ * the application's own layer.
10
+ */
11
+ type SvelteKitServices = HttpClient.HttpClient | SvelteKitRequestEvent;
12
+ /**
13
+ * A per-request Effect runtime: the application layer's services plus the
14
+ * per-request {@link SvelteKitServices} (an `HttpClient` backed by the request's
15
+ * `fetch`, and the {@link SvelteKitRequestEvent}), with the request services
16
+ * built once for the request and shared.
17
+ *
18
+ * A {@link ServerRuntime}'s `handle` hook creates one per request and stores it
19
+ * on `event.locals` (`App.Locals`). Every other wrapper for the same runtime
20
+ * (`serverLoad`, `action`, `endpoint`, ...) then runs on it, so a request's
21
+ * effects share one `HttpClient` and one request scope. Retrieve it from an
22
+ * event with `ServerRuntime.requestRuntime` to run your own effects.
23
+ */
24
+ interface RequestRuntime<R = never> {
25
+ /** Run an effect for this request, returning its `Exit`. */
26
+ readonly runPromiseExit: <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Promise<Exit.Exit<A, unknown>>;
27
+ /** Run an effect for this request, throwing on failure (passing SvelteKit control flow through). */
28
+ readonly runPromise: <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Promise<A>;
29
+ /** Fork an effect for this request, returning the running fiber. */
30
+ readonly runFork: <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Fiber.Fiber<A, unknown>;
31
+ }
32
+ /**
33
+ * A server-side Effect runtime bound to an application layer.
34
+ *
35
+ * Produced by {@link makeServerRuntime}. The wrapper methods mirror the
36
+ * standalone helpers in `@thomasfosterau/effect-sveltekit/server`, but
37
+ * effects additionally have access to the services `R` of the application
38
+ * layer.
39
+ */
40
+ interface ServerRuntime<R = never, E = never> {
41
+ /** The underlying managed runtime for the application layer. */
42
+ readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;
43
+ /**
44
+ * Wraps an Effect-based function as a SvelteKit server `load`.
45
+ */
46
+ readonly serverLoad: <Params extends Record<string, string> = any, ParentData extends Record<string, any> = any, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>) => ServerLoad<Params, ParentData, OutputData, RouteId>;
47
+ /**
48
+ * Wraps an Effect-based function as a SvelteKit form action.
49
+ */
50
+ readonly action: <Params extends Record<string, string> = any, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>) => Action<Params, OutputData, RouteId>;
51
+ /**
52
+ * Wraps an object of Effect-based form actions.
53
+ */
54
+ readonly actions: <Params extends Record<string, string> = any, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(actionsMap: Record<string, (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>>) => Actions<Params, OutputData, RouteId>;
55
+ /**
56
+ * Wraps an Effect-based function as a SvelteKit `handle` hook.
57
+ */
58
+ readonly handle: (fn: (args: Parameters<Handle>[0]) => Effect.Effect<Response, unknown, R | SvelteKitServices>) => Handle;
59
+ /**
60
+ * Wraps an Effect-based function as a SvelteKit server `handleFetch` hook,
61
+ * which intercepts `event.fetch` calls made on the server. The effect runs
62
+ * with the per-request services on top of the application layer and produces
63
+ * the `Response` to use.
64
+ */
65
+ readonly handleFetch: (fn: (args: Parameters<HandleFetch>[0]) => Effect.Effect<Response, unknown, R | SvelteKitServices>) => HandleFetch;
66
+ /**
67
+ * Wraps an Effect-based function as a SvelteKit server `handleError` hook.
68
+ *
69
+ * The effect receives the error and the request event, and produces the
70
+ * shape that SvelteKit exposes to the client (your `App.Error`, or nothing).
71
+ * It runs with the per-request services on top of the application layer.
72
+ */
73
+ readonly handleError: (fn: (args: Parameters<HandleServerError>[0]) => Effect.Effect<Awaited<ReturnType<HandleServerError>>, unknown, R | SvelteKitServices>) => HandleServerError;
74
+ /**
75
+ * Wraps an Effect as a SvelteKit server `init` hook, run once before the
76
+ * server responds to its first request. There is no request event, so the
77
+ * effect only has access to the application layer's services.
78
+ */
79
+ readonly init: (effect: Effect.Effect<void, unknown, R>) => ServerInit;
80
+ /**
81
+ * The per-request {@link RequestRuntime} this runtime's `handle` stored on the
82
+ * event, or `undefined` when `handle` was not used for the request. Use it to
83
+ * run your own effects on the same per-request runtime (shared `HttpClient`
84
+ * and request scope) that `serverLoad` / `action` / `endpoint` use.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * // anywhere with the request event, e.g. another `handle` in the sequence
89
+ * const rt = runtime.requestRuntime(event);
90
+ * await rt?.runPromise(Effect.gen(function* () {
91
+ * const user = yield* Auth.currentUser;
92
+ * event.locals.user = user;
93
+ * }));
94
+ * ```
95
+ */
96
+ readonly requestRuntime: (event: RequestEvent) => RequestRuntime<R> | undefined;
97
+ /**
98
+ * Wraps an Effect-based function as a SvelteKit `+server.ts` endpoint
99
+ * handler (`GET`, `POST`, ...).
100
+ */
101
+ readonly endpoint: <Params extends Record<string, string> = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, R | SvelteKitServices>) => (event: RequestEvent<Params, RouteId>) => Promise<Response>;
102
+ /**
103
+ * Runs an effect for a specific request, providing both the application
104
+ * layer and the per-request services. Failures (other than SvelteKit
105
+ * control flow) are thrown as `EffectCauseError`.
106
+ */
107
+ readonly runPromise: <A>(event: RequestEvent, effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Promise<A>;
108
+ /**
109
+ * Runs an effect for a specific request and returns the Exit, without
110
+ * translating failures into thrown errors.
111
+ */
112
+ readonly runPromiseExit: <A>(event: RequestEvent, effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Promise<Exit.Exit<A, unknown>>;
113
+ /**
114
+ * Disposes the underlying managed runtime, running the layer's finalizers.
115
+ * Call this on server shutdown or in tests.
116
+ */
117
+ readonly dispose: () => Promise<void>;
118
+ }
119
+ /**
120
+ * Creates a {@link ServerRuntime} from an application layer.
121
+ *
122
+ * The layer is built lazily on first use and shared by all requests for the
123
+ * lifetime of the server process. Define it once in a server-only module and
124
+ * import its wrappers from your routes:
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * // src/lib/server/runtime.ts
129
+ * import { makeServerRuntime } from '@thomasfosterau/effect-sveltekit/server';
130
+ * import { Layer } from 'effect';
131
+ * import { Database } from './database';
132
+ *
133
+ * export const runtime = makeServerRuntime(Database.layer);
134
+ * ```
135
+ *
136
+ * ```ts
137
+ * // src/routes/items/+page.server.ts
138
+ * import { runtime } from '$lib/server/runtime';
139
+ * import { Effect } from 'effect';
140
+ * import { Database } from '$lib/server/database';
141
+ *
142
+ * export const load = runtime.serverLoad((event) =>
143
+ * Effect.gen(function* () {
144
+ * const db = yield* Database;
145
+ * return { items: yield* db.listItems() };
146
+ * })
147
+ * );
148
+ * ```
149
+ *
150
+ * Note that the application layer is built outside of any request, so its
151
+ * services cannot depend on the per-request `SvelteKitRequestEvent` or
152
+ * `HttpClient`. Request-specific data should be passed to service methods as
153
+ * arguments instead.
154
+ */
155
+ declare const makeServerRuntime: <R = never, E = never>(layer?: Layer.Layer<R, E>) => ServerRuntime<R, E>;
156
+ /**
157
+ * A universal (client + server) Effect runtime bound to an application layer.
158
+ *
159
+ * Produced by {@link makeUniversalRuntime}. Because universal `load`
160
+ * functions also run in the browser, the layer must only use services that
161
+ * work in both environments.
162
+ */
163
+ interface UniversalRuntime<R = never, E = never> {
164
+ /** The underlying managed runtime for the application layer. */
165
+ readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;
166
+ /**
167
+ * Wraps an Effect-based function as a SvelteKit universal `load`.
168
+ *
169
+ * The effect gets `HttpClient` (backed by the load event's `fetch`) on both
170
+ * client and server, plus the services of the application layer.
171
+ */
172
+ readonly load: <Params extends Record<string, string> = any, 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, R | HttpClient.HttpClient>) => Load<Params, InputData, ParentData, OutputData, RouteId>;
173
+ /**
174
+ * Runs an effect with `HttpClient` backed by the given fetch implementation
175
+ * plus the application layer.
176
+ */
177
+ readonly runPromise: <A>(fetchFn: typeof globalThis.fetch, effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>) => Promise<A>;
178
+ /**
179
+ * Disposes the underlying managed runtime, running the layer's finalizers.
180
+ */
181
+ readonly dispose: () => Promise<void>;
182
+ }
183
+ /**
184
+ * Creates a {@link UniversalRuntime} from an application layer.
185
+ *
186
+ * @example
187
+ * ```ts
188
+ * // src/lib/runtime.ts
189
+ * import { makeUniversalRuntime } from '@thomasfosterau/effect-sveltekit';
190
+ * import { ApiClient } from '$lib/api';
191
+ *
192
+ * export const runtime = makeUniversalRuntime(ApiClient.layer);
193
+ * ```
194
+ *
195
+ * ```ts
196
+ * // src/routes/+page.ts
197
+ * import { runtime } from '$lib/runtime';
198
+ * import { Effect } from 'effect';
199
+ * import { ApiClient } from '$lib/api';
200
+ *
201
+ * export const load = runtime.load((event) =>
202
+ * Effect.gen(function* () {
203
+ * const api = yield* ApiClient;
204
+ * return { stories: yield* api.topStories() };
205
+ * })
206
+ * );
207
+ * ```
208
+ */
209
+ declare const makeUniversalRuntime: <R = never, E = never>(layer?: Layer.Layer<R, E>) => UniversalRuntime<R, E>;
210
+ //#endregion
211
+ export { RequestRuntime, ServerRuntime, SvelteKitServices, UniversalRuntime, makeServerRuntime, makeUniversalRuntime };
212
+ //# sourceMappingURL=app-runtime.d.ts.map
@@ -0,0 +1,173 @@
1
+ import { SvelteKitRequestEvent } from "./runtime.js";
2
+ import { causeToThrowable } from "./internal/error.js";
3
+ import { Effect, Exit, Layer, ManagedRuntime, Scope } from "effect";
4
+ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
5
+ //#region src/app-runtime.ts
6
+ /**
7
+ * Symbol under which a {@link ServerRuntime}'s `handle` stores the per-request
8
+ * {@link RequestRuntime} on `event.locals`. `Symbol.for` so the key is stable
9
+ * across module instances.
10
+ */
11
+ const RequestRuntimeKey = Symbol.for("@thomasfosterau/effect-sveltekit/requestRuntime");
12
+ /** Read the per-request runtime entry an event carries, if any. */
13
+ const requestRuntimeEntry = (event) => event.locals?.[RequestRuntimeKey];
14
+ /** Layer with the per-request services for a server-side request. */
15
+ const requestLayer = (event) => Layer.merge(FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, event.fetch))), Layer.succeed(SvelteKitRequestEvent, event));
16
+ /** Layer providing `HttpClient` backed by an arbitrary fetch implementation. */
17
+ const fetchLayer = (fetchFn) => FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)));
18
+ /** Settle an exit the way SvelteKit expects: return the value or throw. */
19
+ const settle = (exit) => Exit.match(exit, {
20
+ onFailure: (cause) => {
21
+ throw causeToThrowable(cause);
22
+ },
23
+ onSuccess: (value) => value
24
+ });
25
+ /**
26
+ * Creates a {@link ServerRuntime} from an application layer.
27
+ *
28
+ * The layer is built lazily on first use and shared by all requests for the
29
+ * lifetime of the server process. Define it once in a server-only module and
30
+ * import its wrappers from your routes:
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * // src/lib/server/runtime.ts
35
+ * import { makeServerRuntime } from '@thomasfosterau/effect-sveltekit/server';
36
+ * import { Layer } from 'effect';
37
+ * import { Database } from './database';
38
+ *
39
+ * export const runtime = makeServerRuntime(Database.layer);
40
+ * ```
41
+ *
42
+ * ```ts
43
+ * // src/routes/items/+page.server.ts
44
+ * import { runtime } from '$lib/server/runtime';
45
+ * import { Effect } from 'effect';
46
+ * import { Database } from '$lib/server/database';
47
+ *
48
+ * export const load = runtime.serverLoad((event) =>
49
+ * Effect.gen(function* () {
50
+ * const db = yield* Database;
51
+ * return { items: yield* db.listItems() };
52
+ * })
53
+ * );
54
+ * ```
55
+ *
56
+ * Note that the application layer is built outside of any request, so its
57
+ * services cannot depend on the per-request `SvelteKitRequestEvent` or
58
+ * `HttpClient`. Request-specific data should be passed to service methods as
59
+ * arguments instead.
60
+ */
61
+ const makeServerRuntime = (layer) => {
62
+ const runtime = ManagedRuntime.make(layer ?? Layer.empty);
63
+ const owner = {};
64
+ /** A {@link RequestRuntime} that provides the prebuilt request services. */
65
+ const makeRequestRuntime = (requestServices) => {
66
+ const provide = (effect) => Effect.provideContext(effect, requestServices);
67
+ return {
68
+ runPromiseExit: (effect) => runtime.runPromiseExit(provide(effect)),
69
+ runPromise: (effect) => runtime.runPromiseExit(provide(effect)).then(settle),
70
+ runFork: (effect) => runtime.runFork(provide(effect))
71
+ };
72
+ };
73
+ /** The per-request runtime for this runtime carried by the event, if any. */
74
+ const requestRuntimeOf = (event) => {
75
+ const entry = requestRuntimeEntry(event);
76
+ return entry?.owner === owner ? entry.runtime : void 0;
77
+ };
78
+ const runExit = (event, effect) => {
79
+ const requestRuntime = requestRuntimeOf(event);
80
+ if (requestRuntime !== void 0) return requestRuntime.runPromiseExit(effect);
81
+ const provided = Effect.provide(effect, requestLayer(event));
82
+ return runtime.runPromiseExit(provided);
83
+ };
84
+ const run = (event, effect) => runExit(event, effect).then(settle);
85
+ const action = (fn) => (event) => run(event, fn(event));
86
+ /**
87
+ * The `handle` hook: build a per-request runtime, store it on `event.locals`
88
+ * so every other wrapper (and the user's own code) runs on it, run the user's
89
+ * effect, then close the request scope.
90
+ */
91
+ const handle = (fn) => async (input) => {
92
+ const { event } = input;
93
+ const scope = Scope.makeUnsafe();
94
+ const entry = {
95
+ owner,
96
+ runtime: makeRequestRuntime(await runtime.runPromise(Layer.buildWithScope(requestLayer(event), scope)))
97
+ };
98
+ event.locals[RequestRuntimeKey] = entry;
99
+ try {
100
+ return await entry.runtime.runPromise(fn(input));
101
+ } finally {
102
+ await runtime.runPromise(Scope.close(scope, Exit.void));
103
+ }
104
+ };
105
+ return {
106
+ runtime,
107
+ serverLoad: (fn) => (event) => run(event, fn(event)),
108
+ action,
109
+ actions: (actionsMap) => {
110
+ const wrapped = {};
111
+ for (const [key, fn] of Object.entries(actionsMap)) wrapped[key] = action(fn);
112
+ return wrapped;
113
+ },
114
+ handle,
115
+ handleFetch: (fn) => (args) => run(args.event, fn(args)),
116
+ handleError: (fn) => (args) => run(args.event, fn(args)),
117
+ init: (effect) => async () => {
118
+ settle(await runtime.runPromiseExit(effect));
119
+ },
120
+ requestRuntime: requestRuntimeOf,
121
+ endpoint: (fn) => (event) => run(event, fn(event)),
122
+ runPromise: run,
123
+ runPromiseExit: runExit,
124
+ dispose: () => runtime.dispose()
125
+ };
126
+ };
127
+ /**
128
+ * Creates a {@link UniversalRuntime} from an application layer.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * // src/lib/runtime.ts
133
+ * import { makeUniversalRuntime } from '@thomasfosterau/effect-sveltekit';
134
+ * import { ApiClient } from '$lib/api';
135
+ *
136
+ * export const runtime = makeUniversalRuntime(ApiClient.layer);
137
+ * ```
138
+ *
139
+ * ```ts
140
+ * // src/routes/+page.ts
141
+ * import { runtime } from '$lib/runtime';
142
+ * import { Effect } from 'effect';
143
+ * import { ApiClient } from '$lib/api';
144
+ *
145
+ * export const load = runtime.load((event) =>
146
+ * Effect.gen(function* () {
147
+ * const api = yield* ApiClient;
148
+ * return { stories: yield* api.topStories() };
149
+ * })
150
+ * );
151
+ * ```
152
+ */
153
+ const makeUniversalRuntime = (layer) => {
154
+ const runtime = ManagedRuntime.make(layer ?? Layer.empty);
155
+ const run = (fetchFn, effect, event) => {
156
+ const services = event ? requestLayer(event) : fetchLayer(fetchFn);
157
+ const provided = Effect.provide(effect, services);
158
+ return runtime.runPromiseExit(provided).then(settle);
159
+ };
160
+ return {
161
+ runtime,
162
+ load: (fn) => (event) => {
163
+ const serverEvent = event.request ? event : void 0;
164
+ return run(event.fetch, fn(event), serverEvent);
165
+ },
166
+ runPromise: (fetchFn, effect) => run(fetchFn, effect),
167
+ dispose: () => runtime.dispose()
168
+ };
169
+ };
170
+ //#endregion
171
+ export { makeServerRuntime, makeUniversalRuntime };
172
+
173
+ //# sourceMappingURL=app-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-runtime.js","names":[],"sources":["../src/app-runtime.ts"],"sourcesContent":["/**\n * App-wide Effect runtimes for SvelteKit.\n *\n * `makeServerRuntime` / `makeUniversalRuntime` let an application define its\n * Effect services (database clients, API wrappers, config, ...) as a single\n * `Layer`, built once per server process and shared by every request, instead\n * of being rebuilt for each `load` / `action` / endpoint invocation.\n *\n * Per-request services — the {@link SvelteKitRequestEvent} and an `HttpClient`\n * backed by the event's `fetch` — are provided on top of the shared runtime\n * for each call, so SvelteKit's fetch behaviours (cookie forwarding, request\n * deduplication, relative URLs) keep working as expected.\n */\n\nimport type {\n Action,\n Actions,\n Handle,\n HandleFetch,\n HandleServerError,\n Load,\n RequestEvent,\n ServerInit,\n ServerLoad,\n} from \"@sveltejs/kit\";\nimport { Context, Effect, type Fiber, Exit, Layer, ManagedRuntime, Scope } from \"effect\";\nimport * as FetchHttpClient from \"effect/unstable/http/FetchHttpClient\";\nimport type * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport { SvelteKitRequestEvent } from \"./runtime.js\";\nimport { causeToThrowable } from \"./internal/error.js\";\n\n/**\n * The services provided to every wrapped effect on the server, in addition to\n * the application's own layer.\n */\nexport type SvelteKitServices = HttpClient.HttpClient | SvelteKitRequestEvent;\n\n/**\n * A per-request Effect runtime: the application layer's services plus the\n * per-request {@link SvelteKitServices} (an `HttpClient` backed by the request's\n * `fetch`, and the {@link SvelteKitRequestEvent}), with the request services\n * built once for the request and shared.\n *\n * A {@link ServerRuntime}'s `handle` hook creates one per request and stores it\n * on `event.locals` (`App.Locals`). Every other wrapper for the same runtime\n * (`serverLoad`, `action`, `endpoint`, ...) then runs on it, so a request's\n * effects share one `HttpClient` and one request scope. Retrieve it from an\n * event with `ServerRuntime.requestRuntime` to run your own effects.\n */\nexport interface RequestRuntime<R = never> {\n /** Run an effect for this request, returning its `Exit`. */\n readonly runPromiseExit: <A>(\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<Exit.Exit<A, unknown>>;\n /** Run an effect for this request, throwing on failure (passing SvelteKit control flow through). */\n readonly runPromise: <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) => Promise<A>;\n /** Fork an effect for this request, returning the running fiber. */\n readonly runFork: <A>(\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Fiber.Fiber<A, unknown>;\n}\n\n/**\n * Symbol under which a {@link ServerRuntime}'s `handle` stores the per-request\n * {@link RequestRuntime} on `event.locals`. `Symbol.for` so the key is stable\n * across module instances.\n */\nconst RequestRuntimeKey: unique symbol = Symbol.for(\n \"@thomasfosterau/effect-sveltekit/requestRuntime\",\n);\n\n/**\n * The locals entry: the per-request runtime tagged with the `ServerRuntime`\n * that created it, so a different runtime's wrappers ignore it (and fall back\n * to building request services themselves) rather than running on the wrong\n * application layer.\n */\ninterface RequestRuntimeEntry {\n readonly owner: object;\n readonly runtime: RequestRuntime<any>;\n}\n\n/** Read the per-request runtime entry an event carries, if any. */\nconst requestRuntimeEntry = (event: RequestEvent): RequestRuntimeEntry | undefined =>\n (event.locals as Record<PropertyKey, unknown> | undefined)?.[RequestRuntimeKey] as\n | RequestRuntimeEntry\n | undefined;\n\n/** Layer with the per-request services for a server-side request. */\nconst requestLayer = (event: RequestEvent): Layer.Layer<SvelteKitServices> =>\n Layer.merge(\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, event.fetch))),\n Layer.succeed(SvelteKitRequestEvent, event),\n );\n\n/** Layer providing `HttpClient` backed by an arbitrary fetch implementation. */\nconst fetchLayer = (fetchFn: typeof globalThis.fetch): Layer.Layer<HttpClient.HttpClient> =>\n FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)));\n\n/** Settle an exit the way SvelteKit expects: return the value or throw. */\nconst settle = <A>(exit: Exit.Exit<A, unknown>): A =>\n Exit.match(exit, {\n onFailure: (cause) => {\n throw causeToThrowable(cause);\n },\n onSuccess: (value) => value,\n });\n\n/**\n * A server-side Effect runtime bound to an application layer.\n *\n * Produced by {@link makeServerRuntime}. The wrapper methods mirror the\n * standalone helpers in `@thomasfosterau/effect-sveltekit/server`, but\n * effects additionally have access to the services `R` of the application\n * layer.\n */\nexport interface ServerRuntime<R = never, E = never> {\n /** The underlying managed runtime for the application layer. */\n readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `load`.\n */\n readonly serverLoad: <\n Params extends Record<string, string> = any,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>,\n ) => ServerLoad<Params, ParentData, OutputData, RouteId>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit form action.\n */\n readonly action: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>,\n ) => Action<Params, OutputData, RouteId>;\n\n /**\n * Wraps an object of Effect-based form actions.\n */\n readonly actions: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n actionsMap: Record<\n string,\n (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<OutputData, unknown, R | SvelteKitServices>\n >,\n ) => Actions<Params, OutputData, RouteId>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit `handle` hook.\n */\n readonly handle: (\n fn: (args: Parameters<Handle>[0]) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => Handle;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `handleFetch` hook,\n * which intercepts `event.fetch` calls made on the server. The effect runs\n * with the per-request services on top of the application layer and produces\n * the `Response` to use.\n */\n readonly handleFetch: (\n fn: (\n args: Parameters<HandleFetch>[0],\n ) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => HandleFetch;\n\n /**\n * Wraps an Effect-based function as a SvelteKit server `handleError` hook.\n *\n * The effect receives the error and the request event, and produces the\n * shape that SvelteKit exposes to the client (your `App.Error`, or nothing).\n * It runs with the per-request services on top of the application layer.\n */\n readonly handleError: (\n fn: (\n args: Parameters<HandleServerError>[0],\n ) => Effect.Effect<Awaited<ReturnType<HandleServerError>>, unknown, R | SvelteKitServices>,\n ) => HandleServerError;\n\n /**\n * Wraps an Effect as a SvelteKit server `init` hook, run once before the\n * server responds to its first request. There is no request event, so the\n * effect only has access to the application layer's services.\n */\n readonly init: (effect: Effect.Effect<void, unknown, R>) => ServerInit;\n\n /**\n * The per-request {@link RequestRuntime} this runtime's `handle` stored on the\n * event, or `undefined` when `handle` was not used for the request. Use it to\n * run your own effects on the same per-request runtime (shared `HttpClient`\n * and request scope) that `serverLoad` / `action` / `endpoint` use.\n *\n * @example\n * ```ts\n * // anywhere with the request event, e.g. another `handle` in the sequence\n * const rt = runtime.requestRuntime(event);\n * await rt?.runPromise(Effect.gen(function* () {\n * const user = yield* Auth.currentUser;\n * event.locals.user = user;\n * }));\n * ```\n */\n readonly requestRuntime: (event: RequestEvent) => RequestRuntime<R> | undefined;\n\n /**\n * Wraps an Effect-based function as a SvelteKit `+server.ts` endpoint\n * handler (`GET`, `POST`, ...).\n */\n readonly endpoint: <\n Params extends Record<string, string> = any,\n RouteId extends string | null = any,\n >(\n fn: (\n event: RequestEvent<Params, RouteId>,\n ) => Effect.Effect<Response, unknown, R | SvelteKitServices>,\n ) => (event: RequestEvent<Params, RouteId>) => Promise<Response>;\n\n /**\n * Runs an effect for a specific request, providing both the application\n * layer and the per-request services. Failures (other than SvelteKit\n * control flow) are thrown as `EffectCauseError`.\n */\n readonly runPromise: <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<A>;\n\n /**\n * Runs an effect for a specific request and returns the Exit, without\n * translating failures into thrown errors.\n */\n readonly runPromiseExit: <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ) => Promise<Exit.Exit<A, unknown>>;\n\n /**\n * Disposes the underlying managed runtime, running the layer's finalizers.\n * Call this on server shutdown or in tests.\n */\n readonly dispose: () => Promise<void>;\n}\n\n/**\n * Creates a {@link ServerRuntime} from an application layer.\n *\n * The layer is built lazily on first use and shared by all requests for the\n * lifetime of the server process. Define it once in a server-only module and\n * import its wrappers from your routes:\n *\n * @example\n * ```ts\n * // src/lib/server/runtime.ts\n * import { makeServerRuntime } from '@thomasfosterau/effect-sveltekit/server';\n * import { Layer } from 'effect';\n * import { Database } from './database';\n *\n * export const runtime = makeServerRuntime(Database.layer);\n * ```\n *\n * ```ts\n * // src/routes/items/+page.server.ts\n * import { runtime } from '$lib/server/runtime';\n * import { Effect } from 'effect';\n * import { Database } from '$lib/server/database';\n *\n * export const load = runtime.serverLoad((event) =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return { items: yield* db.listItems() };\n * })\n * );\n * ```\n *\n * Note that the application layer is built outside of any request, so its\n * services cannot depend on the per-request `SvelteKitRequestEvent` or\n * `HttpClient`. Request-specific data should be passed to service methods as\n * arguments instead.\n */\nexport const makeServerRuntime = <R = never, E = never>(\n layer?: Layer.Layer<R, E>,\n): ServerRuntime<R, E> => {\n const runtime = ManagedRuntime.make((layer ?? Layer.empty) as Layer.Layer<R, E>);\n\n // Identity token tagging the per-request runtimes this runtime creates, so a\n // different runtime's wrappers don't run on the wrong application layer.\n const owner = {};\n\n /** A {@link RequestRuntime} that provides the prebuilt request services. */\n const makeRequestRuntime = (\n requestServices: Context.Context<SvelteKitServices>,\n ): RequestRuntime<R> => {\n const provide = <A>(effect: Effect.Effect<A, unknown, R | SvelteKitServices>) =>\n Effect.provideContext(effect, requestServices) as Effect.Effect<A, unknown, R>;\n return {\n runPromiseExit: (effect) =>\n runtime.runPromiseExit(provide(effect)) as Promise<Exit.Exit<any, unknown>>,\n runPromise: (effect) => runtime.runPromiseExit(provide(effect)).then(settle),\n runFork: (effect) => runtime.runFork(provide(effect)) as Fiber.Fiber<any, unknown>,\n };\n };\n\n /** The per-request runtime for this runtime carried by the event, if any. */\n const requestRuntimeOf = (event: RequestEvent): RequestRuntime<R> | undefined => {\n const entry = requestRuntimeEntry(event);\n return entry?.owner === owner ? (entry.runtime as RequestRuntime<R>) : undefined;\n };\n\n const runExit = <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ): Promise<Exit.Exit<A, unknown>> => {\n // Reuse the per-request runtime `handle` installed, if any; otherwise build\n // the per-request services for this single call.\n const requestRuntime = requestRuntimeOf(event);\n if (requestRuntime !== undefined) return requestRuntime.runPromiseExit(effect);\n const provided = Effect.provide(effect, requestLayer(event)) as Effect.Effect<A, unknown, R>;\n return runtime.runPromiseExit(provided) as Promise<Exit.Exit<A, unknown>>;\n };\n\n const run = <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R | SvelteKitServices>,\n ): Promise<A> => runExit(event, effect).then(settle);\n\n const action: ServerRuntime<R, E>[\"action\"] = (fn) => (event) => run(event, fn(event));\n\n /**\n * The `handle` hook: build a per-request runtime, store it on `event.locals`\n * so every other wrapper (and the user's own code) runs on it, run the user's\n * effect, then close the request scope.\n */\n const handle: ServerRuntime<R, E>[\"handle\"] = (fn) => async (input) => {\n const { event } = input;\n const scope = Scope.makeUnsafe();\n const requestServices = (await runtime.runPromise(\n Layer.buildWithScope(requestLayer(event), scope),\n )) as Context.Context<SvelteKitServices>;\n\n const entry: RequestRuntimeEntry = { owner, runtime: makeRequestRuntime(requestServices) };\n (event.locals as Record<PropertyKey, unknown>)[RequestRuntimeKey] = entry;\n\n try {\n return await (entry.runtime as RequestRuntime<R>).runPromise(fn(input));\n } finally {\n await runtime.runPromise(Scope.close(scope, Exit.void));\n }\n };\n\n return {\n runtime,\n serverLoad: (fn) => (event) => run(event, fn(event)),\n action,\n actions: (actionsMap) => {\n const wrapped: Record<string, Action<any, any, any>> = {};\n for (const [key, fn] of Object.entries(actionsMap)) {\n wrapped[key] = action(fn);\n }\n return wrapped as never;\n },\n handle,\n handleFetch: (fn) => (args) => run(args.event, fn(args)),\n handleError: (fn) => (args) => run(args.event, fn(args)),\n init: (effect) => async () => {\n // `init` runs outside of any request, so it executes directly on the\n // application runtime without the per-request layer.\n settle(await runtime.runPromiseExit(effect));\n },\n requestRuntime: requestRuntimeOf,\n endpoint: (fn) => (event) => run(event, fn(event)),\n runPromise: run,\n runPromiseExit: runExit,\n dispose: () => runtime.dispose(),\n };\n};\n\n/**\n * A universal (client + server) Effect runtime bound to an application layer.\n *\n * Produced by {@link makeUniversalRuntime}. Because universal `load`\n * functions also run in the browser, the layer must only use services that\n * work in both environments.\n */\nexport interface UniversalRuntime<R = never, E = never> {\n /** The underlying managed runtime for the application layer. */\n readonly runtime: ManagedRuntime.ManagedRuntime<R, E>;\n\n /**\n * Wraps an Effect-based function as a SvelteKit universal `load`.\n *\n * The effect gets `HttpClient` (backed by the load event's `fetch`) on both\n * client and server, plus the services of the application layer.\n */\n readonly load: <\n Params extends Record<string, string> = any,\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, R | HttpClient.HttpClient>,\n ) => Load<Params, InputData, ParentData, OutputData, RouteId>;\n\n /**\n * Runs an effect with `HttpClient` backed by the given fetch implementation\n * plus the application layer.\n */\n readonly runPromise: <A>(\n fetchFn: typeof globalThis.fetch,\n effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>,\n ) => Promise<A>;\n\n /**\n * Disposes the underlying managed runtime, running the layer's finalizers.\n */\n readonly dispose: () => Promise<void>;\n}\n\n/**\n * Creates a {@link UniversalRuntime} from an application layer.\n *\n * @example\n * ```ts\n * // src/lib/runtime.ts\n * import { makeUniversalRuntime } from '@thomasfosterau/effect-sveltekit';\n * import { ApiClient } from '$lib/api';\n *\n * export const runtime = makeUniversalRuntime(ApiClient.layer);\n * ```\n *\n * ```ts\n * // src/routes/+page.ts\n * import { runtime } from '$lib/runtime';\n * import { Effect } from 'effect';\n * import { ApiClient } from '$lib/api';\n *\n * export const load = runtime.load((event) =>\n * Effect.gen(function* () {\n * const api = yield* ApiClient;\n * return { stories: yield* api.topStories() };\n * })\n * );\n * ```\n */\nexport const makeUniversalRuntime = <R = never, E = never>(\n layer?: Layer.Layer<R, E>,\n): UniversalRuntime<R, E> => {\n const runtime = ManagedRuntime.make((layer ?? Layer.empty) as Layer.Layer<R, E>);\n\n const run = <A>(\n fetchFn: typeof globalThis.fetch,\n effect: Effect.Effect<A, unknown, R | HttpClient.HttpClient>,\n event?: RequestEvent,\n ): Promise<A> => {\n // On the server the full request layer (HttpClient + SvelteKitRequestEvent)\n // is available; on the client only HttpClient backed by the event's fetch.\n const services = event ? requestLayer(event) : fetchLayer(fetchFn);\n const provided = Effect.provide(effect, services) as Effect.Effect<A, unknown, R>;\n return runtime.runPromiseExit(provided).then(settle);\n };\n\n return {\n runtime,\n load: (fn) => (event) => {\n const serverEvent = (event as { request?: unknown }).request\n ? (event as unknown as RequestEvent)\n : undefined;\n return run(event.fetch, fn(event), serverEvent);\n },\n runPromise: (fetchFn, effect) => run(fetchFn, effect),\n dispose: () => runtime.dispose(),\n };\n};\n"],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { Effect } from "effect";
2
+ import { ClientInit, HandleClientError } from "@sveltejs/kit";
3
+
4
+ //#region src/client/Hooks.d.ts
5
+ declare namespace Hooks_d_exports {
6
+ export { handleError, init };
7
+ }
8
+ /**
9
+ * Wraps an Effect-based function as the SvelteKit client
10
+ * [`handleError`](https://svelte.dev/docs/kit/hooks#Shared-hooks-handleError)
11
+ * hook, which runs when an unexpected error is thrown while navigating in the
12
+ * browser. The effect receives the error and navigation event and produces the
13
+ * shape exposed to the page (your `App.Error`, or nothing).
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * // src/hooks.client.ts
18
+ * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';
19
+ * import { Effect } from 'effect';
20
+ *
21
+ * export const handleError = Hooks.handleError(({ error, event }) =>
22
+ * Effect.gen(function* () {
23
+ * yield* Effect.logError(`navigation to ${event.url.pathname} failed`, error);
24
+ * return { message: 'Something went wrong' };
25
+ * })
26
+ * );
27
+ * ```
28
+ */
29
+ declare function handleError(fn: (args: Parameters<HandleClientError>[0]) => Effect.Effect<Awaited<ReturnType<HandleClientError>>, unknown>): HandleClientError;
30
+ /**
31
+ * Wraps an Effect as the SvelteKit client
32
+ * [`init`](https://svelte.dev/docs/kit/hooks#Shared-hooks-init) hook, which
33
+ * runs once when the app starts in the browser. The effect runs on a bare
34
+ * Effect runtime.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * // src/hooks.client.ts
39
+ * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';
40
+ * import { Effect } from 'effect';
41
+ *
42
+ * export const init = Hooks.init(Effect.logInfo('client app started'));
43
+ * ```
44
+ */
45
+ declare function init(effect: Effect.Effect<void, unknown>): ClientInit;
46
+ //#endregion
47
+ export { Hooks_d_exports };
48
+ //# sourceMappingURL=Hooks.d.ts.map
@@ -0,0 +1,61 @@
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
+ import { causeToThrowable } from "../internal/error.js";
3
+ import { Effect, Exit } from "effect";
4
+ //#region src/client/Hooks.ts
5
+ var Hooks_exports = /* @__PURE__ */ __exportAll({
6
+ handleError: () => handleError,
7
+ init: () => init
8
+ });
9
+ /** Run a bare Effect (no request context) and settle it the way a hook expects. */
10
+ const runBareHook = async (effect) => Exit.match(await Effect.runPromiseExit(effect), {
11
+ onFailure: (cause) => {
12
+ throw causeToThrowable(cause);
13
+ },
14
+ onSuccess: (value) => value
15
+ });
16
+ /**
17
+ * Wraps an Effect-based function as the SvelteKit client
18
+ * [`handleError`](https://svelte.dev/docs/kit/hooks#Shared-hooks-handleError)
19
+ * hook, which runs when an unexpected error is thrown while navigating in the
20
+ * browser. The effect receives the error and navigation event and produces the
21
+ * shape exposed to the page (your `App.Error`, or nothing).
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * // src/hooks.client.ts
26
+ * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';
27
+ * import { Effect } from 'effect';
28
+ *
29
+ * export const handleError = Hooks.handleError(({ error, event }) =>
30
+ * Effect.gen(function* () {
31
+ * yield* Effect.logError(`navigation to ${event.url.pathname} failed`, error);
32
+ * return { message: 'Something went wrong' };
33
+ * })
34
+ * );
35
+ * ```
36
+ */
37
+ function handleError(fn) {
38
+ return (args) => runBareHook(fn(args));
39
+ }
40
+ /**
41
+ * Wraps an Effect as the SvelteKit client
42
+ * [`init`](https://svelte.dev/docs/kit/hooks#Shared-hooks-init) hook, which
43
+ * runs once when the app starts in the browser. The effect runs on a bare
44
+ * Effect runtime.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * // src/hooks.client.ts
49
+ * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';
50
+ * import { Effect } from 'effect';
51
+ *
52
+ * export const init = Hooks.init(Effect.logInfo('client app started'));
53
+ * ```
54
+ */
55
+ function init(effect) {
56
+ return () => runBareHook(effect);
57
+ }
58
+ //#endregion
59
+ export { Hooks_exports };
60
+
61
+ //# sourceMappingURL=Hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Hooks.js","names":[],"sources":["../../src/client/Hooks.ts"],"sourcesContent":["/**\n * Effect wrappers for SvelteKit's client-side hooks (`src/hooks.client.ts`),\n * exposed as the `Hooks` namespace of `@thomasfosterau/effect-sveltekit/client`.\n *\n * These mirror the server hook helpers in `../server/Hooks.ts`, but run on the\n * client: there is no request context, so each effect runs on a bare Effect\n * runtime. For application services, run the effect through your own runtime\n * instead. Kept free of `$app/*` imports so it stays importable (and testable)\n * outside of a SvelteKit app.\n *\n * @example\n * ```ts\n * // src/hooks.client.ts\n * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';\n * import { Effect } from 'effect';\n *\n * export const handleError = Hooks.handleError(({ error, event }) =>\n * Effect.as(Effect.logError(`navigation to ${event.url.pathname} failed`, error), {\n * message: 'Something went wrong',\n * })\n * );\n * ```\n */\n\nimport type { ClientInit, HandleClientError } from \"@sveltejs/kit\";\nimport { Effect, Exit } from \"effect\";\nimport { causeToThrowable } from \"../internal/error.js\";\n\n/** Run a bare Effect (no request context) and settle it the way a hook expects. */\nconst runBareHook = async <A>(effect: Effect.Effect<A, unknown>): Promise<A> =>\n Exit.match(await Effect.runPromiseExit(effect), {\n onFailure: (cause) => {\n throw causeToThrowable(cause);\n },\n onSuccess: (value) => value,\n });\n\n/**\n * Wraps an Effect-based function as the SvelteKit client\n * [`handleError`](https://svelte.dev/docs/kit/hooks#Shared-hooks-handleError)\n * hook, which runs when an unexpected error is thrown while navigating in the\n * browser. The effect receives the error and navigation event and produces the\n * shape exposed to the page (your `App.Error`, or nothing).\n *\n * @example\n * ```ts\n * // src/hooks.client.ts\n * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';\n * import { Effect } from 'effect';\n *\n * export const handleError = Hooks.handleError(({ error, event }) =>\n * Effect.gen(function* () {\n * yield* Effect.logError(`navigation to ${event.url.pathname} failed`, error);\n * return { message: 'Something went wrong' };\n * })\n * );\n * ```\n */\nexport function handleError(\n fn: (\n args: Parameters<HandleClientError>[0],\n ) => Effect.Effect<Awaited<ReturnType<HandleClientError>>, unknown>,\n): HandleClientError {\n return (args) => runBareHook(fn(args));\n}\n\n/**\n * Wraps an Effect as the SvelteKit client\n * [`init`](https://svelte.dev/docs/kit/hooks#Shared-hooks-init) hook, which\n * runs once when the app starts in the browser. The effect runs on a bare\n * Effect runtime.\n *\n * @example\n * ```ts\n * // src/hooks.client.ts\n * import { Hooks } from '@thomasfosterau/effect-sveltekit/client';\n * import { Effect } from 'effect';\n *\n * export const init = Hooks.init(Effect.logInfo('client app started'));\n * ```\n */\nexport function init(effect: Effect.Effect<void, unknown>): ClientInit {\n return () => runBareHook(effect);\n}\n"],"mappings":";;;;;;;;;AA6BA,MAAM,cAAc,OAAU,WAC5B,KAAK,MAAM,MAAM,OAAO,eAAe,MAAM,GAAG;CAC9C,YAAY,UAAU;EACpB,MAAM,iBAAiB,KAAK;CAC9B;CACA,YAAY,UAAU;AACxB,CAAC;;;;;;;;;;;;;;;;;;;;;;AAuBH,SAAgB,YACd,IAGmB;CACnB,QAAQ,SAAS,YAAY,GAAG,IAAI,CAAC;AACvC;;;;;;;;;;;;;;;;AAiBA,SAAgB,KAAK,QAAkD;CACrE,aAAa,YAAY,MAAM;AACjC"}
@@ -0,0 +1,6 @@
1
+ import { Navigation_d_exports } from "./Navigation.js";
2
+ import { State_d_exports } from "./State.js";
3
+ import { Remote_d_exports } from "./Remote.js";
4
+ import { useRemoteQuery } from "./remote-reactive.js";
5
+ import { Hooks_d_exports } from "./client/Hooks.js";
6
+ export { Hooks_d_exports as Hooks, Navigation_d_exports as Navigation, Remote_d_exports as Remote, State_d_exports as State, useRemoteQuery };
package/dist/client.js ADDED
@@ -0,0 +1,6 @@
1
+ import { Remote_exports } from "./Remote.js";
2
+ import { Navigation_exports } from "./Navigation.js";
3
+ import { State_exports } from "./State.js";
4
+ import { useRemoteQuery } from "./remote-reactive.js";
5
+ import { Hooks_exports } from "./client/Hooks.js";
6
+ export { Hooks_exports as Hooks, Navigation_exports as Navigation, Remote_exports as Remote, State_exports as State, useRemoteQuery };
@@ -0,0 +1,17 @@
1
+ import { ConfigProvider, Layer } from "effect";
2
+
3
+ //#region src/config.d.ts
4
+ /**
5
+ * Creates a {@link ConfigProvider} backed by a SvelteKit env record (e.g. the
6
+ * `env` export of `$env/dynamic/private`, or `import.meta.env`-style objects).
7
+ */
8
+ declare const sveltekitConfigProvider: (env: Record<string, string | undefined>) => ConfigProvider.ConfigProvider;
9
+ /**
10
+ * Creates a `Layer` that installs {@link sveltekitConfigProvider} as the
11
+ * ambient config provider. Provide it to a server/universal runtime so the
12
+ * application layer's `Config` reads resolve from SvelteKit's environment.
13
+ */
14
+ declare const sveltekitConfig: (env: Record<string, string | undefined>) => Layer.Layer<never>;
15
+ //#endregion
16
+ export { sveltekitConfig, sveltekitConfigProvider };
17
+ //# sourceMappingURL=config.d.ts.map
package/dist/config.js ADDED
@@ -0,0 +1,51 @@
1
+ import { ConfigProvider } from "effect";
2
+ //#region src/config.ts
3
+ /**
4
+ * SvelteKit `$env` ⇄ Effect `Config`.
5
+ *
6
+ * Effect services read configuration through {@link Config} / a
7
+ * {@link ConfigProvider}, while SvelteKit exposes configuration through its
8
+ * `$env/static/*` and `$env/dynamic/*` virtual modules. These helpers bridge
9
+ * the two: pass a SvelteKit env record and get back a `ConfigProvider` (or a
10
+ * `Layer` that installs it), so any `Config.string("DATABASE_URL")` resolves
11
+ * from SvelteKit's environment.
12
+ *
13
+ * ```ts
14
+ * // src/lib/server/runtime.ts
15
+ * import { env } from "$env/dynamic/private";
16
+ * import { sveltekitConfig } from "@thomasfosterau/effect-sveltekit/server";
17
+ * import { makeServerRuntime } from "@thomasfosterau/effect-sveltekit/server";
18
+ * import { Layer } from "effect";
19
+ * import { Database } from "./database";
20
+ *
21
+ * export const runtime = makeServerRuntime(
22
+ * // Database.layer reads Config.string("DATABASE_URL"), now backed by $env
23
+ * Database.layer.pipe(Layer.provide(sveltekitConfig(env))),
24
+ * );
25
+ * ```
26
+ *
27
+ * `ConfigProvider.fromEnv` joins/splits path segments on `_`, so nested config
28
+ * (`Config.string("HOST").pipe(...nested("DATABASE"))`) resolves a
29
+ * `DATABASE_HOST` env var just like it does from `process.env`.
30
+ */
31
+ /** Drop `undefined` values so the record satisfies `ConfigProvider.fromEnv`. */
32
+ const definedEntries = (env) => {
33
+ const out = {};
34
+ for (const [key, value] of Object.entries(env)) if (value !== void 0) out[key] = value;
35
+ return out;
36
+ };
37
+ /**
38
+ * Creates a {@link ConfigProvider} backed by a SvelteKit env record (e.g. the
39
+ * `env` export of `$env/dynamic/private`, or `import.meta.env`-style objects).
40
+ */
41
+ const sveltekitConfigProvider = (env) => ConfigProvider.fromEnv({ env: definedEntries(env) });
42
+ /**
43
+ * Creates a `Layer` that installs {@link sveltekitConfigProvider} as the
44
+ * ambient config provider. Provide it to a server/universal runtime so the
45
+ * application layer's `Config` reads resolve from SvelteKit's environment.
46
+ */
47
+ const sveltekitConfig = (env) => ConfigProvider.layer(sveltekitConfigProvider(env));
48
+ //#endregion
49
+ export { sveltekitConfig, sveltekitConfigProvider };
50
+
51
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * SvelteKit `$env` ⇄ Effect `Config`.\n *\n * Effect services read configuration through {@link Config} / a\n * {@link ConfigProvider}, while SvelteKit exposes configuration through its\n * `$env/static/*` and `$env/dynamic/*` virtual modules. These helpers bridge\n * the two: pass a SvelteKit env record and get back a `ConfigProvider` (or a\n * `Layer` that installs it), so any `Config.string(\"DATABASE_URL\")` resolves\n * from SvelteKit's environment.\n *\n * ```ts\n * // src/lib/server/runtime.ts\n * import { env } from \"$env/dynamic/private\";\n * import { sveltekitConfig } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { makeServerRuntime } from \"@thomasfosterau/effect-sveltekit/server\";\n * import { Layer } from \"effect\";\n * import { Database } from \"./database\";\n *\n * export const runtime = makeServerRuntime(\n * // Database.layer reads Config.string(\"DATABASE_URL\"), now backed by $env\n * Database.layer.pipe(Layer.provide(sveltekitConfig(env))),\n * );\n * ```\n *\n * `ConfigProvider.fromEnv` joins/splits path segments on `_`, so nested config\n * (`Config.string(\"HOST\").pipe(...nested(\"DATABASE\"))`) resolves a\n * `DATABASE_HOST` env var just like it does from `process.env`.\n */\n\nimport { ConfigProvider, type Layer } from \"effect\";\n\n/** Drop `undefined` values so the record satisfies `ConfigProvider.fromEnv`. */\nconst definedEntries = (env: Record<string, string | undefined>): Record<string, string> => {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(env)) {\n if (value !== undefined) out[key] = value;\n }\n return out;\n};\n\n/**\n * Creates a {@link ConfigProvider} backed by a SvelteKit env record (e.g. the\n * `env` export of `$env/dynamic/private`, or `import.meta.env`-style objects).\n */\nexport const sveltekitConfigProvider = (\n env: Record<string, string | undefined>,\n): ConfigProvider.ConfigProvider => ConfigProvider.fromEnv({ env: definedEntries(env) });\n\n/**\n * Creates a `Layer` that installs {@link sveltekitConfigProvider} as the\n * ambient config provider. Provide it to a server/universal runtime so the\n * application layer's `Config` reads resolve from SvelteKit's environment.\n */\nexport const sveltekitConfig = (env: Record<string, string | undefined>): Layer.Layer<never> =>\n ConfigProvider.layer(sveltekitConfigProvider(env));\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAM,kBAAkB,QAAoE;CAC1F,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,IAAI,UAAU,KAAA,GAAW,IAAI,OAAO;CAEtC,OAAO;AACT;;;;;AAMA,MAAa,2BACX,QACkC,eAAe,QAAQ,EAAE,KAAK,eAAe,GAAG,EAAE,CAAC;;;;;;AAOvF,MAAa,mBAAmB,QAC9B,eAAe,MAAM,wBAAwB,GAAG,CAAC"}