astro 4.12.2 → 4.12.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 (40) hide show
  1. package/dist/@types/astro.d.ts +6 -4
  2. package/dist/actions/consts.d.ts +3 -0
  3. package/dist/actions/consts.js +6 -0
  4. package/dist/actions/index.d.ts +12 -2
  5. package/dist/actions/index.js +43 -7
  6. package/dist/actions/runtime/middleware.js +80 -23
  7. package/dist/actions/runtime/route.js +2 -3
  8. package/dist/actions/runtime/utils.d.ts +4 -2
  9. package/dist/actions/runtime/utils.js +1 -1
  10. package/dist/actions/runtime/virtual/server.d.ts +6 -5
  11. package/dist/actions/runtime/virtual/server.js +6 -5
  12. package/dist/actions/runtime/virtual/shared.d.ts +7 -0
  13. package/dist/actions/runtime/virtual/shared.js +11 -1
  14. package/dist/cli/add/index.js +1 -1
  15. package/dist/content/vite-plugin-content-assets.js +0 -14
  16. package/dist/core/build/generate.js +1 -1
  17. package/dist/core/build/pipeline.js +0 -1
  18. package/dist/core/build/plugins/plugin-content.js +1 -1
  19. package/dist/core/constants.js +1 -1
  20. package/dist/core/dev/dev.js +1 -1
  21. package/dist/core/errors/dev/utils.js +12 -3
  22. package/dist/core/errors/dev/vite.d.ts +13 -0
  23. package/dist/core/errors/dev/vite.js +18 -4
  24. package/dist/core/errors/errors-data.d.ts +26 -0
  25. package/dist/core/errors/errors-data.js +14 -0
  26. package/dist/core/messages.js +2 -2
  27. package/dist/core/render-context.js +2 -0
  28. package/dist/env/runtime.d.ts +2 -2
  29. package/dist/env/runtime.js +1 -1
  30. package/dist/integrations/hooks.js +1 -1
  31. package/dist/runtime/client/dev-toolbar/apps/audit/rules/a11y.js +0 -14
  32. package/dist/runtime/server/render/astro/instance.d.ts +2 -2
  33. package/dist/runtime/server/render/server-islands.js +2 -1
  34. package/dist/transitions/router.js +0 -8
  35. package/dist/vite-plugin-astro-server/error.js +1 -2
  36. package/dist/vite-plugin-astro-server/route.js +0 -1
  37. package/dist/vite-plugin-astro-server/vite.js +4 -0
  38. package/package.json +7 -7
  39. package/templates/actions.mjs +29 -32
  40. package/templates/env/module.mjs +2 -0
@@ -182,7 +182,7 @@ export interface AstroGlobal<Props extends Record<string, any> = Record<string,
182
182
  * ```
183
183
  */
184
184
  getActionResult: AstroSharedContext['getActionResult'];
185
- /** Redirect to another page (**SSR Only**)
185
+ /** Redirect to another page
186
186
  *
187
187
  * Example usage:
188
188
  * ```typescript
@@ -191,7 +191,7 @@ export interface AstroGlobal<Props extends Record<string, any> = Record<string,
191
191
  * }
192
192
  * ```
193
193
  *
194
- * [Astro reference](https://docs.astro.build/en/guides/server-side-rendering/)
194
+ * [Astro reference](https://docs.astro.build/en/reference/api-reference/#astroredirect)
195
195
  */
196
196
  redirect: AstroSharedContext['redirect'];
197
197
  /**
@@ -2482,7 +2482,7 @@ export interface Page<T = any> {
2482
2482
  total: number;
2483
2483
  /** the current page number, starting from 1 */
2484
2484
  currentPage: number;
2485
- /** number of items per page (default: 25) */
2485
+ /** number of items per page (default: 10) */
2486
2486
  size: number;
2487
2487
  /** number of last page */
2488
2488
  lastPage: number;
@@ -2593,7 +2593,7 @@ interface AstroSharedContext<Props extends Record<string, any> = Record<string,
2593
2593
  /**
2594
2594
  * Get action result on the server when using a form POST.
2595
2595
  */
2596
- getActionResult: <TAccept extends ActionAccept, TInputSchema extends ActionInputSchema<TAccept>, TAction extends ActionClient<unknown, TAccept, TInputSchema>>(action: TAction) => Awaited<ReturnType<TAction['safe']>> | undefined;
2596
+ getActionResult: <TAccept extends ActionAccept, TInputSchema extends ActionInputSchema<TAccept>, TAction extends ActionClient<unknown, TAccept, TInputSchema>>(action: TAction) => Awaited<ReturnType<TAction>> | undefined;
2597
2597
  /**
2598
2598
  * Route parameters for this request if this is a dynamic route.
2599
2599
  */
@@ -3013,6 +3013,7 @@ export interface SSRResult {
3013
3013
  * Whether the page has failed with a non-recoverable error, or the client disconnected.
3014
3014
  */
3015
3015
  cancelled: boolean;
3016
+ base: string;
3016
3017
  styles: Set<SSRElement>;
3017
3018
  scripts: Set<SSRElement>;
3018
3019
  links: Set<SSRElement>;
@@ -3037,6 +3038,7 @@ export interface SSRResult {
3037
3038
  pathname: string;
3038
3039
  cookies: AstroCookies | undefined;
3039
3040
  serverIslandNameMap: Map<string, string>;
3041
+ trailingSlash: AstroConfig['trailingSlash'];
3040
3042
  _metadata: SSRMetadata;
3041
3043
  }
3042
3044
  /**
@@ -1,3 +1,6 @@
1
1
  export declare const VIRTUAL_MODULE_ID = "astro:actions";
2
2
  export declare const RESOLVED_VIRTUAL_MODULE_ID: string;
3
3
  export declare const ACTIONS_TYPES_FILE = "actions.d.ts";
4
+ export declare const VIRTUAL_INTERNAL_MODULE_ID = "astro:internal-actions";
5
+ export declare const RESOLVED_VIRTUAL_INTERNAL_MODULE_ID = "\0astro:internal-actions";
6
+ export declare const NOOP_ACTIONS = "\0noop-actions";
@@ -1,8 +1,14 @@
1
1
  const VIRTUAL_MODULE_ID = "astro:actions";
2
2
  const RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
3
3
  const ACTIONS_TYPES_FILE = "actions.d.ts";
4
+ const VIRTUAL_INTERNAL_MODULE_ID = "astro:internal-actions";
5
+ const RESOLVED_VIRTUAL_INTERNAL_MODULE_ID = "\0astro:internal-actions";
6
+ const NOOP_ACTIONS = "\0noop-actions";
4
7
  export {
5
8
  ACTIONS_TYPES_FILE,
9
+ NOOP_ACTIONS,
10
+ RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
6
11
  RESOLVED_VIRTUAL_MODULE_ID,
12
+ VIRTUAL_INTERNAL_MODULE_ID,
7
13
  VIRTUAL_MODULE_ID
8
14
  };
@@ -1,5 +1,15 @@
1
1
  import fsMod from 'node:fs';
2
- import type { AstroIntegration } from '../@types/astro.js';
3
- export default function astroActions({ fs }: {
2
+ import type { Plugin as VitePlugin } from 'vite';
3
+ import type { AstroIntegration, AstroSettings } from '../@types/astro.js';
4
+ export default function astroActions({ fs, settings, }: {
4
5
  fs?: typeof fsMod;
6
+ settings: AstroSettings;
5
7
  }): AstroIntegration;
8
+ /**
9
+ * This plugin is responsible to load the known file `actions/index.js` / `actions.js`
10
+ * If the file doesn't exist, it returns an empty object.
11
+ * @param settings
12
+ */
13
+ export declare function vitePluginUserActions({ settings }: {
14
+ settings: AstroSettings;
15
+ }): VitePlugin;
@@ -2,8 +2,18 @@ import fsMod from "node:fs";
2
2
  import { ActionsWithoutServerOutputError } from "../core/errors/errors-data.js";
3
3
  import { AstroError } from "../core/errors/errors.js";
4
4
  import { isServerLikeOutput, viteID } from "../core/util.js";
5
- import { ACTIONS_TYPES_FILE, RESOLVED_VIRTUAL_MODULE_ID, VIRTUAL_MODULE_ID } from "./consts.js";
6
- function astroActions({ fs = fsMod }) {
5
+ import {
6
+ ACTIONS_TYPES_FILE,
7
+ NOOP_ACTIONS,
8
+ RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
9
+ RESOLVED_VIRTUAL_MODULE_ID,
10
+ VIRTUAL_INTERNAL_MODULE_ID,
11
+ VIRTUAL_MODULE_ID
12
+ } from "./consts.js";
13
+ function astroActions({
14
+ fs = fsMod,
15
+ settings
16
+ }) {
7
17
  return {
8
18
  name: VIRTUAL_MODULE_ID,
9
19
  hooks: {
@@ -18,10 +28,7 @@ function astroActions({ fs = fsMod }) {
18
28
  );
19
29
  params.updateConfig({
20
30
  vite: {
21
- define: {
22
- "import.meta.env.ACTIONS_PATH": stringifiedActionsImport
23
- },
24
- plugins: [vitePluginActions(fs)]
31
+ plugins: [vitePluginUserActions({ settings }), vitePluginActions(fs)]
25
32
  }
26
33
  });
27
34
  params.injectRoute({
@@ -42,6 +49,34 @@ function astroActions({ fs = fsMod }) {
42
49
  }
43
50
  };
44
51
  }
52
+ function vitePluginUserActions({ settings }) {
53
+ let resolvedActionsId;
54
+ return {
55
+ name: "@astro/plugin-actions",
56
+ async resolveId(id) {
57
+ if (id === NOOP_ACTIONS) {
58
+ return NOOP_ACTIONS;
59
+ }
60
+ if (id === VIRTUAL_INTERNAL_MODULE_ID) {
61
+ const resolvedModule = await this.resolve(
62
+ `${decodeURI(new URL("actions", settings.config.srcDir).pathname)}`
63
+ );
64
+ if (!resolvedModule) {
65
+ return NOOP_ACTIONS;
66
+ }
67
+ resolvedActionsId = resolvedModule.id;
68
+ return RESOLVED_VIRTUAL_INTERNAL_MODULE_ID;
69
+ }
70
+ },
71
+ load(id) {
72
+ if (id === NOOP_ACTIONS) {
73
+ return "export const server = {}";
74
+ } else if (id === RESOLVED_VIRTUAL_INTERNAL_MODULE_ID) {
75
+ return `export { server } from '${resolvedActionsId}';`;
76
+ }
77
+ }
78
+ };
79
+ }
45
80
  const vitePluginActions = (fs) => ({
46
81
  name: VIRTUAL_MODULE_ID,
47
82
  enforce: "pre",
@@ -81,5 +116,6 @@ async function typegen({
81
116
  await fs.promises.writeFile(new URL(ACTIONS_TYPES_FILE, dotAstroDir), content);
82
117
  }
83
118
  export {
84
- astroActions as default
119
+ astroActions as default,
120
+ vitePluginUserActions
85
121
  };
@@ -1,49 +1,106 @@
1
1
  import { yellow } from "kleur/colors";
2
+ import {
3
+ ActionQueryStringInvalidError,
4
+ ActionsUsedWithForGetError
5
+ } from "../../core/errors/errors-data.js";
6
+ import { AstroError } from "../../core/errors/errors.js";
2
7
  import { defineMiddleware } from "../../core/middleware/index.js";
3
8
  import { ApiContextStorage } from "./store.js";
4
9
  import { formContentTypes, getAction, hasContentType } from "./utils.js";
5
- import { callSafely } from "./virtual/shared.js";
10
+ import { getActionQueryString } from "./virtual/shared.js";
6
11
  const onRequest = defineMiddleware(async (context, next) => {
7
12
  const locals = context.locals;
13
+ const { request } = context;
8
14
  if (locals._actionsInternal) return ApiContextStorage.run(context, () => next());
9
- if (context.request.method === "GET") {
10
- return nextWithLocalsStub(next, context);
11
- }
12
- if (context.request.method === "POST" && context.request.body === null) {
15
+ if (request.method === "POST" && request.body === null) {
13
16
  return nextWithStaticStub(next, context);
14
17
  }
15
- const { request, url } = context;
16
- const contentType = request.headers.get("Content-Type");
17
- if (url.pathname.startsWith("/_actions")) return nextWithLocalsStub(next, context);
18
- if (!contentType || !hasContentType(contentType, formContentTypes)) {
19
- return nextWithLocalsStub(next, context);
18
+ const actionName = context.url.searchParams.get("_astroAction");
19
+ if (context.request.method === "POST" && actionName) {
20
+ return handlePost({ context, next, actionName });
21
+ }
22
+ if (context.request.method === "GET" && actionName) {
23
+ throw new AstroError({
24
+ ...ActionsUsedWithForGetError,
25
+ message: ActionsUsedWithForGetError.message(actionName)
26
+ });
20
27
  }
21
- const formData = await request.clone().formData();
22
- const actionPath = formData.get("_astroAction");
23
- if (typeof actionPath !== "string") return nextWithLocalsStub(next, context);
24
- const action = await getAction(actionPath);
25
- if (!action) return nextWithLocalsStub(next, context);
26
- const result = await ApiContextStorage.run(context, () => callSafely(() => action(formData)));
28
+ if (context.request.method === "POST") {
29
+ return handlePostLegacy({ context, next });
30
+ }
31
+ return nextWithLocalsStub(next, context);
32
+ });
33
+ async function handlePost({
34
+ context,
35
+ next,
36
+ actionName
37
+ }) {
38
+ const { request } = context;
39
+ const action = await getAction(actionName);
40
+ if (!action) {
41
+ throw new AstroError({
42
+ ...ActionQueryStringInvalidError,
43
+ message: ActionQueryStringInvalidError.message(actionName)
44
+ });
45
+ }
46
+ const contentType = request.headers.get("content-type");
47
+ let formData;
48
+ if (contentType && hasContentType(contentType, formContentTypes)) {
49
+ formData = await request.clone().formData();
50
+ }
51
+ const actionResult = await ApiContextStorage.run(context, () => action(formData));
52
+ return handleResult({ context, next, actionName, actionResult });
53
+ }
54
+ function handleResult({
55
+ context,
56
+ next,
57
+ actionName,
58
+ actionResult
59
+ }) {
27
60
  const actionsInternal = {
28
61
  getActionResult: (actionFn) => {
29
- if (actionFn.toString() !== actionPath) return Promise.resolve(void 0);
30
- return result;
62
+ if (actionFn.toString() !== getActionQueryString(actionName)) {
63
+ return Promise.resolve(void 0);
64
+ }
65
+ return actionResult;
31
66
  },
32
- actionResult: result
67
+ actionResult
33
68
  };
69
+ const locals = context.locals;
34
70
  Object.defineProperty(locals, "_actionsInternal", { writable: false, value: actionsInternal });
35
71
  return ApiContextStorage.run(context, async () => {
36
72
  const response = await next();
37
- if (result.error) {
73
+ if (actionResult.error) {
38
74
  return new Response(response.body, {
39
- status: result.error.status,
40
- statusText: result.error.name,
75
+ status: actionResult.error.status,
76
+ statusText: actionResult.error.type,
41
77
  headers: response.headers
42
78
  });
43
79
  }
44
80
  return response;
45
81
  });
46
- });
82
+ }
83
+ async function handlePostLegacy({ context, next }) {
84
+ const { request } = context;
85
+ if (context.url.pathname.startsWith("/_actions")) return nextWithLocalsStub(next, context);
86
+ const contentType = request.headers.get("content-type");
87
+ let formData;
88
+ if (contentType && hasContentType(contentType, formContentTypes)) {
89
+ formData = await request.clone().formData();
90
+ }
91
+ if (!formData) return nextWithLocalsStub(next, context);
92
+ const actionName = formData.get("_astroAction");
93
+ if (!actionName) return nextWithLocalsStub(next, context);
94
+ const action = await getAction(actionName);
95
+ if (!action) {
96
+ throw new AstroError({
97
+ ...ActionQueryStringInvalidError,
98
+ message: ActionQueryStringInvalidError.message(actionName)
99
+ });
100
+ }
101
+ const actionResult = await ApiContextStorage.run(context, () => action(formData));
102
+ return handleResult({ context, next, actionName, actionResult });
103
+ }
47
104
  function nextWithStaticStub(next, context) {
48
105
  Object.defineProperty(context.locals, "_actionsInternal", {
49
106
  writable: false,
@@ -1,6 +1,5 @@
1
1
  import { ApiContextStorage } from "./store.js";
2
2
  import { formContentTypes, getAction, hasContentType } from "./utils.js";
3
- import { callSafely } from "./virtual/shared.js";
4
3
  const POST = async (context) => {
5
4
  const { request, url } = context;
6
5
  const action = await getAction(url.pathname);
@@ -19,7 +18,7 @@ const POST = async (context) => {
19
18
  } else {
20
19
  return new Response(null, { status: 415 });
21
20
  }
22
- const result = await ApiContextStorage.run(context, () => callSafely(() => action(args)));
21
+ const result = await ApiContextStorage.run(context, () => action(args));
23
22
  if (result.error) {
24
23
  return new Response(
25
24
  JSON.stringify({
@@ -36,7 +35,7 @@ const POST = async (context) => {
36
35
  );
37
36
  }
38
37
  return new Response(JSON.stringify(result.data), {
39
- status: result.data ? 200 : 204,
38
+ status: result.data !== void 0 ? 200 : 204,
40
39
  headers: {
41
40
  "Content-Type": "application/json"
42
41
  }
@@ -1,12 +1,14 @@
1
+ import type { ZodType } from 'zod';
2
+ import type { ActionAccept, ActionClient } from './virtual/server.js';
1
3
  export declare const formContentTypes: string[];
2
4
  export declare function hasContentType(contentType: string, expected: string[]): boolean;
3
5
  export type MaybePromise<T> = T | Promise<T>;
4
6
  /**
5
7
  * Get server-side action based on the route path.
6
- * Imports from `import.meta.env.ACTIONS_PATH`, which maps to
8
+ * Imports from the virtual module `astro:internal-actions`, which maps to
7
9
  * the user's `src/actions/index.ts` file at build-time.
8
10
  */
9
- export declare function getAction(path: string): Promise<((param: unknown) => MaybePromise<unknown>) | undefined>;
11
+ export declare function getAction(path: string): Promise<ActionClient<unknown, ActionAccept, ZodType> | undefined>;
10
12
  /**
11
13
  * Used to preserve the input schema type in the error object.
12
14
  * This allows for type inference on the `fields` property
@@ -5,7 +5,7 @@ function hasContentType(contentType, expected) {
5
5
  }
6
6
  async function getAction(path) {
7
7
  const pathKeys = path.replace("/_actions/", "").split(".");
8
- let { server: actionLookup } = await import(import.meta.env.ACTIONS_PATH);
8
+ let { server: actionLookup } = await import("astro:internal-actions");
9
9
  for (const key of pathKeys) {
10
10
  if (!(key in actionLookup)) {
11
11
  return void 0;
@@ -10,15 +10,16 @@ export type ActionAccept = 'form' | 'json';
10
10
  export type ActionInputSchema<T extends ActionAccept | undefined> = T extends 'form' ? z.AnyZodObject | z.ZodType<FormData> : z.ZodType;
11
11
  export type ActionHandler<TInputSchema, TOutput> = TInputSchema extends z.ZodType ? (input: z.infer<TInputSchema>, context: ActionAPIContext) => MaybePromise<TOutput> : (input: any, context: ActionAPIContext) => MaybePromise<TOutput>;
12
12
  export type ActionReturnType<T extends ActionHandler<any, any>> = Awaited<ReturnType<T>>;
13
- 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<Awaited<TOutput>>) & {
14
- safe: (input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<SafeResult<z.input<TInputSchema> extends ErrorInferenceObject ? z.input<TInputSchema> : ErrorInferenceObject, Awaited<TOutput>>>;
15
- } : ((input?: any) => Promise<Awaited<TOutput>>) & {
16
- safe: (input?: any) => Promise<SafeResult<never, Awaited<TOutput>>>;
13
+ 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>>>) & {
14
+ queryString: string;
15
+ orThrow: (input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<Awaited<TOutput>>;
16
+ } : (input?: any) => Promise<SafeResult<never, Awaited<TOutput>>> & {
17
+ orThrow: (input?: any) => Promise<Awaited<TOutput>>;
17
18
  };
18
19
  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, }: {
19
20
  input?: TInputSchema;
20
21
  accept?: TAccept;
21
22
  handler: ActionHandler<TInputSchema, TOutput>;
22
- }): ActionClient<TOutput, TAccept, TInputSchema>;
23
+ }): ActionClient<TOutput, TAccept, TInputSchema> & string;
23
24
  /** Transform form data to an object based on a Zod schema. */
24
25
  export declare function formDataToObject<T extends z.AnyZodObject>(formData: FormData, schema: T): Record<string, unknown>;
@@ -10,12 +10,13 @@ function defineAction({
10
10
  handler
11
11
  }) {
12
12
  const serverHandler = accept === "form" ? getFormServerHandler(handler, inputSchema) : getJsonServerHandler(handler, inputSchema);
13
- Object.assign(serverHandler, {
14
- safe: async (unparsedInput) => {
15
- return callSafely(() => serverHandler(unparsedInput));
16
- }
13
+ const safeServerHandler = async (unparsedInput) => {
14
+ return callSafely(() => serverHandler(unparsedInput));
15
+ };
16
+ Object.assign(safeServerHandler, {
17
+ orThrow: serverHandler
17
18
  });
18
- return serverHandler;
19
+ return safeServerHandler;
19
20
  }
20
21
  function getFormServerHandler(handler, inputSchema) {
21
22
  return async (unparsedInput) => {
@@ -31,6 +31,13 @@ export declare class ActionInputError<T extends ErrorInferenceObject> extends Ac
31
31
  constructor(issues: z.ZodIssue[]);
32
32
  }
33
33
  export declare function callSafely<TOutput>(handler: () => MaybePromise<TOutput>): Promise<SafeResult<z.ZodType, TOutput>>;
34
+ export declare function getActionQueryString(name: string): string;
35
+ /**
36
+ * @deprecated You can now pass action functions
37
+ * directly to the `action` attribute on a form.
38
+ *
39
+ * Example: `<form action={actions.like} />`
40
+ */
34
41
  export declare function getActionProps<T extends (args: FormData) => MaybePromise<unknown>>(action: T): {
35
42
  readonly type: "hidden";
36
43
  readonly name: "_astroAction";
@@ -110,11 +110,20 @@ async function callSafely(handler) {
110
110
  };
111
111
  }
112
112
  }
113
+ function getActionQueryString(name) {
114
+ const searchParams = new URLSearchParams({ _astroAction: name });
115
+ return `?${searchParams.toString()}`;
116
+ }
113
117
  function getActionProps(action) {
118
+ const params = new URLSearchParams(action.toString());
119
+ const actionName = params.get("_astroAction");
120
+ if (!actionName) {
121
+ throw new Error("Invalid actions function was passed to getActionProps()");
122
+ }
114
123
  return {
115
124
  type: "hidden",
116
125
  name: "_astroAction",
117
- value: action.toString()
126
+ value: actionName
118
127
  };
119
128
  }
120
129
  export {
@@ -123,5 +132,6 @@ export {
123
132
  ActionInputError,
124
133
  callSafely,
125
134
  getActionProps,
135
+ getActionQueryString,
126
136
  isInputError
127
137
  };
@@ -569,7 +569,7 @@ async function resolveRangeToInstallSpecifier(name, range) {
569
569
  const versions = await fetchPackageVersions(name);
570
570
  if (versions instanceof Error) return name;
571
571
  const stableVersions = versions.filter((v) => !v.includes("-"));
572
- const maxStable = maxSatisfying(stableVersions, range);
572
+ const maxStable = maxSatisfying(stableVersions.length !== 0 ? stableVersions : versions, range);
573
573
  return `${name}@^${maxStable}`;
574
574
  }
575
575
  const INHERITED_FLAGS = /* @__PURE__ */ new Set([
@@ -92,22 +92,9 @@ function astroContentAssetPropagationPlugin({
92
92
  };
93
93
  }
94
94
  function astroConfigBuildPlugin(options, internals) {
95
- let ssrPluginContext = void 0;
96
95
  return {
97
96
  targets: ["server"],
98
97
  hooks: {
99
- "build:before": ({ target }) => {
100
- return {
101
- vitePlugin: {
102
- name: "astro:content-build-plugin",
103
- generateBundle() {
104
- if (target === "server") {
105
- ssrPluginContext = this;
106
- }
107
- }
108
- }
109
- };
110
- },
111
98
  "build:post": ({ ssrOutputs, clientOutputs, mutate }) => {
112
99
  const outputs = ssrOutputs.flatMap((o) => o.output);
113
100
  const prependBase = (src) => {
@@ -187,7 +174,6 @@ function astroConfigBuildPlugin(options, internals) {
187
174
  mutate(chunk, ["server"], newCode);
188
175
  }
189
176
  }
190
- ssrPluginContext = void 0;
191
177
  }
192
178
  }
193
179
  };
@@ -144,7 +144,7 @@ ${bgGreen(black(` ${verb} static routes `))}`);
144
144
  await runHookBuildGenerated({ config, logger });
145
145
  }
146
146
  async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
147
- const { config, internals, logger } = pipeline;
147
+ const { config, logger } = pipeline;
148
148
  const pageModulePromise = ssrEntry.page;
149
149
  const styles = pageData.styles.sort(cssOrder).map(({ sheet }) => sheet).reduce(mergeInlineCss, []);
150
150
  const linkIds = [];
@@ -1,6 +1,5 @@
1
1
  import { getOutputDirectory } from "../../prerender/utils.js";
2
2
  import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
3
- import { DEFAULT_404_COMPONENT } from "../constants.js";
4
3
  import { routeIsFallback, routeIsRedirect } from "../redirects/helpers.js";
5
4
  import { RedirectSinglePageBuiltModule } from "../redirects/index.js";
6
5
  import { Pipeline } from "../render/index.js";
@@ -278,7 +278,7 @@ async function generateContentManifest(opts, lookupMap) {
278
278
  promises.push(
279
279
  limit(async () => {
280
280
  const data = await fsMod.promises.readFile(fileURL, { encoding: "utf8" });
281
- manifest.entries.push([key, checksum(data)]);
281
+ manifest.entries.push([key, checksum(data, fileURL.toString())]);
282
282
  })
283
283
  );
284
284
  }
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "4.12.2";
1
+ const ASTRO_VERSION = "4.12.3";
2
2
  const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
3
3
  const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
4
4
  const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
@@ -19,7 +19,7 @@ async function dev(inlineConfig) {
19
19
  await telemetry.record([]);
20
20
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
21
21
  const logger = restart.container.logger;
22
- const currentVersion = "4.12.2";
22
+ const currentVersion = "4.12.3";
23
23
  const isPrerelease = currentVersion.includes("-");
24
24
  if (!isPrerelease) {
25
25
  try {
@@ -15,7 +15,10 @@ function collectErrorMetadata(e, rootFolder) {
15
15
  err.forEach((error) => {
16
16
  if (e.stack) {
17
17
  const stackInfo = collectInfoFromStacktrace(e);
18
- error.stack = stripAnsi(stackInfo.stack);
18
+ try {
19
+ error.stack = stripAnsi(stackInfo.stack);
20
+ } catch {
21
+ }
19
22
  error.loc = stackInfo.loc;
20
23
  error.plugin = stackInfo.plugin;
21
24
  error.pluginCode = stackInfo.pluginCode;
@@ -40,14 +43,20 @@ function collectErrorMetadata(e, rootFolder) {
40
43
  }
41
44
  error.hint = generateHint(e);
42
45
  if (error.message) {
43
- error.message = stripAnsi(error.message);
46
+ try {
47
+ error.message = stripAnsi(error.message);
48
+ } catch {
49
+ }
44
50
  }
45
51
  });
46
52
  if (!AggregateError.is(e) && Array.isArray(e.errors)) {
47
53
  e.errors.forEach((buildError, i) => {
48
54
  const { location, pluginName, text } = buildError;
49
55
  if (text) {
50
- err[i].message = text;
56
+ try {
57
+ err[i].message = text;
58
+ } catch {
59
+ }
51
60
  }
52
61
  if (location) {
53
62
  err[i].loc = { file: location.file, line: location.line, column: location.column };
@@ -1,3 +1,4 @@
1
+ import type { ShikiTransformer } from 'shiki';
1
2
  import type { ErrorPayload } from 'vite';
2
3
  import type { ModuleLoader } from '../../module-loader/index.js';
3
4
  import { type ErrorWithMetadata } from '../errors.js';
@@ -28,3 +29,15 @@ export interface AstroErrorPayload {
28
29
  * Generate a payload for Vite's error overlay
29
30
  */
30
31
  export declare function getViteErrorPayload(err: ErrorWithMetadata): Promise<AstroErrorPayload>;
32
+ /**
33
+ * Transformer for `shiki`'s legacy `lineOptions`, allows to add classes to specific lines
34
+ * FROM: https://github.com/shikijs/shiki/blob/4a58472070a9a359a4deafec23bb576a73e24c6a/packages/transformers/src/transformers/compact-line-options.ts
35
+ * LICENSE: https://github.com/shikijs/shiki/blob/4a58472070a9a359a4deafec23bb576a73e24c6a/LICENSE
36
+ */
37
+ export declare function transformerCompactLineOptions(lineOptions?: {
38
+ /**
39
+ * 1-based line number.
40
+ */
41
+ line: number;
42
+ classes?: string[];
43
+ }[]): ShikiTransformer;
@@ -86,10 +86,13 @@ async function getViteErrorPayload(err) {
86
86
  highlighterLang = "md";
87
87
  }
88
88
  const highlightedCode = err.fullCode ? await codeToHtml(err.fullCode, {
89
- // @ts-expect-error always assume that shiki can accept the lang string
90
- lang: highlighterLang,
89
+ lang: highlighterLang ?? "text",
91
90
  theme: cssVariablesTheme(),
92
- lineOptions: err.loc?.line ? [{ line: err.loc.line, classes: ["error-line"] }] : void 0
91
+ transformers: [
92
+ transformerCompactLineOptions(
93
+ err.loc?.line ? [{ line: err.loc.line, classes: ["error-line"] }] : void 0
94
+ )
95
+ ]
93
96
  }) : void 0;
94
97
  return {
95
98
  type: "error",
@@ -113,7 +116,18 @@ async function getViteErrorPayload(err) {
113
116
  }
114
117
  };
115
118
  }
119
+ function transformerCompactLineOptions(lineOptions = []) {
120
+ return {
121
+ name: "@shikijs/transformers:compact-line-options",
122
+ line(node, line) {
123
+ const lineOption = lineOptions.find((o) => o.line === line);
124
+ if (lineOption?.classes) this.addClassToHast(node, lineOption.classes);
125
+ return node;
126
+ }
127
+ };
128
+ }
116
129
  export {
117
130
  enhanceViteSSRError,
118
- getViteErrorPayload
131
+ getViteErrorPayload,
132
+ transformerCompactLineOptions
119
133
  };
@@ -1439,6 +1439,32 @@ export declare const ActionsWithoutServerOutputError: {
1439
1439
  message: string;
1440
1440
  hint: string;
1441
1441
  };
1442
+ /**
1443
+ * @docs
1444
+ * @see
1445
+ * - [Actions RFC](https://github.com/withastro/roadmap/blob/actions/proposals/0046-actions.md)
1446
+ * @description
1447
+ * Action was called from a form using a GET request, but only POST requests are supported. This often occurs if `method="POST"` is missing on the form.
1448
+ */
1449
+ export declare const ActionsUsedWithForGetError: {
1450
+ name: string;
1451
+ title: string;
1452
+ message: (actionName: string) => string;
1453
+ hint: string;
1454
+ };
1455
+ /**
1456
+ * @docs
1457
+ * @see
1458
+ * - [Actions RFC](https://github.com/withastro/roadmap/blob/actions/proposals/0046-actions.md)
1459
+ * @description
1460
+ * The server received the query string `?_astroAction=name`, but could not find an action with that name. Use the action function's `.queryString` property to retrieve the form `action` URL.
1461
+ */
1462
+ export declare const ActionQueryStringInvalidError: {
1463
+ name: string;
1464
+ title: string;
1465
+ message: (actionName: string) => string;
1466
+ hint: string;
1467
+ };
1442
1468
  /**
1443
1469
  * @docs
1444
1470
  * @see
@@ -547,6 +547,18 @@ const ActionsWithoutServerOutputError = {
547
547
  message: "Actions enabled without setting a server build output. A server is required to create callable backend functions. To deploy routes to a server, add a server adapter to your astro config.",
548
548
  hint: "Learn about on-demand rendering: https://docs.astro.build/en/basics/rendering-modes/#on-demand-rendered"
549
549
  };
550
+ const ActionsUsedWithForGetError = {
551
+ name: "ActionsUsedWithForGetError",
552
+ title: "An invalid Action query string was passed by a form.",
553
+ message: (actionName) => `Action ${actionName} was called from a form using a GET request, but only POST requests are supported. This often occurs if \`method="POST"\` is missing on the form.`,
554
+ hint: "Actions are experimental. Visit the RFC for usage instructions: https://github.com/withastro/roadmap/blob/actions/proposals/0046-actions.md"
555
+ };
556
+ const ActionQueryStringInvalidError = {
557
+ name: "ActionQueryStringInvalidError",
558
+ title: "An invalid Action query string was passed by a form.",
559
+ message: (actionName) => `The server received the query string \`?_astroAction=${actionName}\`, but could not find an action with that name. If you changed an action's name in development, remove this query param from your URL and refresh.`,
560
+ hint: "Actions are experimental. Visit the RFC for usage instructions: https://github.com/withastro/roadmap/blob/actions/proposals/0046-actions.md"
561
+ };
550
562
  const UnsupportedConfigTransformError = {
551
563
  name: "UnsupportedConfigTransformError",
552
564
  title: "Unsupported transform in content config.",
@@ -556,6 +568,8 @@ Full error: ${parseError}`,
556
568
  };
557
569
  const UnknownError = { name: "UnknownError", title: "Unknown Error." };
558
570
  export {
571
+ ActionQueryStringInvalidError,
572
+ ActionsUsedWithForGetError,
559
573
  ActionsWithoutServerOutputError,
560
574
  AstroGlobNoMatch,
561
575
  AstroGlobUsedOutside,
@@ -38,7 +38,7 @@ function serverStart({
38
38
  host,
39
39
  base
40
40
  }) {
41
- const version = "4.12.2";
41
+ const version = "4.12.3";
42
42
  const localPrefix = `${dim("\u2503")} Local `;
43
43
  const networkPrefix = `${dim("\u2503")} Network `;
44
44
  const emptyPrefix = " ".repeat(11);
@@ -270,7 +270,7 @@ function printHelp({
270
270
  message.push(
271
271
  linebreak(),
272
272
  ` ${bgGreen(black(` ${commandName} `))} ${green(
273
- `v${"4.12.2"}`
273
+ `v${"4.12.3"}`
274
274
  )} ${headline}`
275
275
  );
276
276
  }
@@ -282,6 +282,7 @@ class RenderContext {
282
282
  };
283
283
  const actionResult = hasActionsInternal(this.locals) ? this.locals._actionsInternal?.actionResult : void 0;
284
284
  const result = {
285
+ base: manifest.base,
285
286
  cancelled: false,
286
287
  clientDirectives,
287
288
  inlinedScripts,
@@ -302,6 +303,7 @@ class RenderContext {
302
303
  styles,
303
304
  actionResult,
304
305
  serverIslandNameMap: manifest.serverIslandNameMap ?? /* @__PURE__ */ new Map(),
306
+ trailingSlash: manifest.trailingSlash,
305
307
  _metadata: {
306
308
  hasHydrationScript: false,
307
309
  rendererSpecificHydrationScripts: /* @__PURE__ */ new Set(),
@@ -2,8 +2,8 @@ import { AstroError } from '../core/errors/index.js';
2
2
  import type { ValidationResultInvalid } from './validators.js';
3
3
  export { validateEnvVariable, getEnvFieldType } from './validators.js';
4
4
  export type GetEnv = (key: string) => string | undefined;
5
+ type OnSetGetEnv = (reset: boolean) => void;
5
6
  export declare function setGetEnv(fn: GetEnv, reset?: boolean): void;
6
- declare let _onSetGetEnv: (reset: boolean) => void;
7
- export declare function setOnSetGetEnv(fn: typeof _onSetGetEnv): void;
7
+ export declare function setOnSetGetEnv(fn: OnSetGetEnv): void;
8
8
  export declare function getEnv(...args: Parameters<GetEnv>): string | undefined;
9
9
  export declare function createInvalidVariablesError(key: string, type: string, result: ValidationResultInvalid): AstroError;
@@ -6,7 +6,7 @@ function setGetEnv(fn, reset = false) {
6
6
  _getEnv = fn;
7
7
  _onSetGetEnv(reset);
8
8
  }
9
- let _onSetGetEnv = (reset) => {
9
+ let _onSetGetEnv = () => {
10
10
  };
11
11
  function setOnSetGetEnv(fn) {
12
12
  _onSetGetEnv = fn;
@@ -82,7 +82,7 @@ async function runHookConfigSetup({
82
82
  }
83
83
  if (settings.config.experimental?.actions) {
84
84
  const { default: actionsIntegration } = await import("../actions/index.js");
85
- settings.config.integrations.push(actionsIntegration({ fs }));
85
+ settings.config.integrations.push(actionsIntegration({ fs, settings }));
86
86
  }
87
87
  let updatedConfig = { ...settings.config };
88
88
  let updatedSettings = { ...settings, config: updatedConfig };
@@ -110,10 +110,6 @@ const a11y_required_content = [
110
110
  "h6"
111
111
  ];
112
112
  const a11y_distracting_elements = ["blink", "marquee"];
113
- const a11y_nested_implicit_semantics = /* @__PURE__ */ new Map([
114
- ["header", "banner"],
115
- ["footer", "contentinfo"]
116
- ]);
117
113
  const a11y_implicit_semantics = /* @__PURE__ */ new Map([
118
114
  ["a", "link"],
119
115
  ["area", "link"],
@@ -525,16 +521,6 @@ const a11y = [
525
521
  }
526
522
  }
527
523
  ];
528
- const a11y_labelable = [
529
- "button",
530
- "input",
531
- "keygen",
532
- "meter",
533
- "output",
534
- "progress",
535
- "select",
536
- "textarea"
537
- ];
538
524
  const a11y_non_interactive_element_to_interactive_role_exceptions = {
539
525
  ul: ["listbox", "menu", "menubar", "radiogroup", "tablist", "tree", "treegrid"],
540
526
  ol: ["listbox", "menu", "menubar", "radiogroup", "tablist", "tree", "treegrid"],
@@ -1,6 +1,6 @@
1
1
  import type { SSRResult } from '../../../../@types/astro.js';
2
2
  import type { ComponentSlots } from '../slot.js';
3
- import type { AstroComponentFactory, AstroFactoryReturnValue } from './factory.js';
3
+ import type { AstroComponentFactory } from './factory.js';
4
4
  import type { RenderDestination } from '../common.js';
5
5
  type ComponentProps = Record<string | number, any>;
6
6
  declare const astroComponentInstanceSym: unique symbol;
@@ -12,7 +12,7 @@ export declare class AstroComponentInstance {
12
12
  private readonly factory;
13
13
  private returnValue;
14
14
  constructor(result: SSRResult, props: ComponentProps, slots: ComponentSlots, factory: AstroComponentFactory);
15
- init(result: SSRResult): Promise<AstroFactoryReturnValue>;
15
+ init(result: SSRResult): Promise<import("./factory.js").AstroFactoryReturnValue>;
16
16
  render(destination: RenderDestination): Promise<void>;
17
17
  }
18
18
  export declare function createAstroComponentInstance(result: SSRResult, displayName: string, factory: AstroComponentFactory, props: ComponentProps, slots?: any): AstroComponentInstance;
@@ -37,6 +37,7 @@ function renderServerIsland(result, _displayName, props, slots) {
37
37
  }
38
38
  }
39
39
  const hostId = crypto.randomUUID();
40
+ const serverIslandUrl = `${result.base}_server-islands/${componentId}${result.trailingSlash === "always" ? "/" : ""}`;
40
41
  destination.write(`<script async type="module" data-island-id="${hostId}">
41
42
  let componentId = ${safeJsonStringify(componentId)};
42
43
  let componentExport = ${safeJsonStringify(componentExport)};
@@ -47,7 +48,7 @@ let data = {
47
48
  slots: ${safeJsonStringify(renderedSlots)},
48
49
  };
49
50
 
50
- let response = await fetch('/_server-islands/${componentId}', {
51
+ let response = await fetch('${serverIslandUrl}', {
51
52
  method: 'POST',
52
53
  body: JSON.stringify(data),
53
54
  });
@@ -1,12 +1,4 @@
1
1
  import { TRANSITION_AFTER_SWAP, doPreparation, doSwap } from "./events.js";
2
- import {
3
- deselectScripts,
4
- restoreFocus,
5
- saveFocus,
6
- swapBodyElement,
7
- swapHeadElements,
8
- swapRootAttributes
9
- } from "./swap-functions.js";
10
2
  const inBrowser = import.meta.env.SSR === false;
11
3
  const pushState = inBrowser && history.pushState.bind(history);
12
4
  const replaceState = inBrowser && history.replaceState.bind(history);
@@ -1,7 +1,6 @@
1
1
  import { collectErrorMetadata } from "../core/errors/dev/index.js";
2
- import { AstroErrorData, createSafeError } from "../core/errors/index.js";
2
+ import { createSafeError } from "../core/errors/index.js";
3
3
  import { formatErrorMessage } from "../core/messages.js";
4
- import { eventError, telemetry } from "../events/index.js";
5
4
  function recordServerError(loader, config, { logger }, _err) {
6
5
  const err = createSafeError(_err);
7
6
  try {
@@ -1,5 +1,4 @@
1
1
  import {
2
- DEFAULT_404_COMPONENT,
3
2
  REROUTE_DIRECTIVE_HEADER,
4
3
  REWRITE_DIRECTIVE_HEADER_KEY,
5
4
  clientLocalsSymbol
@@ -1,6 +1,7 @@
1
1
  import npath from "node:path";
2
2
  import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from "../core/constants.js";
3
3
  import { unwrapId } from "../core/util.js";
4
+ import { hasSpecialQueries } from "../vite-plugin-utils/index.js";
4
5
  import { isCSSRequest } from "./util.js";
5
6
  const fileExtensionsToSSR = /* @__PURE__ */ new Set([".astro", ".mdoc", ...SUPPORTED_MARKDOWN_FILE_EXTENSIONS]);
6
7
  const STRIP_QUERY_PARAMS_REGEX = /\?.*$/;
@@ -27,6 +28,9 @@ async function* crawlGraph(loader, _id, isRootFile, scanned = /* @__PURE__ */ ne
27
28
  if (isCSSRequest(id)) {
28
29
  continue;
29
30
  }
31
+ if (hasSpecialQueries(id)) {
32
+ continue;
33
+ }
30
34
  for (const importedModule of entry.importedModules) {
31
35
  if (!importedModule.id) continue;
32
36
  const importedModulePathname = importedModule.id.replace(STRIP_QUERY_PARAMS_REGEX, "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "4.12.2",
3
+ "version": "4.12.3",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -110,7 +110,7 @@
110
110
  "vendor"
111
111
  ],
112
112
  "dependencies": {
113
- "@astrojs/compiler": "^2.9.0",
113
+ "@astrojs/compiler": "^2.9.2",
114
114
  "@babel/core": "^7.24.9",
115
115
  "@babel/generator": "^7.24.10",
116
116
  "@babel/parser": "^7.24.8",
@@ -156,8 +156,8 @@
156
156
  "preferred-pm": "^4.0.0",
157
157
  "prompts": "^2.4.2",
158
158
  "rehype": "^13.0.1",
159
- "semver": "^7.6.2",
160
- "shiki": "^1.10.3",
159
+ "semver": "^7.6.3",
160
+ "shiki": "^1.11.0",
161
161
  "string-width": "^7.2.0",
162
162
  "strip-ansi": "^7.1.0",
163
163
  "tsconfck": "^3.1.1",
@@ -177,7 +177,7 @@
177
177
  "sharp": "^0.33.3"
178
178
  },
179
179
  "devDependencies": {
180
- "@astrojs/check": "^0.8.1",
180
+ "@astrojs/check": "^0.8.2",
181
181
  "@playwright/test": "^1.45.2",
182
182
  "@types/aria-query": "^5.0.4",
183
183
  "@types/babel__generator": "^7.6.8",
@@ -188,7 +188,7 @@
188
188
  "@types/debug": "^4.1.12",
189
189
  "@types/diff": "^5.2.1",
190
190
  "@types/dlv": "^1.1.4",
191
- "@types/dom-view-transitions": "^1.0.4",
191
+ "@types/dom-view-transitions": "^1.0.5",
192
192
  "@types/hast": "^3.0.4",
193
193
  "@types/html-escaper": "^3.0.2",
194
194
  "@types/http-cache-semantics": "^4.0.4",
@@ -211,7 +211,7 @@
211
211
  "rehype-slug": "^6.0.0",
212
212
  "rehype-toc": "^3.0.2",
213
213
  "remark-code-titles": "^0.1.2",
214
- "rollup": "^4.18.1",
214
+ "rollup": "^4.19.0",
215
215
  "sass": "^1.77.8",
216
216
  "srcset-parse": "^1.1.0",
217
217
  "undici": "^6.19.2",
@@ -1,39 +1,36 @@
1
- import { ActionError, callSafely } from 'astro:actions';
1
+ import { ActionError, callSafely, getActionQueryString } from 'astro:actions';
2
2
 
3
- function toActionProxy(actionCallback = {}, aggregatedPath = '/_actions/') {
3
+ function toActionProxy(actionCallback = {}, aggregatedPath = '') {
4
4
  return new Proxy(actionCallback, {
5
5
  get(target, objKey) {
6
- if (objKey in target) {
6
+ if (objKey in target || typeof objKey === 'symbol') {
7
7
  return target[objKey];
8
8
  }
9
9
  const path = aggregatedPath + objKey.toString();
10
- const action = (param) => actionHandler(param, path);
11
- action.toString = () => path;
12
- action.safe = (input) => {
13
- return callSafely(() => action(input));
14
- };
15
- action.safe.toString = () => path;
10
+ const action = (param) => callSafely(() => handleActionOrThrow(param, path));
11
+
12
+ Object.assign(action, {
13
+ queryString: getActionQueryString(path),
14
+ toString: () => action.queryString,
15
+ // Progressive enhancement info for React.
16
+ $$FORM_ACTION: function () {
17
+ return {
18
+ method: 'POST',
19
+ // `name` creates a hidden input.
20
+ // It's unused by Astro, but we can't turn this off.
21
+ // At least use a name that won't conflict with a user's formData.
22
+ name: '_astroAction',
23
+ action: action.toString(),
24
+ };
25
+ },
26
+ // Note: `orThrow` does not have progressive enhancement info.
27
+ // If you want to throw exceptions,
28
+ // you must handle those exceptions with client JS.
29
+ orThrow: (param) => {
30
+ return handleActionOrThrow(param, path);
31
+ },
32
+ });
16
33
 
17
- // Add progressive enhancement info for React.
18
- action.$$FORM_ACTION = function () {
19
- const data = new FormData();
20
- data.set('_astroAction', action.toString());
21
- return {
22
- method: 'POST',
23
- name: action.toString(),
24
- data,
25
- };
26
- };
27
- action.safe.$$FORM_ACTION = function () {
28
- const data = new FormData();
29
- data.set('_astroAction', action.toString());
30
- data.set('_astroActionSafe', 'true');
31
- return {
32
- method: 'POST',
33
- name: action.toString(),
34
- data,
35
- };
36
- };
37
34
  // recurse to construct queries for nested object paths
38
35
  // ex. actions.user.admins.auth()
39
36
  return toActionProxy(action, path + '.');
@@ -46,14 +43,14 @@ function toActionProxy(actionCallback = {}, aggregatedPath = '/_actions/') {
46
43
  * @param {string} path Built path to call action by path name.
47
44
  * Usage: `actions.[name](param)`.
48
45
  */
49
- async function actionHandler(param, path) {
46
+ async function handleActionOrThrow(param, path) {
50
47
  // When running server-side, import the action and call it.
51
48
  if (import.meta.env.SSR) {
52
49
  const { getAction } = await import('astro/actions/runtime/utils.js');
53
50
  const action = await getAction(path);
54
51
  if (!action) throw new Error(`Action not found: ${path}`);
55
52
 
56
- return action(param);
53
+ return action.orThrow(param);
57
54
  }
58
55
 
59
56
  // When running client-side, make a fetch request to the action path.
@@ -72,7 +69,7 @@ async function actionHandler(param, path) {
72
69
  headers.set('Content-Type', 'application/json');
73
70
  headers.set('Content-Length', body?.length.toString() ?? '0');
74
71
  }
75
- const res = await fetch(path, {
72
+ const res = await fetch(`/_actions/${path}`, {
76
73
  method: 'POST',
77
74
  body,
78
75
  headers,
@@ -25,6 +25,8 @@ const _internalGetSecret = (key) => {
25
25
  throw createInvalidVariablesError(key, type, result);
26
26
  };
27
27
 
28
+ // used while generating the virtual module
29
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
28
30
  setOnSetGetEnv((reset) => {
29
31
  // @@ON_SET_GET_ENV@@
30
32
  });