@thomasfosterau/effect-sveltekit 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/api-client.d.ts +32 -0
  2. package/dist/api-client.js +44 -0
  3. package/dist/api-client.js.map +1 -0
  4. package/dist/app-runtime.d.ts +2 -1
  5. package/dist/app-runtime.js +13 -11
  6. package/dist/app-runtime.js.map +1 -1
  7. package/dist/client.d.ts +3 -1
  8. package/dist/client.js +3 -1
  9. package/dist/config.d.ts +33 -2
  10. package/dist/config.js +33 -2
  11. package/dist/config.js.map +1 -1
  12. package/dist/forms.d.ts +30 -0
  13. package/dist/forms.js +66 -0
  14. package/dist/forms.js.map +1 -0
  15. package/dist/index.d.ts +3 -2
  16. package/dist/index.js +3 -2
  17. package/dist/internal/enhance.d.ts +39 -0
  18. package/dist/internal/enhance.js +24 -0
  19. package/dist/internal/enhance.js.map +1 -0
  20. package/dist/internal/remote-run.d.ts +12 -1
  21. package/dist/internal/remote-run.js +3 -5
  22. package/dist/internal/remote-run.js.map +1 -1
  23. package/dist/internal/sse.js +37 -0
  24. package/dist/internal/sse.js.map +1 -0
  25. package/dist/kit.d.ts +130 -0
  26. package/dist/kit.js +185 -0
  27. package/dist/kit.js.map +1 -0
  28. package/dist/realtime.d.ts +105 -0
  29. package/dist/realtime.js +111 -0
  30. package/dist/realtime.js.map +1 -0
  31. package/dist/request-parse.d.ts +89 -0
  32. package/dist/request-parse.js +121 -0
  33. package/dist/request-parse.js.map +1 -0
  34. package/dist/request-runtime.js +17 -16
  35. package/dist/request-runtime.js.map +1 -1
  36. package/dist/request-services.d.ts +120 -0
  37. package/dist/request-services.js +120 -0
  38. package/dist/request-services.js.map +1 -0
  39. package/dist/server/Endpoint.d.ts +62 -2
  40. package/dist/server/Endpoint.js +47 -2
  41. package/dist/server/Endpoint.js.map +1 -1
  42. package/dist/server.d.ts +5 -1
  43. package/dist/server.js +5 -1
  44. package/dist/server.js.map +1 -1
  45. package/dist/streamed-load.d.ts +23 -0
  46. package/dist/streamed-load.js +74 -0
  47. package/dist/streamed-load.js.map +1 -0
  48. package/dist/testing.d.ts +175 -0
  49. package/dist/testing.js +153 -0
  50. package/dist/testing.js.map +1 -0
  51. package/package.json +6 -2
@@ -0,0 +1,175 @@
1
+ import { ErrorTranslator } from "./request-runtime.js";
2
+ import { FormErrorMapper, RunHelpers } from "./internal/remote-run.js";
3
+ import { Effect, Layer } from "effect";
4
+ import { RequestEvent } from "@sveltejs/kit";
5
+
6
+ //#region src/testing.d.ts
7
+ /** Options for {@link TestRequestEvent}. */
8
+ interface TestRequestEventOptions {
9
+ /**
10
+ * The request URL. Accepts a `string` or a `URL` instance.
11
+ * Defaults to `"http://localhost/"`.
12
+ */
13
+ readonly url?: string | URL;
14
+ /** The HTTP method. Defaults to `"GET"`. */
15
+ readonly method?: string;
16
+ /** Additional HTTP headers added to the underlying `Request`. */
17
+ readonly headers?: HeadersInit;
18
+ /** The request body. Only usable with methods that accept a body. */
19
+ readonly body?: BodyInit | null;
20
+ /**
21
+ * Route params (e.g. `{ id: "123" }` for a route like `/items/[id]`).
22
+ * Defaults to `{}`.
23
+ */
24
+ readonly params?: Record<string, string>;
25
+ /**
26
+ * Initial `event.locals` value. Defaults to `{}`.
27
+ * Cast to `App.Locals` — extend it with the properties your handlers need.
28
+ */
29
+ readonly locals?: Record<string, unknown>;
30
+ /**
31
+ * Pre-seeded cookies as `{ name: value }` pairs.
32
+ * The backing store supports `get`, `getAll`, `set`, `delete`, and
33
+ * `serialize`; the object is cast to SvelteKit's `Cookies` interface.
34
+ */
35
+ readonly cookies?: Record<string, string>;
36
+ /**
37
+ * The `event.platform` value, if any.
38
+ * Pass a partial platform object matching the environment the handler targets.
39
+ */
40
+ readonly platform?: unknown;
41
+ /**
42
+ * The `fetch` implementation used by `event.fetch`.
43
+ * Defaults to `globalThis.fetch`. Pass a custom function to intercept or
44
+ * stub network calls made by the handler.
45
+ */
46
+ readonly fetch?: typeof globalThis.fetch;
47
+ }
48
+ /**
49
+ * Create a minimal {@link RequestEvent} for use in unit tests.
50
+ *
51
+ * All fields are optional — omit what the handler under test doesn't inspect.
52
+ * Cookies are backed by an in-memory `Map`; mutations (`set`, `delete`) are
53
+ * reflected in subsequent `get`/`getAll` calls on the same event.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * const event = TestRequestEvent({
58
+ * url: "http://localhost/items/42",
59
+ * params: { id: "42" },
60
+ * locals: { user: { id: "u1", role: "admin" } },
61
+ * cookies: { session: "tok_test" },
62
+ * });
63
+ * ```
64
+ */
65
+ declare function TestRequestEvent(opts?: TestRequestEventOptions): RequestEvent;
66
+ /**
67
+ * Options shared by {@link runServerLoad}, {@link runAction}, and
68
+ * {@link runEndpoint}.
69
+ */
70
+ interface TestRunOptions<R> {
71
+ /**
72
+ * The `Layer` providing the handler effect's required services `R`.
73
+ * Omit when the handler has no service requirements (`R` is `never`).
74
+ */
75
+ readonly layer?: Layer.Layer<R>;
76
+ /**
77
+ * Translate a failed `Cause` at the boundary. Defaults to
78
+ * {@link SvelteKit.translate} (Kit control-flow through; anything else
79
+ * becomes an `EffectCauseError`).
80
+ */
81
+ readonly translateError?: ErrorTranslator;
82
+ }
83
+ /**
84
+ * Options for {@link runRemote}.
85
+ */
86
+ interface RunRemoteOptions<R> {
87
+ /** The `Layer` providing the remote handler's service requirements. */
88
+ readonly layer?: Layer.Layer<R>;
89
+ /**
90
+ * Translate a failed `Cause` at the boundary. Defaults to
91
+ * {@link SvelteKit.translate}.
92
+ */
93
+ readonly translateError?: ErrorTranslator;
94
+ /**
95
+ * Map a remote form handler's failure to SvelteKit field issues. Receives
96
+ * the squashed handler failure and the `{ issue, data }` context. When
97
+ * provided it is called instead of `translateError` on the form error path.
98
+ */
99
+ readonly formError?: FormErrorMapper;
100
+ }
101
+ /**
102
+ * Run an Effect-based server `load` function under test.
103
+ *
104
+ * Wraps `fn` with `makeRuntimeAdapter` using the provided `opts.layer` (or
105
+ * `Layer.empty` when omitted) and calls the resulting `ServerLoad` with
106
+ * `event`. Errors are translated by `opts.translateError` (defaults to
107
+ * {@link SvelteKit.translate}).
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * const data = await runServerLoad(
112
+ * (event) => Effect.gen(function* () {
113
+ * const db = yield* Database;
114
+ * return { items: yield* db.list() };
115
+ * }),
116
+ * TestRequestEvent(),
117
+ * { layer: TestDatabase.layer },
118
+ * );
119
+ * expect(data.items).toHaveLength(0);
120
+ * ```
121
+ */
122
+ declare const runServerLoad: <R, A extends Record<string, any> | void = void>(fn: (event: RequestEvent) => Effect.Effect<A, unknown, R>, event: RequestEvent, opts?: TestRunOptions<R>) => Promise<A>;
123
+ /**
124
+ * Run an Effect-based form action handler under test.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const result = await runAction(
129
+ * (_event) => Effect.succeed({ success: true }),
130
+ * TestRequestEvent({ method: "POST" }),
131
+ * );
132
+ * expect((result as any).success).toBe(true);
133
+ * ```
134
+ */
135
+ declare const runAction: <R, A extends Record<string, any> | void = void>(fn: (event: RequestEvent) => Effect.Effect<A, unknown, R>, event: RequestEvent, opts?: TestRunOptions<R>) => Promise<A>;
136
+ /**
137
+ * Run an Effect-based `+server.ts` endpoint handler under test.
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * const response = await runEndpoint(
142
+ * (_event) => Effect.succeed(new Response(JSON.stringify({ ok: true }))),
143
+ * TestRequestEvent({ url: "http://localhost/api/ping" }),
144
+ * );
145
+ * expect(response.status).toBe(200);
146
+ * ```
147
+ */
148
+ declare const runEndpoint: <R>(fn: (event: RequestEvent) => Effect.Effect<Response, unknown, R>, event: RequestEvent, opts?: TestRunOptions<R>) => Promise<Response>;
149
+ /**
150
+ * Build a `{ run, runForm }` helper pair for directly invoking remote-function
151
+ * Effect handlers under test.
152
+ *
153
+ * Unlike the production `query` / `command` / `form` helpers, which resolve the
154
+ * request event via `$app/server.getRequestEvent()`, this resolves it from the
155
+ * `event` argument you supply — no SvelteKit module-graph mocking required.
156
+ *
157
+ * Use `run` for `query` / `command` handlers and `runForm` for `form` handlers
158
+ * that need the `issue` / `data` context.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * import { TestRequestEvent, runRemote } from "@thomasfosterau/effect-sveltekit/testing";
163
+ *
164
+ * const event = TestRequestEvent();
165
+ * const { run } = runRemote(event, { layer: TestDatabase.layer });
166
+ *
167
+ * // Invoke the underlying Effect directly — no AppServer needed:
168
+ * const items = await run(listItemsEffect());
169
+ * expect(items).toEqual([]);
170
+ * ```
171
+ */
172
+ declare const runRemote: <R = never>(event: RequestEvent, opts?: RunRemoteOptions<R>) => RunHelpers;
173
+ //#endregion
174
+ export { type FormErrorMapper, type RunHelpers, RunRemoteOptions, TestRequestEvent, TestRequestEventOptions, TestRunOptions, runAction, runEndpoint, runRemote, runServerLoad };
175
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1,153 @@
1
+ import { SvelteKit } from "./kit.js";
2
+ import { makeRunHelpers } from "./internal/remote-run.js";
3
+ import { RequestRuntime, makeRuntimeAdapter } from "./request-runtime.js";
4
+ import { Layer } from "effect";
5
+ //#region src/testing.ts
6
+ /** Map-backed Cookies implementation for use in {@link TestRequestEvent}. */
7
+ var TestCookies = class {
8
+ store;
9
+ constructor(initial = {}) {
10
+ this.store = new Map(Object.entries(initial));
11
+ }
12
+ get(name, _opts) {
13
+ return this.store.get(name);
14
+ }
15
+ getAll(_opts) {
16
+ return [...this.store.entries()].map(([name, value]) => ({
17
+ name,
18
+ value
19
+ }));
20
+ }
21
+ set(name, value, _opts) {
22
+ this.store.set(name, value);
23
+ }
24
+ delete(name, _opts) {
25
+ this.store.delete(name);
26
+ }
27
+ serialize(name, value, _opts) {
28
+ return `${name}=${encodeURIComponent(value)}`;
29
+ }
30
+ };
31
+ /**
32
+ * Create a minimal {@link RequestEvent} for use in unit tests.
33
+ *
34
+ * All fields are optional — omit what the handler under test doesn't inspect.
35
+ * Cookies are backed by an in-memory `Map`; mutations (`set`, `delete`) are
36
+ * reflected in subsequent `get`/`getAll` calls on the same event.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const event = TestRequestEvent({
41
+ * url: "http://localhost/items/42",
42
+ * params: { id: "42" },
43
+ * locals: { user: { id: "u1", role: "admin" } },
44
+ * cookies: { session: "tok_test" },
45
+ * });
46
+ * ```
47
+ */
48
+ function TestRequestEvent(opts = {}) {
49
+ const rawUrl = opts.url instanceof URL ? opts.url.toString() : opts.url ?? "http://localhost/";
50
+ const url = new URL(rawUrl);
51
+ const init = { method: opts.method ?? "GET" };
52
+ if (opts.headers !== void 0) init.headers = opts.headers;
53
+ if (opts.body !== void 0) init.body = opts.body;
54
+ const request = new Request(url.toString(), init);
55
+ const cookies = new TestCookies(opts.cookies ?? {});
56
+ const fetchFn = opts.fetch ?? globalThis.fetch;
57
+ return {
58
+ request,
59
+ url,
60
+ params: opts.params ?? {},
61
+ route: { id: null },
62
+ fetch: fetchFn,
63
+ cookies,
64
+ locals: opts.locals ?? {},
65
+ platform: opts.platform,
66
+ isDataRequest: false,
67
+ isSubRequest: false,
68
+ setHeaders: () => {},
69
+ getClientAddress: () => "127.0.0.1"
70
+ };
71
+ }
72
+ const makeAdapter = (opts) => makeRuntimeAdapter(RequestRuntime.fromLayer(opts?.layer ?? Layer.empty), { translateError: opts?.translateError ?? SvelteKit.translate });
73
+ /**
74
+ * Run an Effect-based server `load` function under test.
75
+ *
76
+ * Wraps `fn` with `makeRuntimeAdapter` using the provided `opts.layer` (or
77
+ * `Layer.empty` when omitted) and calls the resulting `ServerLoad` with
78
+ * `event`. Errors are translated by `opts.translateError` (defaults to
79
+ * {@link SvelteKit.translate}).
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const data = await runServerLoad(
84
+ * (event) => Effect.gen(function* () {
85
+ * const db = yield* Database;
86
+ * return { items: yield* db.list() };
87
+ * }),
88
+ * TestRequestEvent(),
89
+ * { layer: TestDatabase.layer },
90
+ * );
91
+ * expect(data.items).toHaveLength(0);
92
+ * ```
93
+ */
94
+ const runServerLoad = (fn, event, opts) => makeAdapter(opts).serverLoad(fn)(event);
95
+ /**
96
+ * Run an Effect-based form action handler under test.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * const result = await runAction(
101
+ * (_event) => Effect.succeed({ success: true }),
102
+ * TestRequestEvent({ method: "POST" }),
103
+ * );
104
+ * expect((result as any).success).toBe(true);
105
+ * ```
106
+ */
107
+ const runAction = (fn, event, opts) => makeAdapter(opts).action(fn)(event);
108
+ /**
109
+ * Run an Effect-based `+server.ts` endpoint handler under test.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const response = await runEndpoint(
114
+ * (_event) => Effect.succeed(new Response(JSON.stringify({ ok: true }))),
115
+ * TestRequestEvent({ url: "http://localhost/api/ping" }),
116
+ * );
117
+ * expect(response.status).toBe(200);
118
+ * ```
119
+ */
120
+ const runEndpoint = (fn, event, opts) => Promise.resolve(makeAdapter(opts).endpoint(fn)(event));
121
+ /**
122
+ * Build a `{ run, runForm }` helper pair for directly invoking remote-function
123
+ * Effect handlers under test.
124
+ *
125
+ * Unlike the production `query` / `command` / `form` helpers, which resolve the
126
+ * request event via `$app/server.getRequestEvent()`, this resolves it from the
127
+ * `event` argument you supply — no SvelteKit module-graph mocking required.
128
+ *
129
+ * Use `run` for `query` / `command` handlers and `runForm` for `form` handlers
130
+ * that need the `issue` / `data` context.
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * import { TestRequestEvent, runRemote } from "@thomasfosterau/effect-sveltekit/testing";
135
+ *
136
+ * const event = TestRequestEvent();
137
+ * const { run } = runRemote(event, { layer: TestDatabase.layer });
138
+ *
139
+ * // Invoke the underlying Effect directly — no AppServer needed:
140
+ * const items = await run(listItemsEffect());
141
+ * expect(items).toEqual([]);
142
+ * ```
143
+ */
144
+ const runRemote = (event, opts) => {
145
+ const translate = opts?.translateError ?? SvelteKit.translate;
146
+ const layer = opts?.layer ?? Layer.empty;
147
+ const provider = RequestRuntime.fromLayer(layer);
148
+ return makeRunHelpers(() => ({ runExit: (effect) => provider.run(event).runPromiseExit(effect) }), translate, opts?.formError);
149
+ };
150
+ //#endregion
151
+ export { TestRequestEvent, runAction, runEndpoint, runRemote, runServerLoad };
152
+
153
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thomasfosterau/effect-sveltekit",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
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",
@@ -47,6 +47,10 @@
47
47
  "types": "./dist/vite.d.ts",
48
48
  "import": "./dist/vite.js"
49
49
  },
50
+ "./testing": {
51
+ "types": "./dist/testing.d.ts",
52
+ "import": "./dist/testing.js"
53
+ },
50
54
  "./adapter": {
51
55
  "types": "./dist/adapter/index.d.ts",
52
56
  "import": "./dist/adapter/index.js"
@@ -70,7 +74,7 @@
70
74
  "dependencies": {
71
75
  "@standard-schema/spec": "^1.1.0",
72
76
  "magic-string": "^0.30.0",
73
- "@thomasfosterau/effect-svelte": "^0.1.0"
77
+ "@thomasfosterau/effect-svelte": "^0.3.0"
74
78
  },
75
79
  "peerDependencies": {
76
80
  "@effect/platform-node": "^4.0.0-beta.75",