astro 4.13.1 → 4.13.3

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 (69) hide show
  1. package/astro-jsx.d.ts +0 -1
  2. package/astro.js +2 -1
  3. package/client.d.ts +1 -1
  4. package/components/Picture.astro +2 -2
  5. package/components/ViewTransitions.astro +1 -1
  6. package/config.d.ts +1 -1
  7. package/dist/@types/astro.d.ts +12 -8
  8. package/dist/actions/consts.d.ts +5 -0
  9. package/dist/actions/consts.js +6 -0
  10. package/dist/actions/runtime/middleware.d.ts +6 -6
  11. package/dist/actions/runtime/middleware.js +71 -77
  12. package/dist/actions/runtime/route.js +12 -19
  13. package/dist/actions/runtime/utils.d.ts +0 -8
  14. package/dist/actions/runtime/utils.js +0 -15
  15. package/dist/actions/runtime/virtual/get-action.d.ts +8 -0
  16. package/dist/actions/runtime/virtual/get-action.js +17 -0
  17. package/dist/actions/runtime/virtual/server.d.ts +1 -1
  18. package/dist/actions/runtime/virtual/shared.d.ts +23 -1
  19. package/dist/actions/runtime/virtual/shared.js +60 -9
  20. package/dist/actions/utils.d.ts +3 -2
  21. package/dist/actions/utils.js +13 -20
  22. package/dist/assets/build/generate.js +1 -1
  23. package/dist/assets/endpoint/generic.js +1 -1
  24. package/dist/assets/endpoint/node.js +3 -3
  25. package/dist/assets/services/sharp.js +1 -1
  26. package/dist/assets/services/vendor/squoosh/avif/avif_node_dec.js +1 -1
  27. package/dist/assets/services/vendor/squoosh/avif/avif_node_enc.js +1 -1
  28. package/dist/assets/services/vendor/squoosh/mozjpeg/mozjpeg_node_dec.js +1 -1
  29. package/dist/assets/services/vendor/squoosh/mozjpeg/mozjpeg_node_enc.js +1 -1
  30. package/dist/assets/services/vendor/squoosh/webp/webp_node_dec.js +1 -1
  31. package/dist/assets/services/vendor/squoosh/webp/webp_node_enc.js +1 -1
  32. package/dist/assets/utils/metadata.js +1 -1
  33. package/dist/assets/utils/node/emitAsset.js +1 -1
  34. package/dist/assets/utils/remoteProbe.js +1 -1
  35. package/dist/assets/utils/vendor/image-size/lookup.js +1 -1
  36. package/dist/assets/utils/vendor/image-size/types/svg.js +4 -4
  37. package/dist/cli/info/index.js +2 -2
  38. package/dist/cli/install-package.js +2 -2
  39. package/dist/core/app/index.js +1 -1
  40. package/dist/core/build/css-asset-name.d.ts +3 -3
  41. package/dist/core/build/css-asset-name.js +15 -8
  42. package/dist/core/build/generate.js +3 -3
  43. package/dist/core/build/index.js +7 -1
  44. package/dist/core/build/plugins/plugin-css.js +2 -2
  45. package/dist/core/config/schema.d.ts +55 -55
  46. package/dist/core/config/tsconfig.d.ts +1 -1
  47. package/dist/core/constants.js +1 -1
  48. package/dist/core/dev/dev.js +2 -2
  49. package/dist/core/errors/dev/vite.js +4 -4
  50. package/dist/core/errors/errors-data.d.ts +5 -0
  51. package/dist/core/messages.js +2 -2
  52. package/dist/core/middleware/index.js +1 -1
  53. package/dist/core/render-context.js +8 -5
  54. package/dist/core/routing/manifest/create.js +2 -2
  55. package/dist/events/error.js +1 -1
  56. package/dist/runtime/client/dev-toolbar/apps/audit/index.js +1 -1
  57. package/dist/runtime/client/dev-toolbar/apps/audit/rules/perf.js +4 -2
  58. package/dist/runtime/server/render/component.js +1 -3
  59. package/dist/transitions/router.js +2 -2
  60. package/dist/type-utils.d.ts +1 -1
  61. package/dist/vite-plugin-astro-server/vite.js +1 -2
  62. package/dist/vite-plugin-env/index.js +0 -1
  63. package/dist/vite-plugin-load-fallback/index.js +3 -3
  64. package/dist/vite-plugin-scanner/index.js +1 -1
  65. package/dist/vite-plugin-scripts/page-ssr.js +1 -1
  66. package/package.json +6 -9
  67. package/templates/actions.mjs +34 -20
  68. package/templates/content/types.d.ts +12 -10
  69. package/types/content.d.ts +1 -1
package/astro-jsx.d.ts CHANGED
@@ -14,7 +14,6 @@ declare namespace astroHTML.JSX {
14
14
  export type Children = Child | Child[];
15
15
 
16
16
  interface ElementChildrenAttribute {
17
- // eslint-disable-next-line @typescript-eslint/ban-types
18
17
  children: {};
19
18
  }
20
19
 
package/astro.js CHANGED
@@ -52,6 +52,7 @@ async function errorNodeUnsupported() {
52
52
  Node.js v${process.versions.node} is not supported by Astro!
53
53
  Please upgrade Node.js to a supported version: "${engines}"\n`);
54
54
 
55
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
55
56
  const ci = typeof require !== 'undefined' ? require('ci-info') : await import('ci-info');
56
57
 
57
58
  // Special instructions for CI environments, which may have special steps needed.
@@ -65,7 +66,7 @@ Please upgrade Node.js to a supported version: "${engines}"\n`);
65
66
  }
66
67
  }
67
68
  console.log(
68
- `${ci.name} CI Environment Detected!\nAdditional steps may be needed to set your Node.js version:`
69
+ `${ci.name} CI Environment Detected!\nAdditional steps may be needed to set your Node.js version:`,
69
70
  );
70
71
  console.log(`Documentation: https://docs.astro.build/en/guides/deploy/`);
71
72
  if (CI_INSTRUCTIONS[platform]) {
package/client.d.ts CHANGED
@@ -50,7 +50,7 @@ declare module 'astro:assets' {
50
50
  * This is functionally equivalent to using the `<Image />` component, as the component calls this function internally.
51
51
  */
52
52
  getImage: (
53
- options: import('./dist/assets/types.js').UnresolvedImageTransform
53
+ options: import('./dist/assets/types.js').UnresolvedImageTransform,
54
54
  ) => Promise<import('./dist/assets/types.js').GetImageResult>;
55
55
  imageConfig: import('./dist/@types/astro.js').AstroConfig['image'];
56
56
  getConfiguredImageService: typeof import('./dist/assets/index.js').getConfiguredImageService;
@@ -53,8 +53,8 @@ const optimizedImages: GetImageResult[] = await Promise.all(
53
53
  format: format,
54
54
  widths: props.widths,
55
55
  densities: props.densities,
56
- })
57
- )
56
+ }),
57
+ ),
58
58
  );
59
59
 
60
60
  let resultFallbackFormat = fallbackFormat ?? defaultFallbackFormat;
@@ -53,7 +53,7 @@ const { fallback = 'animate' } = Astro.props;
53
53
  if (supportsViewTransitions || getFallback() !== 'none') {
54
54
  if (import.meta.env.DEV && window.matchMedia('(prefers-reduced-motion)').matches) {
55
55
  console.warn(
56
- `[transitions]: all view transition animations, including fallback animation, are disabled as this device has the prefer-reduced-motion setting enabled.`
56
+ `[transitions]: all view transition animations, including fallback animation, are disabled as this device has the prefer-reduced-motion setting enabled.`,
57
57
  );
58
58
  }
59
59
  document.addEventListener('click', (ev) => {
package/config.d.ts CHANGED
@@ -17,7 +17,7 @@ export function defineConfig(config: AstroUserConfig): AstroUserConfig;
17
17
  */
18
18
  export function getViteConfig(
19
19
  config: ViteUserConfig,
20
- inlineAstroConfig?: AstroInlineConfig
20
+ inlineAstroConfig?: AstroInlineConfig,
21
21
  ): ViteUserConfigFn;
22
22
 
23
23
  /**
@@ -1842,25 +1842,29 @@ export interface AstroUserConfig {
1842
1842
  * @version 4.10.0
1843
1843
  * @description
1844
1844
  *
1845
- * Enables experimental `astro:env` features .
1845
+ * Enables experimental `astro:env` features.
1846
1846
  *
1847
1847
  * The `astro:env` API lets you configure a type-safe schema for your environment variables, and indicate whether they should be available on the server or the client. Import and use your defined variables from the appropriate `/client` or `/server` module:
1848
1848
  *
1849
1849
  * ```astro
1850
1850
  * ---
1851
- * import { APP_ID } from "astro:env/client"
1852
- * import { API_URL, API_TOKEN, getSecret } from "astro:env/server"
1853
- * const NODE_ENV = getSecret("NODE_ENV")
1851
+ * import { API_URL } from "astro:env/client"
1852
+ * import { API_SECRET_TOKEN } from "astro:env/server"
1854
1853
  *
1855
1854
  * const data = await fetch(`${API_URL}/users`, {
1856
- * method: "POST",
1855
+ * method: "GET",
1857
1856
  * headers: {
1858
1857
  * "Content-Type": "application/json",
1859
- * "Authorization": `Bearer ${API_TOKEN}`
1858
+ * "Authorization": `Bearer ${API_SECRET_TOKEN}`
1860
1859
  * },
1861
- * body: JSON.stringify({ appId: APP_ID, nodeEnv: NODE_ENV })
1862
1860
  * })
1863
1861
  * ---
1862
+ *
1863
+ * <script>
1864
+ * import { API_URL } from "astro:env/client"
1865
+ *
1866
+ * fetch(`${API_URL}/ping`)
1867
+ * </script>
1864
1868
  * ```
1865
1869
  *
1866
1870
  * To define the data type and properties of your environment variables, declare a schema in your Astro config in `experimental.env.schema`. The `envField` helper allows you define your variable as a string, number, or boolean and pass properties in an object:
@@ -1898,7 +1902,7 @@ export interface AstroUserConfig {
1898
1902
  * import { PORT } from "astro:env/server"
1899
1903
  * ```
1900
1904
  *
1901
- * - **Secret server variables**: These variables are not part of your final bundle and can be accessed on the server through the `astro:env/server` module. The `getSecret()` helper function can be used to retrieve secrets not specified in the schema:
1905
+ * - **Secret server variables**: These variables are not part of your final bundle and can be accessed on the server through the `astro:env/server` module. The `getSecret()` helper function can be used to retrieve secrets not specified in the schema. Its implementation is provided by your adapter and defaults to `process.env`:
1902
1906
  *
1903
1907
  * ```js
1904
1908
  * import { API_SECRET, getSecret } from "astro:env/server"
@@ -4,3 +4,8 @@ export declare const ACTIONS_TYPES_FILE = "actions.d.ts";
4
4
  export declare const VIRTUAL_INTERNAL_MODULE_ID = "astro:internal-actions";
5
5
  export declare const RESOLVED_VIRTUAL_INTERNAL_MODULE_ID = "\0astro:internal-actions";
6
6
  export declare const NOOP_ACTIONS = "\0noop-actions";
7
+ export declare const ACTION_QUERY_PARAMS: {
8
+ actionName: string;
9
+ actionPayload: string;
10
+ actionRedirect: string;
11
+ };
@@ -4,8 +4,14 @@ const ACTIONS_TYPES_FILE = "actions.d.ts";
4
4
  const VIRTUAL_INTERNAL_MODULE_ID = "astro:internal-actions";
5
5
  const RESOLVED_VIRTUAL_INTERNAL_MODULE_ID = "\0astro:internal-actions";
6
6
  const NOOP_ACTIONS = "\0noop-actions";
7
+ const ACTION_QUERY_PARAMS = {
8
+ actionName: "_astroAction",
9
+ actionPayload: "_astroActionPayload",
10
+ actionRedirect: "_astroActionRedirect"
11
+ };
7
12
  export {
8
13
  ACTIONS_TYPES_FILE,
14
+ ACTION_QUERY_PARAMS,
9
15
  NOOP_ACTIONS,
10
16
  RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
11
17
  RESOLVED_VIRTUAL_MODULE_ID,
@@ -1,9 +1,9 @@
1
- import type { APIContext } from '../../@types/astro.js';
1
+ import { type SerializedActionResult } from './virtual/shared.js';
2
+ export type ActionPayload = {
3
+ actionResult: SerializedActionResult;
4
+ actionName: string;
5
+ };
2
6
  export type Locals = {
3
- _actionsInternal: {
4
- getActionResult: APIContext['getActionResult'];
5
- callAction: APIContext['callAction'];
6
- actionResult?: ReturnType<APIContext['getActionResult']>;
7
- };
7
+ _actionPayload: ActionPayload;
8
8
  };
9
9
  export declare const onRequest: import("../../@types/astro.js").MiddlewareHandler;
@@ -1,37 +1,59 @@
1
1
  import { yellow } from "kleur/colors";
2
- import {
3
- ActionQueryStringInvalidError,
4
- ActionsUsedWithForGetError
5
- } from "../../core/errors/errors-data.js";
2
+ import { ActionQueryStringInvalidError } from "../../core/errors/errors-data.js";
6
3
  import { AstroError } from "../../core/errors/errors.js";
7
4
  import { defineMiddleware } from "../../core/middleware/index.js";
8
- import { formContentTypes, getAction, hasContentType } from "./utils.js";
9
- import { getActionQueryString } from "./virtual/shared.js";
5
+ import { ACTION_QUERY_PARAMS } from "../consts.js";
6
+ import { formContentTypes, hasContentType } from "./utils.js";
7
+ import { getAction } from "./virtual/get-action.js";
8
+ import {
9
+ serializeActionResult
10
+ } from "./virtual/shared.js";
10
11
  const onRequest = defineMiddleware(async (context, next) => {
11
12
  const locals = context.locals;
12
13
  const { request } = context;
13
- if (locals._actionsInternal) {
14
- locals._actionsInternal.callAction = createCallAction(context);
15
- return next();
14
+ if (locals._actionPayload) return next();
15
+ const actionPayload = context.cookies.get(ACTION_QUERY_PARAMS.actionPayload)?.json();
16
+ if (actionPayload) {
17
+ if (!isActionPayload(actionPayload)) {
18
+ throw new Error("Internal: Invalid action payload in cookie.");
19
+ }
20
+ return renderResult({ context, next, ...actionPayload });
16
21
  }
17
- if (request.method === "POST" && request.body === null) {
18
- return nextWithStaticStub(next, context);
22
+ if (import.meta.env.DEV && request.method === "POST" && request.body === null) {
23
+ console.warn(
24
+ yellow("[astro:actions]"),
25
+ 'POST requests should not be sent to prerendered pages. If you\'re using Actions, disable prerendering with `export const prerender = "false".'
26
+ );
27
+ return next();
19
28
  }
20
- const actionName = context.url.searchParams.get("_astroAction");
29
+ const actionName = context.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
21
30
  if (context.request.method === "POST" && actionName) {
22
31
  return handlePost({ context, next, actionName });
23
32
  }
24
- if (context.request.method === "GET" && actionName) {
25
- throw new AstroError({
26
- ...ActionsUsedWithForGetError,
27
- message: ActionsUsedWithForGetError.message(actionName)
28
- });
29
- }
30
33
  if (context.request.method === "POST") {
31
34
  return handlePostLegacy({ context, next });
32
35
  }
33
- return nextWithLocalsStub(next, context);
36
+ return next();
34
37
  });
38
+ async function renderResult({
39
+ context,
40
+ next,
41
+ actionResult,
42
+ actionName
43
+ }) {
44
+ const locals = context.locals;
45
+ locals._actionPayload = { actionResult, actionName };
46
+ const response = await next();
47
+ context.cookies.delete(ACTION_QUERY_PARAMS.actionPayload);
48
+ if (actionResult.type === "error") {
49
+ return new Response(response.body, {
50
+ status: actionResult.status,
51
+ statusText: actionResult.type,
52
+ headers: response.headers
53
+ });
54
+ }
55
+ return response;
56
+ }
35
57
  async function handlePost({
36
58
  context,
37
59
  next,
@@ -52,47 +74,45 @@ async function handlePost({
52
74
  }
53
75
  const action = baseAction.bind(context);
54
76
  const actionResult = await action(formData);
55
- return handleResult({ context, next, actionName, actionResult });
77
+ if (context.url.searchParams.get(ACTION_QUERY_PARAMS.actionRedirect) === "false") {
78
+ return renderResult({
79
+ context,
80
+ next,
81
+ actionName,
82
+ actionResult: serializeActionResult(actionResult)
83
+ });
84
+ }
85
+ return redirectWithResult({ context, actionName, actionResult });
56
86
  }
57
- async function handleResult({
87
+ async function redirectWithResult({
58
88
  context,
59
- next,
60
89
  actionName,
61
90
  actionResult
62
91
  }) {
63
- const actionsInternal = {
64
- getActionResult: (actionFn) => {
65
- if (actionFn.toString() !== getActionQueryString(actionName)) {
66
- return Promise.resolve(void 0);
67
- }
68
- return actionResult;
69
- },
70
- callAction: createCallAction(context),
71
- actionResult
72
- };
73
- const locals = context.locals;
74
- Object.defineProperty(locals, "_actionsInternal", { writable: false, value: actionsInternal });
75
- const response = await next();
92
+ context.cookies.set(ACTION_QUERY_PARAMS.actionPayload, {
93
+ actionName,
94
+ actionResult: serializeActionResult(actionResult)
95
+ });
76
96
  if (actionResult.error) {
77
- return new Response(response.body, {
78
- status: actionResult.error.status,
79
- statusText: actionResult.error.type,
80
- headers: response.headers
81
- });
97
+ const referer = context.request.headers.get("Referer");
98
+ if (!referer) {
99
+ throw new Error("Internal: Referer unexpectedly missing from Action POST request.");
100
+ }
101
+ return context.redirect(referer);
82
102
  }
83
- return response;
103
+ return context.redirect(context.url.pathname);
84
104
  }
85
105
  async function handlePostLegacy({ context, next }) {
86
106
  const { request } = context;
87
- if (context.url.pathname.startsWith("/_actions")) return nextWithLocalsStub(next, context);
107
+ if (context.url.pathname.startsWith("/_actions")) return next();
88
108
  const contentType = request.headers.get("content-type");
89
109
  let formData;
90
110
  if (contentType && hasContentType(contentType, formContentTypes)) {
91
111
  formData = await request.clone().formData();
92
112
  }
93
- if (!formData) return nextWithLocalsStub(next, context);
94
- const actionName = formData.get("_astroAction");
95
- if (!actionName) return nextWithLocalsStub(next, context);
113
+ if (!formData) return next();
114
+ const actionName = formData.get(ACTION_QUERY_PARAMS.actionName);
115
+ if (!actionName) return next();
96
116
  const baseAction = await getAction(actionName);
97
117
  if (!baseAction) {
98
118
  throw new AstroError({
@@ -102,39 +122,13 @@ async function handlePostLegacy({ context, next }) {
102
122
  }
103
123
  const action = baseAction.bind(context);
104
124
  const actionResult = await action(formData);
105
- return handleResult({ context, next, actionName, actionResult });
106
- }
107
- function nextWithStaticStub(next, context) {
108
- Object.defineProperty(context.locals, "_actionsInternal", {
109
- writable: false,
110
- value: {
111
- getActionResult: () => {
112
- console.warn(
113
- yellow("[astro:actions]"),
114
- "`getActionResult()` should not be called on prerendered pages. Astro can only handle actions for pages rendered on-demand."
115
- );
116
- return void 0;
117
- },
118
- callAction: createCallAction(context)
119
- }
120
- });
121
- return next();
122
- }
123
- function nextWithLocalsStub(next, context) {
124
- Object.defineProperty(context.locals, "_actionsInternal", {
125
- writable: false,
126
- value: {
127
- getActionResult: () => void 0,
128
- callAction: createCallAction(context)
129
- }
130
- });
131
- return next();
125
+ return redirectWithResult({ context, actionName, actionResult });
132
126
  }
133
- function createCallAction(context) {
134
- return (baseAction, input) => {
135
- const action = baseAction.bind(context);
136
- return action(input);
137
- };
127
+ function isActionPayload(json) {
128
+ if (typeof json !== "object" || json == null) return false;
129
+ if (!("actionResult" in json) || typeof json.actionResult !== "object") return false;
130
+ if (!("actionName" in json) || typeof json.actionName !== "string") return false;
131
+ return true;
138
132
  }
139
133
  export {
140
134
  onRequest
@@ -1,4 +1,6 @@
1
- import { formContentTypes, getAction, hasContentType } from "./utils.js";
1
+ import { formContentTypes, hasContentType } from "./utils.js";
2
+ import { getAction } from "./virtual/get-action.js";
3
+ import { serializeActionResult } from "./virtual/shared.js";
2
4
  const POST = async (context) => {
3
5
  const { request, url } = context;
4
6
  const baseAction = await getAction(url.pathname);
@@ -8,7 +10,7 @@ const POST = async (context) => {
8
10
  const contentType = request.headers.get("Content-Type");
9
11
  const contentLength = request.headers.get("Content-Length");
10
12
  let args;
11
- if (contentLength === "0") {
13
+ if (!contentType || contentLength === "0") {
12
14
  args = void 0;
13
15
  } else if (contentType && hasContentType(contentType, formContentTypes)) {
14
16
  args = await request.clone().formData();
@@ -19,25 +21,16 @@ const POST = async (context) => {
19
21
  }
20
22
  const action = baseAction.bind(context);
21
23
  const result = await action(args);
22
- if (result.error) {
23
- return new Response(
24
- JSON.stringify({
25
- ...result.error,
26
- message: result.error.message,
27
- stack: import.meta.env.PROD ? void 0 : result.error.stack
28
- }),
29
- {
30
- status: result.error.status,
31
- headers: {
32
- "Content-Type": "application/json"
33
- }
34
- }
35
- );
24
+ const serialized = serializeActionResult(result);
25
+ if (serialized.type === "empty") {
26
+ return new Response(null, {
27
+ status: serialized.status
28
+ });
36
29
  }
37
- return new Response(JSON.stringify(result.data), {
38
- status: result.data !== void 0 ? 200 : 204,
30
+ return new Response(serialized.body, {
31
+ status: serialized.status,
39
32
  headers: {
40
- "Content-Type": "application/json"
33
+ "Content-Type": serialized.contentType
41
34
  }
42
35
  });
43
36
  };
@@ -1,16 +1,8 @@
1
- import type { ZodType } from 'zod';
2
1
  import type { APIContext } from '../../@types/astro.js';
3
- import type { ActionAccept, ActionClient } from './virtual/server.js';
4
2
  export declare const formContentTypes: string[];
5
3
  export declare function hasContentType(contentType: string, expected: string[]): boolean;
6
4
  export type ActionAPIContext = Omit<APIContext, 'getActionResult' | 'callAction' | 'props'>;
7
5
  export type MaybePromise<T> = T | Promise<T>;
8
- /**
9
- * Get server-side action based on the route path.
10
- * Imports from the virtual module `astro:internal-actions`, which maps to
11
- * the user's `src/actions/index.ts` file at build-time.
12
- */
13
- export declare function getAction(path: string): Promise<ActionClient<unknown, ActionAccept, ZodType> | undefined>;
14
6
  /**
15
7
  * Used to preserve the input schema type in the error object.
16
8
  * This allows for type inference on the `fields` property
@@ -3,22 +3,7 @@ function hasContentType(contentType, expected) {
3
3
  const type = contentType.split(";")[0].toLowerCase();
4
4
  return expected.some((t) => type === t);
5
5
  }
6
- async function getAction(path) {
7
- const pathKeys = path.replace("/_actions/", "").split(".");
8
- let { server: actionLookup } = await import("astro:internal-actions");
9
- for (const key of pathKeys) {
10
- if (!(key in actionLookup)) {
11
- return void 0;
12
- }
13
- actionLookup = actionLookup[key];
14
- }
15
- if (typeof actionLookup !== "function") {
16
- return void 0;
17
- }
18
- return actionLookup;
19
- }
20
6
  export {
21
7
  formContentTypes,
22
- getAction,
23
8
  hasContentType
24
9
  };
@@ -0,0 +1,8 @@
1
+ import type { ZodType } from 'zod';
2
+ import type { ActionAccept, ActionClient } from './server.js';
3
+ /**
4
+ * Get server-side action based on the route path.
5
+ * Imports from the virtual module `astro:internal-actions`, which maps to
6
+ * the user's `src/actions/index.ts` file at build-time.
7
+ */
8
+ export declare function getAction(path: string): Promise<ActionClient<unknown, ActionAccept, ZodType> | undefined>;
@@ -0,0 +1,17 @@
1
+ async function getAction(path) {
2
+ const pathKeys = path.replace("/_actions/", "").split(".");
3
+ let { server: actionLookup } = await import("astro:internal-actions");
4
+ for (const key of pathKeys) {
5
+ if (!(key in actionLookup)) {
6
+ return void 0;
7
+ }
8
+ actionLookup = actionLookup[key];
9
+ }
10
+ if (typeof actionLookup !== "function") {
11
+ return void 0;
12
+ }
13
+ return actionLookup;
14
+ }
15
+ export {
16
+ getAction
17
+ };
@@ -10,7 +10,7 @@ export type ActionReturnType<T extends ActionHandler<any, any>> = Awaited<Return
10
10
  export type ActionClient<TOutput, TAccept extends ActionAccept | undefined, TInputSchema extends ActionInputSchema<TAccept> | undefined> = TInputSchema extends z.ZodType ? ((input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<SafeResult<z.input<TInputSchema> extends ErrorInferenceObject ? z.input<TInputSchema> : ErrorInferenceObject, Awaited<TOutput>>>) & {
11
11
  queryString: string;
12
12
  orThrow: (input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<Awaited<TOutput>>;
13
- } : (input?: any) => Promise<SafeResult<never, Awaited<TOutput>>> & {
13
+ } : ((input?: any) => Promise<SafeResult<never, Awaited<TOutput>>>) & {
14
14
  orThrow: (input?: any) => Promise<Awaited<TOutput>>;
15
15
  };
16
16
  export declare function defineAction<TOutput, TAccept extends ActionAccept | undefined = undefined, TInputSchema extends ActionInputSchema<ActionAccept> | undefined = TAccept extends 'form' ? z.ZodType<FormData> : undefined>({ accept, input: inputSchema, handler, }: {
@@ -1,5 +1,10 @@
1
1
  import type { z } from 'zod';
2
2
  import type { ErrorInferenceObject, MaybePromise } from '../utils.js';
3
+ export declare const ACTION_QUERY_PARAMS: {
4
+ actionName: string;
5
+ actionPayload: string;
6
+ actionRedirect: string;
7
+ };
3
8
  export declare const ACTION_ERROR_CODES: readonly ["BAD_REQUEST", "UNAUTHORIZED", "FORBIDDEN", "NOT_FOUND", "TIMEOUT", "CONFLICT", "PRECONDITION_FAILED", "PAYLOAD_TOO_LARGE", "UNSUPPORTED_MEDIA_TYPE", "UNPROCESSABLE_CONTENT", "TOO_MANY_REQUESTS", "CLIENT_CLOSED_REQUEST", "INTERNAL_SERVER_ERROR"];
4
9
  export type ActionErrorCode = (typeof ACTION_ERROR_CODES)[number];
5
10
  export declare class ActionError<T extends ErrorInferenceObject = ErrorInferenceObject> extends Error {
@@ -13,8 +18,9 @@ export declare class ActionError<T extends ErrorInferenceObject = ErrorInference
13
18
  });
14
19
  static codeToStatus(code: ActionErrorCode): number;
15
20
  static statusToCode(status: number): ActionErrorCode;
16
- static fromResponse(res: Response): Promise<ActionError<ErrorInferenceObject>>;
21
+ static fromJson(body: any): ActionError<ErrorInferenceObject>;
17
22
  }
23
+ export declare function isActionError(error?: unknown): error is ActionError;
18
24
  export declare function isInputError<T extends ErrorInferenceObject>(error?: ActionError<T>): error is ActionInputError<T>;
19
25
  export declare function isInputError(error?: unknown): error is ActionInputError<ErrorInferenceObject>;
20
26
  export type SafeResult<TInput extends ErrorInferenceObject, TOutput> = {
@@ -43,3 +49,19 @@ export declare function getActionProps<T extends (args: FormData) => MaybePromis
43
49
  readonly name: "_astroAction";
44
50
  readonly value: string;
45
51
  };
52
+ export type SerializedActionResult = {
53
+ type: 'data';
54
+ contentType: 'application/json+devalue';
55
+ status: 200;
56
+ body: string;
57
+ } | {
58
+ type: 'error';
59
+ contentType: 'application/json';
60
+ status: number;
61
+ body: string;
62
+ } | {
63
+ type: 'empty';
64
+ status: 204;
65
+ };
66
+ export declare function serializeActionResult(res: SafeResult<any, any>): SerializedActionResult;
67
+ export declare function deserializeActionResult(res: SerializedActionResult): SafeResult<any, any>;
@@ -1,3 +1,6 @@
1
+ import { parse as devalueParse, stringify as devalueStringify } from "devalue";
2
+ import { ACTION_QUERY_PARAMS as _ACTION_QUERY_PARAMS } from "../../consts.js";
3
+ const ACTION_QUERY_PARAMS = _ACTION_QUERY_PARAMS;
1
4
  const ACTION_ERROR_CODES = [
2
5
  "BAD_REQUEST",
3
6
  "UNAUTHORIZED",
@@ -53,22 +56,23 @@ class ActionError extends Error {
53
56
  static statusToCode(status) {
54
57
  return statusToCodeMap[status] ?? "INTERNAL_SERVER_ERROR";
55
58
  }
56
- static async fromResponse(res) {
57
- const body = await res.clone().json();
58
- if (typeof body === "object" && body?.type === "AstroActionInputError" && Array.isArray(body.issues)) {
59
+ static fromJson(body) {
60
+ if (isInputError(body)) {
59
61
  return new ActionInputError(body.issues);
60
62
  }
61
- if (typeof body === "object" && body?.type === "AstroActionError") {
63
+ if (isActionError(body)) {
62
64
  return new ActionError(body);
63
65
  }
64
66
  return new ActionError({
65
- message: res.statusText,
66
- code: ActionError.statusToCode(res.status)
67
+ code: "INTERNAL_SERVER_ERROR"
67
68
  });
68
69
  }
69
70
  }
71
+ function isActionError(error) {
72
+ return typeof error === "object" && error != null && "type" in error && error.type === "AstroActionError";
73
+ }
70
74
  function isInputError(error) {
71
- return error instanceof ActionInputError;
75
+ return typeof error === "object" && error != null && "type" in error && error.type === "AstroActionInputError" && "issues" in error && Array.isArray(error.issues);
72
76
  }
73
77
  class ActionInputError extends ActionError {
74
78
  type = "AstroActionInputError";
@@ -111,7 +115,7 @@ async function callSafely(handler) {
111
115
  }
112
116
  }
113
117
  function getActionQueryString(name) {
114
- const searchParams = new URLSearchParams({ _astroAction: name });
118
+ const searchParams = new URLSearchParams({ [_ACTION_QUERY_PARAMS.actionName]: name });
115
119
  return `?${searchParams.toString()}`;
116
120
  }
117
121
  function getActionProps(action) {
@@ -126,12 +130,59 @@ function getActionProps(action) {
126
130
  value: actionName
127
131
  };
128
132
  }
133
+ function serializeActionResult(res) {
134
+ if (res.error) {
135
+ return {
136
+ type: "error",
137
+ status: res.error.status,
138
+ contentType: "application/json",
139
+ body: JSON.stringify({
140
+ ...res.error,
141
+ message: res.error.message,
142
+ stack: import.meta.env.PROD ? void 0 : res.error.stack
143
+ })
144
+ };
145
+ }
146
+ if (res.data === void 0) {
147
+ return {
148
+ type: "empty",
149
+ status: 204
150
+ };
151
+ }
152
+ return {
153
+ type: "data",
154
+ status: 200,
155
+ contentType: "application/json+devalue",
156
+ body: devalueStringify(res.data, {
157
+ // Add support for URL objects
158
+ URL: (value) => value instanceof URL && value.href
159
+ })
160
+ };
161
+ }
162
+ function deserializeActionResult(res) {
163
+ if (res.type === "error") {
164
+ return { error: ActionError.fromJson(JSON.parse(res.body)), data: void 0 };
165
+ }
166
+ if (res.type === "empty") {
167
+ return { data: void 0, error: void 0 };
168
+ }
169
+ return {
170
+ data: devalueParse(res.body, {
171
+ URL: (href) => new URL(href)
172
+ }),
173
+ error: void 0
174
+ };
175
+ }
129
176
  export {
130
177
  ACTION_ERROR_CODES,
178
+ ACTION_QUERY_PARAMS,
131
179
  ActionError,
132
180
  ActionInputError,
133
181
  callSafely,
182
+ deserializeActionResult,
134
183
  getActionProps,
135
184
  getActionQueryString,
136
- isInputError
185
+ isActionError,
186
+ isInputError,
187
+ serializeActionResult
137
188
  };
@@ -1,5 +1,6 @@
1
1
  import type { APIContext } from '../@types/astro.js';
2
2
  import type { Locals } from './runtime/middleware.js';
3
- export declare function hasActionsInternal(locals: APIContext['locals']): locals is Locals;
3
+ import { type ActionAPIContext } from './runtime/utils.js';
4
+ export declare function hasActionPayload(locals: APIContext['locals']): locals is Locals;
4
5
  export declare function createGetActionResult(locals: APIContext['locals']): APIContext['getActionResult'];
5
- export declare function createCallAction(locals: APIContext['locals']): APIContext['callAction'];
6
+ export declare function createCallAction(context: ActionAPIContext): APIContext['callAction'];