@thomasfosterau/effect-sveltekit 0.4.0 → 0.5.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 +515 -30
- package/dist/Hooks.d.ts +1 -1
- package/dist/Hooks.js.map +1 -1
- package/dist/Navigation.d.ts +17 -6
- package/dist/Navigation.js +17 -5
- package/dist/Navigation.js.map +1 -1
- package/dist/Remote.d.ts +58 -1
- package/dist/Remote.js +16 -0
- package/dist/Remote.js.map +1 -1
- package/dist/State.d.ts +4 -4
- package/dist/State.js.map +1 -1
- package/dist/adapter/handler.d.ts +15 -12
- package/dist/adapter/handler.js +5 -7
- package/dist/adapter/handler.js.map +1 -1
- package/dist/adapter/index.d.ts +1 -0
- package/dist/adapter/index.js +25 -21
- package/dist/adapter/index.js.map +1 -1
- package/dist/adapter/serve.d.ts +1 -0
- package/dist/adapter/serve.js.map +1 -1
- package/dist/api-client.js +1 -1
- package/dist/api-client.js.map +1 -1
- package/dist/app-runtime.d.ts +7 -6
- package/dist/app-runtime.js +5 -5
- package/dist/app-runtime.js.map +1 -1
- package/dist/client/Hooks.d.ts +1 -1
- package/dist/client/Hooks.js.map +1 -1
- package/dist/errors.js +3 -2
- package/dist/errors.js.map +1 -1
- package/dist/forms.d.ts +6 -6
- package/dist/forms.js.map +1 -1
- package/dist/internal/app-state.d.ts +1 -1
- package/dist/internal/app-state.js.map +1 -1
- package/dist/internal/auto.d.ts +2 -1
- package/dist/internal/auto.js.map +1 -1
- package/dist/internal/enhance.d.ts +1 -1
- package/dist/internal/enhance.js.map +1 -1
- package/dist/internal/error.js +23 -2
- package/dist/internal/error.js.map +1 -1
- package/dist/internal/remote-run.d.ts +4 -3
- package/dist/internal/remote-run.js.map +1 -1
- package/dist/kit.js +3 -3
- package/dist/kit.js.map +1 -1
- package/dist/realtime.d.ts +2 -0
- package/dist/realtime.js.map +1 -1
- package/dist/remote-functions.d.ts +18 -4
- package/dist/remote-functions.js +43 -0
- package/dist/remote-functions.js.map +1 -1
- package/dist/remote-reactive.d.ts +1 -1
- package/dist/remote-reactive.js.map +1 -1
- package/dist/response-headers.d.ts +1 -1
- package/dist/response-headers.js.map +1 -1
- package/dist/server/Endpoint.d.ts +2 -2
- package/dist/server/Endpoint.js +2 -2
- package/dist/server/Endpoint.js.map +1 -1
- package/dist/server/Hooks.d.ts +1 -1
- package/dist/server/Hooks.js.map +1 -1
- package/dist/transport.d.ts +1 -1
- package/dist/transport.js.map +1 -1
- package/dist/vite.d.ts +4 -2
- package/dist/vite.js.map +1 -1
- package/package.json +7 -4
package/dist/Navigation.js
CHANGED
|
@@ -26,7 +26,7 @@ import { afterNavigate, beforeNavigate, goto, invalidate, invalidateAll, onNavig
|
|
|
26
26
|
* runtime.runPromise(
|
|
27
27
|
* Effect.gen(function* () {
|
|
28
28
|
* yield* doSave();
|
|
29
|
-
* yield* Navigation.
|
|
29
|
+
* yield* Navigation.refreshAll();
|
|
30
30
|
* yield* Navigation.goto('/done');
|
|
31
31
|
* }),
|
|
32
32
|
* );
|
|
@@ -44,18 +44,30 @@ var Navigation_exports = /* @__PURE__ */ __exportAll({
|
|
|
44
44
|
preloadData: () => preloadData$1,
|
|
45
45
|
refreshAll: () => refreshAll$1
|
|
46
46
|
});
|
|
47
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Navigate to `url`. Wraps `$app/navigation`'s `goto`. SvelteKit rejects a URL
|
|
49
|
+
* outside the app (external, or not matching a route); a rejection surfaces as
|
|
50
|
+
* a defect.
|
|
51
|
+
*/
|
|
48
52
|
const goto$1 = (...args) => Effect.promise(() => goto(...args));
|
|
49
53
|
/** Re-run `load` functions that depend on `resource`. Wraps `invalidate`. */
|
|
50
54
|
const invalidate$1 = (...args) => Effect.promise(() => invalidate(...args));
|
|
51
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Re-run every `load` function for the active page. Wraps `invalidateAll`.
|
|
57
|
+
*
|
|
58
|
+
* @deprecated SvelteKit 3 deprecates `invalidateAll`; use {@link refreshAll},
|
|
59
|
+
* which also refreshes active remote functions and keeps `page.state`.
|
|
60
|
+
*/
|
|
52
61
|
const invalidateAll$1 = () => Effect.promise(() => invalidateAll());
|
|
53
62
|
/** Refresh active remote functions (and, by default, `load`s). Wraps `refreshAll`. */
|
|
54
63
|
const refreshAll$1 = (...args) => Effect.promise(() => refreshAll(...args));
|
|
55
64
|
/** Preload the data for `href`. Wraps `preloadData`. */
|
|
56
65
|
const preloadData$1 = (href) => Effect.promise(() => preloadData(href));
|
|
57
|
-
/**
|
|
58
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Preload the code for the route `id` (e.g. `/blog/[slug]`, without
|
|
68
|
+
* `paths.base`). Wraps `preloadCode`.
|
|
69
|
+
*/
|
|
70
|
+
const preloadCode$1 = (...args) => Effect.promise(() => preloadCode(...args));
|
|
59
71
|
/**
|
|
60
72
|
* Registers a `beforeNavigate` callback and exposes every navigation about to
|
|
61
73
|
* happen as an Effect `Stream`. Call during component initialization.
|
package/dist/Navigation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Navigation.js","names":["goto","kitGoto","invalidate","kitInvalidate","invalidateAll","kitInvalidateAll","refreshAll","kitRefreshAll","preloadData","kitPreloadData","preloadCode","kitPreloadCode","beforeNavigate","kitBeforeNavigate","afterNavigate","kitAfterNavigate","onNavigate","kitOnNavigate"],"sources":["../src/Navigation.ts"],"sourcesContent":["/**\n * Effect wrappers for SvelteKit's client-side\n * [`$app/navigation`](https://svelte.dev/docs/kit/$app-navigation) API.\n *\n * Each of SvelteKit's promise-returning navigation functions is wrapped as an\n * `Effect`, so navigation composes inside Effect pipelines (sequencing,\n * retries, `Effect.race`, tracing spans, ...). Alongside them sit the\n * client-side lifecycle callbacks (`beforeNavigate`, `afterNavigate`,\n * `onNavigate`) bridged to Effect. These run on the client only — calling them\n * during SSR throws, exactly as the underlying `$app/navigation` functions do.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { Navigation } from '@thomasfosterau/effect-sveltekit/client';\n * import { Effect } from 'effect';\n * import { getRuntime } from '@thomasfosterau/effect-svelte';\n *\n * const runtime = getRuntime();\n * const save = () =>\n * runtime.runPromise(\n * Effect.gen(function* () {\n * yield* doSave();\n * yield* Navigation.
|
|
1
|
+
{"version":3,"file":"Navigation.js","names":["goto","kitGoto","invalidate","kitInvalidate","invalidateAll","kitInvalidateAll","refreshAll","kitRefreshAll","preloadData","kitPreloadData","preloadCode","kitPreloadCode","beforeNavigate","kitBeforeNavigate","afterNavigate","kitAfterNavigate","onNavigate","kitOnNavigate"],"sources":["../src/Navigation.ts"],"sourcesContent":["/**\n * Effect wrappers for SvelteKit's client-side\n * [`$app/navigation`](https://svelte.dev/docs/kit/$app-navigation) API.\n *\n * Each of SvelteKit's promise-returning navigation functions is wrapped as an\n * `Effect`, so navigation composes inside Effect pipelines (sequencing,\n * retries, `Effect.race`, tracing spans, ...). Alongside them sit the\n * client-side lifecycle callbacks (`beforeNavigate`, `afterNavigate`,\n * `onNavigate`) bridged to Effect. These run on the client only — calling them\n * during SSR throws, exactly as the underlying `$app/navigation` functions do.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { Navigation } from '@thomasfosterau/effect-sveltekit/client';\n * import { Effect } from 'effect';\n * import { getRuntime } from '@thomasfosterau/effect-svelte';\n *\n * const runtime = getRuntime();\n * const save = () =>\n * runtime.runPromise(\n * Effect.gen(function* () {\n * yield* doSave();\n * yield* Navigation.refreshAll();\n * yield* Navigation.goto('/done');\n * }),\n * );\n * </script>\n * ```\n */\n\nimport {\n afterNavigate as kitAfterNavigate,\n beforeNavigate as kitBeforeNavigate,\n goto as kitGoto,\n invalidate as kitInvalidate,\n invalidateAll as kitInvalidateAll,\n onNavigate as kitOnNavigate,\n preloadCode as kitPreloadCode,\n preloadData as kitPreloadData,\n refreshAll as kitRefreshAll,\n} from \"$app/navigation\";\nimport type { AfterNavigate, BeforeNavigate, OnNavigate } from \"$app/navigation\";\nimport { Effect } from \"effect\";\nimport type { Stream } from \"effect\";\nimport { onNavigateWith, registeredStream } from \"./internal/navigation-events.js\";\n\n/**\n * Navigate to `url`. Wraps `$app/navigation`'s `goto`. SvelteKit rejects a URL\n * outside the app (external, or not matching a route); a rejection surfaces as\n * a defect.\n */\nexport const goto = (...args: Parameters<typeof kitGoto>): Effect.Effect<void> =>\n Effect.promise(() => kitGoto(...args));\n\n/** Re-run `load` functions that depend on `resource`. Wraps `invalidate`. */\nexport const invalidate = (...args: Parameters<typeof kitInvalidate>): Effect.Effect<void> =>\n Effect.promise(() => kitInvalidate(...args));\n\n/**\n * Re-run every `load` function for the active page. Wraps `invalidateAll`.\n *\n * @deprecated SvelteKit 3 deprecates `invalidateAll`; use {@link refreshAll},\n * which also refreshes active remote functions and keeps `page.state`.\n */\nexport const invalidateAll = (): Effect.Effect<void> => Effect.promise(() => kitInvalidateAll());\n\n/** Refresh active remote functions (and, by default, `load`s). Wraps `refreshAll`. */\nexport const refreshAll = (...args: Parameters<typeof kitRefreshAll>): Effect.Effect<void> =>\n Effect.promise(() => kitRefreshAll(...args));\n\n/** Preload the data for `href`. Wraps `preloadData`. */\nexport const preloadData = (\n href: string,\n): Effect.Effect<Awaited<ReturnType<typeof kitPreloadData>>> =>\n Effect.promise(() => kitPreloadData(href));\n\n/**\n * Preload the code for the route `id` (e.g. `/blog/[slug]`, without\n * `paths.base`). Wraps `preloadCode`.\n */\nexport const preloadCode = (...args: Parameters<typeof kitPreloadCode>): Effect.Effect<void> =>\n Effect.promise(() => kitPreloadCode(...args));\n\n/**\n * Registers a `beforeNavigate` callback and exposes every navigation about to\n * happen as an Effect `Stream`. Call during component initialization.\n *\n * Unlike the promise-returning navigation functions above, this is a\n * **callback-registration** function: it must be called *synchronously during\n * component initialization* (SvelteKit wires it to the component's lifecycle).\n * It registers its callback eagerly and feeds an internal `Queue`, returning a\n * `Stream` backed by that queue — the same push-based bridge as `SignalEmitter`\n * in `@thomasfosterau/effect-svelte`. Call it at the top level of `<script>`,\n * exactly where you'd call the bare `beforeNavigate`.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { Navigation } from '@thomasfosterau/effect-sveltekit/client';\n * import { Stream, Effect } from 'effect';\n * import { getRuntime } from '@thomasfosterau/effect-svelte';\n *\n * // Register at init so the callback is wired to the component lifecycle.\n * const navigations = Navigation.beforeNavigate();\n *\n * getRuntime().runFork(\n * Stream.runForEach(navigations, (nav) =>\n * Effect.logInfo(`leaving: ${nav.from?.url.pathname}`),\n * ),\n * );\n * </script>\n * ```\n */\nexport function beforeNavigate(): Stream.Stream<BeforeNavigate> {\n return registeredStream<BeforeNavigate>(kitBeforeNavigate);\n}\n\n/**\n * Registers an `afterNavigate` callback and exposes every completed navigation\n * (including the initial one) as an Effect `Stream`. Call during component\n * initialization.\n *\n * Like {@link beforeNavigate}, this is a **callback-registration** function:\n * call it at the top level of `<script>`, exactly where you'd call the bare\n * `afterNavigate`.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { Navigation } from '@thomasfosterau/effect-sveltekit/client';\n * import { Stream, Effect } from 'effect';\n * import { getRuntime } from '@thomasfosterau/effect-svelte';\n *\n * // Register at init so the callback is wired to the component lifecycle.\n * const navigations = Navigation.afterNavigate();\n *\n * getRuntime().runFork(\n * Stream.runForEach(navigations, (nav) =>\n * Effect.logInfo(`navigated: ${nav.to?.url.pathname}`),\n * ),\n * );\n * </script>\n * ```\n */\nexport function afterNavigate(): Stream.Stream<AfterNavigate> {\n return registeredStream<AfterNavigate>(kitAfterNavigate);\n}\n\n/**\n * Runs an `Effect` for each navigation, registered via `$app/navigation`'s\n * `onNavigate`. Call during component initialization.\n *\n * SvelteKit's `onNavigate` callback may return a cleanup function (or a promise\n * of one) that runs once the navigation completes and the DOM has updated; to\n * support that, `fn`'s Effect may succeed with a `() => void` cleanup, which is\n * forwarded to SvelteKit. The Effect is run with `runEffect` (default\n * {@link Effect.runPromise}); pass a managed runtime's `runPromise` to supply\n * services to `fn`.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { Navigation } from '@thomasfosterau/effect-sveltekit/client';\n * import { Effect } from 'effect';\n *\n * Navigation.onNavigate((nav) =>\n * Effect.gen(function* () {\n * yield* Effect.logInfo(`navigating to ${nav.to?.url.pathname}`);\n * // Optionally return a cleanup that runs after the navigation:\n * return () => console.log(\"navigation done\");\n * }),\n * );\n * </script>\n * ```\n */\nexport function onNavigate(\n fn: (navigation: OnNavigate) => Effect.Effect<(() => void) | void>,\n runEffect: <X>(effect: Effect.Effect<X>) => Promise<X> = Effect.runPromise,\n): void {\n onNavigateWith<OnNavigate>(kitOnNavigate, fn, runEffect);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,MAAaA,UAAQ,GAAG,SACtB,OAAO,cAAcC,KAAQ,GAAG,IAAI,CAAC;;AAGvC,MAAaC,gBAAc,GAAG,SAC5B,OAAO,cAAcC,WAAc,GAAG,IAAI,CAAC;;;;;;;AAQ7C,MAAaC,wBAA2C,OAAO,cAAcC,cAAiB,CAAC;;AAG/F,MAAaC,gBAAc,GAAG,SAC5B,OAAO,cAAcC,WAAc,GAAG,IAAI,CAAC;;AAG7C,MAAaC,iBACX,SAEA,OAAO,cAAcC,YAAe,IAAI,CAAC;;;;;AAM3C,MAAaC,iBAAe,GAAG,SAC7B,OAAO,cAAcC,YAAe,GAAG,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgC9C,SAAgBC,mBAAgD;CAC9D,OAAO,iBAAiCC,cAAiB;AAC3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgBC,kBAA8C;CAC5D,OAAO,iBAAgCC,aAAgB;AACzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgBC,aACd,IACA,YAAyD,OAAO,YAC1D;CACN,eAA2BC,YAAe,IAAI,SAAS;AACzD"}
|
package/dist/Remote.d.ts
CHANGED
|
@@ -1,11 +1,38 @@
|
|
|
1
1
|
import { Effect, Stream } from "effect";
|
|
2
|
-
import { RemoteCommand, RemoteLiveQuery, RemotePrerenderFunction, RemoteQuery, RemoteQueryFunction, RemoteQueryOverride } from "
|
|
2
|
+
import { RemoteCommand, RemoteLiveQuery, RemotePrerenderFunction, RemoteQuery, RemoteQueryFunction, RemoteQueryOverride } from "$app/server";
|
|
3
3
|
declare namespace Remote_d_exports {
|
|
4
4
|
export { RemoteSubmit, callCommand, callCommandWithUpdates, callPrerender, callQuery, liveQueryStream, submitForm, submitFormWithUpdates };
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Calls a SvelteKit remote `query` as an Effect that succeeds with its output.
|
|
8
|
+
*
|
|
9
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
10
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
11
|
+
*/
|
|
6
12
|
declare function callQuery<Output>(query: RemoteQueryFunction<void, Output>): Effect.Effect<Output, unknown, never>;
|
|
13
|
+
/**
|
|
14
|
+
* Calls a SvelteKit remote `query` with `input` as an Effect that succeeds
|
|
15
|
+
* with its output.
|
|
16
|
+
*
|
|
17
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
18
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
19
|
+
*/
|
|
7
20
|
declare function callQuery<Input, Output>(query: RemoteQueryFunction<Input, Output>, input: Input): Effect.Effect<Output, unknown, never>;
|
|
21
|
+
/**
|
|
22
|
+
* Calls a SvelteKit remote `command` as an Effect that succeeds with its
|
|
23
|
+
* output.
|
|
24
|
+
*
|
|
25
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
26
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
27
|
+
*/
|
|
8
28
|
declare function callCommand<Output>(command: RemoteCommand<void, Output>): Effect.Effect<Output, unknown, never>;
|
|
29
|
+
/**
|
|
30
|
+
* Calls a SvelteKit remote `command` with `input` as an Effect that succeeds
|
|
31
|
+
* with its output.
|
|
32
|
+
*
|
|
33
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
34
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
35
|
+
*/
|
|
9
36
|
declare function callCommand<Input, Output>(command: RemoteCommand<Input, Output>, input: Input): Effect.Effect<Output, unknown, never>;
|
|
10
37
|
/**
|
|
11
38
|
* Calls a remote command and refreshes the given queries in the same round
|
|
@@ -14,9 +41,26 @@ declare function callCommand<Input, Output>(command: RemoteCommand<Input, Output
|
|
|
14
41
|
* Newer SvelteKit versions cache remote query instances, so a command that
|
|
15
42
|
* changes data those queries depend on must list them here (or call
|
|
16
43
|
* `query.refresh()` afterwards) for their cached values to update.
|
|
44
|
+
*
|
|
45
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
46
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
17
47
|
*/
|
|
18
48
|
declare function callCommandWithUpdates<Input, Output>(command: RemoteCommand<Input, Output>, input: Input, ...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Effect.Effect<Output, unknown, never>;
|
|
49
|
+
/**
|
|
50
|
+
* Calls a SvelteKit remote `prerender` function as an Effect that succeeds
|
|
51
|
+
* with its output.
|
|
52
|
+
*
|
|
53
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
54
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
55
|
+
*/
|
|
19
56
|
declare function callPrerender<Output>(prerender: RemotePrerenderFunction<void, Output>): Effect.Effect<Output, unknown, never>;
|
|
57
|
+
/**
|
|
58
|
+
* Calls a SvelteKit remote `prerender` function with `input` as an Effect
|
|
59
|
+
* that succeeds with its output.
|
|
60
|
+
*
|
|
61
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
62
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
63
|
+
*/
|
|
20
64
|
declare function callPrerender<Input, Output>(prerender: RemotePrerenderFunction<Input, Output>, input: Input): Effect.Effect<Output, unknown, never>;
|
|
21
65
|
/**
|
|
22
66
|
* The shape of an enhanced remote form `submit` function. The promise result
|
|
@@ -26,7 +70,20 @@ declare function callPrerender<Input, Output>(prerender: RemotePrerenderFunction
|
|
|
26
70
|
type RemoteSubmit = () => PromiseLike<unknown> & {
|
|
27
71
|
updates: (...queries: Array<RemoteQuery<any> | RemoteQueryOverride>) => PromiseLike<unknown>;
|
|
28
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* Runs an enhanced remote form's `submit()` as an Effect.
|
|
75
|
+
*
|
|
76
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
77
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
78
|
+
*/
|
|
29
79
|
declare function submitForm(submit: () => PromiseLike<unknown>): Effect.Effect<void, unknown, never>;
|
|
80
|
+
/**
|
|
81
|
+
* Runs an enhanced remote form's `submit()` and refreshes the given queries in
|
|
82
|
+
* the same round trip (SvelteKit's single-flight mutations).
|
|
83
|
+
*
|
|
84
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
85
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
86
|
+
*/
|
|
30
87
|
declare function submitFormWithUpdates(submit: RemoteSubmit, ...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Effect.Effect<void, unknown, never>;
|
|
31
88
|
/**
|
|
32
89
|
* Consumes a SvelteKit live query (`query.live(...)`) as an Effect `Stream`.
|
package/dist/Remote.js
CHANGED
|
@@ -28,6 +28,9 @@ function callCommand(command, input) {
|
|
|
28
28
|
* Newer SvelteKit versions cache remote query instances, so a command that
|
|
29
29
|
* changes data those queries depend on must list them here (or call
|
|
30
30
|
* `query.refresh()` afterwards) for their cached values to update.
|
|
31
|
+
*
|
|
32
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
33
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
31
34
|
*/
|
|
32
35
|
function callCommandWithUpdates(command, input, ...queries) {
|
|
33
36
|
return fromRemotePromise(() => command(input).updates(...queries));
|
|
@@ -36,9 +39,22 @@ function callPrerender(prerender, input) {
|
|
|
36
39
|
if (arguments.length === 1) return fromRemotePromise(() => prerender(void 0));
|
|
37
40
|
return fromRemotePromise(() => prerender(input));
|
|
38
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Runs an enhanced remote form's `submit()` as an Effect.
|
|
44
|
+
*
|
|
45
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
46
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
47
|
+
*/
|
|
39
48
|
function submitForm(submit) {
|
|
40
49
|
return Effect.asVoid(fromRemotePromise(() => submit()));
|
|
41
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Runs an enhanced remote form's `submit()` and refreshes the given queries in
|
|
53
|
+
* the same round trip (SvelteKit's single-flight mutations).
|
|
54
|
+
*
|
|
55
|
+
* A rejected call (including a thrown `error()` / `redirect()`) surfaces as a
|
|
56
|
+
* defect, not on the error channel, despite the declared `unknown` error type.
|
|
57
|
+
*/
|
|
42
58
|
function submitFormWithUpdates(submit, ...queries) {
|
|
43
59
|
return Effect.asVoid(fromRemotePromise(() => submit().updates(...queries)));
|
|
44
60
|
}
|
package/dist/Remote.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Remote.js","names":[],"sources":["../src/Remote.ts"],"sourcesContent":["import type {\n RemoteCommand,\n RemoteLiveQuery,\n RemotePrerenderFunction,\n RemoteQuery,\n RemoteQueryFunction,\n RemoteQueryOverride,\n} from \"
|
|
1
|
+
{"version":3,"file":"Remote.js","names":[],"sources":["../src/Remote.ts"],"sourcesContent":["import type {\n RemoteCommand,\n RemoteLiveQuery,\n RemotePrerenderFunction,\n RemoteQuery,\n RemoteQueryFunction,\n RemoteQueryOverride,\n} from \"$app/server\";\nimport { Effect, Stream } from \"effect\";\n\nfunction fromRemotePromise<A>(thunk: () => PromiseLike<A>): Effect.Effect<A, unknown, never> {\n return Effect.promise(() => Promise.resolve(thunk()));\n}\n\n/**\n * Calls a SvelteKit remote `query` as an Effect that succeeds with its output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callQuery<Output>(\n query: RemoteQueryFunction<void, Output>,\n): Effect.Effect<Output, unknown, never>;\n/**\n * Calls a SvelteKit remote `query` with `input` as an Effect that succeeds\n * with its output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callQuery<Input, Output>(\n query: RemoteQueryFunction<Input, Output>,\n input: Input,\n): Effect.Effect<Output, unknown, never>;\nexport function callQuery<Input, Output>(\n query: RemoteQueryFunction<Input, Output>,\n input?: Input,\n): Effect.Effect<Output, unknown, never> {\n if (arguments.length === 1) {\n return fromRemotePromise(() =>\n (query as unknown as RemoteQueryFunction<void, Output>)(undefined),\n );\n }\n\n return fromRemotePromise(() => query(input as Input));\n}\n\n/**\n * Calls a SvelteKit remote `command` as an Effect that succeeds with its\n * output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callCommand<Output>(\n command: RemoteCommand<void, Output>,\n): Effect.Effect<Output, unknown, never>;\n/**\n * Calls a SvelteKit remote `command` with `input` as an Effect that succeeds\n * with its output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callCommand<Input, Output>(\n command: RemoteCommand<Input, Output>,\n input: Input,\n): Effect.Effect<Output, unknown, never>;\nexport function callCommand<Input, Output>(\n command: RemoteCommand<Input, Output>,\n input?: Input,\n): Effect.Effect<Output, unknown, never> {\n if (arguments.length === 1) {\n return fromRemotePromise(() => (command as unknown as RemoteCommand<void, Output>)(undefined));\n }\n\n return fromRemotePromise(() => command(input as Input));\n}\n\n/**\n * Calls a remote command and refreshes the given queries in the same round\n * trip (SvelteKit's single-flight mutations).\n *\n * Newer SvelteKit versions cache remote query instances, so a command that\n * changes data those queries depend on must list them here (or call\n * `query.refresh()` afterwards) for their cached values to update.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callCommandWithUpdates<Input, Output>(\n command: RemoteCommand<Input, Output>,\n input: Input,\n ...queries: Array<RemoteQuery<any> | RemoteQueryOverride>\n): Effect.Effect<Output, unknown, never> {\n return fromRemotePromise(() => command(input).updates(...queries));\n}\n\n/**\n * Calls a SvelteKit remote `prerender` function as an Effect that succeeds\n * with its output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callPrerender<Output>(\n prerender: RemotePrerenderFunction<void, Output>,\n): Effect.Effect<Output, unknown, never>;\n/**\n * Calls a SvelteKit remote `prerender` function with `input` as an Effect\n * that succeeds with its output.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function callPrerender<Input, Output>(\n prerender: RemotePrerenderFunction<Input, Output>,\n input: Input,\n): Effect.Effect<Output, unknown, never>;\nexport function callPrerender<Input, Output>(\n prerender: RemotePrerenderFunction<Input, Output>,\n input?: Input,\n): Effect.Effect<Output, unknown, never> {\n if (arguments.length === 1) {\n return fromRemotePromise(() =>\n (prerender as unknown as RemotePrerenderFunction<void, Output>)(undefined),\n );\n }\n\n return fromRemotePromise(() => prerender(input as Input));\n}\n\n/**\n * The shape of an enhanced remote form `submit` function. The promise result\n * is left open (SvelteKit returns `Promise<void>` in older versions and\n * `Promise<boolean>` in newer ones).\n */\nexport type RemoteSubmit = () => PromiseLike<unknown> & {\n updates: (...queries: Array<RemoteQuery<any> | RemoteQueryOverride>) => PromiseLike<unknown>;\n};\n\n/**\n * Runs an enhanced remote form's `submit()` as an Effect.\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function submitForm(\n submit: () => PromiseLike<unknown>,\n): Effect.Effect<void, unknown, never> {\n return Effect.asVoid(fromRemotePromise(() => submit()));\n}\n\n/**\n * Runs an enhanced remote form's `submit()` and refreshes the given queries in\n * the same round trip (SvelteKit's single-flight mutations).\n *\n * A rejected call (including a thrown `error()` / `redirect()`) surfaces as a\n * defect, not on the error channel, despite the declared `unknown` error type.\n */\nexport function submitFormWithUpdates(\n submit: RemoteSubmit,\n ...queries: Array<RemoteQuery<any> | RemoteQueryOverride>\n): Effect.Effect<void, unknown, never> {\n return Effect.asVoid(fromRemotePromise(() => submit().updates(...queries)));\n}\n\n/**\n * Consumes a SvelteKit live query (`query.live(...)`) as an Effect `Stream`.\n *\n * A `RemoteLiveQuery<T>` is an `AsyncIterable<T>` that yields every update\n * pushed from the server; this turns it into a `Stream` so live data can drive\n * Effect pipelines (and, via `useStream`, Svelte runes).\n *\n * @example\n * ```ts\n * import { Remote } from '@thomasfosterau/effect-sveltekit';\n * import { messages } from './chat.remote';\n * import { useStream } from '@thomasfosterau/effect-svelte';\n *\n * const feed = useStream(Remote.liveQueryStream(messages()));\n * ```\n */\nexport function liveQueryStream<Output>(\n query: RemoteLiveQuery<Output>,\n): Stream.Stream<Output, unknown> {\n return Stream.fromAsyncIterable(query, (error) => error);\n}\n"],"mappings":";;;;;;;;;;;;AAUA,SAAS,kBAAqB,OAA+D;CAC3F,OAAO,OAAO,cAAc,QAAQ,QAAQ,MAAM,CAAC,CAAC;AACtD;AAsBA,SAAgB,UACd,OACA,OACuC;CACvC,IAAI,UAAU,WAAW,GACvB,OAAO,wBACJ,MAAuD,KAAA,CAAS,CACnE;CAGF,OAAO,wBAAwB,MAAM,KAAc,CAAC;AACtD;AAuBA,SAAgB,YACd,SACA,OACuC;CACvC,IAAI,UAAU,WAAW,GACvB,OAAO,wBAAyB,QAAmD,KAAA,CAAS,CAAC;CAG/F,OAAO,wBAAwB,QAAQ,KAAc,CAAC;AACxD;;;;;;;;;;;;AAaA,SAAgB,uBACd,SACA,OACA,GAAG,SACoC;CACvC,OAAO,wBAAwB,QAAQ,KAAK,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAC;AACnE;AAuBA,SAAgB,cACd,WACA,OACuC;CACvC,IAAI,UAAU,WAAW,GACvB,OAAO,wBACJ,UAA+D,KAAA,CAAS,CAC3E;CAGF,OAAO,wBAAwB,UAAU,KAAc,CAAC;AAC1D;;;;;;;AAiBA,SAAgB,WACd,QACqC;CACrC,OAAO,OAAO,OAAO,wBAAwB,OAAO,CAAC,CAAC;AACxD;;;;;;;;AASA,SAAgB,sBACd,QACA,GAAG,SACkC;CACrC,OAAO,OAAO,OAAO,wBAAwB,OAAO,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC;AAC5E;;;;;;;;;;;;;;;;;AAkBA,SAAgB,gBACd,OACgC;CAChC,OAAO,OAAO,kBAAkB,QAAQ,UAAU,KAAK;AACzD"}
|
package/dist/State.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { PageSnapshot } from "./internal/app-state.js";
|
|
2
2
|
import { Stream } from "effect";
|
|
3
|
-
import { Page } from "
|
|
3
|
+
import { Page } from "$app/state";
|
|
4
4
|
declare namespace State_d_exports {
|
|
5
|
-
export { PageSnapshot, page };
|
|
5
|
+
export { PageSnapshot, page$1 as page };
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
8
|
* A `Stream` of the current SvelteKit `page`, emitting a shallow snapshot
|
|
9
9
|
* immediately and again after every navigation. Call during component
|
|
10
10
|
* initialization.
|
|
11
11
|
*/
|
|
12
|
-
declare function page(): Stream.Stream<PageSnapshot<Page>>;
|
|
12
|
+
declare function page$1(): Stream.Stream<PageSnapshot<Page>>;
|
|
13
13
|
//#endregion
|
|
14
|
-
export { type PageSnapshot, State_d_exports, page };
|
|
14
|
+
export { type PageSnapshot, State_d_exports, page$1 as page };
|
|
15
15
|
//# sourceMappingURL=State.d.ts.map
|
package/dist/State.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"State.js","names":["page","kitPage"],"sources":["../src/State.ts"],"sourcesContent":["/**\n * A minimal Effect-side bridge for SvelteKit's\n * [`$app/state`](https://svelte.dev/docs/kit/$app-state) `page`.\n *\n * `$app/state` exposes rune-reactive `page` / `navigating` / `updated` for use\n * inside components. This bridges `page` to an Effect `Stream` so navigation can\n * drive Effect pipelines without touching Svelte runes: it emits the current\n * `page` immediately, then again after each navigation, by registering\n * `afterNavigate` (from `$app/navigation`) and reading `page` (from\n * `$app/state`) at callback time.\n *\n * Each emission is a *shallow snapshot* (`url`, `params`, `route`, `data`,\n * `status`, `error`, `form`, `state`) rather than the live `$app/state` proxy:\n * the proxy mutates in place on every navigation, so a captured reference is\n * unreliable for downstream consumers — the snapshot gives each emission a\n * stable value. Reading at `afterNavigate` time also avoids needing a `.svelte`\n * runes file.\n *\n * Like the navigation-event helpers, `page` registers its callback during\n * component initialization (where `$app/navigation` requires), so call it at the\n * top level of `<script>`. Client-only.\n *\n * NOTE: this is intentionally minimal. A richer reactive bridge (a `CurrentPage`\n * service readable from any Effect, `navigating` / `updated` streams) may later\n * compose with the official `@effect/atom-svelte` integration (see issue #23)\n * rather than re-implementing runes plumbing here.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { State } from '@thomasfosterau/effect-sveltekit/client';\n * import { Stream, Effect } from 'effect';\n * import { getRuntime } from '@thomasfosterau/effect-svelte';\n *\n * const pages = State.page();\n * getRuntime().runFork(\n * Stream.runForEach(pages, (page) =>\n * Effect.logInfo(`page: ${page.url.pathname}`),\n * ),\n * );\n * </script>\n * ```\n */\n\nimport { afterNavigate as kitAfterNavigate } from \"$app/navigation\";\nimport { page as kitPage } from \"$app/state\";\nimport type { Page } from \"
|
|
1
|
+
{"version":3,"file":"State.js","names":["page","kitPage"],"sources":["../src/State.ts"],"sourcesContent":["/**\n * A minimal Effect-side bridge for SvelteKit's\n * [`$app/state`](https://svelte.dev/docs/kit/$app-state) `page`.\n *\n * `$app/state` exposes rune-reactive `page` / `navigating` / `updated` for use\n * inside components. This bridges `page` to an Effect `Stream` so navigation can\n * drive Effect pipelines without touching Svelte runes: it emits the current\n * `page` immediately, then again after each navigation, by registering\n * `afterNavigate` (from `$app/navigation`) and reading `page` (from\n * `$app/state`) at callback time.\n *\n * Each emission is a *shallow snapshot* (`url`, `params`, `route`, `data`,\n * `status`, `error`, `form`, `state`) rather than the live `$app/state` proxy:\n * the proxy mutates in place on every navigation, so a captured reference is\n * unreliable for downstream consumers — the snapshot gives each emission a\n * stable value. Reading at `afterNavigate` time also avoids needing a `.svelte`\n * runes file.\n *\n * Like the navigation-event helpers, `page` registers its callback during\n * component initialization (where `$app/navigation` requires), so call it at the\n * top level of `<script>`. Client-only.\n *\n * NOTE: this is intentionally minimal. A richer reactive bridge (a `CurrentPage`\n * service readable from any Effect, `navigating` / `updated` streams) may later\n * compose with the official `@effect/atom-svelte` integration (see issue #23)\n * rather than re-implementing runes plumbing here.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { State } from '@thomasfosterau/effect-sveltekit/client';\n * import { Stream, Effect } from 'effect';\n * import { getRuntime } from '@thomasfosterau/effect-svelte';\n *\n * const pages = State.page();\n * getRuntime().runFork(\n * Stream.runForEach(pages, (page) =>\n * Effect.logInfo(`page: ${page.url.pathname}`),\n * ),\n * );\n * </script>\n * ```\n */\n\nimport { afterNavigate as kitAfterNavigate } from \"$app/navigation\";\nimport { page as kitPage } from \"$app/state\";\nimport type { Page } from \"$app/state\";\nimport type { Stream } from \"effect\";\nimport { type PageSnapshot, pageSnapshotStream } from \"./internal/app-state.js\";\n\n/** A shallow snapshot of SvelteKit's `page` — see {@link page}. */\nexport type { PageSnapshot } from \"./internal/app-state.js\";\n\n/**\n * A `Stream` of the current SvelteKit `page`, emitting a shallow snapshot\n * immediately and again after every navigation. Call during component\n * initialization.\n */\nexport function page(): Stream.Stream<PageSnapshot<Page>> {\n return pageSnapshotStream<Page>(\n (callback) => {\n kitAfterNavigate(() => {\n callback();\n });\n },\n () => kitPage,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,SAAgBA,SAA0C;CACxD,OAAO,oBACJ,aAAa;EACZ,oBAAuB;GACrB,SAAS;EACX,CAAC;CACH,SACMC,IACR;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AssetSource, FileSystemAssetServices } from "./assets.js";
|
|
2
2
|
import { Effect, Layer } from "effect";
|
|
3
|
-
import {
|
|
3
|
+
import { Server } from "@sveltejs/kit";
|
|
4
4
|
import * as HttpServerRequest from "effect/http/HttpServerRequest";
|
|
5
5
|
import * as HttpServerResponse from "effect/http/HttpServerResponse";
|
|
6
6
|
//#region src/adapter/handler.d.ts
|
|
@@ -17,11 +17,17 @@ type SvelteKitHttpAppServices = FileSystemAssetServices;
|
|
|
17
17
|
* is the asset source's services (defaults to the file-system services).
|
|
18
18
|
*/
|
|
19
19
|
type SvelteKitHttpApp<R = SvelteKitHttpAppServices> = Effect.Effect<HttpServerResponse.HttpServerResponse, never, R | HttpServerRequest.HttpServerRequest>;
|
|
20
|
+
/** The SvelteKit build output {@link makeHandler} serves. */
|
|
20
21
|
interface SvelteKitHandlerOptions {
|
|
21
|
-
/** The SvelteKit `
|
|
22
|
-
readonly
|
|
23
|
-
/**
|
|
24
|
-
|
|
22
|
+
/** The SvelteKit `server` instance exported by the build output (`./server.js`). */
|
|
23
|
+
readonly server: Server;
|
|
24
|
+
/**
|
|
25
|
+
* The application path, including any `paths.base` without its leading
|
|
26
|
+
* slash (`"_app"`, or `"my-app/_app"` under a base), exported by the build
|
|
27
|
+
* output (`./manifest.js`). Its `immutable/` subtree gets immutable cache
|
|
28
|
+
* headers.
|
|
29
|
+
*/
|
|
30
|
+
readonly appPath: string;
|
|
25
31
|
/** The set of prerendered paths exported by the build output (`./manifest.js`). */
|
|
26
32
|
readonly prerendered: ReadonlySet<string>;
|
|
27
33
|
/** The configured `paths.base`, exported by the build output (`./manifest.js`). */
|
|
@@ -35,10 +41,8 @@ interface SvelteKitHandlerOptions {
|
|
|
35
41
|
}
|
|
36
42
|
/** Options for the platform-neutral SSR core, {@link makeHandlerWith}. */
|
|
37
43
|
interface SvelteKitNeutralHandlerOptions<RAssets> {
|
|
38
|
-
/** The SvelteKit `
|
|
39
|
-
readonly
|
|
40
|
-
/** The SSR manifest exported by the build output. */
|
|
41
|
-
readonly manifest: SSRManifest;
|
|
44
|
+
/** The SvelteKit `server` instance exported by the build output (`./server.js`). */
|
|
45
|
+
readonly server: Server;
|
|
42
46
|
/** Environment variables passed to the SvelteKit server. */
|
|
43
47
|
readonly env?: Record<string, string | undefined> | undefined;
|
|
44
48
|
/** Prefix for the runtime configuration environment variables (ORIGIN, ...). */
|
|
@@ -96,14 +100,13 @@ declare const makeHandler: (options: SvelteKitHandlerOptions) => Effect.Effect<S
|
|
|
96
100
|
* ```ts
|
|
97
101
|
* // Workers: a fetch handler with assets from env.ASSETS
|
|
98
102
|
* import { makeHandlerWith, AssetSource, toWebHandler } from "...adapter/handler";
|
|
99
|
-
* import {
|
|
100
|
-
* import { manifest } from "./manifest.js";
|
|
103
|
+
* import { server } from "./server.js";
|
|
101
104
|
*
|
|
102
105
|
* export default {
|
|
103
106
|
* fetch: (request, env, ctx) =>
|
|
104
107
|
* toWebHandler(
|
|
105
108
|
* makeHandlerWith({
|
|
106
|
-
*
|
|
109
|
+
* server, env,
|
|
107
110
|
* assets: AssetSource.cloudflareAssets(env.ASSETS),
|
|
108
111
|
* read: () => { throw new Error("read() not supported on this deployment"); },
|
|
109
112
|
* }),
|
package/dist/adapter/handler.js
CHANGED
|
@@ -89,14 +89,13 @@ const makeHandler = (options) => Effect.gen(function* () {
|
|
|
89
89
|
const context = yield* Effect.context();
|
|
90
90
|
const serverDir = path.join(options.dir, "server");
|
|
91
91
|
return yield* makeHandlerWith({
|
|
92
|
-
|
|
93
|
-
manifest: options.manifest,
|
|
92
|
+
server: options.server,
|
|
94
93
|
env: options.env,
|
|
95
94
|
envPrefix: options.envPrefix,
|
|
96
95
|
read: (file) => Stream.toReadableStreamWith(fs.stream(path.join(serverDir, file)), context),
|
|
97
96
|
assets: AssetSource.fileSystem({
|
|
98
97
|
dir: options.dir,
|
|
99
|
-
appPath: options.
|
|
98
|
+
appPath: options.appPath,
|
|
100
99
|
prerendered: options.prerendered
|
|
101
100
|
})
|
|
102
101
|
});
|
|
@@ -117,14 +116,13 @@ const makeHandler = (options) => Effect.gen(function* () {
|
|
|
117
116
|
* ```ts
|
|
118
117
|
* // Workers: a fetch handler with assets from env.ASSETS
|
|
119
118
|
* import { makeHandlerWith, AssetSource, toWebHandler } from "...adapter/handler";
|
|
120
|
-
* import {
|
|
121
|
-
* import { manifest } from "./manifest.js";
|
|
119
|
+
* import { server } from "./server.js";
|
|
122
120
|
*
|
|
123
121
|
* export default {
|
|
124
122
|
* fetch: (request, env, ctx) =>
|
|
125
123
|
* toWebHandler(
|
|
126
124
|
* makeHandlerWith({
|
|
127
|
-
*
|
|
125
|
+
* server, env,
|
|
128
126
|
* assets: AssetSource.cloudflareAssets(env.ASSETS),
|
|
129
127
|
* read: () => { throw new Error("read() not supported on this deployment"); },
|
|
130
128
|
* }),
|
|
@@ -134,7 +132,7 @@ const makeHandler = (options) => Effect.gen(function* () {
|
|
|
134
132
|
*/
|
|
135
133
|
const makeHandlerWith = (options) => Effect.gen(function* () {
|
|
136
134
|
const config = yield* handlerConfig(options.envPrefix ?? "").pipe(Effect.orDie);
|
|
137
|
-
const server =
|
|
135
|
+
const server = options.server;
|
|
138
136
|
yield* Effect.promise(() => server.init({
|
|
139
137
|
env: options.env ?? {},
|
|
140
138
|
read: options.read
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.js","names":[],"sources":["../../src/adapter/handler.ts"],"sourcesContent":["/**\n * Runtime handler for the Effect SvelteKit adapter.\n *\n * `makeHandler` turns a built SvelteKit app (the `Server` class and manifest\n * emitted by `vite build`) into an Effect HTTP application that serves\n * static client assets, prerendered pages, and server-side rendered routes from\n * disk — the Node default.\n *\n * {@link makeHandlerWith} is the platform-neutral core: it takes an injected\n * {@link AssetSource} (where assets come from) and a `read` capability (how to\n * read server assets), so the same SvelteKit `HttpApp` can run on a Node server\n * or on Cloudflare Workers (assets from `env.ASSETS`).\n *\n * The returned app is an ordinary Effect value: it can be served with the\n * generated `build/index.js` entry (see `./serve.js`), mounted inside any\n * existing `effect/http` HTTP server, or converted to a `fetch` handler\n * (see {@link toWebHandler}).\n */\n\nimport type { SSRManifest, Server as SvelteKitServer } from \"@sveltejs/kit\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as HttpMethod from \"effect/http/HttpMethod\";\nimport * as HttpEffect from \"effect/http/HttpEffect\";\nimport * as HttpServerRequest from \"effect/http/HttpServerRequest\";\nimport * as HttpServerResponse from \"effect/http/HttpServerResponse\";\nimport * as Path from \"effect/Path\";\nimport { Config, Context, Effect, Layer, Option, Stream } from \"effect\";\nimport { AssetSource, type FileSystemAssetServices } from \"./assets.js\";\n\nexport { AssetSource, type FileSystemAssetServices } from \"./assets.js\";\n\n/**\n * The services required by the SvelteKit HTTP application when serving assets\n * from the file system (the Node default). All of them are provided by\n * `NodeHttpServer.layer` from `@effect/platform-node`.\n */\nexport type SvelteKitHttpAppServices = FileSystemAssetServices;\n\n/**\n * The SvelteKit application as a composable Effect HTTP handler: an effect that\n * produces an {@link HttpServerResponse.HttpServerResponse} for the current\n * request. The `HttpServerRequest` is supplied per request by the server. `R`\n * is the asset source's services (defaults to the file-system services).\n */\nexport type SvelteKitHttpApp<R = SvelteKitHttpAppServices> = Effect.Effect<\n HttpServerResponse.HttpServerResponse,\n never,\n R | HttpServerRequest.HttpServerRequest\n>;\n\nexport interface SvelteKitHandlerOptions {\n /** The SvelteKit `Server` class exported by the build output (`./server/index.js`). */\n readonly Server: typeof SvelteKitServer;\n /** The SSR manifest exported by the build output (`./manifest.js`). */\n readonly manifest: SSRManifest;\n /** The set of prerendered paths exported by the build output (`./manifest.js`). */\n readonly prerendered: ReadonlySet<string>;\n /** The configured `paths.base`, exported by the build output (`./manifest.js`). */\n readonly base: string;\n /** Absolute path of the build output directory (contains `client/`, `server/`, `prerendered/`). */\n readonly dir: string;\n /** Environment variables passed to the SvelteKit server (e.g. `process.env`). */\n readonly env?: Record<string, string | undefined>;\n /** Prefix for the runtime configuration environment variables (ORIGIN, ...). */\n readonly envPrefix?: string;\n}\n\n/** Options for the platform-neutral SSR core, {@link makeHandlerWith}. */\nexport interface SvelteKitNeutralHandlerOptions<RAssets> {\n /** The SvelteKit `Server` class exported by the build output. */\n readonly Server: typeof SvelteKitServer;\n /** The SSR manifest exported by the build output. */\n readonly manifest: SSRManifest;\n /** Environment variables passed to the SvelteKit server. */\n readonly env?: Record<string, string | undefined> | undefined;\n /** Prefix for the runtime configuration environment variables (ORIGIN, ...). */\n readonly envPrefix?: string | undefined;\n /** Where static client assets and prerendered pages come from. */\n readonly assets: AssetSource<RAssets>;\n /**\n * How to read a server asset (backs `read()` from `$app/server`). On Node\n * this streams from `<dir>/server`; on Workers, back it with the platform's\n * own mechanism.\n */\n readonly read: (file: string) => ReadableStream<Uint8Array>;\n}\n\n/** Runtime configuration, read from the environment (compatible with adapter-node). */\nconst handlerConfig = (prefix: string) =>\n Config.all({\n origin: Config.option(Config.String(`${prefix}ORIGIN`)),\n protocolHeader: Config.option(Config.String(`${prefix}PROTOCOL_HEADER`)),\n hostHeader: Config.option(Config.String(`${prefix}HOST_HEADER`)),\n portHeader: Config.option(Config.String(`${prefix}PORT_HEADER`)),\n addressHeader: Config.option(Config.String(`${prefix}ADDRESS_HEADER`)),\n xffDepth: Config.Int(`${prefix}XFF_DEPTH`).pipe(Config.withDefault(1)),\n });\n\ntype HandlerConfig = Effect.Success<ReturnType<typeof handlerConfig>>;\n\n/** Compute the request origin from configuration and headers (adapter-node semantics). */\nconst originOf = (request: HttpServerRequest.HttpServerRequest, config: HandlerConfig): string => {\n if (Option.isSome(config.origin)) return config.origin.value;\n\n const headers = request.headers;\n const protocolHeader = Option.getOrUndefined(config.protocolHeader)?.toLowerCase();\n const hostHeader = Option.getOrUndefined(config.hostHeader)?.toLowerCase() ?? \"host\";\n const portHeader = Option.getOrUndefined(config.portHeader)?.toLowerCase();\n\n const protocol = (protocolHeader && headers[protocolHeader]) || \"http\";\n const host = headers[hostHeader] ?? \"localhost\";\n const port = portHeader ? headers[portHeader] : undefined;\n\n return port ? `${protocol}://${host.split(\":\")[0]}:${port}` : `${protocol}://${host}`;\n};\n\n/** Resolve the client address, honouring ADDRESS_HEADER / XFF_DEPTH if configured. */\nconst clientAddressOf = (\n request: HttpServerRequest.HttpServerRequest,\n config: HandlerConfig,\n): string => {\n const addressHeader = Option.getOrUndefined(config.addressHeader)?.toLowerCase();\n if (addressHeader) {\n const value = request.headers[addressHeader];\n if (value) {\n if (addressHeader === \"x-forwarded-for\") {\n const addresses = value.split(\",\").map((address) => address.trim());\n if (config.xffDepth < 1 || config.xffDepth > addresses.length) {\n return addresses[0] ?? \"\";\n }\n return addresses[addresses.length - config.xffDepth]!;\n }\n return value;\n }\n }\n return Option.getOrElse(request.remoteAddress, () => \"\");\n};\n\n/** Build a web `Request` for SvelteKit from an `HttpServerRequest`. */\nconst toWebRequest = (\n request: HttpServerRequest.HttpServerRequest,\n url: URL,\n context: Context.Context<never>,\n): Request => {\n // `Headers` is a redactable record-like value; copy the enumerable string\n // entries into a plain record for the web `Request` constructor.\n const init: RequestInit & { duplex?: \"half\" } = {\n method: request.method,\n headers: { ...request.headers },\n };\n if (HttpMethod.hasBody(request.method)) {\n init.body = Stream.toReadableStreamWith(request.stream, context);\n init.duplex = \"half\";\n }\n return new Request(url, init);\n};\n\n/**\n * Creates the SvelteKit HTTP handler.\n *\n * The returned effect initialises the SvelteKit server (running your app's\n * server `init` hook) and yields an HTTP handler that:\n *\n * 1. serves static client assets from `<dir>/client` (with immutable cache\n * headers for hashed build assets),\n * 2. serves prerendered pages from `<dir>/prerendered`,\n * 3. falls back to server-side rendering via the SvelteKit server.\n *\n * @example\n * ```ts\n * // Mounting SvelteKit inside an existing effect/http server:\n * import { handler } from \"./build/handler.js\";\n * import * as HttpServer from \"effect/http/HttpServer\";\n * import { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\";\n * import { Effect, Layer } from \"effect\";\n * import { createServer } from \"node:http\";\n *\n * const serverLayer = Layer.unwrap(\n * Effect.map(handler, (app) => HttpServer.serve(app))\n * ).pipe(Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })));\n *\n * NodeRuntime.runMain(Layer.launch(serverLayer));\n * ```\n */\nexport const makeHandler = (\n options: SvelteKitHandlerOptions,\n): Effect.Effect<SvelteKitHttpApp, never, SvelteKitHttpAppServices> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const context = yield* Effect.context<never>();\n\n const serverDir = path.join(options.dir, \"server\");\n\n return yield* makeHandlerWith<SvelteKitHttpAppServices>({\n Server: options.Server,\n manifest: options.manifest,\n env: options.env,\n envPrefix: options.envPrefix,\n // `read` backs the `read()` helper from `$app/server`, streaming server\n // assets from the build output.\n read: (file) => Stream.toReadableStreamWith(fs.stream(path.join(serverDir, file)), context),\n assets: AssetSource.fileSystem({\n dir: options.dir,\n appPath: options.manifest.appPath,\n prerendered: options.prerendered,\n }),\n });\n });\n\n/**\n * The platform-neutral SSR core. Like {@link makeHandler}, but the asset source\n * and the server-asset `read` capability are injected, so the same SvelteKit\n * `HttpApp` can run on Node (file-system assets) or on Cloudflare Workers\n * (`AssetSource.cloudflareAssets(env.ASSETS)`).\n *\n * The returned app:\n *\n * 1. falls through to the injected {@link AssetSource} for static client assets\n * and prerendered pages (GET/HEAD), then\n * 2. server-side renders via the SvelteKit server.\n *\n * @example\n * ```ts\n * // Workers: a fetch handler with assets from env.ASSETS\n * import { makeHandlerWith, AssetSource, toWebHandler } from \"...adapter/handler\";\n * import { Server } from \"./server/index.js\";\n * import { manifest } from \"./manifest.js\";\n *\n * export default {\n * fetch: (request, env, ctx) =>\n * toWebHandler(\n * makeHandlerWith({\n * Server, manifest, env,\n * assets: AssetSource.cloudflareAssets(env.ASSETS),\n * read: () => { throw new Error(\"read() not supported on this deployment\"); },\n * }),\n * )(request),\n * };\n * ```\n */\nexport const makeHandlerWith = <RAssets = never>(\n options: SvelteKitNeutralHandlerOptions<RAssets>,\n): Effect.Effect<SvelteKitHttpApp<RAssets>, never, never> =>\n Effect.gen(function* () {\n const config = yield* handlerConfig(options.envPrefix ?? \"\").pipe(Effect.orDie);\n\n // Initialise the SvelteKit server. `read` backs `read()` from `$app/server`.\n const server = new options.Server(options.manifest);\n yield* Effect.promise(() =>\n server.init({\n env: (options.env ?? {}) as Record<string, string>,\n read: options.read,\n }),\n );\n\n // The HTTP handler itself, evaluated once per request.\n return Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const requestContext = yield* Effect.context<never>();\n\n const origin = originOf(request, config);\n const url = new URL(request.url, origin);\n\n let pathname: string;\n try {\n pathname = decodeURIComponent(url.pathname);\n } catch {\n return HttpServerResponse.empty({ status: 400 });\n }\n\n // 1. Static client assets / prerendered pages, via the injected source.\n // Only GET/HEAD reach assets; build a body-less request for the source.\n if (request.method === \"GET\" || request.method === \"HEAD\") {\n const assetRequest = new Request(url, {\n method: request.method,\n headers: { ...request.headers },\n });\n const asset = yield* options.assets.serveAsset(assetRequest, pathname);\n if (Option.isSome(asset)) return asset.value;\n }\n\n // 2. Server-side rendering.\n const webRequest = toWebRequest(request, url, requestContext);\n const response = yield* Effect.promise(() =>\n server.respond(webRequest, {\n getClientAddress: () => clientAddressOf(request, config),\n }),\n );\n return HttpServerResponse.fromWeb(response);\n });\n });\n\n/**\n * Convert a SvelteKit `HttpApp` to a Web `fetch` handler, providing the asset\n * source's services with `layer`. Use this for a Workers `fetch` export or any\n * runtime that speaks the Web platform.\n *\n * @example\n * ```ts\n * const fetchHandler = toWebHandler(\n * makeHandlerWith({ ..., assets: AssetSource.cloudflareAssets(env.ASSETS) }),\n * Layer.empty,\n * );\n * ```\n */\nexport const toWebHandler = <RAssets, E>(\n app: Effect.Effect<SvelteKitHttpApp<RAssets>, never, never>,\n layer: Layer.Layer<RAssets, E>,\n): ((request: Request) => Promise<Response>) => {\n // Build the app (server.init) and the per-request web handler once, lazily on\n // the first request, then dispatch each request through it.\n let built: Promise<(request: Request) => Promise<Response>> | undefined;\n const build = (): Promise<(request: Request) => Promise<Response>> =>\n (built ??= Effect.runPromise(app).then(\n (inner) =>\n HttpEffect.toWebHandlerLayer(inner, layer).handler as (\n request: Request,\n ) => Promise<Response>,\n ));\n return async (request) => (await build())(request);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"handler.js","names":[],"sources":["../../src/adapter/handler.ts"],"sourcesContent":["/**\n * Runtime handler for the Effect SvelteKit adapter.\n *\n * `makeHandler` turns a built SvelteKit app (the `server` instance the adapter\n * generates with `builder.generateServerInstance`) into an Effect HTTP application that serves\n * static client assets, prerendered pages, and server-side rendered routes from\n * disk — the Node default.\n *\n * {@link makeHandlerWith} is the platform-neutral core: it takes an injected\n * {@link AssetSource} (where assets come from) and a `read` capability (how to\n * read server assets), so the same SvelteKit `HttpApp` can run on a Node server\n * or on Cloudflare Workers (assets from `env.ASSETS`).\n *\n * The returned app is an ordinary Effect value: it can be served with the\n * generated `build/index.js` entry (see `./serve.js`), mounted inside any\n * existing `effect/http` HTTP server, or converted to a `fetch` handler\n * (see {@link toWebHandler}).\n */\n\nimport type { Server as SvelteKitServer } from \"@sveltejs/kit\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as HttpMethod from \"effect/http/HttpMethod\";\nimport * as HttpEffect from \"effect/http/HttpEffect\";\nimport * as HttpServerRequest from \"effect/http/HttpServerRequest\";\nimport * as HttpServerResponse from \"effect/http/HttpServerResponse\";\nimport * as Path from \"effect/Path\";\nimport { Config, Context, Effect, Layer, Option, Stream } from \"effect\";\nimport { AssetSource, type FileSystemAssetServices } from \"./assets.js\";\n\nexport { AssetSource, type FileSystemAssetServices } from \"./assets.js\";\n\n/**\n * The services required by the SvelteKit HTTP application when serving assets\n * from the file system (the Node default). All of them are provided by\n * `NodeHttpServer.layer` from `@effect/platform-node`.\n */\nexport type SvelteKitHttpAppServices = FileSystemAssetServices;\n\n/**\n * The SvelteKit application as a composable Effect HTTP handler: an effect that\n * produces an {@link HttpServerResponse.HttpServerResponse} for the current\n * request. The `HttpServerRequest` is supplied per request by the server. `R`\n * is the asset source's services (defaults to the file-system services).\n */\nexport type SvelteKitHttpApp<R = SvelteKitHttpAppServices> = Effect.Effect<\n HttpServerResponse.HttpServerResponse,\n never,\n R | HttpServerRequest.HttpServerRequest\n>;\n\n/** The SvelteKit build output {@link makeHandler} serves. */\nexport interface SvelteKitHandlerOptions {\n /** The SvelteKit `server` instance exported by the build output (`./server.js`). */\n readonly server: SvelteKitServer;\n /**\n * The application path, including any `paths.base` without its leading\n * slash (`\"_app\"`, or `\"my-app/_app\"` under a base), exported by the build\n * output (`./manifest.js`). Its `immutable/` subtree gets immutable cache\n * headers.\n */\n readonly appPath: string;\n /** The set of prerendered paths exported by the build output (`./manifest.js`). */\n readonly prerendered: ReadonlySet<string>;\n /** The configured `paths.base`, exported by the build output (`./manifest.js`). */\n readonly base: string;\n /** Absolute path of the build output directory (contains `client/`, `server/`, `prerendered/`). */\n readonly dir: string;\n /** Environment variables passed to the SvelteKit server (e.g. `process.env`). */\n readonly env?: Record<string, string | undefined>;\n /** Prefix for the runtime configuration environment variables (ORIGIN, ...). */\n readonly envPrefix?: string;\n}\n\n/** Options for the platform-neutral SSR core, {@link makeHandlerWith}. */\nexport interface SvelteKitNeutralHandlerOptions<RAssets> {\n /** The SvelteKit `server` instance exported by the build output (`./server.js`). */\n readonly server: SvelteKitServer;\n /** Environment variables passed to the SvelteKit server. */\n readonly env?: Record<string, string | undefined> | undefined;\n /** Prefix for the runtime configuration environment variables (ORIGIN, ...). */\n readonly envPrefix?: string | undefined;\n /** Where static client assets and prerendered pages come from. */\n readonly assets: AssetSource<RAssets>;\n /**\n * How to read a server asset (backs `read()` from `$app/server`). On Node\n * this streams from `<dir>/server`; on Workers, back it with the platform's\n * own mechanism.\n */\n readonly read: (file: string) => ReadableStream<Uint8Array>;\n}\n\n/** Runtime configuration, read from the environment (compatible with adapter-node). */\nconst handlerConfig = (prefix: string) =>\n Config.all({\n origin: Config.option(Config.String(`${prefix}ORIGIN`)),\n protocolHeader: Config.option(Config.String(`${prefix}PROTOCOL_HEADER`)),\n hostHeader: Config.option(Config.String(`${prefix}HOST_HEADER`)),\n portHeader: Config.option(Config.String(`${prefix}PORT_HEADER`)),\n addressHeader: Config.option(Config.String(`${prefix}ADDRESS_HEADER`)),\n xffDepth: Config.Int(`${prefix}XFF_DEPTH`).pipe(Config.withDefault(1)),\n });\n\ntype HandlerConfig = Effect.Success<ReturnType<typeof handlerConfig>>;\n\n/** Compute the request origin from configuration and headers (adapter-node semantics). */\nconst originOf = (request: HttpServerRequest.HttpServerRequest, config: HandlerConfig): string => {\n if (Option.isSome(config.origin)) return config.origin.value;\n\n const headers = request.headers;\n const protocolHeader = Option.getOrUndefined(config.protocolHeader)?.toLowerCase();\n const hostHeader = Option.getOrUndefined(config.hostHeader)?.toLowerCase() ?? \"host\";\n const portHeader = Option.getOrUndefined(config.portHeader)?.toLowerCase();\n\n const protocol = (protocolHeader && headers[protocolHeader]) || \"http\";\n const host = headers[hostHeader] ?? \"localhost\";\n const port = portHeader ? headers[portHeader] : undefined;\n\n return port ? `${protocol}://${host.split(\":\")[0]}:${port}` : `${protocol}://${host}`;\n};\n\n/** Resolve the client address, honouring ADDRESS_HEADER / XFF_DEPTH if configured. */\nconst clientAddressOf = (\n request: HttpServerRequest.HttpServerRequest,\n config: HandlerConfig,\n): string => {\n const addressHeader = Option.getOrUndefined(config.addressHeader)?.toLowerCase();\n if (addressHeader) {\n const value = request.headers[addressHeader];\n if (value) {\n if (addressHeader === \"x-forwarded-for\") {\n const addresses = value.split(\",\").map((address) => address.trim());\n if (config.xffDepth < 1 || config.xffDepth > addresses.length) {\n return addresses[0] ?? \"\";\n }\n return addresses[addresses.length - config.xffDepth]!;\n }\n return value;\n }\n }\n return Option.getOrElse(request.remoteAddress, () => \"\");\n};\n\n/** Build a web `Request` for SvelteKit from an `HttpServerRequest`. */\nconst toWebRequest = (\n request: HttpServerRequest.HttpServerRequest,\n url: URL,\n context: Context.Context<never>,\n): Request => {\n // `Headers` is a redactable record-like value; copy the enumerable string\n // entries into a plain record for the web `Request` constructor.\n const init: RequestInit & { duplex?: \"half\" } = {\n method: request.method,\n headers: { ...request.headers },\n };\n if (HttpMethod.hasBody(request.method)) {\n init.body = Stream.toReadableStreamWith(request.stream, context);\n init.duplex = \"half\";\n }\n return new Request(url, init);\n};\n\n/**\n * Creates the SvelteKit HTTP handler.\n *\n * The returned effect initialises the SvelteKit server (running your app's\n * server `init` hook) and yields an HTTP handler that:\n *\n * 1. serves static client assets from `<dir>/client` (with immutable cache\n * headers for hashed build assets),\n * 2. serves prerendered pages from `<dir>/prerendered`,\n * 3. falls back to server-side rendering via the SvelteKit server.\n *\n * @example\n * ```ts\n * // Mounting SvelteKit inside an existing effect/http server:\n * import { handler } from \"./build/handler.js\";\n * import * as HttpServer from \"effect/http/HttpServer\";\n * import { NodeHttpServer, NodeRuntime } from \"@effect/platform-node\";\n * import { Effect, Layer } from \"effect\";\n * import { createServer } from \"node:http\";\n *\n * const serverLayer = Layer.unwrap(\n * Effect.map(handler, (app) => HttpServer.serve(app))\n * ).pipe(Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })));\n *\n * NodeRuntime.runMain(Layer.launch(serverLayer));\n * ```\n */\nexport const makeHandler = (\n options: SvelteKitHandlerOptions,\n): Effect.Effect<SvelteKitHttpApp, never, SvelteKitHttpAppServices> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const context = yield* Effect.context<never>();\n\n const serverDir = path.join(options.dir, \"server\");\n\n return yield* makeHandlerWith<SvelteKitHttpAppServices>({\n server: options.server,\n env: options.env,\n envPrefix: options.envPrefix,\n // `read` backs the `read()` helper from `$app/server`, streaming server\n // assets from the build output.\n read: (file) => Stream.toReadableStreamWith(fs.stream(path.join(serverDir, file)), context),\n assets: AssetSource.fileSystem({\n dir: options.dir,\n appPath: options.appPath,\n prerendered: options.prerendered,\n }),\n });\n });\n\n/**\n * The platform-neutral SSR core. Like {@link makeHandler}, but the asset source\n * and the server-asset `read` capability are injected, so the same SvelteKit\n * `HttpApp` can run on Node (file-system assets) or on Cloudflare Workers\n * (`AssetSource.cloudflareAssets(env.ASSETS)`).\n *\n * The returned app:\n *\n * 1. falls through to the injected {@link AssetSource} for static client assets\n * and prerendered pages (GET/HEAD), then\n * 2. server-side renders via the SvelteKit server.\n *\n * @example\n * ```ts\n * // Workers: a fetch handler with assets from env.ASSETS\n * import { makeHandlerWith, AssetSource, toWebHandler } from \"...adapter/handler\";\n * import { server } from \"./server.js\";\n *\n * export default {\n * fetch: (request, env, ctx) =>\n * toWebHandler(\n * makeHandlerWith({\n * server, env,\n * assets: AssetSource.cloudflareAssets(env.ASSETS),\n * read: () => { throw new Error(\"read() not supported on this deployment\"); },\n * }),\n * )(request),\n * };\n * ```\n */\nexport const makeHandlerWith = <RAssets = never>(\n options: SvelteKitNeutralHandlerOptions<RAssets>,\n): Effect.Effect<SvelteKitHttpApp<RAssets>, never, never> =>\n Effect.gen(function* () {\n const config = yield* handlerConfig(options.envPrefix ?? \"\").pipe(Effect.orDie);\n\n // Initialise the SvelteKit server. `read` backs `read()` from `$app/server`.\n const server = options.server;\n yield* Effect.promise(() =>\n server.init({\n env: options.env ?? {},\n read: options.read,\n }),\n );\n\n // The HTTP handler itself, evaluated once per request.\n return Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const requestContext = yield* Effect.context<never>();\n\n const origin = originOf(request, config);\n const url = new URL(request.url, origin);\n\n let pathname: string;\n try {\n pathname = decodeURIComponent(url.pathname);\n } catch {\n return HttpServerResponse.empty({ status: 400 });\n }\n\n // 1. Static client assets / prerendered pages, via the injected source.\n // Only GET/HEAD reach assets; build a body-less request for the source.\n if (request.method === \"GET\" || request.method === \"HEAD\") {\n const assetRequest = new Request(url, {\n method: request.method,\n headers: { ...request.headers },\n });\n const asset = yield* options.assets.serveAsset(assetRequest, pathname);\n if (Option.isSome(asset)) return asset.value;\n }\n\n // 2. Server-side rendering.\n const webRequest = toWebRequest(request, url, requestContext);\n const response = yield* Effect.promise(() =>\n server.respond(webRequest, {\n getClientAddress: () => clientAddressOf(request, config),\n }),\n );\n return HttpServerResponse.fromWeb(response);\n });\n });\n\n/**\n * Convert a SvelteKit `HttpApp` to a Web `fetch` handler, providing the asset\n * source's services with `layer`. Use this for a Workers `fetch` export or any\n * runtime that speaks the Web platform.\n *\n * @example\n * ```ts\n * const fetchHandler = toWebHandler(\n * makeHandlerWith({ ..., assets: AssetSource.cloudflareAssets(env.ASSETS) }),\n * Layer.empty,\n * );\n * ```\n */\nexport const toWebHandler = <RAssets, E>(\n app: Effect.Effect<SvelteKitHttpApp<RAssets>, never, never>,\n layer: Layer.Layer<RAssets, E>,\n): ((request: Request) => Promise<Response>) => {\n // Build the app (server.init) and the per-request web handler once, lazily on\n // the first request, then dispatch each request through it.\n let built: Promise<(request: Request) => Promise<Response>> | undefined;\n const build = (): Promise<(request: Request) => Promise<Response>> =>\n (built ??= Effect.runPromise(app).then(\n (inner) =>\n HttpEffect.toWebHandlerLayer(inner, layer).handler as (\n request: Request,\n ) => Promise<Response>,\n ));\n return async (request) => (await build())(request);\n};\n"],"mappings":"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"}
|
package/dist/adapter/index.d.ts
CHANGED
package/dist/adapter/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeFileSync } from "node:fs";
|
|
1
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
//#region src/adapter/index.ts
|
|
3
3
|
/**
|
|
4
4
|
* Effect deployment adapter for SvelteKit.
|
|
@@ -10,14 +10,14 @@ import { writeFileSync } from "node:fs";
|
|
|
10
10
|
* shutdown that runs all finalizers).
|
|
11
11
|
*
|
|
12
12
|
* ```js
|
|
13
|
-
* //
|
|
13
|
+
* // vite.config.ts
|
|
14
|
+
* import { sveltekit } from "@sveltejs/kit/vite";
|
|
14
15
|
* import adapter from "@thomasfosterau/effect-sveltekit/adapter";
|
|
16
|
+
* import { defineConfig } from "vite";
|
|
15
17
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* };
|
|
19
|
-
*
|
|
20
|
-
* export default config;
|
|
18
|
+
* export default defineConfig({
|
|
19
|
+
* plugins: [sveltekit({ adapter: adapter() })],
|
|
20
|
+
* });
|
|
21
21
|
* ```
|
|
22
22
|
*
|
|
23
23
|
* The build output (`build/` by default) contains:
|
|
@@ -25,7 +25,9 @@ import { writeFileSync } from "node:fs";
|
|
|
25
25
|
* - `index.js` — runs the production server: `node build`
|
|
26
26
|
* - `handler.js` — exports the SvelteKit app as a composable Effect HTTP
|
|
27
27
|
* handler, for mounting inside an existing `effect/http` server
|
|
28
|
-
* - `client/`, `server/`, `prerendered/`, `
|
|
28
|
+
* - `client/`, `server/`, `prerendered/`, `server.js` (the SvelteKit
|
|
29
|
+
* `server` instance), `manifest.js` (prerendered paths, base and app path) —
|
|
30
|
+
* the built app
|
|
29
31
|
*
|
|
30
32
|
* Unlike `adapter-node`, the server output is not bundled: the deployment
|
|
31
33
|
* needs `node_modules` with the app's production dependencies installed
|
|
@@ -39,8 +41,8 @@ import { dirname } from "node:path";
|
|
|
39
41
|
import { fileURLToPath } from "node:url";
|
|
40
42
|
import process from "node:process";
|
|
41
43
|
import { makeHandler } from "${PACKAGE}/adapter/handler";
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
+
import { server } from "./server.js";
|
|
45
|
+
import { prerendered, base, appPath } from "./manifest.js";
|
|
44
46
|
|
|
45
47
|
const dir = dirname(fileURLToPath(import.meta.url));
|
|
46
48
|
|
|
@@ -51,8 +53,8 @@ const dir = dirname(fileURLToPath(import.meta.url));
|
|
|
51
53
|
* \`effect/http\` HTTP server.
|
|
52
54
|
*/
|
|
53
55
|
export const handler = makeHandler({
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
server,
|
|
57
|
+
appPath,
|
|
56
58
|
prerendered,
|
|
57
59
|
base,
|
|
58
60
|
dir,
|
|
@@ -77,8 +79,7 @@ serve(handler, { envPrefix: ${JSON.stringify(envPrefix)} });
|
|
|
77
79
|
const fetchEntryTemplate = (envPrefix) => `\
|
|
78
80
|
import { Layer } from "effect";
|
|
79
81
|
import { makeHandlerWith, AssetSource, toWebHandler } from "${PACKAGE}/adapter/handler";
|
|
80
|
-
import {
|
|
81
|
-
import { manifest } from "./manifest.js";
|
|
82
|
+
import { server } from "./server.js";
|
|
82
83
|
|
|
83
84
|
let fetchHandler;
|
|
84
85
|
|
|
@@ -87,8 +88,7 @@ export default {
|
|
|
87
88
|
fetch(request, env, ctx) {
|
|
88
89
|
fetchHandler ??= toWebHandler(
|
|
89
90
|
makeHandlerWith({
|
|
90
|
-
|
|
91
|
-
manifest,
|
|
91
|
+
server,
|
|
92
92
|
env,
|
|
93
93
|
envPrefix: ${JSON.stringify(envPrefix)},
|
|
94
94
|
// Static assets come from the Workers ASSETS binding (configure it to
|
|
@@ -113,9 +113,12 @@ function effectAdapter(options = {}) {
|
|
|
113
113
|
return {
|
|
114
114
|
name: `${PACKAGE}/adapter`,
|
|
115
115
|
async adapt(builder) {
|
|
116
|
-
const base = builder.config.
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
const base = builder.config.paths.base;
|
|
117
|
+
rmSync(out, {
|
|
118
|
+
recursive: true,
|
|
119
|
+
force: true
|
|
120
|
+
});
|
|
121
|
+
mkdirSync(out, { recursive: true });
|
|
119
122
|
builder.log.minor("Copying assets");
|
|
120
123
|
builder.writeClient(`${out}/client${base}`);
|
|
121
124
|
builder.writePrerendered(`${out}/prerendered${base}`);
|
|
@@ -125,10 +128,11 @@ function effectAdapter(options = {}) {
|
|
|
125
128
|
}
|
|
126
129
|
builder.log.minor("Building server");
|
|
127
130
|
builder.writeServer(`${out}/server`);
|
|
131
|
+
builder.generateServerInstance(`${out}/server.js`, { serverDirectory: `${out}/server` });
|
|
128
132
|
writeFileSync(`${out}/manifest.js`, [
|
|
129
|
-
`export const manifest = ${builder.generateManifest({ relativePath: "./server" })};`,
|
|
130
133
|
`export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});`,
|
|
131
|
-
`export const base = ${JSON.stringify(base)}
|
|
134
|
+
`export const base = ${JSON.stringify(base)};`,
|
|
135
|
+
`export const appPath = ${JSON.stringify(builder.getAppPath())};`
|
|
132
136
|
].join("\n\n"));
|
|
133
137
|
builder.log.minor("Writing server entry");
|
|
134
138
|
writeFileSync(`${out}/handler.js`, handlerTemplate(envPrefix));
|