@farm.js/core 0.1.0-beta.13 → 0.1.0-beta.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/api/route-manager.cjs.map +1 -1
  2. package/dist/api/route-manager.mjs +2 -2
  3. package/dist/api/runtime.cjs.map +1 -1
  4. package/dist/api/runtime.mjs +1 -1
  5. package/dist/api.cjs +18 -10
  6. package/dist/api.cjs.map +1 -1
  7. package/dist/api.mjs +3 -3
  8. package/dist/{chunk-B232I2GP.mjs → chunk-3HIVDX7J.mjs} +12 -1
  9. package/dist/chunk-3HIVDX7J.mjs.map +1 -0
  10. package/dist/{chunk-JDEOUK5V.mjs → chunk-6YXKHAFU.mjs} +3 -3
  11. package/dist/{chunk-VSDZEB5M.mjs → chunk-AN3PQVKH.mjs} +14 -16
  12. package/dist/{chunk-VSDZEB5M.mjs.map → chunk-AN3PQVKH.mjs.map} +1 -1
  13. package/dist/{chunk-Z5CQMP54.mjs → chunk-ARRYKOI5.mjs} +2 -2
  14. package/dist/{chunk-KLA67WKG.mjs → chunk-B3FFOIWS.mjs} +2 -2
  15. package/dist/{chunk-3WIRO3N7.mjs → chunk-BWJC5ZOD.mjs} +2 -2
  16. package/dist/{chunk-3WIRO3N7.mjs.map → chunk-BWJC5ZOD.mjs.map} +1 -1
  17. package/dist/{chunk-PFE6GMNE.mjs → chunk-HMRFYJK6.mjs} +19 -11
  18. package/dist/chunk-HMRFYJK6.mjs.map +1 -0
  19. package/dist/{chunk-32W4QEJP.mjs → chunk-KFTGPFRD.mjs} +1 -1
  20. package/dist/chunk-KFTGPFRD.mjs.map +1 -0
  21. package/dist/{chunk-Z3YG4OZT.mjs → chunk-OSWV5IJB.mjs} +140 -41
  22. package/dist/chunk-OSWV5IJB.mjs.map +1 -0
  23. package/dist/{chunk-OPHK6OJN.mjs → chunk-SLDBGSC3.mjs} +2 -2
  24. package/dist/{chunk-L2PE36GQ.mjs → chunk-T2KL7VEL.mjs} +3 -3
  25. package/dist/{chunk-WDU6QXNK.mjs → chunk-UCYWGN2P.mjs} +1 -1
  26. package/dist/chunk-UCYWGN2P.mjs.map +1 -0
  27. package/dist/{chunk-65A7QIKI.mjs → chunk-UMVLB3PN.mjs} +3 -3
  28. package/dist/chunk-UMVLB3PN.mjs.map +1 -0
  29. package/dist/{chunk-VY2UYN5R.mjs → chunk-VPCFICID.mjs} +82 -3
  30. package/dist/chunk-VPCFICID.mjs.map +1 -0
  31. package/dist/{chunk-H3TE3LOV.mjs → chunk-XMFTIVJH.mjs} +2 -2
  32. package/dist/{chunk-UB5UOSHB.mjs → chunk-ZVMV3THA.mjs} +71 -2
  33. package/dist/chunk-ZVMV3THA.mjs.map +1 -0
  34. package/dist/client.cjs.map +1 -1
  35. package/dist/client.mjs +3 -3
  36. package/dist/deployment.cjs +12 -0
  37. package/dist/deployment.cjs.map +1 -1
  38. package/dist/deployment.mjs +3 -1
  39. package/dist/{devtools-GAZ7F3KL.mjs → devtools-E4YNCY5J.mjs} +4 -4
  40. package/dist/docs.cjs.map +1 -1
  41. package/dist/docs.mjs +4 -4
  42. package/dist/index.cjs +332 -58
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.mjs +28 -16
  45. package/dist/index.mjs.map +1 -1
  46. package/dist/internal/build-runtime.cjs +159 -46
  47. package/dist/internal/build-runtime.cjs.map +1 -1
  48. package/dist/internal/build-runtime.mjs +11 -11
  49. package/dist/internal/config-runtime.cjs.map +1 -1
  50. package/dist/internal/config-runtime.mjs +3 -3
  51. package/dist/navigation.cjs.map +1 -1
  52. package/dist/navigation.mjs +3 -3
  53. package/dist/plugin.cjs +141 -34
  54. package/dist/plugin.cjs.map +1 -1
  55. package/dist/plugin.mjs +8 -8
  56. package/dist/server-action-security.cjs +23 -1
  57. package/dist/server-action-security.cjs.map +1 -1
  58. package/dist/server-action-security.mjs +1 -1
  59. package/dist/server-fn-client.cjs.map +1 -1
  60. package/dist/server-fn-client.mjs +1 -1
  61. package/dist/server-fn.cjs +140 -3
  62. package/dist/server-fn.cjs.map +1 -1
  63. package/dist/server-fn.mjs +14 -3
  64. package/dist/server-query.cjs +99 -1
  65. package/dist/server-query.cjs.map +1 -1
  66. package/dist/server-query.mjs +3 -3
  67. package/dist/server.cjs +260 -37
  68. package/dist/server.cjs.map +1 -1
  69. package/dist/server.mjs +11 -11
  70. package/dist/testing.cjs.map +1 -1
  71. package/dist/testing.mjs +3 -3
  72. package/dist/version.cjs +1 -1
  73. package/dist/version.cjs.map +1 -1
  74. package/dist/version.mjs +1 -1
  75. package/dist/vite.cjs +142 -35
  76. package/dist/vite.cjs.map +1 -1
  77. package/dist/vite.mjs +8 -8
  78. package/package.json +1 -1
  79. package/types/client.d.ts +115 -4
  80. package/dist/chunk-32W4QEJP.mjs.map +0 -1
  81. package/dist/chunk-65A7QIKI.mjs.map +0 -1
  82. package/dist/chunk-B232I2GP.mjs.map +0 -1
  83. package/dist/chunk-PFE6GMNE.mjs.map +0 -1
  84. package/dist/chunk-UB5UOSHB.mjs.map +0 -1
  85. package/dist/chunk-VY2UYN5R.mjs.map +0 -1
  86. package/dist/chunk-WDU6QXNK.mjs.map +0 -1
  87. package/dist/chunk-Z3YG4OZT.mjs.map +0 -1
  88. /package/dist/{chunk-JDEOUK5V.mjs.map → chunk-6YXKHAFU.mjs.map} +0 -0
  89. /package/dist/{chunk-Z5CQMP54.mjs.map → chunk-ARRYKOI5.mjs.map} +0 -0
  90. /package/dist/{chunk-KLA67WKG.mjs.map → chunk-B3FFOIWS.mjs.map} +0 -0
  91. /package/dist/{chunk-OPHK6OJN.mjs.map → chunk-SLDBGSC3.mjs.map} +0 -0
  92. /package/dist/{chunk-L2PE36GQ.mjs.map → chunk-T2KL7VEL.mjs.map} +0 -0
  93. /package/dist/{chunk-H3TE3LOV.mjs.map → chunk-XMFTIVJH.mjs.map} +0 -0
  94. /package/dist/{devtools-GAZ7F3KL.mjs.map → devtools-E4YNCY5J.mjs.map} +0 -0
package/dist/plugin.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  import "./chunk-RQ633A5I.mjs";
6
6
  import {
7
7
  farmPlugin
8
- } from "./chunk-Z3YG4OZT.mjs";
8
+ } from "./chunk-OSWV5IJB.mjs";
9
9
  import "./chunk-LGHEOHBU.mjs";
10
10
  import "./chunk-FRPWCMMG.mjs";
11
11
  import "./chunk-53MVSX3R.mjs";
@@ -14,7 +14,7 @@ import "./chunk-V6WE6JSZ.mjs";
14
14
  import "./chunk-3XNTL3FQ.mjs";
15
15
  import "./chunk-CGIXMLSY.mjs";
16
16
  import "./chunk-USWZZUEH.mjs";
17
- import "./chunk-H3TE3LOV.mjs";
17
+ import "./chunk-XMFTIVJH.mjs";
18
18
  import "./chunk-7HO6AEOP.mjs";
19
19
  import "./chunk-USLFXFFH.mjs";
20
20
  import "./chunk-I4CUBGVF.mjs";
@@ -23,7 +23,7 @@ import "./chunk-YW7OBE6Q.mjs";
23
23
  import "./chunk-74YTBPTY.mjs";
24
24
  import "./chunk-7ZVN3YZI.mjs";
25
25
  import "./chunk-GY2STC2S.mjs";
26
- import "./chunk-UB5UOSHB.mjs";
26
+ import "./chunk-ZVMV3THA.mjs";
27
27
  import "./chunk-ZVWA4ZQ3.mjs";
28
28
  import "./chunk-7FBJLMQH.mjs";
29
29
  import "./chunk-XIVV5IWN.mjs";
@@ -34,7 +34,7 @@ import "./chunk-P7QDS5AT.mjs";
34
34
  import "./chunk-T4QJQK76.mjs";
35
35
  import "./chunk-E6EPN5BT.mjs";
36
36
  import "./chunk-QGOHVJ6J.mjs";
37
- import "./chunk-Z5CQMP54.mjs";
37
+ import "./chunk-ARRYKOI5.mjs";
38
38
  import "./chunk-2ED5Q2GH.mjs";
39
39
  import "./chunk-GN72QAIH.mjs";
40
40
  import "./chunk-FQYDHKKU.mjs";
@@ -42,13 +42,13 @@ import "./chunk-C2ROET2V.mjs";
42
42
  import "./chunk-QKVUNGAH.mjs";
43
43
  import "./chunk-GQVGIUSN.mjs";
44
44
  import "./chunk-J4FNJ5IU.mjs";
45
- import "./chunk-PFE6GMNE.mjs";
45
+ import "./chunk-HMRFYJK6.mjs";
46
46
  import "./chunk-RHIFZZUI.mjs";
47
47
  import "./chunk-LM2YNO4U.mjs";
48
- import "./chunk-3WIRO3N7.mjs";
49
- import "./chunk-KLA67WKG.mjs";
48
+ import "./chunk-BWJC5ZOD.mjs";
49
+ import "./chunk-B3FFOIWS.mjs";
50
50
  import "./chunk-ITN462YM.mjs";
51
- import "./chunk-B232I2GP.mjs";
51
+ import "./chunk-3HIVDX7J.mjs";
52
52
  import "./chunk-JK25YEN6.mjs";
53
53
  import "./chunk-GS6K5OCM.mjs";
54
54
  import "./chunk-44ZCFSKE.mjs";
@@ -59,6 +59,26 @@ function subscribeFarmCacheTask(listener) {
59
59
  }
60
60
  __name(subscribeFarmCacheTask, "subscribeFarmCacheTask");
61
61
 
62
+ // src/server-fn-error.ts
63
+ var FARM_SERVER_FN_FAILURE_SYMBOL = /* @__PURE__ */ Symbol.for("farm.server-fn.failure");
64
+ function isServerFnFailure(value) {
65
+ if (!value || typeof value !== "object") return false;
66
+ const candidate = value;
67
+ return candidate[FARM_SERVER_FN_FAILURE_SYMBOL] === true && candidate.name === "ServerFnFailure" && typeof candidate.code === "string" && Number.isInteger(candidate.status) && (candidate.status ?? 0) >= 400 && (candidate.status ?? 0) <= 599;
68
+ }
69
+ __name(isServerFnFailure, "isServerFnFailure");
70
+ function serializeServerFnFailure(value) {
71
+ if (!isServerFnFailure(value)) return null;
72
+ return {
73
+ name: "ServerFnFailure",
74
+ message: value.message,
75
+ code: value.code,
76
+ status: value.status,
77
+ data: value.data
78
+ };
79
+ }
80
+ __name(serializeServerFnFailure, "serializeServerFnFailure");
81
+
62
82
  // src/server-action-security.ts
63
83
  var DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT = 1e6;
64
84
  var _ServerActionRequestError = class _ServerActionRequestError extends Error {
@@ -172,7 +192,9 @@ function createServerActionRequestErrorResponse(error) {
172
192
  });
173
193
  }
174
194
  __name(createServerActionRequestErrorResponse, "createServerActionRequestErrorResponse");
175
- function sanitizeServerActionError(_error) {
195
+ function sanitizeServerActionError(error) {
196
+ const declaredFailure = serializeServerFnFailure(error);
197
+ if (declaredFailure) return declaredFailure;
176
198
  return {
177
199
  name: "ServerActionError",
178
200
  message: "Server function failed"
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server-action-security.ts","../src/cache-invalidation.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { subscribeFarmCacheInvalidation, subscribeFarmCacheTask } from \"./cache-invalidation\";\n\nexport const DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT = 1_000_000;\n\nexport interface FarmServerActionsConfig {\n /** Additional trusted origins or host patterns, such as https://app.example.com. */\n allowedOrigins?: readonly string[];\n /** Maximum encoded request body size in bytes or as a size string such as \"1mb\". */\n bodySizeLimit?: number | string;\n}\n\nexport interface ResolvedFarmServerActionsConfig {\n allowedOrigins: readonly string[];\n bodySizeLimit: number;\n}\n\nexport type ServerActionRequestKind = \"javascript\" | \"form\";\n\nexport interface PreparedServerActionRequest {\n body: string | FormData;\n contentType: string;\n}\n\nexport type SanitizedServerActionError = {\n name: \"ServerActionError\";\n message: \"Server function failed\";\n};\n\ntype ServerActionRequestErrorCode =\n | \"BODY_TOO_LARGE\"\n | \"INVALID_ACTION_ID\"\n | \"INVALID_BODY\"\n | \"INVALID_CONTENT_LENGTH\"\n | \"INVALID_METHOD\"\n | \"INVALID_ORIGIN\"\n | \"MISSING_ORIGIN\"\n | \"UNSUPPORTED_CONTENT_TYPE\";\n\nexport class ServerActionRequestError extends Error {\n readonly code: ServerActionRequestErrorCode;\n readonly status: number;\n\n constructor(code: ServerActionRequestErrorCode, status: number, message: string) {\n super(message);\n this.name = \"ServerActionRequestError\";\n this.code = code;\n this.status = status;\n }\n}\n\ntype ServerActionExecutionContext = {\n request: Request;\n signal: AbortSignal;\n invalidations: Set<string>;\n cacheTasks: Set<Promise<void>>;\n};\n\nconst SERVER_ACTION_STORAGE_KEY = Symbol.for(\"farm.serverActionStorage\");\nconst FALLBACK_ABORT_CONTROLLER_KEY = Symbol.for(\"farm.serverActionFallbackAbortController\");\nconst FORM_ACTION_CONTENT_TYPES = new Set([\n \"application/x-www-form-urlencoded\",\n \"multipart/form-data\",\n]);\nconst JAVASCRIPT_ACTION_CONTENT_TYPES = new Set([\n \"application/octet-stream\",\n \"application/x-www-form-urlencoded\",\n \"multipart/form-data\",\n \"text/plain\",\n]);\n\ntype GlobalWithServerActionStorage = typeof globalThis & {\n [SERVER_ACTION_STORAGE_KEY]?: AsyncLocalStorage<ServerActionExecutionContext>;\n [FALLBACK_ABORT_CONTROLLER_KEY]?: AbortController;\n};\n\nexport function resolveServerActionsConfig(\n config: FarmServerActionsConfig | undefined,\n): ResolvedFarmServerActionsConfig {\n const allowedOrigins = (config?.allowedOrigins ?? []).map(normalizeAllowedOriginPattern);\n const bodySizeLimit = parseBodySizeLimit(\n config?.bodySizeLimit ?? DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT,\n );\n\n return Object.freeze({\n allowedOrigins: Object.freeze(allowedOrigins),\n bodySizeLimit,\n });\n}\n\nexport function validateServerActionRequest(\n request: Request,\n config: ResolvedFarmServerActionsConfig,\n): void {\n if (request.method.toUpperCase() !== \"POST\") {\n throw new ServerActionRequestError(\n \"INVALID_METHOD\",\n 405,\n \"Server actions only accept POST requests\",\n );\n }\n\n const requestUrl = new URL(request.url);\n const sourceOrigin = getRequestSourceOrigin(request);\n const fetchSite = request.headers.get(\"sec-fetch-site\")?.trim().toLowerCase();\n\n if (!sourceOrigin) {\n if (fetchSite !== \"same-origin\") {\n throw new ServerActionRequestError(\n \"MISSING_ORIGIN\",\n 403,\n \"Server action request is missing same-origin metadata\",\n );\n }\n return;\n }\n\n const matchesRequest =\n sourceOrigin === requestUrl.origin || matchesHostHeader(sourceOrigin, request);\n const matchesConfiguredOrigin = config.allowedOrigins.some((pattern) =>\n matchesAllowedOrigin(sourceOrigin, pattern),\n );\n\n if (!matchesRequest && !matchesConfiguredOrigin) {\n throw new ServerActionRequestError(\n \"INVALID_ORIGIN\",\n 403,\n \"Server action origin does not match the request origin\",\n );\n }\n\n if (fetchSite === \"cross-site\" && !matchesConfiguredOrigin) {\n throw new ServerActionRequestError(\n \"INVALID_ORIGIN\",\n 403,\n \"Cross-site server action request was rejected\",\n );\n }\n}\n\nexport async function prepareServerActionRequest(\n request: Request,\n config: ResolvedFarmServerActionsConfig,\n kind: ServerActionRequestKind,\n actionId?: string | null,\n): Promise<PreparedServerActionRequest> {\n validateServerActionRequest(request, config);\n\n if (kind === \"javascript\") {\n validateActionId(actionId);\n }\n\n const contentType = getSupportedContentType(request, kind);\n const bytes = await readBodyWithLimit(request, config.bodySizeLimit);\n\n if (kind === \"form\" || contentType === \"multipart/form-data\") {\n return {\n body: await parseFormData(request, bytes),\n contentType,\n };\n }\n\n return {\n body: new TextDecoder().decode(bytes),\n contentType,\n };\n}\n\nexport function createServerActionRequestErrorResponse(error: unknown): Response | null {\n if (!(error instanceof ServerActionRequestError)) {\n return null;\n }\n\n const headers = new Headers({\n \"cache-control\": \"no-store\",\n \"content-type\": \"text/plain; charset=utf-8\",\n \"x-content-type-options\": \"nosniff\",\n });\n if (error.status === 405) {\n headers.set(\"allow\", \"POST\");\n }\n\n return new Response(getPublicErrorMessage(error.status), {\n status: error.status,\n headers,\n });\n}\n\nexport function sanitizeServerActionError(_error: unknown): SanitizedServerActionError {\n return {\n name: \"ServerActionError\",\n message: \"Server function failed\",\n };\n}\n\nexport async function runWithServerActionRequest<T>(\n request: Request,\n callback: () => T | Promise<T>,\n): Promise<T> {\n throwIfAborted(request.signal);\n const context: ServerActionExecutionContext = {\n request,\n signal: request.signal,\n invalidations: new Set(),\n cacheTasks: new Set(),\n };\n\n return getServerActionStorage().run(context, async () => {\n try {\n const result = await callback();\n await Promise.all(context.cacheTasks);\n return result;\n } catch (error) {\n await Promise.allSettled(context.cacheTasks);\n throw error;\n }\n });\n}\n\nexport function getServerActionExecutionContext(): ServerActionExecutionContext | undefined {\n return getServerActionStorage().getStore();\n}\n\nexport function getServerActionSignal(): AbortSignal {\n return getServerActionExecutionContext()?.signal ?? getFallbackAbortController().signal;\n}\n\nexport function getServerActionInvalidations(): readonly string[] {\n return Array.from(getServerActionExecutionContext()?.invalidations ?? []);\n}\n\nsubscribeFarmCacheInvalidation((key) => {\n getServerActionExecutionContext()?.invalidations.add(key);\n});\n\nsubscribeFarmCacheTask((task) => {\n getServerActionExecutionContext()?.cacheTasks.add(task);\n});\n\nfunction getServerActionStorage(): AsyncLocalStorage<ServerActionExecutionContext> {\n const globalState = globalThis as GlobalWithServerActionStorage;\n if (!globalState[SERVER_ACTION_STORAGE_KEY]) {\n globalState[SERVER_ACTION_STORAGE_KEY] = new AsyncLocalStorage<ServerActionExecutionContext>();\n }\n return globalState[SERVER_ACTION_STORAGE_KEY]!;\n}\n\nfunction getFallbackAbortController(): AbortController {\n const globalState = globalThis as GlobalWithServerActionStorage;\n if (!globalState[FALLBACK_ABORT_CONTROLLER_KEY]) {\n globalState[FALLBACK_ABORT_CONTROLLER_KEY] = new AbortController();\n }\n return globalState[FALLBACK_ABORT_CONTROLLER_KEY]!;\n}\n\nfunction parseBodySizeLimit(value: number | string): number {\n if (typeof value === \"number\") {\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw new TypeError(\"serverActions.bodySizeLimit must be a positive safe integer\");\n }\n return value;\n }\n\n const match = value\n .trim()\n .toLowerCase()\n .match(/^(\\d+(?:\\.\\d+)?)\\s*(b|kb|mb|gb|kib|mib|gib)?$/);\n if (!match) {\n throw new TypeError(\n 'serverActions.bodySizeLimit must be bytes or a size string such as \"500kb\" or \"1mb\"',\n );\n }\n\n const amount = Number(match[1]);\n const unit = match[2] ?? \"b\";\n const multiplier: Record<string, number> = {\n b: 1,\n kb: 1_000,\n mb: 1_000_000,\n gb: 1_000_000_000,\n kib: 1_024,\n mib: 1_048_576,\n gib: 1_073_741_824,\n };\n const bytes = Math.floor(amount * multiplier[unit]);\n\n if (!Number.isSafeInteger(bytes) || bytes <= 0) {\n throw new TypeError(\"serverActions.bodySizeLimit must resolve to a positive safe integer\");\n }\n\n return bytes;\n}\n\nfunction normalizeAllowedOriginPattern(value: string): string {\n const pattern = value.trim().toLowerCase();\n if (!pattern) {\n throw new TypeError(\"serverActions.allowedOrigins cannot contain empty values\");\n }\n\n if (pattern.includes(\"*\")) {\n if (!/^(?:https?:\\/\\/)?\\*\\.[a-z0-9.-]+(?::\\d+)?$/.test(pattern)) {\n throw new TypeError(`Invalid serverActions.allowedOrigins pattern: ${JSON.stringify(value)}`);\n }\n return pattern;\n }\n\n if (pattern.includes(\"://\")) {\n let parsed: URL;\n try {\n parsed = new URL(pattern);\n } catch {\n throw new TypeError(`Invalid serverActions.allowedOrigins value: ${JSON.stringify(value)}`);\n }\n\n if (\n (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") ||\n parsed.username ||\n parsed.password ||\n parsed.pathname !== \"/\" ||\n parsed.search ||\n parsed.hash\n ) {\n throw new TypeError(\n `serverActions.allowedOrigins must contain origins without paths: ${JSON.stringify(value)}`,\n );\n }\n return parsed.origin;\n }\n\n if (/[/@?#]/.test(pattern)) {\n throw new TypeError(\n `serverActions.allowedOrigins must contain origins or hosts: ${JSON.stringify(value)}`,\n );\n }\n\n try {\n return new URL(`http://${pattern}`).host;\n } catch {\n throw new TypeError(`Invalid serverActions.allowedOrigins value: ${JSON.stringify(value)}`);\n }\n}\n\nfunction getRequestSourceOrigin(request: Request): string | null {\n const origin = request.headers.get(\"origin\")?.trim();\n if (origin) {\n return parseSourceOrigin(origin);\n }\n\n const referer = request.headers.get(\"referer\")?.trim();\n if (referer) {\n return parseSourceOrigin(referer);\n }\n\n return null;\n}\n\nfunction parseSourceOrigin(value: string): string {\n if (value === \"null\") {\n throw new ServerActionRequestError(\"INVALID_ORIGIN\", 403, \"Opaque origins are not allowed\");\n }\n\n try {\n const parsed = new URL(value);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new Error(\"Unsupported protocol\");\n }\n return parsed.origin;\n } catch {\n throw new ServerActionRequestError(\"INVALID_ORIGIN\", 403, \"Invalid request origin\");\n }\n}\n\nfunction matchesHostHeader(sourceOrigin: string, request: Request): boolean {\n const host = request.headers.get(\"host\")?.trim().toLowerCase();\n if (!host) return false;\n\n try {\n const source = new URL(sourceOrigin);\n const target = new URL(request.url);\n return source.protocol === target.protocol && source.host.toLowerCase() === host;\n } catch {\n return false;\n }\n}\n\nfunction matchesAllowedOrigin(sourceOrigin: string, pattern: string): boolean {\n const source = new URL(sourceOrigin);\n if (!pattern.includes(\"*\")) {\n return pattern.includes(\"://\") ? source.origin === pattern : source.host === pattern;\n }\n\n const schemeEnd = pattern.indexOf(\"://\");\n const scheme = schemeEnd === -1 ? null : pattern.slice(0, schemeEnd + 1);\n const hostPattern = pattern.slice(schemeEnd === -1 ? 0 : schemeEnd + 3);\n const [wildcardHost, port] = splitHostAndPort(hostPattern);\n const baseHost = wildcardHost.slice(2);\n\n if (scheme && source.protocol !== scheme) return false;\n if (port && getEffectivePort(source) !== port) return false;\n if (!port && source.port) return false;\n\n return source.hostname.endsWith(`.${baseHost}`) && source.hostname !== baseHost;\n}\n\nfunction splitHostAndPort(value: string): [string, string | null] {\n const separator = value.lastIndexOf(\":\");\n if (separator === -1) return [value, null];\n return [value.slice(0, separator), value.slice(separator + 1)];\n}\n\nfunction getEffectivePort(url: URL): string {\n if (url.port) return url.port;\n if (url.protocol === \"https:\") return \"443\";\n if (url.protocol === \"http:\") return \"80\";\n return \"\";\n}\n\nfunction validateActionId(actionId?: string | null): asserts actionId is string {\n if (!actionId || actionId.length > 4096 || hasControlCharacters(actionId)) {\n throw new ServerActionRequestError(\"INVALID_ACTION_ID\", 400, \"Invalid server action id\");\n }\n}\n\nfunction hasControlCharacters(value: string): boolean {\n for (let index = 0; index < value.length; index++) {\n const code = value.charCodeAt(index);\n if (code <= 31 || code === 127) return true;\n }\n return false;\n}\n\nfunction getSupportedContentType(request: Request, kind: ServerActionRequestKind): string {\n const header = request.headers.get(\"content-type\")?.trim().toLowerCase();\n const contentType = header?.split(\";\", 1)[0]?.trim() ?? \"\";\n const supported = kind === \"form\" ? FORM_ACTION_CONTENT_TYPES : JAVASCRIPT_ACTION_CONTENT_TYPES;\n\n if (!supported.has(contentType)) {\n throw new ServerActionRequestError(\n \"UNSUPPORTED_CONTENT_TYPE\",\n 415,\n \"Unsupported server action content type\",\n );\n }\n\n return contentType;\n}\n\nasync function readBodyWithLimit(request: Request, limit: number): Promise<Uint8Array> {\n const contentLength = request.headers.get(\"content-length\")?.trim();\n if (contentLength) {\n if (!/^\\d+$/.test(contentLength)) {\n throw new ServerActionRequestError(\n \"INVALID_CONTENT_LENGTH\",\n 400,\n \"Invalid content-length header\",\n );\n }\n if (Number(contentLength) > limit) {\n throw new ServerActionRequestError(\"BODY_TOO_LARGE\", 413, \"Server action body is too large\");\n }\n }\n\n throwIfAborted(request.signal);\n if (!request.body) return new Uint8Array();\n\n const reader = request.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n const cancelBodyRead = () => {\n void reader.cancel(request.signal.reason).catch(() => {});\n };\n\n request.signal.addEventListener(\"abort\", cancelBodyRead, { once: true });\n\n try {\n while (true) {\n throwIfAborted(request.signal);\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n\n total += value.byteLength;\n if (total > limit) {\n await reader.cancel(\"Server action body is too large\");\n throw new ServerActionRequestError(\n \"BODY_TOO_LARGE\",\n 413,\n \"Server action body is too large\",\n );\n }\n chunks.push(value);\n }\n } catch (error) {\n if (request.signal.aborted) throwIfAborted(request.signal);\n throw error;\n } finally {\n request.signal.removeEventListener(\"abort\", cancelBodyRead);\n reader.releaseLock();\n }\n\n throwIfAborted(request.signal);\n const body = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return body;\n}\n\nasync function parseFormData(request: Request, bytes: Uint8Array): Promise<FormData> {\n const body = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(body).set(bytes);\n const copy = new Request(request.url, {\n method: \"POST\",\n headers: request.headers,\n body,\n });\n\n try {\n return await copy.formData();\n } catch {\n throw new ServerActionRequestError(\"INVALID_BODY\", 400, \"Invalid server action form body\");\n }\n}\n\nfunction getPublicErrorMessage(status: number): string {\n switch (status) {\n case 400:\n return \"Bad Request\";\n case 403:\n return \"Forbidden\";\n case 405:\n return \"Method Not Allowed\";\n case 413:\n return \"Payload Too Large\";\n case 415:\n return \"Unsupported Media Type\";\n default:\n return \"Server Action Request Failed\";\n }\n}\n\nfunction throwIfAborted(signal: AbortSignal): void {\n if (!signal.aborted) return;\n if (signal.reason !== undefined) throw signal.reason;\n throw new DOMException(\"The operation was aborted\", \"AbortError\");\n}\n","export type FarmCacheInvalidationListener = (key: string) => void;\nexport type FarmCacheTaskListener = (task: Promise<void>) => void;\n\nexport const FARM_CACHE_INVALIDATION_HEADER = \"x-farm-cache-invalidations\";\n\ntype FarmCacheInvalidationState = {\n listeners: Set<FarmCacheInvalidationListener>;\n taskListeners: Set<FarmCacheTaskListener>;\n};\n\nconst FARM_CACHE_INVALIDATION_STATE = Symbol.for(\"farm.cacheInvalidationState\");\nconst globalState = globalThis as typeof globalThis & {\n [FARM_CACHE_INVALIDATION_STATE]?: FarmCacheInvalidationState;\n};\n\nfunction getFarmCacheInvalidationState(): FarmCacheInvalidationState {\n return (globalState[FARM_CACHE_INVALIDATION_STATE] ??= {\n listeners: new Set(),\n taskListeners: new Set(),\n });\n}\n\nexport function notifyFarmCacheInvalidation(key: string): void {\n if (typeof key !== \"string\" || key.length === 0) return;\n\n for (const listener of getFarmCacheInvalidationState().listeners) {\n listener(key);\n }\n}\n\nexport function notifyFarmCacheTask(task: Promise<void>): void {\n for (const listener of getFarmCacheInvalidationState().taskListeners) {\n listener(task);\n }\n}\n\nexport function applyFarmCacheInvalidations(keys: unknown): void {\n if (!Array.isArray(keys)) return;\n\n for (const key of keys) {\n if (typeof key === \"string\") {\n notifyFarmCacheInvalidation(key);\n }\n }\n}\n\nexport function encodeFarmCacheInvalidations(keys: readonly string[]): string | null {\n const normalized = Array.from(\n new Set(keys.filter((key) => typeof key === \"string\" && key.length > 0)),\n );\n if (normalized.length === 0) return null;\n return encodeURIComponent(JSON.stringify(normalized));\n}\n\nexport function decodeFarmCacheInvalidations(value: string | null | undefined): readonly string[] {\n if (!value) return [];\n\n try {\n const parsed = JSON.parse(decodeURIComponent(value));\n return Array.isArray(parsed)\n ? Array.from(\n new Set(parsed.filter((key): key is string => typeof key === \"string\" && key.length > 0)),\n )\n : [];\n } catch {\n return [];\n }\n}\n\nexport function subscribeFarmCacheInvalidation(\n listener: FarmCacheInvalidationListener,\n): () => void {\n const state = getFarmCacheInvalidationState();\n state.listeners.add(listener);\n return () => state.listeners.delete(listener);\n}\n\nexport function subscribeFarmCacheTask(listener: FarmCacheTaskListener): () => void {\n const state = getFarmCacheInvalidationState();\n state.taskListeners.add(listener);\n return () => state.taskListeners.delete(listener);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAAkC;;;ACUlC,IAAM,gCAAgC,uBAAO,IAAI,6BAA6B;AAC9E,IAAM,cAAc;AAIpB,SAAS,gCAA4D;AACnE,SAAQ,4FAA+C;AAAA,IACrD,WAAW,oBAAI,IAAI;AAAA,IACnB,eAAe,oBAAI,IAAI;AAAA,EACzB;AACF;AALS;AAsDF,SAAS,+BACd,UACY;AACZ,QAAM,QAAQ,8BAA8B;AAC5C,QAAM,UAAU,IAAI,QAAQ;AAC5B,SAAO,MAAM,MAAM,UAAU,OAAO,QAAQ;AAC9C;AANgB;AAQT,SAAS,uBAAuB,UAA6C;AAClF,QAAM,QAAQ,8BAA8B;AAC5C,QAAM,cAAc,IAAI,QAAQ;AAChC,SAAO,MAAM,MAAM,cAAc,OAAO,QAAQ;AAClD;AAJgB;;;AD1ET,IAAM,wCAAwC;AAoC9C,IAAM,4BAAN,MAAM,kCAAiC,MAAM;AAAA,EAIlD,YAAY,MAAoC,QAAgB,SAAiB;AAC/E,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AAVoD;AAA7C,IAAM,2BAAN;AAmBP,IAAM,4BAA4B,uBAAO,IAAI,0BAA0B;AACvE,IAAM,gCAAgC,uBAAO,IAAI,0CAA0C;AAC3F,IAAM,4BAA4B,oBAAI,IAAI;AAAA,EACxC;AAAA,EACA;AACF,CAAC;AACD,IAAM,kCAAkC,oBAAI,IAAI;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAOM,SAAS,2BACd,QACiC;AACjC,QAAM,kBAAkB,QAAQ,kBAAkB,CAAC,GAAG,IAAI,6BAA6B;AACvF,QAAM,gBAAgB;AAAA,IACpB,QAAQ,iBAAiB;AAAA,EAC3B;AAEA,SAAO,OAAO,OAAO;AAAA,IACnB,gBAAgB,OAAO,OAAO,cAAc;AAAA,IAC5C;AAAA,EACF,CAAC;AACH;AAZgB;AAcT,SAAS,4BACd,SACA,QACM;AACN,MAAI,QAAQ,OAAO,YAAY,MAAM,QAAQ;AAC3C,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,IAAI,IAAI,QAAQ,GAAG;AACtC,QAAM,eAAe,uBAAuB,OAAO;AACnD,QAAM,YAAY,QAAQ,QAAQ,IAAI,gBAAgB,GAAG,KAAK,EAAE,YAAY;AAE5E,MAAI,CAAC,cAAc;AACjB,QAAI,cAAc,eAAe;AAC/B,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA;AAAA,EACF;AAEA,QAAM,iBACJ,iBAAiB,WAAW,UAAU,kBAAkB,cAAc,OAAO;AAC/E,QAAM,0BAA0B,OAAO,eAAe;AAAA,IAAK,CAAC,YAC1D,qBAAqB,cAAc,OAAO;AAAA,EAC5C;AAEA,MAAI,CAAC,kBAAkB,CAAC,yBAAyB;AAC/C,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,gBAAgB,CAAC,yBAAyB;AAC1D,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAhDgB;AAkDhB,eAAsB,2BACpB,SACA,QACA,MACA,UACsC;AACtC,8BAA4B,SAAS,MAAM;AAE3C,MAAI,SAAS,cAAc;AACzB,qBAAiB,QAAQ;AAAA,EAC3B;AAEA,QAAM,cAAc,wBAAwB,SAAS,IAAI;AACzD,QAAM,QAAQ,MAAM,kBAAkB,SAAS,OAAO,aAAa;AAEnE,MAAI,SAAS,UAAU,gBAAgB,uBAAuB;AAC5D,WAAO;AAAA,MACL,MAAM,MAAM,cAAc,SAAS,KAAK;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,IACpC;AAAA,EACF;AACF;AA1BsB;AA4Bf,SAAS,uCAAuC,OAAiC;AACtF,MAAI,EAAE,iBAAiB,2BAA2B;AAChD,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,IAAI,QAAQ;AAAA,IAC1B,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,0BAA0B;AAAA,EAC5B,CAAC;AACD,MAAI,MAAM,WAAW,KAAK;AACxB,YAAQ,IAAI,SAAS,MAAM;AAAA,EAC7B;AAEA,SAAO,IAAI,SAAS,sBAAsB,MAAM,MAAM,GAAG;AAAA,IACvD,QAAQ,MAAM;AAAA,IACd;AAAA,EACF,CAAC;AACH;AAlBgB;AAoBT,SAAS,0BAA0B,QAA6C;AACrF,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AACF;AALgB;AAOhB,eAAsB,2BACpB,SACA,UACY;AACZ,iBAAe,QAAQ,MAAM;AAC7B,QAAM,UAAwC;AAAA,IAC5C;AAAA,IACA,QAAQ,QAAQ;AAAA,IAChB,eAAe,oBAAI,IAAI;AAAA,IACvB,YAAY,oBAAI,IAAI;AAAA,EACtB;AAEA,SAAO,uBAAuB,EAAE,IAAI,SAAS,YAAY;AACvD,QAAI;AACF,YAAM,SAAS,MAAM,SAAS;AAC9B,YAAM,QAAQ,IAAI,QAAQ,UAAU;AACpC,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,QAAQ,WAAW,QAAQ,UAAU;AAC3C,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AACH;AAtBsB;AAwBf,SAAS,kCAA4E;AAC1F,SAAO,uBAAuB,EAAE,SAAS;AAC3C;AAFgB;AAIT,SAAS,wBAAqC;AACnD,SAAO,gCAAgC,GAAG,UAAU,2BAA2B,EAAE;AACnF;AAFgB;AAIT,SAAS,+BAAkD;AAChE,SAAO,MAAM,KAAK,gCAAgC,GAAG,iBAAiB,CAAC,CAAC;AAC1E;AAFgB;AAIhB,+BAA+B,CAAC,QAAQ;AACtC,kCAAgC,GAAG,cAAc,IAAI,GAAG;AAC1D,CAAC;AAED,uBAAuB,CAAC,SAAS;AAC/B,kCAAgC,GAAG,WAAW,IAAI,IAAI;AACxD,CAAC;AAED,SAAS,yBAA0E;AACjF,QAAMA,eAAc;AACpB,MAAI,CAACA,aAAY,yBAAyB,GAAG;AAC3C,IAAAA,aAAY,yBAAyB,IAAI,IAAI,0CAAgD;AAAA,EAC/F;AACA,SAAOA,aAAY,yBAAyB;AAC9C;AANS;AAQT,SAAS,6BAA8C;AACrD,QAAMA,eAAc;AACpB,MAAI,CAACA,aAAY,6BAA6B,GAAG;AAC/C,IAAAA,aAAY,6BAA6B,IAAI,IAAI,gBAAgB;AAAA,EACnE;AACA,SAAOA,aAAY,6BAA6B;AAClD;AANS;AAQT,SAAS,mBAAmB,OAAgC;AAC1D,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,CAAC,OAAO,cAAc,KAAK,KAAK,SAAS,GAAG;AAC9C,YAAM,IAAI,UAAU,6DAA6D;AAAA,IACnF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MACX,KAAK,EACL,YAAY,EACZ,MAAM,+CAA+C;AACxD,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,OAAO,MAAM,CAAC,CAAC;AAC9B,QAAM,OAAO,MAAM,CAAC,KAAK;AACzB,QAAM,aAAqC;AAAA,IACzC,GAAG;AAAA,IACH,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACA,QAAM,QAAQ,KAAK,MAAM,SAAS,WAAW,IAAI,CAAC;AAElD,MAAI,CAAC,OAAO,cAAc,KAAK,KAAK,SAAS,GAAG;AAC9C,UAAM,IAAI,UAAU,qEAAqE;AAAA,EAC3F;AAEA,SAAO;AACT;AApCS;AAsCT,SAAS,8BAA8B,OAAuB;AAC5D,QAAM,UAAU,MAAM,KAAK,EAAE,YAAY;AACzC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,UAAU,0DAA0D;AAAA,EAChF;AAEA,MAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,QAAI,CAAC,6CAA6C,KAAK,OAAO,GAAG;AAC/D,YAAM,IAAI,UAAU,iDAAiD,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,IAC9F;AACA,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,SAAS,KAAK,GAAG;AAC3B,QAAI;AACJ,QAAI;AACF,eAAS,IAAI,IAAI,OAAO;AAAA,IAC1B,QAAQ;AACN,YAAM,IAAI,UAAU,+CAA+C,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,IAC5F;AAEA,QACG,OAAO,aAAa,WAAW,OAAO,aAAa,YACpD,OAAO,YACP,OAAO,YACP,OAAO,aAAa,OACpB,OAAO,UACP,OAAO,MACP;AACA,YAAM,IAAI;AAAA,QACR,oEAAoE,KAAK,UAAU,KAAK,CAAC;AAAA,MAC3F;AAAA,IACF;AACA,WAAO,OAAO;AAAA,EAChB;AAEA,MAAI,SAAS,KAAK,OAAO,GAAG;AAC1B,UAAM,IAAI;AAAA,MACR,+DAA+D,KAAK,UAAU,KAAK,CAAC;AAAA,IACtF;AAAA,EACF;AAEA,MAAI;AACF,WAAO,IAAI,IAAI,UAAU,OAAO,EAAE,EAAE;AAAA,EACtC,QAAQ;AACN,UAAM,IAAI,UAAU,+CAA+C,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,EAC5F;AACF;AA/CS;AAiDT,SAAS,uBAAuB,SAAiC;AAC/D,QAAM,SAAS,QAAQ,QAAQ,IAAI,QAAQ,GAAG,KAAK;AACnD,MAAI,QAAQ;AACV,WAAO,kBAAkB,MAAM;AAAA,EACjC;AAEA,QAAM,UAAU,QAAQ,QAAQ,IAAI,SAAS,GAAG,KAAK;AACrD,MAAI,SAAS;AACX,WAAO,kBAAkB,OAAO;AAAA,EAClC;AAEA,SAAO;AACT;AAZS;AAcT,SAAS,kBAAkB,OAAuB;AAChD,MAAI,UAAU,QAAQ;AACpB,UAAM,IAAI,yBAAyB,kBAAkB,KAAK,gCAAgC;AAAA,EAC5F;AAEA,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,KAAK;AAC5B,QAAI,OAAO,aAAa,WAAW,OAAO,aAAa,UAAU;AAC/D,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AACA,WAAO,OAAO;AAAA,EAChB,QAAQ;AACN,UAAM,IAAI,yBAAyB,kBAAkB,KAAK,wBAAwB;AAAA,EACpF;AACF;AAdS;AAgBT,SAAS,kBAAkB,cAAsB,SAA2B;AAC1E,QAAM,OAAO,QAAQ,QAAQ,IAAI,MAAM,GAAG,KAAK,EAAE,YAAY;AAC7D,MAAI,CAAC,KAAM,QAAO;AAElB,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,YAAY;AACnC,UAAM,SAAS,IAAI,IAAI,QAAQ,GAAG;AAClC,WAAO,OAAO,aAAa,OAAO,YAAY,OAAO,KAAK,YAAY,MAAM;AAAA,EAC9E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAXS;AAaT,SAAS,qBAAqB,cAAsB,SAA0B;AAC5E,QAAM,SAAS,IAAI,IAAI,YAAY;AACnC,MAAI,CAAC,QAAQ,SAAS,GAAG,GAAG;AAC1B,WAAO,QAAQ,SAAS,KAAK,IAAI,OAAO,WAAW,UAAU,OAAO,SAAS;AAAA,EAC/E;AAEA,QAAM,YAAY,QAAQ,QAAQ,KAAK;AACvC,QAAM,SAAS,cAAc,KAAK,OAAO,QAAQ,MAAM,GAAG,YAAY,CAAC;AACvE,QAAM,cAAc,QAAQ,MAAM,cAAc,KAAK,IAAI,YAAY,CAAC;AACtE,QAAM,CAAC,cAAc,IAAI,IAAI,iBAAiB,WAAW;AACzD,QAAM,WAAW,aAAa,MAAM,CAAC;AAErC,MAAI,UAAU,OAAO,aAAa,OAAQ,QAAO;AACjD,MAAI,QAAQ,iBAAiB,MAAM,MAAM,KAAM,QAAO;AACtD,MAAI,CAAC,QAAQ,OAAO,KAAM,QAAO;AAEjC,SAAO,OAAO,SAAS,SAAS,IAAI,QAAQ,EAAE,KAAK,OAAO,aAAa;AACzE;AAjBS;AAmBT,SAAS,iBAAiB,OAAwC;AAChE,QAAM,YAAY,MAAM,YAAY,GAAG;AACvC,MAAI,cAAc,GAAI,QAAO,CAAC,OAAO,IAAI;AACzC,SAAO,CAAC,MAAM,MAAM,GAAG,SAAS,GAAG,MAAM,MAAM,YAAY,CAAC,CAAC;AAC/D;AAJS;AAMT,SAAS,iBAAiB,KAAkB;AAC1C,MAAI,IAAI,KAAM,QAAO,IAAI;AACzB,MAAI,IAAI,aAAa,SAAU,QAAO;AACtC,MAAI,IAAI,aAAa,QAAS,QAAO;AACrC,SAAO;AACT;AALS;AAOT,SAAS,iBAAiB,UAAsD;AAC9E,MAAI,CAAC,YAAY,SAAS,SAAS,QAAQ,qBAAqB,QAAQ,GAAG;AACzE,UAAM,IAAI,yBAAyB,qBAAqB,KAAK,0BAA0B;AAAA,EACzF;AACF;AAJS;AAMT,SAAS,qBAAqB,OAAwB;AACpD,WAAS,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS;AACjD,UAAM,OAAO,MAAM,WAAW,KAAK;AACnC,QAAI,QAAQ,MAAM,SAAS,IAAK,QAAO;AAAA,EACzC;AACA,SAAO;AACT;AANS;AAQT,SAAS,wBAAwB,SAAkB,MAAuC;AACxF,QAAM,SAAS,QAAQ,QAAQ,IAAI,cAAc,GAAG,KAAK,EAAE,YAAY;AACvE,QAAM,cAAc,QAAQ,MAAM,KAAK,CAAC,EAAE,CAAC,GAAG,KAAK,KAAK;AACxD,QAAM,YAAY,SAAS,SAAS,4BAA4B;AAEhE,MAAI,CAAC,UAAU,IAAI,WAAW,GAAG;AAC/B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAdS;AAgBT,eAAe,kBAAkB,SAAkB,OAAoC;AACrF,QAAM,gBAAgB,QAAQ,QAAQ,IAAI,gBAAgB,GAAG,KAAK;AAClE,MAAI,eAAe;AACjB,QAAI,CAAC,QAAQ,KAAK,aAAa,GAAG;AAChC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,OAAO,aAAa,IAAI,OAAO;AACjC,YAAM,IAAI,yBAAyB,kBAAkB,KAAK,iCAAiC;AAAA,IAC7F;AAAA,EACF;AAEA,iBAAe,QAAQ,MAAM;AAC7B,MAAI,CAAC,QAAQ,KAAM,QAAO,IAAI,WAAW;AAEzC,QAAM,SAAS,QAAQ,KAAK,UAAU;AACtC,QAAM,SAAuB,CAAC;AAC9B,MAAI,QAAQ;AACZ,QAAM,iBAAiB,6BAAM;AAC3B,SAAK,OAAO,OAAO,QAAQ,OAAO,MAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAC1D,GAFuB;AAIvB,UAAQ,OAAO,iBAAiB,SAAS,gBAAgB,EAAE,MAAM,KAAK,CAAC;AAEvE,MAAI;AACF,WAAO,MAAM;AACX,qBAAe,QAAQ,MAAM;AAC7B,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AACV,UAAI,CAAC,MAAO;AAEZ,eAAS,MAAM;AACf,UAAI,QAAQ,OAAO;AACjB,cAAM,OAAO,OAAO,iCAAiC;AACrD,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,aAAO,KAAK,KAAK;AAAA,IACnB;AAAA,EACF,SAAS,OAAO;AACd,QAAI,QAAQ,OAAO,QAAS,gBAAe,QAAQ,MAAM;AACzD,UAAM;AAAA,EACR,UAAE;AACA,YAAQ,OAAO,oBAAoB,SAAS,cAAc;AAC1D,WAAO,YAAY;AAAA,EACrB;AAEA,iBAAe,QAAQ,MAAM;AAC7B,QAAM,OAAO,IAAI,WAAW,KAAK;AACjC,MAAI,SAAS;AACb,aAAW,SAAS,QAAQ;AAC1B,SAAK,IAAI,OAAO,MAAM;AACtB,cAAU,MAAM;AAAA,EAClB;AACA,SAAO;AACT;AA7De;AA+Df,eAAe,cAAc,SAAkB,OAAsC;AACnF,QAAM,OAAO,IAAI,YAAY,MAAM,UAAU;AAC7C,MAAI,WAAW,IAAI,EAAE,IAAI,KAAK;AAC9B,QAAM,OAAO,IAAI,QAAQ,QAAQ,KAAK;AAAA,IACpC,QAAQ;AAAA,IACR,SAAS,QAAQ;AAAA,IACjB;AAAA,EACF,CAAC;AAED,MAAI;AACF,WAAO,MAAM,KAAK,SAAS;AAAA,EAC7B,QAAQ;AACN,UAAM,IAAI,yBAAyB,gBAAgB,KAAK,iCAAiC;AAAA,EAC3F;AACF;AAde;AAgBf,SAAS,sBAAsB,QAAwB;AACrD,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAfS;AAiBT,SAAS,eAAe,QAA2B;AACjD,MAAI,CAAC,OAAO,QAAS;AACrB,MAAI,OAAO,WAAW,OAAW,OAAM,OAAO;AAC9C,QAAM,IAAI,aAAa,6BAA6B,YAAY;AAClE;AAJS;","names":["globalState"]}
1
+ {"version":3,"sources":["../src/server-action-security.ts","../src/cache-invalidation.ts","../src/server-fn-error.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { subscribeFarmCacheInvalidation, subscribeFarmCacheTask } from \"./cache-invalidation\";\nimport { serializeServerFnFailure, type SerializedServerFnFailure } from \"./server-fn-error\";\n\nexport const DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT = 1_000_000;\n\nexport interface FarmServerActionsConfig {\n /** Additional trusted origins or host patterns, such as https://app.example.com. */\n allowedOrigins?: readonly string[];\n /** Maximum encoded request body size in bytes or as a size string such as \"1mb\". */\n bodySizeLimit?: number | string;\n}\n\nexport interface ResolvedFarmServerActionsConfig {\n allowedOrigins: readonly string[];\n bodySizeLimit: number;\n}\n\nexport type ServerActionRequestKind = \"javascript\" | \"form\";\n\nexport interface PreparedServerActionRequest {\n body: string | FormData;\n contentType: string;\n}\n\nexport type SanitizedServerActionError =\n | {\n name: \"ServerActionError\";\n message: \"Server function failed\";\n }\n | SerializedServerFnFailure;\n\ntype ServerActionRequestErrorCode =\n | \"BODY_TOO_LARGE\"\n | \"INVALID_ACTION_ID\"\n | \"INVALID_BODY\"\n | \"INVALID_CONTENT_LENGTH\"\n | \"INVALID_METHOD\"\n | \"INVALID_ORIGIN\"\n | \"MISSING_ORIGIN\"\n | \"UNSUPPORTED_CONTENT_TYPE\";\n\nexport class ServerActionRequestError extends Error {\n readonly code: ServerActionRequestErrorCode;\n readonly status: number;\n\n constructor(code: ServerActionRequestErrorCode, status: number, message: string) {\n super(message);\n this.name = \"ServerActionRequestError\";\n this.code = code;\n this.status = status;\n }\n}\n\ntype ServerActionExecutionContext = {\n request: Request;\n signal: AbortSignal;\n invalidations: Set<string>;\n cacheTasks: Set<Promise<void>>;\n};\n\nconst SERVER_ACTION_STORAGE_KEY = Symbol.for(\"farm.serverActionStorage\");\nconst FALLBACK_ABORT_CONTROLLER_KEY = Symbol.for(\"farm.serverActionFallbackAbortController\");\nconst FORM_ACTION_CONTENT_TYPES = new Set([\n \"application/x-www-form-urlencoded\",\n \"multipart/form-data\",\n]);\nconst JAVASCRIPT_ACTION_CONTENT_TYPES = new Set([\n \"application/octet-stream\",\n \"application/x-www-form-urlencoded\",\n \"multipart/form-data\",\n \"text/plain\",\n]);\n\ntype GlobalWithServerActionStorage = typeof globalThis & {\n [SERVER_ACTION_STORAGE_KEY]?: AsyncLocalStorage<ServerActionExecutionContext>;\n [FALLBACK_ABORT_CONTROLLER_KEY]?: AbortController;\n};\n\nexport function resolveServerActionsConfig(\n config: FarmServerActionsConfig | undefined,\n): ResolvedFarmServerActionsConfig {\n const allowedOrigins = (config?.allowedOrigins ?? []).map(normalizeAllowedOriginPattern);\n const bodySizeLimit = parseBodySizeLimit(\n config?.bodySizeLimit ?? DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT,\n );\n\n return Object.freeze({\n allowedOrigins: Object.freeze(allowedOrigins),\n bodySizeLimit,\n });\n}\n\nexport function validateServerActionRequest(\n request: Request,\n config: ResolvedFarmServerActionsConfig,\n): void {\n if (request.method.toUpperCase() !== \"POST\") {\n throw new ServerActionRequestError(\n \"INVALID_METHOD\",\n 405,\n \"Server actions only accept POST requests\",\n );\n }\n\n const requestUrl = new URL(request.url);\n const sourceOrigin = getRequestSourceOrigin(request);\n const fetchSite = request.headers.get(\"sec-fetch-site\")?.trim().toLowerCase();\n\n if (!sourceOrigin) {\n if (fetchSite !== \"same-origin\") {\n throw new ServerActionRequestError(\n \"MISSING_ORIGIN\",\n 403,\n \"Server action request is missing same-origin metadata\",\n );\n }\n return;\n }\n\n const matchesRequest =\n sourceOrigin === requestUrl.origin || matchesHostHeader(sourceOrigin, request);\n const matchesConfiguredOrigin = config.allowedOrigins.some((pattern) =>\n matchesAllowedOrigin(sourceOrigin, pattern),\n );\n\n if (!matchesRequest && !matchesConfiguredOrigin) {\n throw new ServerActionRequestError(\n \"INVALID_ORIGIN\",\n 403,\n \"Server action origin does not match the request origin\",\n );\n }\n\n if (fetchSite === \"cross-site\" && !matchesConfiguredOrigin) {\n throw new ServerActionRequestError(\n \"INVALID_ORIGIN\",\n 403,\n \"Cross-site server action request was rejected\",\n );\n }\n}\n\nexport async function prepareServerActionRequest(\n request: Request,\n config: ResolvedFarmServerActionsConfig,\n kind: ServerActionRequestKind,\n actionId?: string | null,\n): Promise<PreparedServerActionRequest> {\n validateServerActionRequest(request, config);\n\n if (kind === \"javascript\") {\n validateActionId(actionId);\n }\n\n const contentType = getSupportedContentType(request, kind);\n const bytes = await readBodyWithLimit(request, config.bodySizeLimit);\n\n if (kind === \"form\" || contentType === \"multipart/form-data\") {\n return {\n body: await parseFormData(request, bytes),\n contentType,\n };\n }\n\n return {\n body: new TextDecoder().decode(bytes),\n contentType,\n };\n}\n\nexport function createServerActionRequestErrorResponse(error: unknown): Response | null {\n if (!(error instanceof ServerActionRequestError)) {\n return null;\n }\n\n const headers = new Headers({\n \"cache-control\": \"no-store\",\n \"content-type\": \"text/plain; charset=utf-8\",\n \"x-content-type-options\": \"nosniff\",\n });\n if (error.status === 405) {\n headers.set(\"allow\", \"POST\");\n }\n\n return new Response(getPublicErrorMessage(error.status), {\n status: error.status,\n headers,\n });\n}\n\nexport function sanitizeServerActionError(error: unknown): SanitizedServerActionError {\n const declaredFailure = serializeServerFnFailure(error);\n if (declaredFailure) return declaredFailure;\n\n return {\n name: \"ServerActionError\",\n message: \"Server function failed\",\n };\n}\n\nexport async function runWithServerActionRequest<T>(\n request: Request,\n callback: () => T | Promise<T>,\n): Promise<T> {\n throwIfAborted(request.signal);\n const context: ServerActionExecutionContext = {\n request,\n signal: request.signal,\n invalidations: new Set(),\n cacheTasks: new Set(),\n };\n\n return getServerActionStorage().run(context, async () => {\n try {\n const result = await callback();\n await Promise.all(context.cacheTasks);\n return result;\n } catch (error) {\n await Promise.allSettled(context.cacheTasks);\n throw error;\n }\n });\n}\n\nexport function getServerActionExecutionContext(): ServerActionExecutionContext | undefined {\n return getServerActionStorage().getStore();\n}\n\nexport function getServerActionSignal(): AbortSignal {\n return getServerActionExecutionContext()?.signal ?? getFallbackAbortController().signal;\n}\n\nexport function getServerActionInvalidations(): readonly string[] {\n return Array.from(getServerActionExecutionContext()?.invalidations ?? []);\n}\n\nsubscribeFarmCacheInvalidation((key) => {\n getServerActionExecutionContext()?.invalidations.add(key);\n});\n\nsubscribeFarmCacheTask((task) => {\n getServerActionExecutionContext()?.cacheTasks.add(task);\n});\n\nfunction getServerActionStorage(): AsyncLocalStorage<ServerActionExecutionContext> {\n const globalState = globalThis as GlobalWithServerActionStorage;\n if (!globalState[SERVER_ACTION_STORAGE_KEY]) {\n globalState[SERVER_ACTION_STORAGE_KEY] = new AsyncLocalStorage<ServerActionExecutionContext>();\n }\n return globalState[SERVER_ACTION_STORAGE_KEY]!;\n}\n\nfunction getFallbackAbortController(): AbortController {\n const globalState = globalThis as GlobalWithServerActionStorage;\n if (!globalState[FALLBACK_ABORT_CONTROLLER_KEY]) {\n globalState[FALLBACK_ABORT_CONTROLLER_KEY] = new AbortController();\n }\n return globalState[FALLBACK_ABORT_CONTROLLER_KEY]!;\n}\n\nfunction parseBodySizeLimit(value: number | string): number {\n if (typeof value === \"number\") {\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw new TypeError(\"serverActions.bodySizeLimit must be a positive safe integer\");\n }\n return value;\n }\n\n const match = value\n .trim()\n .toLowerCase()\n .match(/^(\\d+(?:\\.\\d+)?)\\s*(b|kb|mb|gb|kib|mib|gib)?$/);\n if (!match) {\n throw new TypeError(\n 'serverActions.bodySizeLimit must be bytes or a size string such as \"500kb\" or \"1mb\"',\n );\n }\n\n const amount = Number(match[1]);\n const unit = match[2] ?? \"b\";\n const multiplier: Record<string, number> = {\n b: 1,\n kb: 1_000,\n mb: 1_000_000,\n gb: 1_000_000_000,\n kib: 1_024,\n mib: 1_048_576,\n gib: 1_073_741_824,\n };\n const bytes = Math.floor(amount * multiplier[unit]);\n\n if (!Number.isSafeInteger(bytes) || bytes <= 0) {\n throw new TypeError(\"serverActions.bodySizeLimit must resolve to a positive safe integer\");\n }\n\n return bytes;\n}\n\nfunction normalizeAllowedOriginPattern(value: string): string {\n const pattern = value.trim().toLowerCase();\n if (!pattern) {\n throw new TypeError(\"serverActions.allowedOrigins cannot contain empty values\");\n }\n\n if (pattern.includes(\"*\")) {\n if (!/^(?:https?:\\/\\/)?\\*\\.[a-z0-9.-]+(?::\\d+)?$/.test(pattern)) {\n throw new TypeError(`Invalid serverActions.allowedOrigins pattern: ${JSON.stringify(value)}`);\n }\n return pattern;\n }\n\n if (pattern.includes(\"://\")) {\n let parsed: URL;\n try {\n parsed = new URL(pattern);\n } catch {\n throw new TypeError(`Invalid serverActions.allowedOrigins value: ${JSON.stringify(value)}`);\n }\n\n if (\n (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") ||\n parsed.username ||\n parsed.password ||\n parsed.pathname !== \"/\" ||\n parsed.search ||\n parsed.hash\n ) {\n throw new TypeError(\n `serverActions.allowedOrigins must contain origins without paths: ${JSON.stringify(value)}`,\n );\n }\n return parsed.origin;\n }\n\n if (/[/@?#]/.test(pattern)) {\n throw new TypeError(\n `serverActions.allowedOrigins must contain origins or hosts: ${JSON.stringify(value)}`,\n );\n }\n\n try {\n return new URL(`http://${pattern}`).host;\n } catch {\n throw new TypeError(`Invalid serverActions.allowedOrigins value: ${JSON.stringify(value)}`);\n }\n}\n\nfunction getRequestSourceOrigin(request: Request): string | null {\n const origin = request.headers.get(\"origin\")?.trim();\n if (origin) {\n return parseSourceOrigin(origin);\n }\n\n const referer = request.headers.get(\"referer\")?.trim();\n if (referer) {\n return parseSourceOrigin(referer);\n }\n\n return null;\n}\n\nfunction parseSourceOrigin(value: string): string {\n if (value === \"null\") {\n throw new ServerActionRequestError(\"INVALID_ORIGIN\", 403, \"Opaque origins are not allowed\");\n }\n\n try {\n const parsed = new URL(value);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new Error(\"Unsupported protocol\");\n }\n return parsed.origin;\n } catch {\n throw new ServerActionRequestError(\"INVALID_ORIGIN\", 403, \"Invalid request origin\");\n }\n}\n\nfunction matchesHostHeader(sourceOrigin: string, request: Request): boolean {\n const host = request.headers.get(\"host\")?.trim().toLowerCase();\n if (!host) return false;\n\n try {\n const source = new URL(sourceOrigin);\n const target = new URL(request.url);\n return source.protocol === target.protocol && source.host.toLowerCase() === host;\n } catch {\n return false;\n }\n}\n\nfunction matchesAllowedOrigin(sourceOrigin: string, pattern: string): boolean {\n const source = new URL(sourceOrigin);\n if (!pattern.includes(\"*\")) {\n return pattern.includes(\"://\") ? source.origin === pattern : source.host === pattern;\n }\n\n const schemeEnd = pattern.indexOf(\"://\");\n const scheme = schemeEnd === -1 ? null : pattern.slice(0, schemeEnd + 1);\n const hostPattern = pattern.slice(schemeEnd === -1 ? 0 : schemeEnd + 3);\n const [wildcardHost, port] = splitHostAndPort(hostPattern);\n const baseHost = wildcardHost.slice(2);\n\n if (scheme && source.protocol !== scheme) return false;\n if (port && getEffectivePort(source) !== port) return false;\n if (!port && source.port) return false;\n\n return source.hostname.endsWith(`.${baseHost}`) && source.hostname !== baseHost;\n}\n\nfunction splitHostAndPort(value: string): [string, string | null] {\n const separator = value.lastIndexOf(\":\");\n if (separator === -1) return [value, null];\n return [value.slice(0, separator), value.slice(separator + 1)];\n}\n\nfunction getEffectivePort(url: URL): string {\n if (url.port) return url.port;\n if (url.protocol === \"https:\") return \"443\";\n if (url.protocol === \"http:\") return \"80\";\n return \"\";\n}\n\nfunction validateActionId(actionId?: string | null): asserts actionId is string {\n if (!actionId || actionId.length > 4096 || hasControlCharacters(actionId)) {\n throw new ServerActionRequestError(\"INVALID_ACTION_ID\", 400, \"Invalid server action id\");\n }\n}\n\nfunction hasControlCharacters(value: string): boolean {\n for (let index = 0; index < value.length; index++) {\n const code = value.charCodeAt(index);\n if (code <= 31 || code === 127) return true;\n }\n return false;\n}\n\nfunction getSupportedContentType(request: Request, kind: ServerActionRequestKind): string {\n const header = request.headers.get(\"content-type\")?.trim().toLowerCase();\n const contentType = header?.split(\";\", 1)[0]?.trim() ?? \"\";\n const supported = kind === \"form\" ? FORM_ACTION_CONTENT_TYPES : JAVASCRIPT_ACTION_CONTENT_TYPES;\n\n if (!supported.has(contentType)) {\n throw new ServerActionRequestError(\n \"UNSUPPORTED_CONTENT_TYPE\",\n 415,\n \"Unsupported server action content type\",\n );\n }\n\n return contentType;\n}\n\nasync function readBodyWithLimit(request: Request, limit: number): Promise<Uint8Array> {\n const contentLength = request.headers.get(\"content-length\")?.trim();\n if (contentLength) {\n if (!/^\\d+$/.test(contentLength)) {\n throw new ServerActionRequestError(\n \"INVALID_CONTENT_LENGTH\",\n 400,\n \"Invalid content-length header\",\n );\n }\n if (Number(contentLength) > limit) {\n throw new ServerActionRequestError(\"BODY_TOO_LARGE\", 413, \"Server action body is too large\");\n }\n }\n\n throwIfAborted(request.signal);\n if (!request.body) return new Uint8Array();\n\n const reader = request.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n const cancelBodyRead = () => {\n void reader.cancel(request.signal.reason).catch(() => {});\n };\n\n request.signal.addEventListener(\"abort\", cancelBodyRead, { once: true });\n\n try {\n while (true) {\n throwIfAborted(request.signal);\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n\n total += value.byteLength;\n if (total > limit) {\n await reader.cancel(\"Server action body is too large\");\n throw new ServerActionRequestError(\n \"BODY_TOO_LARGE\",\n 413,\n \"Server action body is too large\",\n );\n }\n chunks.push(value);\n }\n } catch (error) {\n if (request.signal.aborted) throwIfAborted(request.signal);\n throw error;\n } finally {\n request.signal.removeEventListener(\"abort\", cancelBodyRead);\n reader.releaseLock();\n }\n\n throwIfAborted(request.signal);\n const body = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return body;\n}\n\nasync function parseFormData(request: Request, bytes: Uint8Array): Promise<FormData> {\n const body = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(body).set(bytes);\n const copy = new Request(request.url, {\n method: \"POST\",\n headers: request.headers,\n body,\n });\n\n try {\n return await copy.formData();\n } catch {\n throw new ServerActionRequestError(\"INVALID_BODY\", 400, \"Invalid server action form body\");\n }\n}\n\nfunction getPublicErrorMessage(status: number): string {\n switch (status) {\n case 400:\n return \"Bad Request\";\n case 403:\n return \"Forbidden\";\n case 405:\n return \"Method Not Allowed\";\n case 413:\n return \"Payload Too Large\";\n case 415:\n return \"Unsupported Media Type\";\n default:\n return \"Server Action Request Failed\";\n }\n}\n\nfunction throwIfAborted(signal: AbortSignal): void {\n if (!signal.aborted) return;\n if (signal.reason !== undefined) throw signal.reason;\n throw new DOMException(\"The operation was aborted\", \"AbortError\");\n}\n","export type FarmCacheInvalidationListener = (key: string) => void;\nexport type FarmCacheTaskListener = (task: Promise<void>) => void;\n\nexport const FARM_CACHE_INVALIDATION_HEADER = \"x-farm-cache-invalidations\";\n\ntype FarmCacheInvalidationState = {\n listeners: Set<FarmCacheInvalidationListener>;\n taskListeners: Set<FarmCacheTaskListener>;\n};\n\nconst FARM_CACHE_INVALIDATION_STATE = Symbol.for(\"farm.cacheInvalidationState\");\nconst globalState = globalThis as typeof globalThis & {\n [FARM_CACHE_INVALIDATION_STATE]?: FarmCacheInvalidationState;\n};\n\nfunction getFarmCacheInvalidationState(): FarmCacheInvalidationState {\n return (globalState[FARM_CACHE_INVALIDATION_STATE] ??= {\n listeners: new Set(),\n taskListeners: new Set(),\n });\n}\n\nexport function notifyFarmCacheInvalidation(key: string): void {\n if (typeof key !== \"string\" || key.length === 0) return;\n\n for (const listener of getFarmCacheInvalidationState().listeners) {\n listener(key);\n }\n}\n\nexport function notifyFarmCacheTask(task: Promise<void>): void {\n for (const listener of getFarmCacheInvalidationState().taskListeners) {\n listener(task);\n }\n}\n\nexport function applyFarmCacheInvalidations(keys: unknown): void {\n if (!Array.isArray(keys)) return;\n\n for (const key of keys) {\n if (typeof key === \"string\") {\n notifyFarmCacheInvalidation(key);\n }\n }\n}\n\nexport function encodeFarmCacheInvalidations(keys: readonly string[]): string | null {\n const normalized = Array.from(\n new Set(keys.filter((key) => typeof key === \"string\" && key.length > 0)),\n );\n if (normalized.length === 0) return null;\n return encodeURIComponent(JSON.stringify(normalized));\n}\n\nexport function decodeFarmCacheInvalidations(value: string | null | undefined): readonly string[] {\n if (!value) return [];\n\n try {\n const parsed = JSON.parse(decodeURIComponent(value));\n return Array.isArray(parsed)\n ? Array.from(\n new Set(parsed.filter((key): key is string => typeof key === \"string\" && key.length > 0)),\n )\n : [];\n } catch {\n return [];\n }\n}\n\nexport function subscribeFarmCacheInvalidation(\n listener: FarmCacheInvalidationListener,\n): () => void {\n const state = getFarmCacheInvalidationState();\n state.listeners.add(listener);\n return () => state.listeners.delete(listener);\n}\n\nexport function subscribeFarmCacheTask(listener: FarmCacheTaskListener): () => void {\n const state = getFarmCacheInvalidationState();\n state.taskListeners.add(listener);\n return () => state.taskListeners.delete(listener);\n}\n","export const FARM_SERVER_FN_FAILURE_SYMBOL = Symbol.for(\"farm.server-fn.failure\");\n\nexport type SerializedServerFnFailure = {\n name: \"ServerFnFailure\";\n message: string;\n code: string;\n status: number;\n data: unknown;\n};\n\nexport class ServerFnFailure<\n TCode extends string = string,\n TData = unknown,\n TStatus extends number = number,\n> extends Error {\n readonly name = \"ServerFnFailure\" as const;\n readonly code: TCode;\n readonly data: TData;\n readonly status: TStatus;\n\n constructor(\n code: TCode,\n data: TData,\n options: {\n status: TStatus;\n message: string;\n },\n ) {\n super(options.message);\n this.code = code;\n this.data = data;\n this.status = options.status;\n\n Object.defineProperty(this, FARM_SERVER_FN_FAILURE_SYMBOL, {\n value: true,\n enumerable: false,\n });\n }\n}\n\nexport class ServerActionError extends Error {\n readonly name = \"ServerActionError\" as const;\n\n constructor(message = \"Server function failed\") {\n super(message);\n }\n}\n\nexport function isServerFnFailure(value: unknown): value is ServerFnFailure {\n if (!value || typeof value !== \"object\") return false;\n\n const candidate = value as Partial<ServerFnFailure> & {\n [FARM_SERVER_FN_FAILURE_SYMBOL]?: unknown;\n };\n return (\n candidate[FARM_SERVER_FN_FAILURE_SYMBOL] === true &&\n candidate.name === \"ServerFnFailure\" &&\n typeof candidate.code === \"string\" &&\n Number.isInteger(candidate.status) &&\n (candidate.status ?? 0) >= 400 &&\n (candidate.status ?? 0) <= 599\n );\n}\n\nexport function serializeServerFnFailure(value: unknown): SerializedServerFnFailure | null {\n if (!isServerFnFailure(value)) return null;\n\n return {\n name: \"ServerFnFailure\",\n message: value.message,\n code: value.code,\n status: value.status,\n data: value.data,\n };\n}\n\nexport function createServerFnTransportError(value: unknown): ServerFnFailure | ServerActionError {\n if (isSerializedServerFnFailure(value)) {\n return new ServerFnFailure(value.code, value.data, {\n status: value.status,\n message: value.message,\n });\n }\n\n const message =\n value && typeof value === \"object\" && \"message\" in value && typeof value.message === \"string\"\n ? value.message\n : \"Server function failed\";\n return new ServerActionError(message);\n}\n\nfunction isSerializedServerFnFailure(value: unknown): value is SerializedServerFnFailure {\n if (!value || typeof value !== \"object\") return false;\n\n const candidate = value as Partial<SerializedServerFnFailure>;\n return (\n candidate.name === \"ServerFnFailure\" &&\n typeof candidate.message === \"string\" &&\n typeof candidate.code === \"string\" &&\n Number.isInteger(candidate.status) &&\n (candidate.status ?? 0) >= 400 &&\n (candidate.status ?? 0) <= 599 &&\n \"data\" in candidate\n );\n}\n"],"mappings":"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","names":["globalState"]}
@@ -10,7 +10,7 @@ import {
10
10
  runWithServerActionRequest,
11
11
  sanitizeServerActionError,
12
12
  validateServerActionRequest
13
- } from "./chunk-UB5UOSHB.mjs";
13
+ } from "./chunk-ZVMV3THA.mjs";
14
14
  import "./chunk-RBRLLZL5.mjs";
15
15
  import "./chunk-CGILA3WO.mjs";
16
16
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server-fn-client.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { ServerFn } from \"./server-fn\";\n\nexport type ServerFnActionStatus = \"idle\" | \"pending\" | \"success\" | \"error\";\n\nexport type ServerFnSubmit<TInput, TResult> = [unknown] extends [TInput]\n ? (input?: TInput | FormData) => Promise<TResult>\n : (input: TInput | FormData) => Promise<TResult>;\n\nexport type ServerFnOptimisticContext<TInput, TResult> = {\n input: TInput | FormData | undefined;\n formData?: FormData;\n current: TResult | null;\n};\n\nexport type UseServerFnOptions<TResult, TError extends Error = Error, TInput = unknown> = {\n initialResult?: TResult | null;\n resetOnSubmit?: boolean;\n throwOnFormError?: boolean;\n optimistic?: (context: ServerFnOptimisticContext<TInput, TResult>) => TResult | null | undefined;\n rollbackOnError?: boolean;\n onSuccess?: (result: TResult) => void;\n onError?: (error: TError) => void;\n onSettled?: (result: TResult | null, error: TError | null) => void;\n};\n\nexport type UseServerFnReturn<TInput, TResult, TError extends Error = Error> = {\n pending: boolean;\n status: ServerFnActionStatus;\n result: TResult | null;\n error: TError | null;\n submit: ServerFnSubmit<TInput, TResult>;\n formAction: (formData: FormData) => Promise<void>;\n reset: () => void;\n};\n\ntype ServerFnActionState<TResult, TError extends Error> = {\n pendingCount: number;\n status: ServerFnActionStatus;\n result: TResult | null;\n error: TError | null;\n};\n\ntype ServerFnActionCallbacks<TResult, TError extends Error> = Pick<\n UseServerFnOptions<TResult, TError, unknown>,\n \"onError\" | \"onSettled\" | \"onSuccess\" | \"throwOnFormError\"\n>;\n\nexport function useServerFn<TInput, TResult, TError extends Error = Error>(\n serverFn: ServerFn<TInput, TResult>,\n options: UseServerFnOptions<TResult, TError, TInput> = {},\n): UseServerFnReturn<TInput, TResult, TError> {\n const { initialResult = null, resetOnSubmit = true } = options;\n const callbacksRef = useRef<ServerFnActionCallbacks<TResult, TError>>({});\n const requestIdRef = useRef(0);\n const initialState: ServerFnActionState<TResult, TError> = {\n pendingCount: 0,\n status: \"idle\",\n result: initialResult,\n error: null,\n };\n const stateRef = useRef<ServerFnActionState<TResult, TError>>(initialState);\n const [state, setState] = useState<ServerFnActionState<TResult, TError>>(initialState);\n const setActionState = useCallback(\n (\n value:\n | ServerFnActionState<TResult, TError>\n | ((current: ServerFnActionState<TResult, TError>) => ServerFnActionState<TResult, TError>),\n ) => {\n setState((current) => {\n const next = typeof value === \"function\" ? value(current) : value;\n stateRef.current = next;\n return next;\n });\n },\n [],\n );\n\n callbacksRef.current = {\n onError: options.onError,\n onSettled: options.onSettled,\n onSuccess: options.onSuccess,\n throwOnFormError: options.throwOnFormError,\n };\n\n const submit = useCallback(\n async (input?: TInput | FormData) => {\n const requestId = ++requestIdRef.current;\n const formData = isFormData(input) ? input : undefined;\n const previousResult = stateRef.current.result;\n const optimisticResult = options.optimistic?.({\n input,\n formData,\n current: previousResult,\n });\n const hasOptimisticResult = optimisticResult !== undefined;\n\n setActionState((current) => ({\n pendingCount: current.pendingCount + 1,\n status: \"pending\",\n result: hasOptimisticResult ? optimisticResult : resetOnSubmit ? null : current.result,\n error: null,\n }));\n\n try {\n const result = await serverFn(input as TInput | FormData);\n const isLatestRequest = requestId === requestIdRef.current;\n\n setActionState((current) => {\n const pendingCount = Math.max(0, current.pendingCount - 1);\n\n if (!isLatestRequest) {\n return {\n ...current,\n pendingCount,\n status: pendingCount > 0 ? \"pending\" : current.status,\n };\n }\n\n return {\n pendingCount,\n status: pendingCount > 0 ? \"pending\" : \"success\",\n result,\n error: null,\n };\n });\n\n if (isLatestRequest) {\n callbacksRef.current.onSuccess?.(result);\n callbacksRef.current.onSettled?.(result, null);\n }\n\n return result;\n } catch (cause) {\n const error = normalizeServerFnError(cause) as TError;\n const isLatestRequest = requestId === requestIdRef.current;\n\n setActionState((current) => {\n const pendingCount = Math.max(0, current.pendingCount - 1);\n\n if (!isLatestRequest) {\n return {\n ...current,\n pendingCount,\n status: pendingCount > 0 ? \"pending\" : current.status,\n };\n }\n\n return {\n pendingCount,\n status: pendingCount > 0 ? \"pending\" : \"error\",\n result:\n hasOptimisticResult && options.rollbackOnError\n ? previousResult\n : resetOnSubmit\n ? null\n : current.result,\n error,\n };\n });\n\n if (isLatestRequest) {\n callbacksRef.current.onError?.(error);\n callbacksRef.current.onSettled?.(null, error);\n }\n\n throw error;\n }\n },\n [options.optimistic, options.rollbackOnError, resetOnSubmit, serverFn, setActionState],\n ) as ServerFnSubmit<TInput, TResult>;\n\n const formAction = useCallback(\n async (formData: FormData) => {\n try {\n await submit(formData as TInput | FormData);\n } catch (error) {\n if (callbacksRef.current.throwOnFormError) {\n throw error;\n }\n }\n },\n [submit],\n );\n\n const reset = useCallback(() => {\n requestIdRef.current += 1;\n setActionState({\n pendingCount: 0,\n status: \"idle\",\n result: initialResult,\n error: null,\n });\n }, [initialResult, setActionState]);\n\n return useMemo(\n () => ({\n pending: state.pendingCount > 0,\n status: state.status,\n result: state.result,\n error: state.error,\n submit,\n formAction,\n reset,\n }),\n [formAction, reset, state.error, state.pendingCount, state.result, state.status, submit],\n );\n}\n\nfunction normalizeServerFnError(error: unknown) {\n if (error instanceof Error) return error;\n\n const normalized = new Error(typeof error === \"string\" ? error : \"Server function failed\");\n (normalized as Error & { cause?: unknown }).cause = error;\n return normalized;\n}\n\nfunction isFormData(value: unknown): value is FormData {\n if (!value || typeof value !== \"object\") return false;\n\n if (typeof FormData !== \"undefined\" && value instanceof FormData) {\n return true;\n }\n\n return (\n Object.prototype.toString.call(value) === \"[object FormData]\" &&\n typeof (value as { entries?: unknown }).entries === \"function\"\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/server-fn-client.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { ServerFn } from \"./server-fn\";\n\nexport type ServerFnActionStatus = \"idle\" | \"pending\" | \"success\" | \"error\";\n\nexport type ServerFnSubmit<TInput, TResult> = [unknown] extends [TInput]\n ? (input?: TInput | FormData) => Promise<TResult>\n : (input: TInput | FormData) => Promise<TResult>;\n\nexport type ServerFnOptimisticContext<TInput, TResult> = {\n input: TInput | FormData | undefined;\n formData?: FormData;\n current: TResult | null;\n};\n\nexport type UseServerFnOptions<TResult, TError extends Error = Error, TInput = unknown> = {\n initialResult?: TResult | null;\n resetOnSubmit?: boolean;\n throwOnFormError?: boolean;\n optimistic?: (context: ServerFnOptimisticContext<TInput, TResult>) => TResult | null | undefined;\n rollbackOnError?: boolean;\n onSuccess?: (result: TResult) => void;\n onError?: (error: TError) => void;\n onSettled?: (result: TResult | null, error: TError | null) => void;\n};\n\nexport type UseServerFnReturn<TInput, TResult, TError extends Error = Error> = {\n pending: boolean;\n status: ServerFnActionStatus;\n result: TResult | null;\n error: TError | null;\n submit: ServerFnSubmit<TInput, TResult>;\n formAction: (formData: FormData) => Promise<void>;\n reset: () => void;\n};\n\ntype ServerFnActionState<TResult, TError extends Error> = {\n pendingCount: number;\n status: ServerFnActionStatus;\n result: TResult | null;\n error: TError | null;\n};\n\ntype ServerFnActionCallbacks<TResult, TError extends Error> = Pick<\n UseServerFnOptions<TResult, TError, unknown>,\n \"onError\" | \"onSettled\" | \"onSuccess\" | \"throwOnFormError\"\n>;\n\nexport function useServerFn<TInput, TResult, TError extends Error = Error>(\n serverFn: ServerFn<TInput, TResult, TError>,\n options: UseServerFnOptions<TResult, TError, TInput> = {},\n): UseServerFnReturn<TInput, TResult, TError> {\n const { initialResult = null, resetOnSubmit = true } = options;\n const callbacksRef = useRef<ServerFnActionCallbacks<TResult, TError>>({});\n const requestIdRef = useRef(0);\n const initialState: ServerFnActionState<TResult, TError> = {\n pendingCount: 0,\n status: \"idle\",\n result: initialResult,\n error: null,\n };\n const stateRef = useRef<ServerFnActionState<TResult, TError>>(initialState);\n const [state, setState] = useState<ServerFnActionState<TResult, TError>>(initialState);\n const setActionState = useCallback(\n (\n value:\n | ServerFnActionState<TResult, TError>\n | ((current: ServerFnActionState<TResult, TError>) => ServerFnActionState<TResult, TError>),\n ) => {\n setState((current) => {\n const next = typeof value === \"function\" ? value(current) : value;\n stateRef.current = next;\n return next;\n });\n },\n [],\n );\n\n callbacksRef.current = {\n onError: options.onError,\n onSettled: options.onSettled,\n onSuccess: options.onSuccess,\n throwOnFormError: options.throwOnFormError,\n };\n\n const submit = useCallback(\n async (input?: TInput | FormData) => {\n const requestId = ++requestIdRef.current;\n const formData = isFormData(input) ? input : undefined;\n const previousResult = stateRef.current.result;\n const optimisticResult = options.optimistic?.({\n input,\n formData,\n current: previousResult,\n });\n const hasOptimisticResult = optimisticResult !== undefined;\n\n setActionState((current) => ({\n pendingCount: current.pendingCount + 1,\n status: \"pending\",\n result: hasOptimisticResult ? optimisticResult : resetOnSubmit ? null : current.result,\n error: null,\n }));\n\n try {\n const result = await serverFn(input as TInput | FormData);\n const isLatestRequest = requestId === requestIdRef.current;\n\n setActionState((current) => {\n const pendingCount = Math.max(0, current.pendingCount - 1);\n\n if (!isLatestRequest) {\n return {\n ...current,\n pendingCount,\n status: pendingCount > 0 ? \"pending\" : current.status,\n };\n }\n\n return {\n pendingCount,\n status: pendingCount > 0 ? \"pending\" : \"success\",\n result,\n error: null,\n };\n });\n\n if (isLatestRequest) {\n callbacksRef.current.onSuccess?.(result);\n callbacksRef.current.onSettled?.(result, null);\n }\n\n return result;\n } catch (cause) {\n const error = normalizeServerFnError(cause) as TError;\n const isLatestRequest = requestId === requestIdRef.current;\n\n setActionState((current) => {\n const pendingCount = Math.max(0, current.pendingCount - 1);\n\n if (!isLatestRequest) {\n return {\n ...current,\n pendingCount,\n status: pendingCount > 0 ? \"pending\" : current.status,\n };\n }\n\n return {\n pendingCount,\n status: pendingCount > 0 ? \"pending\" : \"error\",\n result:\n hasOptimisticResult && options.rollbackOnError\n ? previousResult\n : resetOnSubmit\n ? null\n : current.result,\n error,\n };\n });\n\n if (isLatestRequest) {\n callbacksRef.current.onError?.(error);\n callbacksRef.current.onSettled?.(null, error);\n }\n\n throw error;\n }\n },\n [options.optimistic, options.rollbackOnError, resetOnSubmit, serverFn, setActionState],\n ) as ServerFnSubmit<TInput, TResult>;\n\n const formAction = useCallback(\n async (formData: FormData) => {\n try {\n await submit(formData as TInput | FormData);\n } catch (error) {\n if (callbacksRef.current.throwOnFormError) {\n throw error;\n }\n }\n },\n [submit],\n );\n\n const reset = useCallback(() => {\n requestIdRef.current += 1;\n setActionState({\n pendingCount: 0,\n status: \"idle\",\n result: initialResult,\n error: null,\n });\n }, [initialResult, setActionState]);\n\n return useMemo(\n () => ({\n pending: state.pendingCount > 0,\n status: state.status,\n result: state.result,\n error: state.error,\n submit,\n formAction,\n reset,\n }),\n [formAction, reset, state.error, state.pendingCount, state.result, state.status, submit],\n );\n}\n\nfunction normalizeServerFnError(error: unknown) {\n if (error instanceof Error) return error;\n\n const normalized = new Error(typeof error === \"string\" ? error : \"Server function failed\");\n (normalized as Error & { cause?: unknown }).cause = error;\n return normalized;\n}\n\nfunction isFormData(value: unknown): value is FormData {\n if (!value || typeof value !== \"object\") return false;\n\n if (typeof FormData !== \"undefined\" && value instanceof FormData) {\n return true;\n }\n\n return (\n Object.prototype.toString.call(value) === \"[object FormData]\" &&\n typeof (value as { entries?: unknown }).entries === \"function\"\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  useServerFn
4
- } from "./chunk-32W4QEJP.mjs";
4
+ } from "./chunk-KFTGPFRD.mjs";
5
5
  import "./chunk-CGILA3WO.mjs";
6
6
  export {
7
7
  useServerFn
@@ -21,10 +21,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/server-fn.ts
22
22
  var server_fn_exports = {};
23
23
  __export(server_fn_exports, {
24
+ FARM_SERVER_FN_FAILURE_SYMBOL: () => FARM_SERVER_FN_FAILURE_SYMBOL,
24
25
  FARM_SERVER_FN_SYMBOL: () => FARM_SERVER_FN_SYMBOL,
26
+ ServerActionError: () => ServerActionError,
27
+ ServerFnFailure: () => ServerFnFailure,
25
28
  _parseServerFnSchema: () => _parseServerFnSchema,
26
29
  createServerFn: () => createServerFn,
27
- createServerMiddleware: () => createServerMiddleware
30
+ createServerFnTransportError: () => createServerFnTransportError,
31
+ createServerMiddleware: () => createServerMiddleware,
32
+ isServerFnFailure: () => isServerFnFailure
28
33
  });
29
34
  module.exports = __toCommonJS(server_fn_exports);
30
35
 
@@ -67,6 +72,55 @@ function subscribeFarmCacheTask(listener) {
67
72
  }
68
73
  __name(subscribeFarmCacheTask, "subscribeFarmCacheTask");
69
74
 
75
+ // src/server-fn-error.ts
76
+ var FARM_SERVER_FN_FAILURE_SYMBOL = /* @__PURE__ */ Symbol.for("farm.server-fn.failure");
77
+ var _ServerFnFailure = class _ServerFnFailure extends Error {
78
+ constructor(code, data, options) {
79
+ super(options.message);
80
+ this.name = "ServerFnFailure";
81
+ this.code = code;
82
+ this.data = data;
83
+ this.status = options.status;
84
+ Object.defineProperty(this, FARM_SERVER_FN_FAILURE_SYMBOL, {
85
+ value: true,
86
+ enumerable: false
87
+ });
88
+ }
89
+ };
90
+ __name(_ServerFnFailure, "ServerFnFailure");
91
+ var ServerFnFailure = _ServerFnFailure;
92
+ var _ServerActionError = class _ServerActionError extends Error {
93
+ constructor(message = "Server function failed") {
94
+ super(message);
95
+ this.name = "ServerActionError";
96
+ }
97
+ };
98
+ __name(_ServerActionError, "ServerActionError");
99
+ var ServerActionError = _ServerActionError;
100
+ function isServerFnFailure(value) {
101
+ if (!value || typeof value !== "object") return false;
102
+ const candidate = value;
103
+ return candidate[FARM_SERVER_FN_FAILURE_SYMBOL] === true && candidate.name === "ServerFnFailure" && typeof candidate.code === "string" && Number.isInteger(candidate.status) && (candidate.status ?? 0) >= 400 && (candidate.status ?? 0) <= 599;
104
+ }
105
+ __name(isServerFnFailure, "isServerFnFailure");
106
+ function createServerFnTransportError(value) {
107
+ if (isSerializedServerFnFailure(value)) {
108
+ return new ServerFnFailure(value.code, value.data, {
109
+ status: value.status,
110
+ message: value.message
111
+ });
112
+ }
113
+ const message = value && typeof value === "object" && "message" in value && typeof value.message === "string" ? value.message : "Server function failed";
114
+ return new ServerActionError(message);
115
+ }
116
+ __name(createServerFnTransportError, "createServerFnTransportError");
117
+ function isSerializedServerFnFailure(value) {
118
+ if (!value || typeof value !== "object") return false;
119
+ const candidate = value;
120
+ return candidate.name === "ServerFnFailure" && typeof candidate.message === "string" && typeof candidate.code === "string" && Number.isInteger(candidate.status) && (candidate.status ?? 0) >= 400 && (candidate.status ?? 0) <= 599 && "data" in candidate;
121
+ }
122
+ __name(isSerializedServerFnFailure, "isSerializedServerFnFailure");
123
+
70
124
  // src/server-action-security.ts
71
125
  var SERVER_ACTION_STORAGE_KEY = /* @__PURE__ */ Symbol.for("farm.serverActionStorage");
72
126
  var FALLBACK_ABORT_CONTROLLER_KEY = /* @__PURE__ */ Symbol.for("farm.serverActionFallbackAbortController");
@@ -877,6 +931,8 @@ function createServerFn(options) {
877
931
  throw new TypeError("createServerFn requires a handler function");
878
932
  }
879
933
  const middleware = resolveServerFnMiddleware(options.middleware);
934
+ const errors = normalizeServerFnErrors(options.errors);
935
+ const error = createServerFnErrorHandler(errors);
880
936
  const serverFn = /* @__PURE__ */ __name(async (value) => {
881
937
  const formData = isFormData(value) ? value : void 0;
882
938
  const rawInput = formData ? formDataToObject(formData) : value;
@@ -894,7 +950,7 @@ function createServerFn(options) {
894
950
  let resolvedContext = Object.freeze(/* @__PURE__ */ Object.create(null));
895
951
  const result = await runServerFnMiddleware(middleware, handlerContext, async (context) => {
896
952
  resolvedContext = context.context;
897
- return options.handler(context);
953
+ return options.handler({ ...context, error });
898
954
  });
899
955
  const parsedResult = await _parseServerFnSchema(options.output, result, "output");
900
956
  if (options.invalidates) {
@@ -924,6 +980,10 @@ function createServerFn(options) {
924
980
  value: options.output,
925
981
  enumerable: false
926
982
  },
983
+ __farmServerFnError: {
984
+ value: void 0,
985
+ enumerable: false
986
+ },
927
987
  __farmServerFnMiddleware: {
928
988
  value: middleware,
929
989
  enumerable: false
@@ -962,6 +1022,78 @@ async function runServerFnMiddleware(middleware, handlerContext, handler) {
962
1022
  return dispatch(0, Object.freeze(/* @__PURE__ */ Object.create(null)));
963
1023
  }
964
1024
  __name(runServerFnMiddleware, "runServerFnMiddleware");
1025
+ function normalizeServerFnErrors(definitions) {
1026
+ if (definitions === void 0) return Object.freeze({});
1027
+ if (!isPlainServerFnObject(definitions)) {
1028
+ throw new TypeError("createServerFn errors must be an object");
1029
+ }
1030
+ const normalized = /* @__PURE__ */ Object.create(null);
1031
+ for (const [code, definition] of Object.entries(definitions)) {
1032
+ if (!code.trim()) {
1033
+ throw new TypeError("Server function error codes cannot be empty");
1034
+ }
1035
+ if (!definition || typeof definition !== "object") {
1036
+ throw new TypeError(`Server function error "${code}" must be an object`);
1037
+ }
1038
+ if (!Number.isInteger(definition.status) || definition.status < 400 || definition.status > 599) {
1039
+ throw new TypeError(
1040
+ `Server function error "${code}" status must be an integer between 400 and 599`
1041
+ );
1042
+ }
1043
+ if (!definition.data || typeof definition.data.parse !== "function" && typeof definition.data.safeParse !== "function") {
1044
+ throw new TypeError(
1045
+ `Server function error "${code}" data must provide synchronous parse() or safeParse()`
1046
+ );
1047
+ }
1048
+ if (definition.message !== void 0 && typeof definition.message !== "string") {
1049
+ throw new TypeError(`Server function error "${code}" message must be a string`);
1050
+ }
1051
+ normalized[code] = Object.freeze({ ...definition });
1052
+ }
1053
+ return Object.freeze(normalized);
1054
+ }
1055
+ __name(normalizeServerFnErrors, "normalizeServerFnErrors");
1056
+ function createServerFnErrorHandler(definitions) {
1057
+ return ((code, data) => {
1058
+ const definition = definitions[code];
1059
+ if (!definition) {
1060
+ throw new TypeError(`Server function error "${code}" is not declared`);
1061
+ }
1062
+ const parsed = parseServerFnFailureData(code, definition.data, data);
1063
+ throw new ServerFnFailure(code, parsed, {
1064
+ status: definition.status,
1065
+ message: definition.message ?? "Server function failed"
1066
+ });
1067
+ });
1068
+ }
1069
+ __name(createServerFnErrorHandler, "createServerFnErrorHandler");
1070
+ function parseServerFnFailureData(code, schema, value) {
1071
+ if (typeof schema.safeParse === "function") {
1072
+ const result2 = schema.safeParse(value);
1073
+ if (isPromiseLike(result2)) {
1074
+ throw new TypeError(`Server function error "${code}" data schema must parse synchronously`);
1075
+ }
1076
+ return unwrapSafeParseResult(result2);
1077
+ }
1078
+ const result = schema.parse(value);
1079
+ if (isPromiseLike(result)) {
1080
+ throw new TypeError(`Server function error "${code}" data schema must parse synchronously`);
1081
+ }
1082
+ return result;
1083
+ }
1084
+ __name(parseServerFnFailureData, "parseServerFnFailureData");
1085
+ function isPlainServerFnObject(value) {
1086
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1087
+ const prototype = Object.getPrototypeOf(value);
1088
+ return prototype === Object.prototype || prototype === null;
1089
+ }
1090
+ __name(isPlainServerFnObject, "isPlainServerFnObject");
1091
+ function isPromiseLike(value) {
1092
+ return Boolean(
1093
+ value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function"
1094
+ );
1095
+ }
1096
+ __name(isPromiseLike, "isPromiseLike");
965
1097
  function resolveServerFnMiddleware(middleware) {
966
1098
  const resolved = [];
967
1099
  const seen = /* @__PURE__ */ new Set();
@@ -1058,9 +1190,14 @@ function isFormData(value) {
1058
1190
  __name(isFormData, "isFormData");
1059
1191
  // Annotate the CommonJS export names for ESM import in node:
1060
1192
  0 && (module.exports = {
1193
+ FARM_SERVER_FN_FAILURE_SYMBOL,
1061
1194
  FARM_SERVER_FN_SYMBOL,
1195
+ ServerActionError,
1196
+ ServerFnFailure,
1062
1197
  _parseServerFnSchema,
1063
1198
  createServerFn,
1064
- createServerMiddleware
1199
+ createServerFnTransportError,
1200
+ createServerMiddleware,
1201
+ isServerFnFailure
1065
1202
  });
1066
1203
  //# sourceMappingURL=server-fn.cjs.map