@thomasfosterau/effect-sveltekit 0.0.0 → 0.1.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.
@@ -1,37 +1,37 @@
1
1
  import { SvelteKitRuntime } from "./runtime.js";
2
+ import { isBareHandler as isBareHandler$1, toStandardValidator } from "./internal/remote-validators.js";
3
+ import { defaultTranslate, makeRunHelpers } from "./internal/remote-run.js";
2
4
  import "effect";
3
5
  import "effect/unstable/http/HttpClient";
4
6
  import * as AppServer from "$app/server";
5
7
  //#region src/remote.server.ts
6
- function runRemoteEffect(effect) {
8
+ /** The default helpers: a fresh SvelteKit per-request runtime, passthrough translation. */
9
+ const defaultHelpers = makeRunHelpers(() => {
7
10
  const event = AppServer.getRequestEvent();
8
- return SvelteKitRuntime.forRequest(event).runPromise(effect);
9
- }
11
+ return { runExit: (effect) => SvelteKitRuntime.forRequest(event).runPromiseExit(effect) };
12
+ }, defaultTranslate);
13
+ /** Build helpers from a {@link RequestRuntimeProvider}, translator, and form-error mapper. */
14
+ const helpersFromProvider = (provider, translate, formError) => makeRunHelpers(() => {
15
+ const event = AppServer.getRequestEvent();
16
+ return {
17
+ runExit: (effect) => provider.run(event).runPromiseExit(effect),
18
+ dispose: () => provider.disposeAfterResponse?.(event)
19
+ };
20
+ }, translate, formError);
10
21
  function unavailable(name) {
11
22
  throw new Error(`${name} is not available in the installed SvelteKit version. Update @sveltejs/kit to use this API.`);
12
23
  }
13
- /**
14
- * Detects a Standard Schema validator via its `~standard` marker property.
15
- *
16
- * This cannot be a simple `typeof value === "function"` check on the handler
17
- * position: some Standard Schema implementations (such as Effect's
18
- * `Schema.toStandardSchemaV1`) return callable class values, which would be
19
- * mistaken for handler functions.
20
- */
21
- const isStandardSchema = (value) => value !== null && (typeof value === "object" || typeof value === "function") && "~standard" in value;
22
- /** True when the argument is the no-validation handler overload. */
23
- const isBareHandler = (value) => typeof value === "function" && !isStandardSchema(value);
24
- function query(arg1, arg2) {
24
+ /** True when the argument is the no-validation handler overload (typed narrowing). */
25
+ const isBareHandler = (value) => isBareHandler$1(value);
26
+ const makeQuery = (run) => ((arg1, arg2) => {
25
27
  if (isBareHandler(arg1)) {
26
28
  const handler = arg1;
27
- return AppServer.query(() => runRemoteEffect(handler()));
29
+ return AppServer.query(() => run(handler()));
28
30
  }
29
31
  if (!arg2) throw new Error("query requires a handler function when a schema is provided");
30
- return AppServer.query(arg1, (arg) => runRemoteEffect(arg2(arg)));
31
- }
32
- function queryBatch(validate, fn) {
33
- return AppServer.query.batch(validate, (args) => runRemoteEffect(fn(args)));
34
- }
32
+ return AppServer.query(toStandardValidator(arg1), (arg) => run(arg2(arg)));
33
+ });
34
+ const makeQueryBatch = (run) => ((validate, fn) => AppServer.query.batch(toStandardValidator(validate), (args) => run(fn(args))));
35
35
  const appServerCompat = AppServer;
36
36
  const queryCompat = AppServer.query;
37
37
  function getQueryLive() {
@@ -44,45 +44,100 @@ function getRequested() {
44
44
  if (typeof requestedFn !== "function") unavailable("requested");
45
45
  return requestedFn;
46
46
  }
47
- function queryLive(validate, fn) {
48
- return getQueryLive()(validate, (arg) => runRemoteEffect(fn(arg)));
49
- }
50
- function form(arg1, arg2) {
47
+ const makeQueryLive = (run) => ((validate, fn) => {
48
+ return getQueryLive()(toStandardValidator(validate), (arg) => run(fn(arg)));
49
+ });
50
+ const makeForm = (helpers) => ((arg1, arg2) => {
51
51
  if (isBareHandler(arg1)) {
52
52
  const handler = arg1;
53
- return AppServer.form(() => runRemoteEffect(handler()));
53
+ return AppServer.form(() => helpers.run(handler()));
54
54
  }
55
55
  if (!arg2) throw new Error("form requires a handler function when a schema is provided");
56
- return AppServer.form(arg1, (data, issue) => runRemoteEffect(arg2(data, issue)));
57
- }
58
- function command(arg1, arg2) {
56
+ return AppServer.form(toStandardValidator(arg1), (data, issue) => helpers.runForm(arg2(data, issue), {
57
+ issue,
58
+ data
59
+ }));
60
+ });
61
+ const makeCommand = (run) => ((arg1, arg2) => {
59
62
  if (isBareHandler(arg1)) {
60
63
  const handler = arg1;
61
- return AppServer.command(() => runRemoteEffect(handler()));
64
+ return AppServer.command(() => run(handler()));
62
65
  }
63
66
  if (!arg2) throw new Error("command requires a handler function when a schema is provided");
64
- return AppServer.command(arg1, (arg) => runRemoteEffect(arg2(arg)));
65
- }
66
- function prerender(arg1, arg2, arg3) {
67
+ return AppServer.command(toStandardValidator(arg1), (arg) => run(arg2(arg)));
68
+ });
69
+ const makePrerender = (run) => ((arg1, arg2, arg3) => {
67
70
  if (isBareHandler(arg1)) {
68
71
  const handler = arg1;
69
- return AppServer.prerender(() => runRemoteEffect(handler()), arg2);
72
+ return AppServer.prerender(() => run(handler()), arg2);
70
73
  }
71
74
  if (typeof arg2 !== "function") throw new Error("prerender requires a handler function when a schema is provided");
72
- return AppServer.prerender(arg1, (arg) => runRemoteEffect(arg2(arg)), arg3);
73
- }
75
+ return AppServer.prerender(toStandardValidator(arg1), (arg) => run(arg2(arg)), arg3);
76
+ });
74
77
  function requested(...args) {
75
78
  return getRequested()(...args);
76
79
  }
80
+ const makeQueryNamespace = (run) => Object.assign(makeQuery(run), {
81
+ batch: makeQueryBatch(run),
82
+ live: makeQueryLive(run)
83
+ });
84
+ const query = makeQueryNamespace(defaultHelpers.run);
85
+ query.batch;
86
+ query.live;
87
+ const form = makeForm(defaultHelpers);
88
+ const command = makeCommand(defaultHelpers.run);
89
+ const prerender = makePrerender(defaultHelpers.run);
90
+ /**
91
+ * Bind the remote-function helpers to a {@link RequestRuntimeProvider} (and an
92
+ * optional boundary error translator), so their handlers run on the provider's
93
+ * services `Env` — your own application catalogue — instead of the fixed
94
+ * {@link SvelteKitServices}. The Standard-Schema / `"unchecked"` validation
95
+ * argument and the native return types (`RemoteQueryFunction`, `RemoteForm`,
96
+ * ...) are exactly as the standalone helpers.
97
+ *
98
+ * For remote forms, the failure path runs `opts.formError` when provided —
99
+ * mapping a structured/nested error shape into SvelteKit's field issues (via the
100
+ * `issue` helper / `invalid()`) so a domain validation failure drives
101
+ * field-level invalidation rather than a generic failure. Otherwise the form
102
+ * falls back to `translateError`.
103
+ *
104
+ * @example
105
+ * ```ts
106
+ * const { query, form, command } = RemoteFunction.withRuntime(
107
+ * RequestRuntime.fromLocals((event) => event.locals.runtime),
108
+ * {
109
+ * translateError: toSvelteKit,
110
+ * formError: (error, { issue }) => {
111
+ * if (error instanceof ValidationFailure) issue.field(error.field, error.message);
112
+ * invalid();
113
+ * },
114
+ * },
115
+ * );
116
+ *
117
+ * export const items = query(() =>
118
+ * Effect.gen(function* () {
119
+ * const db = yield* Database;
120
+ * return yield* db.list();
121
+ * }),
122
+ * );
123
+ * ```
124
+ */
125
+ function withRuntime(provider, opts) {
126
+ const helpers = helpersFromProvider(provider, opts?.translateError ?? defaultTranslate, opts?.formError);
127
+ return {
128
+ query: makeQueryNamespace(helpers.run),
129
+ form: makeForm(helpers),
130
+ command: makeCommand(helpers.run),
131
+ prerender: makePrerender(helpers.run)
132
+ };
133
+ }
77
134
  const RemoteFunction = {
78
- query: Object.assign(query, {
79
- batch: queryBatch,
80
- live: queryLive
81
- }),
135
+ query,
82
136
  form,
83
137
  command,
84
138
  prerender,
85
- requested
139
+ requested,
140
+ withRuntime
86
141
  };
87
142
  //#endregion
88
143
  export { RemoteFunction };
@@ -1 +1 @@
1
- {"version":3,"file":"remote.server.js","names":[],"sources":["../src/remote.server.ts"],"sourcesContent":["import * as AppServer from \"$app/server\";\nimport type {\n RemoteCommand,\n RemoteForm,\n RemoteFormInput,\n RemotePrerenderFunction,\n RemoteQueryFunction,\n InvalidField,\n} from \"@sveltejs/kit\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport { Effect } from \"effect\";\nimport { SvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\n\ntype SvelteKitEffectEnv = HttpClient.HttpClient | SvelteKitRequestEvent;\n\nfunction runRemoteEffect<A, E, R extends SvelteKitEffectEnv>(\n effect: Effect.Effect<A, E, R>,\n): Promise<A> {\n const event = AppServer.getRequestEvent();\n const runtime = SvelteKitRuntime.forRequest(event);\n return runtime.runPromise(effect);\n}\n\nfunction unavailable(name: string): never {\n throw new Error(\n `${name} is not available in the installed SvelteKit version. Update @sveltejs/kit to use this API.`,\n );\n}\n\ntype UnknownEffect = Effect.Effect<unknown, unknown, SvelteKitEffectEnv>;\ntype EffectHandler = (arg: unknown) => UnknownEffect;\ntype EffectFormHandler = (data: unknown, issue: unknown) => UnknownEffect;\ntype EffectVoidHandler = () => UnknownEffect;\n\n/**\n * Detects a Standard Schema validator via its `~standard` marker property.\n *\n * This cannot be a simple `typeof value === \"function\"` check on the handler\n * position: some Standard Schema implementations (such as Effect's\n * `Schema.toStandardSchemaV1`) return callable class values, which would be\n * mistaken for handler functions.\n */\nconst isStandardSchema = (value: unknown): value is StandardSchemaV1 =>\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n \"~standard\" in value;\n\n/** True when the argument is the no-validation handler overload. */\nconst isBareHandler = (value: unknown): value is EffectVoidHandler =>\n typeof value === \"function\" && !isStandardSchema(value);\n\nexport function query<Output>(\n fn: () => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n): RemoteQueryFunction<void, Output>;\nexport function query<Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n): RemoteQueryFunction<Input, Output>;\nexport function query<Schema extends StandardSchemaV1, Output>(\n schema: Schema,\n fn: (\n arg: StandardSchemaV1.InferOutput<Schema>,\n ) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n): RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output>;\nexport function query(\n arg1: unknown,\n arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, SvelteKitEffectEnv>,\n): any {\n if (isBareHandler(arg1)) {\n const handler = arg1;\n return AppServer.query(() => runRemoteEffect(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"query requires a handler function when a schema is provided\");\n }\n\n return AppServer.query(arg1 as \"unchecked\", (arg: unknown) => runRemoteEffect(arg2(arg)));\n}\n\nexport function queryBatch<Input, Output>(\n validate: \"unchecked\",\n fn: (\n args: Input[],\n ) => Effect.Effect<(arg: Input, idx: number) => Output, unknown, SvelteKitEffectEnv>,\n): RemoteQueryFunction<Input, Output>;\nexport function queryBatch<Schema extends StandardSchemaV1, Output>(\n schema: Schema,\n fn: (\n args: StandardSchemaV1.InferOutput<Schema>[],\n ) => Effect.Effect<\n (arg: StandardSchemaV1.InferOutput<Schema>, idx: number) => Output,\n unknown,\n SvelteKitEffectEnv\n >,\n): RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output>;\nexport function queryBatch(\n validate: unknown,\n fn: (\n args: unknown[],\n ) => Effect.Effect<(arg: unknown, idx: number) => unknown, unknown, SvelteKitEffectEnv>,\n): any {\n return AppServer.query.batch(validate as \"unchecked\", (args: unknown[]) =>\n runRemoteEffect(fn(args)),\n );\n}\n\ntype QueryLiveFactory = (\n validate: unknown,\n fn: (arg: unknown) => Promise<AsyncIterable<unknown>> | AsyncIterable<unknown>,\n) => unknown;\n\ntype QueryWithCompat = typeof AppServer.query & {\n live?: QueryLiveFactory;\n};\n\ntype AppServerWithCompat = typeof AppServer & {\n requested?: (...args: unknown[]) => unknown;\n};\n\nconst appServerCompat = AppServer as AppServerWithCompat;\nconst queryCompat = AppServer.query as QueryWithCompat;\n\nfunction getQueryLive(): QueryLiveFactory {\n const live = queryCompat.live;\n\n if (typeof live !== \"function\") {\n unavailable(\"query.live\");\n }\n\n return live;\n}\n\nfunction getRequested(): (...args: unknown[]) => unknown {\n const requestedFn = appServerCompat.requested;\n\n if (typeof requestedFn !== \"function\") {\n unavailable(\"requested\");\n }\n\n return requestedFn;\n}\n\nexport function queryLive<Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<AsyncIterable<Output>, unknown, SvelteKitEffectEnv>,\n): RemoteQueryFunction<Input, Output>;\nexport function queryLive<Schema extends StandardSchemaV1, Output>(\n schema: Schema,\n fn: (\n arg: StandardSchemaV1.InferOutput<Schema>,\n ) => Effect.Effect<AsyncIterable<Output>, unknown, SvelteKitEffectEnv>,\n): RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output>;\nexport function queryLive(\n validate: unknown,\n fn: (arg: unknown) => Effect.Effect<AsyncIterable<unknown>, unknown, SvelteKitEffectEnv>,\n): any {\n const live = getQueryLive();\n return live(validate, (arg) => runRemoteEffect(fn(arg)));\n}\n\nexport function form<Output>(\n fn: () => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n): RemoteForm<void, Output>;\nexport function form<Input extends RemoteFormInput, Output>(\n validate: \"unchecked\",\n fn: (\n data: Input,\n issue: InvalidField<Input>,\n ) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n): RemoteForm<Input, Output>;\nexport function form<Schema extends StandardSchemaV1<RemoteFormInput, Record<string, any>>, Output>(\n validate: Schema,\n fn: (\n data: StandardSchemaV1.InferOutput<Schema>,\n issue: InvalidField<StandardSchemaV1.InferInput<Schema>>,\n ) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n): RemoteForm<StandardSchemaV1.InferInput<Schema>, Output>;\nexport function form(arg1: unknown, arg2?: unknown): any {\n if (isBareHandler(arg1)) {\n const handler = arg1;\n return AppServer.form(() => runRemoteEffect(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"form requires a handler function when a schema is provided\");\n }\n\n return AppServer.form(arg1 as \"unchecked\", (data: unknown, issue: unknown) =>\n runRemoteEffect((arg2 as EffectFormHandler)(data, issue)),\n );\n}\n\nexport function command<Output>(\n fn: () => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n): RemoteCommand<void, Output>;\nexport function command<Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n): RemoteCommand<Input, Output>;\nexport function command<Schema extends StandardSchemaV1, Output>(\n validate: Schema,\n fn: (\n arg: StandardSchemaV1.InferOutput<Schema>,\n ) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n): RemoteCommand<StandardSchemaV1.InferInput<Schema>, Output>;\nexport function command(\n arg1: unknown,\n arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, SvelteKitEffectEnv>,\n): any {\n if (isBareHandler(arg1)) {\n const handler = arg1;\n return AppServer.command(() => runRemoteEffect(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"command requires a handler function when a schema is provided\");\n }\n\n return AppServer.command(arg1 as \"unchecked\", (arg: unknown) => runRemoteEffect(arg2(arg)));\n}\n\nexport function prerender<Output>(\n fn: () => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n options?: {\n inputs?: () => Promise<void[]> | void[];\n dynamic?: boolean;\n },\n): RemotePrerenderFunction<void, Output>;\nexport function prerender<Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n options?: {\n inputs?: () => Promise<Input[]> | Input[];\n dynamic?: boolean;\n },\n): RemotePrerenderFunction<Input, Output>;\nexport function prerender<Schema extends StandardSchemaV1, Output>(\n schema: Schema,\n fn: (\n arg: StandardSchemaV1.InferOutput<Schema>,\n ) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n options?: {\n inputs?: () =>\n | Promise<StandardSchemaV1.InferInput<Schema>[]>\n | StandardSchemaV1.InferInput<Schema>[];\n dynamic?: boolean;\n },\n): RemotePrerenderFunction<StandardSchemaV1.InferInput<Schema>, Output>;\nexport function prerender(\n arg1: unknown,\n arg2?:\n | ((arg: unknown) => Effect.Effect<unknown, unknown, SvelteKitEffectEnv>)\n | {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n arg3?: {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n): any {\n if (isBareHandler(arg1)) {\n const handler = arg1;\n return AppServer.prerender(\n () => runRemoteEffect(handler()),\n arg2 as { inputs?: () => Promise<void[]> | void[]; dynamic?: boolean } | undefined,\n );\n }\n\n if (typeof arg2 !== \"function\") {\n throw new Error(\"prerender requires a handler function when a schema is provided\");\n }\n\n return AppServer.prerender(\n arg1 as \"unchecked\",\n (arg: unknown) => runRemoteEffect((arg2 as EffectHandler)(arg)),\n arg3,\n );\n}\n\nexport function requested<Result = unknown>(...args: unknown[]): Result {\n const requestedFn = getRequested();\n return requestedFn(...args) as Result;\n}\n\ntype QueryNamespace = {\n <Output>(\n fn: () => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n ): RemoteQueryFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n ): RemoteQueryFunction<Input, Output>;\n <Schema extends StandardSchemaV1, Output>(\n schema: Schema,\n fn: (\n arg: StandardSchemaV1.InferOutput<Schema>,\n ) => Effect.Effect<Output, unknown, SvelteKitEffectEnv>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, Output>;\n batch: typeof queryBatch;\n live: typeof queryLive;\n};\n\nconst queryWithHelpers = Object.assign(query, {\n batch: queryBatch,\n live: queryLive,\n}) as QueryNamespace;\n\nexport const RemoteFunction = {\n query: queryWithHelpers,\n form,\n command,\n prerender,\n requested,\n};\n"],"mappings":";;;;;AAgBA,SAAS,gBACP,QACY;CACZ,MAAM,QAAQ,UAAU,gBAAgB;CAExC,OADgB,iBAAiB,WAAW,KAC/B,EAAE,WAAW,MAAM;AAClC;AAEA,SAAS,YAAY,MAAqB;CACxC,MAAM,IAAI,MACR,GAAG,KAAK,4FACV;AACF;;;;;;;;;AAeA,MAAM,oBAAoB,UACxB,UAAU,SACT,OAAO,UAAU,YAAY,OAAO,UAAU,eAC/C,eAAe;;AAGjB,MAAM,iBAAiB,UACrB,OAAO,UAAU,cAAc,CAAC,iBAAiB,KAAK;AAexD,SAAgB,MACd,MACA,MACK;CACL,IAAI,cAAc,IAAI,GAAG;EACvB,MAAM,UAAU;EAChB,OAAO,UAAU,YAAY,gBAAgB,QAAQ,CAAC,CAAC;CACzD;CAEA,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,6DAA6D;CAG/E,OAAO,UAAU,MAAM,OAAsB,QAAiB,gBAAgB,KAAK,GAAG,CAAC,CAAC;AAC1F;AAkBA,SAAgB,WACd,UACA,IAGK;CACL,OAAO,UAAU,MAAM,MAAM,WAA0B,SACrD,gBAAgB,GAAG,IAAI,CAAC,CAC1B;AACF;AAeA,MAAM,kBAAkB;AACxB,MAAM,cAAc,UAAU;AAE9B,SAAS,eAAiC;CACxC,MAAM,OAAO,YAAY;CAEzB,IAAI,OAAO,SAAS,YAClB,YAAY,YAAY;CAG1B,OAAO;AACT;AAEA,SAAS,eAAgD;CACvD,MAAM,cAAc,gBAAgB;CAEpC,IAAI,OAAO,gBAAgB,YACzB,YAAY,WAAW;CAGzB,OAAO;AACT;AAYA,SAAgB,UACd,UACA,IACK;CAEL,OADa,aACH,EAAE,WAAW,QAAQ,gBAAgB,GAAG,GAAG,CAAC,CAAC;AACzD;AAmBA,SAAgB,KAAK,MAAe,MAAqB;CACvD,IAAI,cAAc,IAAI,GAAG;EACvB,MAAM,UAAU;EAChB,OAAO,UAAU,WAAW,gBAAgB,QAAQ,CAAC,CAAC;CACxD;CAEA,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,4DAA4D;CAG9E,OAAO,UAAU,KAAK,OAAsB,MAAe,UACzD,gBAAiB,KAA2B,MAAM,KAAK,CAAC,CAC1D;AACF;AAeA,SAAgB,QACd,MACA,MACK;CACL,IAAI,cAAc,IAAI,GAAG;EACvB,MAAM,UAAU;EAChB,OAAO,UAAU,cAAc,gBAAgB,QAAQ,CAAC,CAAC;CAC3D;CAEA,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,+DAA+D;CAGjF,OAAO,UAAU,QAAQ,OAAsB,QAAiB,gBAAgB,KAAK,GAAG,CAAC,CAAC;AAC5F;AA6BA,SAAgB,UACd,MACA,MAMA,MAIK;CACL,IAAI,cAAc,IAAI,GAAG;EACvB,MAAM,UAAU;EAChB,OAAO,UAAU,gBACT,gBAAgB,QAAQ,CAAC,GAC/B,IACF;CACF;CAEA,IAAI,OAAO,SAAS,YAClB,MAAM,IAAI,MAAM,iEAAiE;CAGnF,OAAO,UAAU,UACf,OACC,QAAiB,gBAAiB,KAAuB,GAAG,CAAC,GAC9D,IACF;AACF;AAEA,SAAgB,UAA4B,GAAG,MAAyB;CAEtE,OADoB,aACH,EAAE,GAAG,IAAI;AAC5B;AAyBA,MAAa,iBAAiB;CAC5B,OANuB,OAAO,OAAO,OAAO;EAC5C,OAAO;EACP,MAAM;CACR,CAGS;CACP;CACA;CACA;CACA;AACF"}
1
+ {"version":3,"file":"remote.server.js","names":["isBareHandlerValue"],"sources":["../src/remote.server.ts"],"sourcesContent":["import * as AppServer from \"$app/server\";\nimport type {\n RemoteCommand,\n RemoteForm,\n RemoteFormInput,\n RemotePrerenderFunction,\n RemoteQueryFunction,\n InvalidField,\n} from \"@sveltejs/kit\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport { Effect, type Schema } from \"effect\";\nimport { SvelteKitRuntime, SvelteKitRequestEvent } from \"./runtime.js\";\nimport {\n isBareHandler as isBareHandlerValue,\n toStandardValidator,\n} from \"./internal/remote-validators.js\";\nimport {\n defaultTranslate,\n makeRunHelpers,\n type FormErrorMapper,\n type RunHelpers,\n type Runner,\n} from \"./internal/remote-run.js\";\nimport type { ErrorTranslator, RequestRuntimeProvider } from \"./request-runtime.js\";\n\nexport type { FormErrorMapper } from \"./internal/remote-run.js\";\n\ntype SvelteKitEffectEnv = HttpClient.HttpClient | SvelteKitRequestEvent;\n\n/** The default helpers: a fresh SvelteKit per-request runtime, passthrough translation. */\nconst defaultHelpers: RunHelpers = makeRunHelpers(() => {\n const event = AppServer.getRequestEvent();\n return { runExit: (effect) => SvelteKitRuntime.forRequest(event).runPromiseExit(effect) };\n}, defaultTranslate);\n\n/** Build helpers from a {@link RequestRuntimeProvider}, translator, and form-error mapper. */\nconst helpersFromProvider = (\n provider: RequestRuntimeProvider<any, any>,\n translate: ErrorTranslator,\n formError?: FormErrorMapper,\n): RunHelpers =>\n makeRunHelpers(\n () => {\n const event = AppServer.getRequestEvent();\n return {\n runExit: (effect) => provider.run(event).runPromiseExit(effect),\n dispose: () => provider.disposeAfterResponse?.(event),\n };\n },\n translate,\n formError,\n );\n\nfunction unavailable(name: string): never {\n throw new Error(\n `${name} is not available in the installed SvelteKit version. Update @sveltejs/kit to use this API.`,\n );\n}\n\ntype EffectHandler<Env> = (arg: unknown) => Effect.Effect<unknown, unknown, Env>;\ntype EffectFormHandler<Env> = (\n data: unknown,\n issue: unknown,\n) => Effect.Effect<unknown, unknown, Env>;\ntype EffectVoidHandler<Env> = () => Effect.Effect<unknown, unknown, Env>;\n\n/** True when the argument is the no-validation handler overload (typed narrowing). */\nconst isBareHandler = <Env>(value: unknown): value is EffectVoidHandler<Env> =>\n isBareHandlerValue(value);\n\n/** The public shape of the `query` helper, parameterised by its handler env. */\nexport interface QueryFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteQueryFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQuery = <Env>(run: Runner): QueryFn<Env> =>\n ((arg1: unknown, arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, Env>): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.query(() => run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"query requires a handler function when a schema is provided\");\n }\n\n return AppServer.query(toStandardValidator(arg1) as \"unchecked\", (arg: unknown) =>\n run(arg2(arg)),\n );\n }) as QueryFn<Env>;\n\nexport interface QueryBatchFn<Env> {\n <Input, Output>(\n validate: \"unchecked\",\n fn: (args: Input[]) => Effect.Effect<(arg: Input, idx: number) => Output, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (\n args: StandardSchemaV1.InferOutput<Sch>[],\n ) => Effect.Effect<\n (arg: StandardSchemaV1.InferOutput<Sch>, idx: number) => Output,\n unknown,\n Env\n >,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (args: S[\"Type\"][]) => Effect.Effect<(arg: S[\"Type\"], idx: number) => Output, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQueryBatch = <Env>(run: Runner): QueryBatchFn<Env> =>\n ((\n validate: unknown,\n fn: (args: unknown[]) => Effect.Effect<(arg: unknown, idx: number) => unknown, unknown, Env>,\n ): any =>\n AppServer.query.batch(toStandardValidator(validate) as \"unchecked\", (args: unknown[]) =>\n run(fn(args)),\n )) as QueryBatchFn<Env>;\n\ntype QueryLiveFactory = (\n validate: unknown,\n fn: (arg: unknown) => Promise<AsyncIterable<unknown>> | AsyncIterable<unknown>,\n) => unknown;\n\ntype QueryWithCompat = typeof AppServer.query & {\n live?: QueryLiveFactory;\n};\n\ntype AppServerWithCompat = typeof AppServer & {\n requested?: (...args: unknown[]) => unknown;\n};\n\nconst appServerCompat = AppServer as AppServerWithCompat;\nconst queryCompat = AppServer.query as QueryWithCompat;\n\nfunction getQueryLive(): QueryLiveFactory {\n const live = queryCompat.live;\n\n if (typeof live !== \"function\") {\n unavailable(\"query.live\");\n }\n\n return live;\n}\n\nfunction getRequested(): (...args: unknown[]) => unknown {\n const requestedFn = appServerCompat.requested;\n\n if (typeof requestedFn !== \"function\") {\n unavailable(\"requested\");\n }\n\n return requestedFn;\n}\n\nexport interface QueryLiveFn<Env> {\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (\n arg: StandardSchemaV1.InferOutput<Sch>,\n ) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<AsyncIterable<Output>, unknown, Env>,\n ): RemoteQueryFunction<S[\"Encoded\"], Output>;\n}\n\nconst makeQueryLive = <Env>(run: Runner): QueryLiveFn<Env> =>\n ((\n validate: unknown,\n fn: (arg: unknown) => Effect.Effect<AsyncIterable<unknown>, unknown, Env>,\n ): any => {\n const live = getQueryLive();\n return live(toStandardValidator(validate), (arg) => run(fn(arg)));\n }) as QueryLiveFn<Env>;\n\nexport interface FormFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteForm<void, Output>;\n <Input extends RemoteFormInput, Output>(\n validate: \"unchecked\",\n fn: (data: Input, issue: InvalidField<Input>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<Input, Output>;\n <Sch extends StandardSchemaV1<RemoteFormInput, Record<string, any>>, Output>(\n validate: Sch,\n fn: (\n data: StandardSchemaV1.InferOutput<Sch>,\n issue: InvalidField<StandardSchemaV1.InferInput<Sch>>,\n ) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top & { readonly Encoded: RemoteFormInput }, Output>(\n validate: S,\n fn: (data: S[\"Type\"], issue: InvalidField<S[\"Encoded\"]>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteForm<S[\"Encoded\"], Output>;\n}\n\nconst makeForm = <Env>(helpers: RunHelpers): FormFn<Env> =>\n ((arg1: unknown, arg2?: unknown): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.form(() => helpers.run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"form requires a handler function when a schema is provided\");\n }\n\n return AppServer.form(\n toStandardValidator(arg1) as \"unchecked\",\n (data: unknown, issue: unknown) =>\n helpers.runForm((arg2 as EffectFormHandler<Env>)(data, issue), {\n issue: issue as InvalidField<any>,\n data,\n }),\n );\n }) as FormFn<Env>;\n\nexport interface CommandFn<Env> {\n <Output>(fn: () => Effect.Effect<Output, unknown, Env>): RemoteCommand<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n validate: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n validate: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n ): RemoteCommand<S[\"Encoded\"], Output>;\n}\n\nconst makeCommand = <Env>(run: Runner): CommandFn<Env> =>\n ((arg1: unknown, arg2?: (arg: unknown) => Effect.Effect<unknown, unknown, Env>): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.command(() => run(handler()));\n }\n\n if (!arg2) {\n throw new Error(\"command requires a handler function when a schema is provided\");\n }\n\n return AppServer.command(toStandardValidator(arg1) as \"unchecked\", (arg: unknown) =>\n run(arg2(arg)),\n );\n }) as CommandFn<Env>;\n\nexport interface PrerenderFn<Env> {\n <Output>(\n fn: () => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<void[]> | void[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<void, Output>;\n <Input, Output>(\n validate: \"unchecked\",\n fn: (arg: Input) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<Input[]> | Input[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<Input, Output>;\n <Sch extends StandardSchemaV1, Output>(\n schema: Sch,\n fn: (arg: StandardSchemaV1.InferOutput<Sch>) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () =>\n | Promise<StandardSchemaV1.InferInput<Sch>[]>\n | StandardSchemaV1.InferInput<Sch>[];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<StandardSchemaV1.InferInput<Sch>, Output>;\n <S extends Schema.Top, Output>(\n schema: S,\n fn: (arg: S[\"Type\"]) => Effect.Effect<Output, unknown, Env>,\n options?: {\n inputs?: () => Promise<S[\"Encoded\"][]> | S[\"Encoded\"][];\n dynamic?: boolean;\n },\n ): RemotePrerenderFunction<S[\"Encoded\"], Output>;\n}\n\nconst makePrerender = <Env>(run: Runner): PrerenderFn<Env> =>\n ((\n arg1: unknown,\n arg2?:\n | ((arg: unknown) => Effect.Effect<unknown, unknown, Env>)\n | {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n arg3?: {\n inputs?: () => Promise<unknown[]> | unknown[];\n dynamic?: boolean;\n },\n ): any => {\n if (isBareHandler<Env>(arg1)) {\n const handler = arg1;\n return AppServer.prerender(\n () => run(handler()),\n arg2 as { inputs?: () => Promise<void[]> | void[]; dynamic?: boolean } | undefined,\n );\n }\n\n if (typeof arg2 !== \"function\") {\n throw new Error(\"prerender requires a handler function when a schema is provided\");\n }\n\n return AppServer.prerender(\n toStandardValidator(arg1) as \"unchecked\",\n (arg: unknown) => run((arg2 as EffectHandler<Env>)(arg)),\n arg3,\n );\n }) as PrerenderFn<Env>;\n\nexport function requested<Result = unknown>(...args: unknown[]): Result {\n const requestedFn = getRequested();\n return requestedFn(...args) as Result;\n}\n\n/** A `query` with its `batch` / `live` helpers attached. */\nexport type QueryNamespace<Env> = QueryFn<Env> & {\n batch: QueryBatchFn<Env>;\n live: QueryLiveFn<Env>;\n};\n\nconst makeQueryNamespace = <Env>(run: Runner): QueryNamespace<Env> =>\n Object.assign(makeQuery<Env>(run), {\n batch: makeQueryBatch<Env>(run),\n live: makeQueryLive<Env>(run),\n }) as QueryNamespace<Env>;\n\n// ── Standalone exports (default SvelteKit per-request runtime) ──────────────\n\nexport const query: QueryNamespace<SvelteKitEffectEnv> = makeQueryNamespace(defaultHelpers.run);\nexport const queryBatch: QueryBatchFn<SvelteKitEffectEnv> = query.batch;\nexport const queryLive: QueryLiveFn<SvelteKitEffectEnv> = query.live;\nexport const form: FormFn<SvelteKitEffectEnv> = makeForm(defaultHelpers);\nexport const command: CommandFn<SvelteKitEffectEnv> = makeCommand(defaultHelpers.run);\nexport const prerender: PrerenderFn<SvelteKitEffectEnv> = makePrerender(defaultHelpers.run);\n\n/** The set of remote-function helpers bound to a particular runner. */\nexport interface RemoteFunctions<Env> {\n readonly query: QueryNamespace<Env>;\n readonly form: FormFn<Env>;\n readonly command: CommandFn<Env>;\n readonly prerender: PrerenderFn<Env>;\n}\n\n/**\n * Bind the remote-function helpers to a {@link RequestRuntimeProvider} (and an\n * optional boundary error translator), so their handlers run on the provider's\n * services `Env` — your own application catalogue — instead of the fixed\n * {@link SvelteKitServices}. The Standard-Schema / `\"unchecked\"` validation\n * argument and the native return types (`RemoteQueryFunction`, `RemoteForm`,\n * ...) are exactly as the standalone helpers.\n *\n * For remote forms, the failure path runs `opts.formError` when provided —\n * mapping a structured/nested error shape into SvelteKit's field issues (via the\n * `issue` helper / `invalid()`) so a domain validation failure drives\n * field-level invalidation rather than a generic failure. Otherwise the form\n * falls back to `translateError`.\n *\n * @example\n * ```ts\n * const { query, form, command } = RemoteFunction.withRuntime(\n * RequestRuntime.fromLocals((event) => event.locals.runtime),\n * {\n * translateError: toSvelteKit,\n * formError: (error, { issue }) => {\n * if (error instanceof ValidationFailure) issue.field(error.field, error.message);\n * invalid();\n * },\n * },\n * );\n *\n * export const items = query(() =>\n * Effect.gen(function* () {\n * const db = yield* Database;\n * return yield* db.list();\n * }),\n * );\n * ```\n */\nexport function withRuntime<R>(\n provider: RequestRuntimeProvider<R, any>,\n opts?: { translateError?: ErrorTranslator; formError?: FormErrorMapper },\n): RemoteFunctions<R> {\n const helpers = helpersFromProvider(\n provider,\n opts?.translateError ?? defaultTranslate,\n opts?.formError,\n );\n return {\n query: makeQueryNamespace<R>(helpers.run),\n form: makeForm<R>(helpers),\n command: makeCommand<R>(helpers.run),\n prerender: makePrerender<R>(helpers.run),\n };\n}\n\nexport const RemoteFunction = {\n query,\n form,\n command,\n prerender,\n requested,\n withRuntime,\n};\n"],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ import { Cause, Context, Effect, Exit, Fiber, Layer, ManagedRuntime } from "effect";
2
+ import { Action, Actions, RequestEvent, RequestHandler, ServerLoad } from "@sveltejs/kit";
3
+
4
+ //#region src/request-runtime.d.ts
5
+ /**
6
+ * The runners a {@link RequestRuntimeProvider} resolves for a single request:
7
+ * a way to run an `Effect<A, unknown, R>` that requires the provider's services
8
+ * `R`. The error channel is `unknown` because a boundary translator (see
9
+ * {@link makeRuntimeAdapter}) decides how failures surface.
10
+ */
11
+ interface RequestRuntimeRunners<in R> {
12
+ /** Run the request's effect, keeping its `Exit` (never rejects). */
13
+ readonly runPromiseExit: <A>(effect: Effect.Effect<A, unknown, R>) => Promise<Exit.Exit<A, unknown>>;
14
+ /** Fork the request's effect, returning the running fiber. */
15
+ readonly runFork: <A>(effect: Effect.Effect<A, unknown, R>) => Fiber.Fiber<A, unknown>;
16
+ }
17
+ /**
18
+ * How to execute a request's effects. Resolved per request, at call time, so a
19
+ * runtime the consumer rebuilds mid-request is honoured automatically.
20
+ *
21
+ * Implementations own the scope/lifecycle of whatever they build. `R` is the
22
+ * set of services the handler effect may require; `E` is the layer construction
23
+ * error type (informational — failures surface through the translator).
24
+ */
25
+ interface RequestRuntimeProvider<R, E = never> {
26
+ /** Resolve the runners for a request. Consulted per call, never cached. */
27
+ readonly run: (event: RequestEvent) => RequestRuntimeRunners<R>;
28
+ /**
29
+ * Optional teardown to run after the response is produced. Called by
30
+ * {@link makeRuntimeAdapter} once the handler has settled — e.g. to route a
31
+ * per-request resource's disposal through `ctx.waitUntil` on Workers.
32
+ */
33
+ readonly disposeAfterResponse?: (event: RequestEvent) => void;
34
+ /** Phantom marker so `E` participates in inference. */
35
+ readonly _E?: (_: never) => E;
36
+ }
37
+ /** Options for {@link RequestRuntime.fromLayerPerRequest}. */
38
+ interface FromLayerPerRequestOptions {
39
+ /**
40
+ * Defer teardown of the per-request runtime to after the response is
41
+ * produced, instead of awaiting `dispose()` inline. On Workers, route it
42
+ * through `ctx.waitUntil` so disposal does not fight response streaming or
43
+ * socket pinning:
44
+ *
45
+ * ```ts
46
+ * { afterResponse: (event, dispose) => event.platform!.ctx.waitUntil(dispose()) }
47
+ * ```
48
+ */
49
+ readonly afterResponse?: (event: RequestEvent, dispose: () => Promise<void>) => void;
50
+ }
51
+ /**
52
+ * Constructors for {@link RequestRuntimeProvider}. Each expresses one way to
53
+ * obtain a request's execution context; the wrappers don't care which.
54
+ */
55
+ declare const RequestRuntime: {
56
+ /**
57
+ * Adopt a `ManagedRuntime` the consumer already built (the build-once model,
58
+ * expressed as a provider).
59
+ */
60
+ fromManagedRuntime: <R, E>(rt: ManagedRuntime.ManagedRuntime<R, E>) => RequestRuntimeProvider<R, E>;
61
+ /**
62
+ * Let the package build a single `ManagedRuntime` from a layer, lazily on
63
+ * first use and shared across requests (the existing build-once-per-process
64
+ * model).
65
+ */
66
+ fromLayer: <R, E>(layer: Layer.Layer<R, E>) => RequestRuntimeProvider<R, E>;
67
+ /**
68
+ * Adopt a runtime the consumer stashed on the request (e.g. on
69
+ * `event.locals`). Resolved at call time, so a runtime rebuilt mid-request is
70
+ * honoured automatically. The getter may return a `ManagedRuntime` or a
71
+ * built service `Context`.
72
+ */
73
+ fromLocals: <R>(get: (event: RequestEvent) => ManagedRuntime.ManagedRuntime<R, unknown> | Context.Context<R>) => RequestRuntimeProvider<R>;
74
+ /**
75
+ * Let the package build *and dispose* a `ManagedRuntime` per request from a
76
+ * layer factory. Each handler invocation builds its own runtime from
77
+ * `layer(event)` and disposes it once the effect settles — so a per-request
78
+ * resource (a DB pool) dies with the request. Use `opts.afterResponse` to
79
+ * defer disposal to after the response (Workers `ctx.waitUntil`).
80
+ *
81
+ * For sharing one runtime across every load/action of a request, prefer
82
+ * {@link RequestRuntime.fromLocals} with a runtime the consumer builds in
83
+ * `hooks.server.ts`.
84
+ */
85
+ fromLayerPerRequest: <R, E>(layer: (event: RequestEvent) => Layer.Layer<R, E>, opts?: FromLayerPerRequestOptions) => RequestRuntimeProvider<R, E>;
86
+ };
87
+ /** A boundary translator: turn a failed `Cause` into a thrown value. */
88
+ type ErrorTranslator = (cause: Cause.Cause<unknown>) => never;
89
+ /** Options shared by {@link makeRuntimeAdapter}. */
90
+ interface RuntimeAdapterOptions {
91
+ /**
92
+ * Translate a failed `Cause` into SvelteKit control flow at the boundary.
93
+ * Called for `serverLoad` / `action` / `actions` failures; it may throw
94
+ * `redirect()` / `error()` (and, for remote forms, call `invalid()`).
95
+ * Defaults to the package's passthrough: SvelteKit's own `redirect()` /
96
+ * `error()` are rethrown, anything else becomes an `EffectCauseError` (a 500).
97
+ */
98
+ readonly translateError?: ErrorTranslator;
99
+ }
100
+ /** Options for the adapter's `endpoint` wrapper. */
101
+ interface AdapterEndpointOptions {
102
+ /**
103
+ * Include full error details (`Cause.pretty`) in the default 500 JSON body.
104
+ * Ignored when a `translateError` is configured. Defaults to `false`.
105
+ */
106
+ readonly exposeErrors?: boolean;
107
+ }
108
+ /** The wrappers produced by {@link makeRuntimeAdapter}, bound to a provider. */
109
+ interface RuntimeAdapter<R> {
110
+ readonly serverLoad: <Params extends Record<string, string> = any, ParentData extends Record<string, any> = any, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>) => ServerLoad<Params, ParentData, OutputData, RouteId>;
111
+ readonly action: <Params extends Record<string, string> = any, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>) => Action<Params, OutputData, RouteId>;
112
+ readonly actions: <Params extends Record<string, string> = any, OutputData extends Record<string, any> | void = any, RouteId extends string | null = any>(map: Record<string, (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>>) => Actions<Params, OutputData, RouteId>;
113
+ readonly endpoint: <Params extends Record<string, string> = any, RouteId extends string | null = any>(fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, R>, options?: AdapterEndpointOptions) => RequestHandler<Params, RouteId>;
114
+ }
115
+ /**
116
+ * Bind the SvelteKit server wrappers to a {@link RequestRuntimeProvider} (and an
117
+ * optional boundary error translator).
118
+ *
119
+ * Unlike the standalone `serverLoad` / `action` / `endpoint`, the handler
120
+ * effect's required services `R` come from the provider — your own application
121
+ * catalogue — not the fixed {@link SvelteKitServices}. The package does not
122
+ * merge `HttpClient` / `SvelteKitRequestEvent` onto this path; add them to your
123
+ * own layer if you want them.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * // Package owns the runtime per request on Workers, disposed via waitUntil.
128
+ * const { serverLoad } = makeRuntimeAdapter(
129
+ * RequestRuntime.fromLayerPerRequest((event) => AppLayer(event.platform!.env), {
130
+ * afterResponse: (event, dispose) => event.platform!.ctx.waitUntil(dispose()),
131
+ * }),
132
+ * );
133
+ * ```
134
+ */
135
+ declare const makeRuntimeAdapter: <R>(provider: RequestRuntimeProvider<R, any>, opts?: RuntimeAdapterOptions) => RuntimeAdapter<R>;
136
+ //#endregion
137
+ export { AdapterEndpointOptions, ErrorTranslator, FromLayerPerRequestOptions, RequestRuntime, RequestRuntimeProvider, RequestRuntimeRunners, RuntimeAdapter, RuntimeAdapterOptions, makeRuntimeAdapter };
138
+ //# sourceMappingURL=request-runtime.d.ts.map
@@ -0,0 +1,186 @@
1
+ import { causeToThrowable } from "./internal/error.js";
2
+ import { Cause, Effect, Exit, ManagedRuntime } from "effect";
3
+ import { isHttpError, isRedirect } from "@sveltejs/kit";
4
+ //#region src/request-runtime.ts
5
+ /**
6
+ * A general, Effect-idiomatic way to provide the per-request execution context
7
+ * for SvelteKit server wrappers.
8
+ *
9
+ * Where {@link makeServerRuntime} bakes in "the package owns a runtime, built
10
+ * once per process," this module factors out the single decision *how to obtain
11
+ * the execution context for this request* behind one open abstraction — a
12
+ * {@link RequestRuntimeProvider}. Existing models (a build-once
13
+ * `ManagedRuntime`, a layer built once), a consumer-owned runtime resolved from
14
+ * `event.locals`, and a package-built per-request runtime are all just
15
+ * {@link RequestRuntime} constructors of the same provider, not separate code
16
+ * paths.
17
+ *
18
+ * {@link makeRuntimeAdapter} binds the `serverLoad` / `action` / `actions` /
19
+ * `endpoint` wrappers to a provider (and an optional boundary error
20
+ * translator), so the handler effect's requirement channel `R` is the
21
+ * *provider's* services — your own application catalogue — rather than the fixed
22
+ * {@link SvelteKitServices}.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * // The consumer owns a per-request ManagedRuntime on event.locals.runtime
27
+ * // (rebuilt mid-request); the package adopts it, resolved at call time.
28
+ * import { makeRuntimeAdapter, RequestRuntime } from "@thomasfosterau/effect-sveltekit/server";
29
+ *
30
+ * const { serverLoad, action, actions, endpoint } = makeRuntimeAdapter(
31
+ * RequestRuntime.fromLocals((event) => event.locals.runtime),
32
+ * { translateError: toSvelteKit },
33
+ * );
34
+ *
35
+ * export const load = serverLoad((event) =>
36
+ * Effect.gen(function* () {
37
+ * const db = yield* Database; // app service from locals.runtime
38
+ * return { items: yield* db.list() };
39
+ * }),
40
+ * );
41
+ * ```
42
+ */
43
+ /** Runners backed by an existing `ManagedRuntime`. */
44
+ const runnersFromManagedRuntime = (rt) => ({
45
+ runPromiseExit: (effect) => rt.runPromiseExit(effect),
46
+ runFork: (effect) => rt.runFork(effect)
47
+ });
48
+ /** Runners backed by a built service `Context` (run on the default scheduler). */
49
+ const runnersFromContext = (context) => ({
50
+ runPromiseExit: (effect) => Effect.runPromiseExitWith(context)(effect),
51
+ runFork: (effect) => Effect.runForkWith(context)(effect)
52
+ });
53
+ /** Runners for a target that is either a `ManagedRuntime` or a `Context`. */
54
+ const runnersFor = (target) => ManagedRuntime.isManagedRuntime(target) ? runnersFromManagedRuntime(target) : runnersFromContext(target);
55
+ /**
56
+ * Constructors for {@link RequestRuntimeProvider}. Each expresses one way to
57
+ * obtain a request's execution context; the wrappers don't care which.
58
+ */
59
+ const RequestRuntime = {
60
+ /**
61
+ * Adopt a `ManagedRuntime` the consumer already built (the build-once model,
62
+ * expressed as a provider).
63
+ */
64
+ fromManagedRuntime: (rt) => {
65
+ const runners = runnersFromManagedRuntime(rt);
66
+ return { run: () => runners };
67
+ },
68
+ /**
69
+ * Let the package build a single `ManagedRuntime` from a layer, lazily on
70
+ * first use and shared across requests (the existing build-once-per-process
71
+ * model).
72
+ */
73
+ fromLayer: (layer) => {
74
+ let runners;
75
+ return { run: () => runners ??= runnersFromManagedRuntime(ManagedRuntime.make(layer)) };
76
+ },
77
+ /**
78
+ * Adopt a runtime the consumer stashed on the request (e.g. on
79
+ * `event.locals`). Resolved at call time, so a runtime rebuilt mid-request is
80
+ * honoured automatically. The getter may return a `ManagedRuntime` or a
81
+ * built service `Context`.
82
+ */
83
+ fromLocals: (get) => ({ run: (event) => runnersFor(get(event)) }),
84
+ /**
85
+ * Let the package build *and dispose* a `ManagedRuntime` per request from a
86
+ * layer factory. Each handler invocation builds its own runtime from
87
+ * `layer(event)` and disposes it once the effect settles — so a per-request
88
+ * resource (a DB pool) dies with the request. Use `opts.afterResponse` to
89
+ * defer disposal to after the response (Workers `ctx.waitUntil`).
90
+ *
91
+ * For sharing one runtime across every load/action of a request, prefer
92
+ * {@link RequestRuntime.fromLocals} with a runtime the consumer builds in
93
+ * `hooks.server.ts`.
94
+ */
95
+ fromLayerPerRequest: (layer, opts) => ({ run: (event) => {
96
+ const dispose = (rt) => () => rt.dispose();
97
+ const teardown = (rt) => {
98
+ if (opts?.afterResponse) {
99
+ opts.afterResponse(event, dispose(rt));
100
+ return Promise.resolve();
101
+ }
102
+ return rt.dispose();
103
+ };
104
+ return {
105
+ runPromiseExit: async (effect) => {
106
+ const rt = ManagedRuntime.make(layer(event));
107
+ try {
108
+ return await rt.runPromiseExit(effect);
109
+ } finally {
110
+ await teardown(rt);
111
+ }
112
+ },
113
+ runFork: (effect) => {
114
+ const rt = ManagedRuntime.make(layer(event));
115
+ const fiber = rt.runFork(effect);
116
+ fiber.addObserver(() => void teardown(rt));
117
+ return fiber;
118
+ }
119
+ };
120
+ } })
121
+ };
122
+ /**
123
+ * Bind the SvelteKit server wrappers to a {@link RequestRuntimeProvider} (and an
124
+ * optional boundary error translator).
125
+ *
126
+ * Unlike the standalone `serverLoad` / `action` / `endpoint`, the handler
127
+ * effect's required services `R` come from the provider — your own application
128
+ * catalogue — not the fixed {@link SvelteKitServices}. The package does not
129
+ * merge `HttpClient` / `SvelteKitRequestEvent` onto this path; add them to your
130
+ * own layer if you want them.
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * // Package owns the runtime per request on Workers, disposed via waitUntil.
135
+ * const { serverLoad } = makeRuntimeAdapter(
136
+ * RequestRuntime.fromLayerPerRequest((event) => AppLayer(event.platform!.env), {
137
+ * afterResponse: (event, dispose) => event.platform!.ctx.waitUntil(dispose()),
138
+ * }),
139
+ * );
140
+ * ```
141
+ */
142
+ const makeRuntimeAdapter = (provider, opts) => {
143
+ const translateError = opts?.translateError ?? ((cause) => {
144
+ throw causeToThrowable(cause);
145
+ });
146
+ const runSettle = async (event, effect) => {
147
+ const exit = await provider.run(event).runPromiseExit(effect);
148
+ try {
149
+ if (Exit.isSuccess(exit)) return exit.value;
150
+ return translateError(exit.cause);
151
+ } finally {
152
+ provider.disposeAfterResponse?.(event);
153
+ }
154
+ };
155
+ const action = (fn) => (event) => runSettle(event, fn(event));
156
+ return {
157
+ serverLoad: (fn) => (event) => runSettle(event, fn(event)),
158
+ action,
159
+ actions: (map) => {
160
+ const wrapped = {};
161
+ for (const [key, fn] of Object.entries(map)) wrapped[key] = action(fn);
162
+ return wrapped;
163
+ },
164
+ endpoint: (fn, options) => async (event) => {
165
+ const exit = await provider.run(event).runPromiseExit(fn(event));
166
+ try {
167
+ if (Exit.isSuccess(exit)) return exit.value;
168
+ if (opts?.translateError) return opts.translateError(exit.cause);
169
+ const squashed = Cause.squash(exit.cause);
170
+ if (isRedirect(squashed) || isHttpError(squashed)) throw squashed;
171
+ console.error("Endpoint error:", Cause.pretty(exit.cause));
172
+ const message = options?.exposeErrors ? Cause.pretty(exit.cause) : "Internal server error";
173
+ return new Response(JSON.stringify({ error: message }), {
174
+ status: 500,
175
+ headers: { "Content-Type": "application/json" }
176
+ });
177
+ } finally {
178
+ provider.disposeAfterResponse?.(event);
179
+ }
180
+ }
181
+ };
182
+ };
183
+ //#endregion
184
+ export { RequestRuntime, makeRuntimeAdapter };
185
+
186
+ //# sourceMappingURL=request-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-runtime.js","names":[],"sources":["../src/request-runtime.ts"],"sourcesContent":["/**\n * A general, Effect-idiomatic way to provide the per-request execution context\n * for SvelteKit server wrappers.\n *\n * Where {@link makeServerRuntime} bakes in \"the package owns a runtime, built\n * once per process,\" this module factors out the single decision *how to obtain\n * the execution context for this request* behind one open abstraction — a\n * {@link RequestRuntimeProvider}. Existing models (a build-once\n * `ManagedRuntime`, a layer built once), a consumer-owned runtime resolved from\n * `event.locals`, and a package-built per-request runtime are all just\n * {@link RequestRuntime} constructors of the same provider, not separate code\n * paths.\n *\n * {@link makeRuntimeAdapter} binds the `serverLoad` / `action` / `actions` /\n * `endpoint` wrappers to a provider (and an optional boundary error\n * translator), so the handler effect's requirement channel `R` is the\n * *provider's* services — your own application catalogue — rather than the fixed\n * {@link SvelteKitServices}.\n *\n * @example\n * ```ts\n * // The consumer owns a per-request ManagedRuntime on event.locals.runtime\n * // (rebuilt mid-request); the package adopts it, resolved at call time.\n * import { makeRuntimeAdapter, RequestRuntime } from \"@thomasfosterau/effect-sveltekit/server\";\n *\n * const { serverLoad, action, actions, endpoint } = makeRuntimeAdapter(\n * RequestRuntime.fromLocals((event) => event.locals.runtime),\n * { translateError: toSvelteKit },\n * );\n *\n * export const load = serverLoad((event) =>\n * Effect.gen(function* () {\n * const db = yield* Database; // app service from locals.runtime\n * return { items: yield* db.list() };\n * }),\n * );\n * ```\n */\n\nimport {\n isHttpError,\n isRedirect,\n type Action,\n type Actions,\n type RequestEvent,\n type RequestHandler,\n type ServerLoad,\n} from \"@sveltejs/kit\";\nimport { Cause, Context, Effect, Exit, type Fiber, Layer, ManagedRuntime } from \"effect\";\nimport { causeToThrowable } from \"./internal/error.js\";\n\n/**\n * The runners a {@link RequestRuntimeProvider} resolves for a single request:\n * a way to run an `Effect<A, unknown, R>` that requires the provider's services\n * `R`. The error channel is `unknown` because a boundary translator (see\n * {@link makeRuntimeAdapter}) decides how failures surface.\n */\nexport interface RequestRuntimeRunners<in R> {\n /** Run the request's effect, keeping its `Exit` (never rejects). */\n readonly runPromiseExit: <A>(\n effect: Effect.Effect<A, unknown, R>,\n ) => Promise<Exit.Exit<A, unknown>>;\n /** Fork the request's effect, returning the running fiber. */\n readonly runFork: <A>(effect: Effect.Effect<A, unknown, R>) => Fiber.Fiber<A, unknown>;\n}\n\n/**\n * How to execute a request's effects. Resolved per request, at call time, so a\n * runtime the consumer rebuilds mid-request is honoured automatically.\n *\n * Implementations own the scope/lifecycle of whatever they build. `R` is the\n * set of services the handler effect may require; `E` is the layer construction\n * error type (informational — failures surface through the translator).\n */\nexport interface RequestRuntimeProvider<R, E = never> {\n /** Resolve the runners for a request. Consulted per call, never cached. */\n readonly run: (event: RequestEvent) => RequestRuntimeRunners<R>;\n /**\n * Optional teardown to run after the response is produced. Called by\n * {@link makeRuntimeAdapter} once the handler has settled — e.g. to route a\n * per-request resource's disposal through `ctx.waitUntil` on Workers.\n */\n readonly disposeAfterResponse?: (event: RequestEvent) => void;\n /** Phantom marker so `E` participates in inference. */\n readonly _E?: (_: never) => E;\n}\n\n/** Runners backed by an existing `ManagedRuntime`. */\nconst runnersFromManagedRuntime = <R>(\n rt: ManagedRuntime.ManagedRuntime<R, unknown>,\n): RequestRuntimeRunners<R> => ({\n runPromiseExit: (effect) => rt.runPromiseExit(effect) as Promise<Exit.Exit<never, unknown>>,\n runFork: (effect) => rt.runFork(effect) as Fiber.Fiber<never, unknown>,\n});\n\n/** Runners backed by a built service `Context` (run on the default scheduler). */\nconst runnersFromContext = <R>(context: Context.Context<R>): RequestRuntimeRunners<R> => ({\n runPromiseExit: (effect) => Effect.runPromiseExitWith(context)(effect),\n runFork: (effect) => Effect.runForkWith(context)(effect),\n});\n\n/** Runners for a target that is either a `ManagedRuntime` or a `Context`. */\nconst runnersFor = <R>(\n target: ManagedRuntime.ManagedRuntime<R, unknown> | Context.Context<R>,\n): RequestRuntimeRunners<R> =>\n ManagedRuntime.isManagedRuntime(target)\n ? runnersFromManagedRuntime(target as ManagedRuntime.ManagedRuntime<R, unknown>)\n : runnersFromContext(target as Context.Context<R>);\n\n/** Options for {@link RequestRuntime.fromLayerPerRequest}. */\nexport interface FromLayerPerRequestOptions {\n /**\n * Defer teardown of the per-request runtime to after the response is\n * produced, instead of awaiting `dispose()` inline. On Workers, route it\n * through `ctx.waitUntil` so disposal does not fight response streaming or\n * socket pinning:\n *\n * ```ts\n * { afterResponse: (event, dispose) => event.platform!.ctx.waitUntil(dispose()) }\n * ```\n */\n readonly afterResponse?: (event: RequestEvent, dispose: () => Promise<void>) => void;\n}\n\n/**\n * Constructors for {@link RequestRuntimeProvider}. Each expresses one way to\n * obtain a request's execution context; the wrappers don't care which.\n */\nexport const RequestRuntime = {\n /**\n * Adopt a `ManagedRuntime` the consumer already built (the build-once model,\n * expressed as a provider).\n */\n fromManagedRuntime: <R, E>(\n rt: ManagedRuntime.ManagedRuntime<R, E>,\n ): RequestRuntimeProvider<R, E> => {\n const runners = runnersFromManagedRuntime(rt as ManagedRuntime.ManagedRuntime<R, unknown>);\n return { run: () => runners };\n },\n\n /**\n * Let the package build a single `ManagedRuntime` from a layer, lazily on\n * first use and shared across requests (the existing build-once-per-process\n * model).\n */\n fromLayer: <R, E>(layer: Layer.Layer<R, E>): RequestRuntimeProvider<R, E> => {\n let runners: RequestRuntimeRunners<R> | undefined;\n return {\n run: () =>\n (runners ??= runnersFromManagedRuntime(\n ManagedRuntime.make(layer) as ManagedRuntime.ManagedRuntime<R, unknown>,\n )),\n };\n },\n\n /**\n * Adopt a runtime the consumer stashed on the request (e.g. on\n * `event.locals`). Resolved at call time, so a runtime rebuilt mid-request is\n * honoured automatically. The getter may return a `ManagedRuntime` or a\n * built service `Context`.\n */\n fromLocals: <R>(\n get: (event: RequestEvent) => ManagedRuntime.ManagedRuntime<R, unknown> | Context.Context<R>,\n ): RequestRuntimeProvider<R> => ({\n run: (event) => runnersFor(get(event)),\n }),\n\n /**\n * Let the package build *and dispose* a `ManagedRuntime` per request from a\n * layer factory. Each handler invocation builds its own runtime from\n * `layer(event)` and disposes it once the effect settles — so a per-request\n * resource (a DB pool) dies with the request. Use `opts.afterResponse` to\n * defer disposal to after the response (Workers `ctx.waitUntil`).\n *\n * For sharing one runtime across every load/action of a request, prefer\n * {@link RequestRuntime.fromLocals} with a runtime the consumer builds in\n * `hooks.server.ts`.\n */\n fromLayerPerRequest: <R, E>(\n layer: (event: RequestEvent) => Layer.Layer<R, E>,\n opts?: FromLayerPerRequestOptions,\n ): RequestRuntimeProvider<R, E> => ({\n run: (event) => {\n const dispose = (rt: ManagedRuntime.ManagedRuntime<R, E>) => () => rt.dispose();\n const teardown = (rt: ManagedRuntime.ManagedRuntime<R, E>): Promise<void> => {\n if (opts?.afterResponse) {\n opts.afterResponse(event, dispose(rt));\n return Promise.resolve();\n }\n return rt.dispose();\n };\n return {\n runPromiseExit: async (effect) => {\n const rt = ManagedRuntime.make(layer(event));\n try {\n return (await rt.runPromiseExit(effect)) as Exit.Exit<never, unknown>;\n } finally {\n await teardown(rt);\n }\n },\n runFork: (effect) => {\n const rt = ManagedRuntime.make(layer(event));\n const fiber = rt.runFork(effect) as Fiber.Fiber<never, unknown>;\n fiber.addObserver(() => void teardown(rt));\n return fiber;\n },\n };\n },\n }),\n};\n\n/** A boundary translator: turn a failed `Cause` into a thrown value. */\nexport type ErrorTranslator = (cause: Cause.Cause<unknown>) => never;\n\n/** Options shared by {@link makeRuntimeAdapter}. */\nexport interface RuntimeAdapterOptions {\n /**\n * Translate a failed `Cause` into SvelteKit control flow at the boundary.\n * Called for `serverLoad` / `action` / `actions` failures; it may throw\n * `redirect()` / `error()` (and, for remote forms, call `invalid()`).\n * Defaults to the package's passthrough: SvelteKit's own `redirect()` /\n * `error()` are rethrown, anything else becomes an `EffectCauseError` (a 500).\n */\n readonly translateError?: ErrorTranslator;\n}\n\n/** Options for the adapter's `endpoint` wrapper. */\nexport interface AdapterEndpointOptions {\n /**\n * Include full error details (`Cause.pretty`) in the default 500 JSON body.\n * Ignored when a `translateError` is configured. Defaults to `false`.\n */\n readonly exposeErrors?: boolean;\n}\n\n/** The wrappers produced by {@link makeRuntimeAdapter}, bound to a provider. */\nexport interface RuntimeAdapter<R> {\n readonly serverLoad: <\n Params extends Record<string, string> = any,\n ParentData extends Record<string, any> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>,\n ) => ServerLoad<Params, ParentData, OutputData, RouteId>;\n readonly action: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>,\n ) => Action<Params, OutputData, RouteId>;\n readonly actions: <\n Params extends Record<string, string> = any,\n OutputData extends Record<string, any> | void = any,\n RouteId extends string | null = any,\n >(\n map: Record<\n string,\n (event: RequestEvent<Params, RouteId>) => Effect.Effect<OutputData, unknown, R>\n >,\n ) => Actions<Params, OutputData, RouteId>;\n readonly endpoint: <\n Params extends Record<string, string> = any,\n RouteId extends string | null = any,\n >(\n fn: (event: RequestEvent<Params, RouteId>) => Effect.Effect<Response, unknown, R>,\n options?: AdapterEndpointOptions,\n ) => RequestHandler<Params, RouteId>;\n}\n\n/**\n * Bind the SvelteKit server wrappers to a {@link RequestRuntimeProvider} (and an\n * optional boundary error translator).\n *\n * Unlike the standalone `serverLoad` / `action` / `endpoint`, the handler\n * effect's required services `R` come from the provider — your own application\n * catalogue — not the fixed {@link SvelteKitServices}. The package does not\n * merge `HttpClient` / `SvelteKitRequestEvent` onto this path; add them to your\n * own layer if you want them.\n *\n * @example\n * ```ts\n * // Package owns the runtime per request on Workers, disposed via waitUntil.\n * const { serverLoad } = makeRuntimeAdapter(\n * RequestRuntime.fromLayerPerRequest((event) => AppLayer(event.platform!.env), {\n * afterResponse: (event, dispose) => event.platform!.ctx.waitUntil(dispose()),\n * }),\n * );\n * ```\n */\nexport const makeRuntimeAdapter = <R>(\n provider: RequestRuntimeProvider<R, any>,\n opts?: RuntimeAdapterOptions,\n): RuntimeAdapter<R> => {\n const translateError: ErrorTranslator =\n opts?.translateError ??\n ((cause) => {\n throw causeToThrowable(cause);\n });\n\n const runSettle = async <A>(\n event: RequestEvent,\n effect: Effect.Effect<A, unknown, R>,\n ): Promise<A> => {\n const exit = await provider.run(event).runPromiseExit(effect);\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n return translateError(exit.cause);\n } finally {\n provider.disposeAfterResponse?.(event);\n }\n };\n\n const action: RuntimeAdapter<R>[\"action\"] = (fn) => (event) => runSettle(event, fn(event));\n\n return {\n serverLoad: (fn) => (event) => runSettle(event, fn(event)),\n action,\n actions: (map) => {\n const wrapped: Record<string, Action<any, any, any>> = {};\n for (const [key, fn] of Object.entries(map)) wrapped[key] = action(fn);\n return wrapped as never;\n },\n endpoint: (fn, options) => async (event) => {\n const exit = await provider.run(event as RequestEvent).runPromiseExit(fn(event));\n try {\n if (Exit.isSuccess(exit)) return exit.value;\n // A configured translator owns the whole failure path (control flow,\n // structured error bodies, ...).\n if (opts?.translateError) return opts.translateError(exit.cause);\n // Default: pass SvelteKit control flow through, otherwise a 500.\n const squashed = Cause.squash(exit.cause);\n if (isRedirect(squashed) || isHttpError(squashed)) throw squashed;\n console.error(\"Endpoint error:\", Cause.pretty(exit.cause));\n const message = options?.exposeErrors ? Cause.pretty(exit.cause) : \"Internal server error\";\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } finally {\n provider.disposeAfterResponse?.(event as RequestEvent);\n }\n },\n };\n};\n"],"mappings":"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"}