astro 5.4.1 → 5.4.2

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 (46) hide show
  1. package/components/Debug.astro +3 -2
  2. package/dist/actions/consts.d.ts +2 -2
  3. package/dist/actions/consts.js +4 -4
  4. package/dist/actions/integration.js +1 -5
  5. package/dist/actions/loadActions.d.ts +8 -0
  6. package/dist/actions/loadActions.js +13 -0
  7. package/dist/actions/plugins.d.ts +8 -0
  8. package/dist/actions/plugins.js +24 -5
  9. package/dist/actions/runtime/virtual/server.d.ts +2 -2
  10. package/dist/actions/runtime/virtual/server.js +1 -2
  11. package/dist/actions/runtime/virtual/shared.d.ts +2 -0
  12. package/dist/actions/runtime/virtual/shared.js +8 -1
  13. package/dist/content/content-layer.js +3 -3
  14. package/dist/content/runtime.js +2 -1
  15. package/dist/core/app/types.d.ts +6 -0
  16. package/dist/core/base-pipeline.d.ts +9 -1
  17. package/dist/core/base-pipeline.js +40 -1
  18. package/dist/core/build/generate.js +5 -3
  19. package/dist/core/build/internal.d.ts +2 -1
  20. package/dist/core/build/internal.js +3 -1
  21. package/dist/core/build/plugins/index.js +2 -0
  22. package/dist/core/build/plugins/plugin-actions.d.ts +4 -0
  23. package/dist/core/build/plugins/plugin-actions.js +16 -0
  24. package/dist/core/build/plugins/plugin-middleware.d.ts +0 -1
  25. package/dist/core/build/plugins/plugin-middleware.js +0 -2
  26. package/dist/core/build/plugins/plugin-ssr.js +4 -1
  27. package/dist/core/constants.js +1 -1
  28. package/dist/core/dev/dev.js +1 -1
  29. package/dist/core/errors/errors-data.d.ts +11 -0
  30. package/dist/core/errors/errors-data.js +6 -0
  31. package/dist/core/messages.js +2 -2
  32. package/dist/core/render-context.d.ts +7 -5
  33. package/dist/core/render-context.js +28 -13
  34. package/dist/transitions/router.js +2 -1
  35. package/dist/transitions/swap-functions.d.ts +1 -0
  36. package/dist/transitions/swap-functions.js +15 -11
  37. package/dist/vite-plugin-astro-server/pipeline.d.ts +0 -1
  38. package/dist/vite-plugin-astro-server/pipeline.js +0 -10
  39. package/dist/vite-plugin-astro-server/plugin.js +1 -1
  40. package/dist/vite-plugin-astro-server/route.js +5 -1
  41. package/package.json +16 -16
  42. package/templates/actions.mjs +8 -4
  43. package/dist/actions/runtime/middleware.d.ts +0 -1
  44. package/dist/actions/runtime/middleware.js +0 -14
  45. package/dist/actions/runtime/virtual/get-action.d.ts +0 -8
  46. package/dist/actions/runtime/virtual/get-action.js +0 -29
@@ -21,8 +21,9 @@ const value = Astro.props[key];
21
21
  font-size: 14px;
22
22
  padding: 1rem 1.5rem;
23
23
  background: white;
24
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
25
- 'Open Sans', 'Helvetica Neue', sans-serif;
24
+ font-family:
25
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
26
+ 'Helvetica Neue', sans-serif;
26
27
  }
27
28
 
28
29
  .astro-debug-header,
@@ -1,8 +1,8 @@
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";
4
+ export declare const ASTRO_ACTIONS_INTERNAL_MODULE_ID = "astro-internal:actions";
5
+ export declare const RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID: string;
6
6
  export declare const NOOP_ACTIONS = "\0noop-actions";
7
7
  export declare const ACTION_QUERY_PARAMS: {
8
8
  actionName: string;
@@ -1,8 +1,8 @@
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";
4
+ const ASTRO_ACTIONS_INTERNAL_MODULE_ID = "astro-internal:actions";
5
+ const RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID = "\0" + ASTRO_ACTIONS_INTERNAL_MODULE_ID;
6
6
  const NOOP_ACTIONS = "\0noop-actions";
7
7
  const ACTION_QUERY_PARAMS = {
8
8
  actionName: "_action",
@@ -13,9 +13,9 @@ export {
13
13
  ACTIONS_TYPES_FILE,
14
14
  ACTION_QUERY_PARAMS,
15
15
  ACTION_RPC_ROUTE_PATTERN,
16
+ ASTRO_ACTIONS_INTERNAL_MODULE_ID,
16
17
  NOOP_ACTIONS,
17
- RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
18
+ RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID,
18
19
  RESOLVED_VIRTUAL_MODULE_ID,
19
- VIRTUAL_INTERNAL_MODULE_ID,
20
20
  VIRTUAL_MODULE_ID
21
21
  };
@@ -8,17 +8,13 @@ function astroIntegrationActionsRouteHandler({
8
8
  return {
9
9
  name: VIRTUAL_MODULE_ID,
10
10
  hooks: {
11
- async "astro:config:setup"(params) {
11
+ async "astro:config:setup"() {
12
12
  settings.injectedRoutes.push({
13
13
  pattern: ACTION_RPC_ROUTE_PATTERN,
14
14
  entrypoint: "astro/actions/runtime/route.js",
15
15
  prerender: false,
16
16
  origin: "internal"
17
17
  });
18
- params.addMiddleware({
19
- entrypoint: "astro/actions/runtime/middleware.js",
20
- order: "post"
21
- });
22
18
  },
23
19
  "astro:config:done": async (params) => {
24
20
  if (params.buildOutput === "static") {
@@ -0,0 +1,8 @@
1
+ import type { SSRActions } from '../core/app/types.js';
2
+ import type { ModuleLoader } from '../core/module-loader/index.js';
3
+ /**
4
+ * It accepts a module loader and the astro settings, and it attempts to load the middlewares defined in the configuration.
5
+ *
6
+ * If not middlewares were not set, the function returns an empty array.
7
+ */
8
+ export declare function loadActions(moduleLoader: ModuleLoader): Promise<SSRActions>;
@@ -0,0 +1,13 @@
1
+ import { ActionsCantBeLoaded } from "../core/errors/errors-data.js";
2
+ import { AstroError } from "../core/errors/index.js";
3
+ import { ASTRO_ACTIONS_INTERNAL_MODULE_ID } from "./consts.js";
4
+ async function loadActions(moduleLoader) {
5
+ try {
6
+ return await moduleLoader.import(ASTRO_ACTIONS_INTERNAL_MODULE_ID);
7
+ } catch (error) {
8
+ throw new AstroError(ActionsCantBeLoaded, { cause: error });
9
+ }
10
+ }
11
+ export {
12
+ loadActions
13
+ };
@@ -1,5 +1,7 @@
1
1
  import type fsMod from 'node:fs';
2
2
  import type { Plugin as VitePlugin } from 'vite';
3
+ import type { BuildInternals } from '../core/build/internal.js';
4
+ import type { StaticBuildOptions } from '../core/build/types.js';
3
5
  import type { AstroSettings } from '../types/astro.js';
4
6
  /**
5
7
  * This plugin is responsible to load the known file `actions/index.js` / `actions.js`
@@ -9,6 +11,12 @@ import type { AstroSettings } from '../types/astro.js';
9
11
  export declare function vitePluginUserActions({ settings }: {
10
12
  settings: AstroSettings;
11
13
  }): VitePlugin;
14
+ /**
15
+ * This plugin is used to retrieve the final entry point of the bundled actions.ts file
16
+ * @param opts
17
+ * @param internals
18
+ */
19
+ export declare function vitePluginActionsBuild(opts: StaticBuildOptions, internals: BuildInternals): VitePlugin;
12
20
  export declare function vitePluginActions({ fs, settings, }: {
13
21
  fs: typeof fsMod;
14
22
  settings: AstroSettings;
@@ -1,9 +1,11 @@
1
+ import { addRollupInput } from "../core/build/add-rollup-input.js";
1
2
  import { shouldAppendForwardSlash } from "../core/build/util.js";
3
+ import { getOutputDirectory } from "../prerender/utils.js";
2
4
  import {
5
+ ASTRO_ACTIONS_INTERNAL_MODULE_ID,
3
6
  NOOP_ACTIONS,
4
- RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
7
+ RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID,
5
8
  RESOLVED_VIRTUAL_MODULE_ID,
6
- VIRTUAL_INTERNAL_MODULE_ID,
7
9
  VIRTUAL_MODULE_ID
8
10
  } from "./consts.js";
9
11
  import { isActionsFilePresent } from "./utils.js";
@@ -15,7 +17,7 @@ function vitePluginUserActions({ settings }) {
15
17
  if (id === NOOP_ACTIONS) {
16
18
  return NOOP_ACTIONS;
17
19
  }
18
- if (id === VIRTUAL_INTERNAL_MODULE_ID) {
20
+ if (id === ASTRO_ACTIONS_INTERNAL_MODULE_ID) {
19
21
  const resolvedModule = await this.resolve(
20
22
  `${decodeURI(new URL("actions", settings.config.srcDir).pathname)}`
21
23
  );
@@ -23,18 +25,34 @@ function vitePluginUserActions({ settings }) {
23
25
  return NOOP_ACTIONS;
24
26
  }
25
27
  resolvedActionsId = resolvedModule.id;
26
- return RESOLVED_VIRTUAL_INTERNAL_MODULE_ID;
28
+ return RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID;
27
29
  }
28
30
  },
29
31
  load(id) {
30
32
  if (id === NOOP_ACTIONS) {
31
33
  return "export const server = {}";
32
- } else if (id === RESOLVED_VIRTUAL_INTERNAL_MODULE_ID) {
34
+ } else if (id === RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID) {
33
35
  return `export { server } from '${resolvedActionsId}';`;
34
36
  }
35
37
  }
36
38
  };
37
39
  }
40
+ function vitePluginActionsBuild(opts, internals) {
41
+ return {
42
+ name: "@astro/plugin-actions-build",
43
+ options(options) {
44
+ return addRollupInput(options, [ASTRO_ACTIONS_INTERNAL_MODULE_ID]);
45
+ },
46
+ writeBundle(_, bundle) {
47
+ for (const [chunkName, chunk] of Object.entries(bundle)) {
48
+ if (chunk.type !== "asset" && chunk.facadeModuleId === RESOLVED_ASTRO_ACTIONS_INTERNAL_MODULE_ID) {
49
+ const outputDirectory = getOutputDirectory(opts.settings);
50
+ internals.astroActionsEntryPoint = new URL(chunkName, outputDirectory);
51
+ }
52
+ }
53
+ }
54
+ };
55
+ }
38
56
  function vitePluginActions({
39
57
  fs,
40
58
  settings
@@ -83,5 +101,6 @@ export * from 'astro/actions/runtime/virtual/client.js';`;
83
101
  }
84
102
  export {
85
103
  vitePluginActions,
104
+ vitePluginActionsBuild,
86
105
  vitePluginUserActions
87
106
  };
@@ -19,7 +19,7 @@ export declare function defineAction<TOutput, TAccept extends ActionAccept | und
19
19
  }): ActionClient<TOutput, TAccept, TInputSchema> & string;
20
20
  /** Transform form data to an object based on a Zod schema. */
21
21
  export declare function formDataToObject<T extends z.AnyZodObject>(formData: FormData, schema: T): Record<string, unknown>;
22
- export type ActionMiddlewareContext = {
22
+ export type AstroActionContext = {
23
23
  /** Information about an incoming action request. */
24
24
  action?: {
25
25
  /** Whether an action was called using an RPC function or by using an HTML form action. */
@@ -47,4 +47,4 @@ export type ActionMiddlewareContext = {
47
47
  /**
48
48
  * Access information about Action requests from middleware.
49
49
  */
50
- export declare function getActionContext(context: APIContext): ActionMiddlewareContext;
50
+ export declare function getActionContext(context: APIContext): AstroActionContext;
@@ -11,7 +11,6 @@ import {
11
11
  hasContentType,
12
12
  isActionAPIContext
13
13
  } from "../utils.js";
14
- import { getAction } from "./get-action.js";
15
14
  import {
16
15
  ACTION_QUERY_PARAMS,
17
16
  ActionError,
@@ -151,7 +150,7 @@ function getActionContext(context) {
151
150
  pipeline.manifest.trailingSlash,
152
151
  pipeline.manifest.buildFormat
153
152
  ) ? removeTrailingForwardSlash(callerInfo.name) : callerInfo.name;
154
- const baseAction = await getAction(callerInfoName);
153
+ const baseAction = await pipeline.getAction(callerInfoName);
155
154
  let input;
156
155
  try {
157
156
  input = await parseRequestBody(context.request);
@@ -1,4 +1,5 @@
1
1
  import type { z } from 'zod';
2
+ import { AstroError } from '../../../core/errors/errors.js';
2
3
  import { appendForwardSlash as _appendForwardSlash } from '../../../core/path.js';
3
4
  import type { ErrorInferenceObject, MaybePromise, ActionAPIContext as _ActionAPIContext } from '../utils.js';
4
5
  export type ActionAPIContext = _ActionAPIContext;
@@ -56,3 +57,4 @@ export type SerializedActionResult = {
56
57
  };
57
58
  export declare function serializeActionResult(res: SafeResult<any, any>): SerializedActionResult;
58
59
  export declare function deserializeActionResult(res: SerializedActionResult): SafeResult<any, any>;
60
+ export declare function astroCalledServerError(): AstroError;
@@ -1,6 +1,9 @@
1
1
  import { parse as devalueParse, stringify as devalueStringify } from "devalue";
2
2
  import { REDIRECT_STATUS_CODES } from "../../../core/constants.js";
3
- import { ActionsReturnedInvalidDataError } from "../../../core/errors/errors-data.js";
3
+ import {
4
+ ActionCalledFromServerError,
5
+ ActionsReturnedInvalidDataError
6
+ } from "../../../core/errors/errors-data.js";
4
7
  import { AstroError } from "../../../core/errors/errors.js";
5
8
  import { appendForwardSlash as _appendForwardSlash } from "../../../core/path.js";
6
9
  import { ACTION_QUERY_PARAMS as _ACTION_QUERY_PARAMS } from "../../consts.js";
@@ -224,12 +227,16 @@ const actionResultErrorStack = /* @__PURE__ */ function actionResultErrorStackFn
224
227
  }
225
228
  };
226
229
  }();
230
+ function astroCalledServerError() {
231
+ return new AstroError(ActionCalledFromServerError);
232
+ }
227
233
  export {
228
234
  ACTION_ERROR_CODES,
229
235
  ACTION_QUERY_PARAMS,
230
236
  ActionError,
231
237
  ActionInputError,
232
238
  appendForwardSlash,
239
+ astroCalledServerError,
233
240
  callSafely,
234
241
  deserializeActionResult,
235
242
  getActionQueryString,
@@ -153,7 +153,7 @@ ${contentConfig.error.message}`);
153
153
  logger.info("Content config changed");
154
154
  shouldClear = true;
155
155
  }
156
- if (previousAstroVersion && previousAstroVersion !== "5.4.1") {
156
+ if (previousAstroVersion && previousAstroVersion !== "5.4.2") {
157
157
  logger.info("Astro version changed");
158
158
  shouldClear = true;
159
159
  }
@@ -161,8 +161,8 @@ ${contentConfig.error.message}`);
161
161
  logger.info("Clearing content store");
162
162
  this.#store.clearAll();
163
163
  }
164
- if ("5.4.1") {
165
- await this.#store.metaStore().set("astro-version", "5.4.1");
164
+ if ("5.4.2") {
165
+ await this.#store.metaStore().set("astro-version", "5.4.2");
166
166
  }
167
167
  if (currentConfigDigest) {
168
168
  await this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -1,3 +1,4 @@
1
+ import { escape } from "html-escaper";
1
2
  import { Traverse } from "neotraverse/modern";
2
3
  import pLimit from "p-limit";
3
4
  import { ZodIssueCode, z } from "zod";
@@ -324,7 +325,7 @@ async function updateImageReferencesInBody(html, fileName) {
324
325
  ...attributes,
325
326
  src: image.src,
326
327
  srcset: image.srcSet.attribute
327
- }).map(([key, value]) => value ? `${key}=${JSON.stringify(String(value))}` : "").join(" ");
328
+ }).map(([key, value]) => value ? `${key}="${escape(value)}"` : "").join(" ");
328
329
  });
329
330
  }
330
331
  function updateImageReferencesInData(data, fileName, imageAssetMap) {
@@ -1,3 +1,5 @@
1
+ import type { ZodType } from 'zod';
2
+ import type { ActionAccept, ActionClient } from '../../actions/runtime/virtual/server.js';
1
3
  import type { RoutingStrategies } from '../../i18n/utils.js';
2
4
  import type { ComponentInstance, SerializedRouteData } from '../../types/astro.js';
3
5
  import type { AstroMiddlewareInstance } from '../../types/public/common.js';
@@ -65,6 +67,7 @@ export type SSRManifest = {
65
67
  key: Promise<CryptoKey>;
66
68
  i18n: SSRManifestI18n | undefined;
67
69
  middleware?: () => Promise<AstroMiddlewareInstance> | AstroMiddlewareInstance;
70
+ actions?: SSRActions;
68
71
  checkOrigin: boolean;
69
72
  sessionConfig?: ResolvedSessionConfig<any>;
70
73
  cacheDir: string | URL;
@@ -74,6 +77,9 @@ export type SSRManifest = {
74
77
  buildClientDir: string | URL;
75
78
  buildServerDir: string | URL;
76
79
  };
80
+ export type SSRActions = {
81
+ server: Record<string, ActionClient<unknown, ActionAccept, ZodType>>;
82
+ };
77
83
  export type SSRManifestI18n = {
78
84
  fallback: Record<string, string> | undefined;
79
85
  fallbackType: 'redirect' | 'rewrite';
@@ -1,7 +1,10 @@
1
+ import type { ZodType } from 'zod';
2
+ import type { ActionAccept, ActionClient } from '../actions/runtime/virtual/server.js';
1
3
  import type { ComponentInstance } from '../types/astro.js';
2
4
  import type { MiddlewareHandler, RewritePayload } from '../types/public/common.js';
3
5
  import type { RuntimeMode } from '../types/public/config.js';
4
6
  import type { RouteData, SSRLoadedRenderer, SSRManifest, SSRResult } from '../types/public/internal.js';
7
+ import type { SSRActions } from './app/types.js';
5
8
  import type { Logger } from './logger/core.js';
6
9
  import { RouteCache } from './render/route-cache.js';
7
10
  /**
@@ -48,8 +51,10 @@ export declare abstract class Pipeline {
48
51
  route: string;
49
52
  component: string;
50
53
  }[];
54
+ readonly actions: SSRActions | undefined;
51
55
  readonly internalMiddleware: MiddlewareHandler[];
52
56
  resolvedMiddleware: MiddlewareHandler | undefined;
57
+ resolvedActions: SSRActions | undefined;
53
58
  constructor(logger: Logger, manifest: SSRManifest,
54
59
  /**
55
60
  * "development" or "production" only
@@ -76,7 +81,7 @@ export declare abstract class Pipeline {
76
81
  matchesComponent(filePath: URL): boolean;
77
82
  route: string;
78
83
  component: string;
79
- }[]);
84
+ }[], actions?: SSRActions | undefined);
80
85
  abstract headElements(routeData: RouteData): Promise<HeadElements> | HeadElements;
81
86
  abstract componentMetadata(routeData: RouteData): Promise<SSRResult['componentMetadata']> | void;
82
87
  /**
@@ -100,6 +105,9 @@ export declare abstract class Pipeline {
100
105
  * it returns a no-op function
101
106
  */
102
107
  getMiddleware(): Promise<MiddlewareHandler>;
108
+ setActions(actions: SSRActions): void;
109
+ getActions(): Promise<SSRActions>;
110
+ getAction(path: string): Promise<ActionClient<unknown, ActionAccept, ZodType>>;
103
111
  }
104
112
  export interface HeadElements extends Pick<SSRResult, 'scripts' | 'styles' | 'links'> {
105
113
  }
@@ -1,11 +1,13 @@
1
1
  import { createI18nMiddleware } from "../i18n/middleware.js";
2
2
  import { createOriginCheckMiddleware } from "./app/middlewares.js";
3
+ import { ActionNotFoundError } from "./errors/errors-data.js";
4
+ import { AstroError } from "./errors/index.js";
3
5
  import { NOOP_MIDDLEWARE_FN } from "./middleware/noop-middleware.js";
4
6
  import { sequence } from "./middleware/sequence.js";
5
7
  import { RouteCache } from "./render/route-cache.js";
6
8
  import { createDefaultRoutes } from "./routing/default.js";
7
9
  class Pipeline {
8
- constructor(logger, manifest, runtimeMode, renderers, resolve, serverLike, streaming, adapterName = manifest.adapterName, clientDirectives = manifest.clientDirectives, inlinedScripts = manifest.inlinedScripts, compressHTML = manifest.compressHTML, i18n = manifest.i18n, middleware = manifest.middleware, routeCache = new RouteCache(logger, runtimeMode), site = manifest.site ? new URL(manifest.site) : void 0, defaultRoutes = createDefaultRoutes(manifest)) {
10
+ constructor(logger, manifest, runtimeMode, renderers, resolve, serverLike, streaming, adapterName = manifest.adapterName, clientDirectives = manifest.clientDirectives, inlinedScripts = manifest.inlinedScripts, compressHTML = manifest.compressHTML, i18n = manifest.i18n, middleware = manifest.middleware, routeCache = new RouteCache(logger, runtimeMode), site = manifest.site ? new URL(manifest.site) : void 0, defaultRoutes = createDefaultRoutes(manifest), actions = manifest.actions) {
9
11
  this.logger = logger;
10
12
  this.manifest = manifest;
11
13
  this.runtimeMode = runtimeMode;
@@ -22,6 +24,7 @@ class Pipeline {
22
24
  this.routeCache = routeCache;
23
25
  this.site = site;
24
26
  this.defaultRoutes = defaultRoutes;
27
+ this.actions = actions;
25
28
  this.internalMiddleware = [];
26
29
  if (i18n?.strategy !== "manual") {
27
30
  this.internalMiddleware.push(
@@ -31,6 +34,7 @@ class Pipeline {
31
34
  }
32
35
  internalMiddleware;
33
36
  resolvedMiddleware = void 0;
37
+ resolvedActions = void 0;
34
38
  /**
35
39
  * Resolves the middleware from the manifest, and returns the `onRequest` function. If `onRequest` isn't there,
36
40
  * it returns a no-op function
@@ -52,6 +56,41 @@ class Pipeline {
52
56
  return this.resolvedMiddleware;
53
57
  }
54
58
  }
59
+ setActions(actions) {
60
+ this.resolvedActions = actions;
61
+ }
62
+ async getActions() {
63
+ if (this.resolvedActions) {
64
+ return this.resolvedActions;
65
+ } else if (this.actions) {
66
+ return this.actions;
67
+ }
68
+ return { server: {} };
69
+ }
70
+ async getAction(path) {
71
+ const pathKeys = path.split(".").map((key) => decodeURIComponent(key));
72
+ let { server } = await this.getActions();
73
+ if (!server || !(typeof server === "object")) {
74
+ throw new TypeError(
75
+ `Expected \`server\` export in actions file to be an object. Received ${typeof server}.`
76
+ );
77
+ }
78
+ for (const key of pathKeys) {
79
+ if (!(key in server)) {
80
+ throw new AstroError({
81
+ ...ActionNotFoundError,
82
+ message: ActionNotFoundError.message(pathKeys.join("."))
83
+ });
84
+ }
85
+ server = server[key];
86
+ }
87
+ if (typeof server !== "function") {
88
+ throw new TypeError(
89
+ `Expected handler for action ${pathKeys.join(".")} to be a function. Received ${typeof server}.`
90
+ );
91
+ }
92
+ return server;
93
+ }
55
94
  }
56
95
  export {
57
96
  Pipeline
@@ -44,11 +44,13 @@ async function generatePages(options, internals) {
44
44
  const renderersEntryUrl = new URL("renderers.mjs", baseDirectory);
45
45
  const renderers = await import(renderersEntryUrl.toString());
46
46
  const middleware = internals.middlewareEntryPoint ? await import(internals.middlewareEntryPoint.toString()).then((mod) => mod.onRequest) : NOOP_MIDDLEWARE_FN;
47
+ const actions = internals.astroActionsEntryPoint ? await import(internals.astroActionsEntryPoint.toString()).then((mod) => mod) : { server: {} };
47
48
  manifest = createBuildManifest(
48
49
  options.settings,
49
50
  internals,
50
51
  renderers.renderers,
51
52
  middleware,
53
+ actions,
52
54
  options.key
53
55
  );
54
56
  }
@@ -270,8 +272,7 @@ function getUrlForPath(pathname, base, origin, format, trailingSlash, routeType)
270
272
  const buildPathRelative = removeTrailingForwardSlash(removeLeadingForwardSlash(pathname)) + ending;
271
273
  buildPathname = joinPaths(base, buildPathRelative);
272
274
  }
273
- const url = new URL(buildPathname, origin);
274
- return url;
275
+ return new URL(buildPathname, origin);
275
276
  }
276
277
  async function generatePath(pathname, pipeline, gopts, route) {
277
278
  const { mod } = gopts;
@@ -358,7 +359,7 @@ function getPrettyRouteName(route) {
358
359
  }
359
360
  return route.component;
360
361
  }
361
- function createBuildManifest(settings, internals, renderers, middleware, key) {
362
+ function createBuildManifest(settings, internals, renderers, middleware, actions, key) {
362
363
  let i18nManifest = void 0;
363
364
  if (settings.config.i18n) {
364
365
  i18nManifest = {
@@ -399,6 +400,7 @@ function createBuildManifest(settings, internals, renderers, middleware, key) {
399
400
  onRequest: middleware
400
401
  };
401
402
  },
403
+ actions,
402
404
  checkOrigin: (settings.config.security?.checkOrigin && settings.buildOutput === "server") ?? false,
403
405
  key
404
406
  };
@@ -69,7 +69,8 @@ export interface BuildInternals {
69
69
  manifestFileName?: string;
70
70
  entryPoints: Map<RouteData, URL>;
71
71
  componentMetadata: SSRResult['componentMetadata'];
72
- middlewareEntryPoint?: URL;
72
+ middlewareEntryPoint: URL | undefined;
73
+ astroActionsEntryPoint: URL | undefined;
73
74
  /**
74
75
  * Chunks in the bundle that are only used in prerendering that we can delete later
75
76
  */
@@ -17,7 +17,9 @@ function createBuildInternals() {
17
17
  staticFiles: /* @__PURE__ */ new Set(),
18
18
  componentMetadata: /* @__PURE__ */ new Map(),
19
19
  entryPoints: /* @__PURE__ */ new Map(),
20
- prerenderOnlyChunks: []
20
+ prerenderOnlyChunks: [],
21
+ astroActionsEntryPoint: void 0,
22
+ middlewareEntryPoint: void 0
21
23
  };
22
24
  }
23
25
  function trackPageData(internals, _component, pageData, componentModuleId, componentURL) {
@@ -1,5 +1,6 @@
1
1
  import { astroConfigBuildPlugin } from "../../../content/vite-plugin-content-assets.js";
2
2
  import { astroHeadBuildPlugin } from "../../../vite-plugin-head/index.js";
3
+ import { pluginActions } from "./plugin-actions.js";
3
4
  import { pluginAnalyzer } from "./plugin-analyzer.js";
4
5
  import { pluginChunks } from "./plugin-chunks.js";
5
6
  import { pluginComponentEntry } from "./plugin-component-entry.js";
@@ -19,6 +20,7 @@ function registerAllPlugins({ internals, options, register }) {
19
20
  register(pluginManifest(options, internals));
20
21
  register(pluginRenderers(options));
21
22
  register(pluginMiddleware(options, internals));
23
+ register(pluginActions(options, internals));
22
24
  register(pluginPages(options, internals));
23
25
  register(pluginCSS(options, internals));
24
26
  register(astroHeadBuildPlugin(internals));
@@ -0,0 +1,4 @@
1
+ import type { BuildInternals } from '../internal.js';
2
+ import type { AstroBuildPlugin } from '../plugin.js';
3
+ import type { StaticBuildOptions } from '../types.js';
4
+ export declare function pluginActions(opts: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin;
@@ -0,0 +1,16 @@
1
+ import { vitePluginActionsBuild } from "../../../actions/plugins.js";
2
+ function pluginActions(opts, internals) {
3
+ return {
4
+ targets: ["server"],
5
+ hooks: {
6
+ "build:before": () => {
7
+ return {
8
+ vitePlugin: vitePluginActionsBuild(opts, internals)
9
+ };
10
+ }
11
+ }
12
+ };
13
+ }
14
+ export {
15
+ pluginActions
16
+ };
@@ -1,5 +1,4 @@
1
1
  import type { BuildInternals } from '../internal.js';
2
2
  import type { AstroBuildPlugin } from '../plugin.js';
3
3
  import type { StaticBuildOptions } from '../types.js';
4
- export { MIDDLEWARE_MODULE_ID } from '../../middleware/vite-plugin.js';
5
4
  export declare function pluginMiddleware(opts: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin;
@@ -1,5 +1,4 @@
1
1
  import { vitePluginMiddlewareBuild } from "../../middleware/vite-plugin.js";
2
- import { MIDDLEWARE_MODULE_ID } from "../../middleware/vite-plugin.js";
3
2
  function pluginMiddleware(opts, internals) {
4
3
  return {
5
4
  targets: ["server"],
@@ -13,6 +12,5 @@ function pluginMiddleware(opts, internals) {
13
12
  };
14
13
  }
15
14
  export {
16
- MIDDLEWARE_MODULE_ID,
17
15
  pluginMiddleware
18
16
  };
@@ -1,8 +1,9 @@
1
+ import { ASTRO_ACTIONS_INTERNAL_MODULE_ID } from "../../../actions/consts.js";
2
+ import { MIDDLEWARE_MODULE_ID } from "../../middleware/vite-plugin.js";
1
3
  import { routeIsRedirect } from "../../redirects/index.js";
2
4
  import { VIRTUAL_ISLAND_MAP_ID } from "../../server-islands/vite-plugin-server-islands.js";
3
5
  import { addRollupInput } from "../add-rollup-input.js";
4
6
  import { SSR_MANIFEST_VIRTUAL_MODULE_ID } from "./plugin-manifest.js";
5
- import { MIDDLEWARE_MODULE_ID } from "./plugin-middleware.js";
6
7
  import { ASTRO_PAGE_MODULE_ID } from "./plugin-pages.js";
7
8
  import { RENDERERS_MODULE_ID } from "./plugin-renderers.js";
8
9
  import { getVirtualModulePageName } from "./util.js";
@@ -138,6 +139,7 @@ function generateSSRCode(adapter, middlewareId) {
138
139
  const edgeMiddleware = adapter?.adapterFeatures?.edgeMiddleware ?? false;
139
140
  const imports = [
140
141
  `import { renderers } from '${RENDERERS_MODULE_ID}';`,
142
+ `import * as actions from '${ASTRO_ACTIONS_INTERNAL_MODULE_ID}';`,
141
143
  `import * as serverEntrypointModule from '${ADAPTER_VIRTUAL_MODULE_ID}';`,
142
144
  `import { manifest as defaultManifest } from '${SSR_MANIFEST_VIRTUAL_MODULE_ID}';`,
143
145
  `import { serverIslandMap } from '${VIRTUAL_ISLAND_MAP_ID}';`
@@ -148,6 +150,7 @@ function generateSSRCode(adapter, middlewareId) {
148
150
  ` pageMap,`,
149
151
  ` serverIslandMap,`,
150
152
  ` renderers,`,
153
+ ` actions,`,
151
154
  ` middleware: ${edgeMiddleware ? "undefined" : `() => import("${middlewareId}")`}`,
152
155
  `});`,
153
156
  `const _args = ${adapter.args ? JSON.stringify(adapter.args, null, 4) : "undefined"};`,
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "5.4.1";
1
+ const ASTRO_VERSION = "5.4.2";
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";
@@ -22,7 +22,7 @@ async function dev(inlineConfig) {
22
22
  await telemetry.record([]);
23
23
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
24
24
  const logger = restart.container.logger;
25
- const currentVersion = "5.4.1";
25
+ const currentVersion = "5.4.2";
26
26
  const isPrerelease = currentVersion.includes("-");
27
27
  if (!isPrerelease) {
28
28
  try {
@@ -803,6 +803,17 @@ export declare const MiddlewareCantBeLoaded: {
803
803
  title: string;
804
804
  message: string;
805
805
  };
806
+ /**
807
+ * @docs
808
+ * @description
809
+ * Thrown in development mode when the actions file can't be loaded.
810
+ *
811
+ */
812
+ export declare const ActionsCantBeLoaded: {
813
+ name: string;
814
+ title: string;
815
+ message: string;
816
+ };
806
817
  /**
807
818
  * @docs
808
819
  * @see
@@ -285,6 +285,11 @@ const MiddlewareCantBeLoaded = {
285
285
  title: "Can't load the middleware.",
286
286
  message: "An unknown error was thrown while loading your middleware."
287
287
  };
288
+ const ActionsCantBeLoaded = {
289
+ name: "ActionsCantBeLoaded",
290
+ title: "Can't load the Astro actions.",
291
+ message: "An unknown error was thrown while loading the Astro actions file."
292
+ };
288
293
  const LocalImageUsedWrongly = {
289
294
  name: "LocalImageUsedWrongly",
290
295
  title: "Local images must be imported.",
@@ -666,6 +671,7 @@ const SessionConfigWithoutFlagError = {
666
671
  export {
667
672
  ActionCalledFromServerError,
668
673
  ActionNotFoundError,
674
+ ActionsCantBeLoaded,
669
675
  ActionsReturnedInvalidDataError,
670
676
  ActionsWithoutServerOutputError,
671
677
  AdapterSupportOutputMismatch,
@@ -38,7 +38,7 @@ function serverStart({
38
38
  host,
39
39
  base
40
40
  }) {
41
- const version = "5.4.1";
41
+ const version = "5.4.2";
42
42
  const localPrefix = `${dim("\u2503")} Local `;
43
43
  const networkPrefix = `${dim("\u2503")} Network `;
44
44
  const emptyPrefix = " ".repeat(11);
@@ -281,7 +281,7 @@ function printHelp({
281
281
  message.push(
282
282
  linebreak(),
283
283
  ` ${bgGreen(black(` ${commandName} `))} ${green(
284
- `v${"5.4.1"}`
284
+ `v${"5.4.2"}`
285
285
  )} ${headline}`
286
286
  );
287
287
  }
@@ -3,6 +3,7 @@ import type { ComponentInstance } from '../types/astro.js';
3
3
  import type { MiddlewareHandler, Props } from '../types/public/common.js';
4
4
  import type { APIContext, AstroGlobal, AstroGlobalPartial } from '../types/public/context.js';
5
5
  import type { RouteData, SSRResult } from '../types/public/internal.js';
6
+ import type { SSRActions } from './app/types.js';
6
7
  import { AstroCookies } from './cookies/index.js';
7
8
  import { type Pipeline } from './render/index.js';
8
9
  import { AstroSession } from './session.js';
@@ -16,6 +17,7 @@ export declare class RenderContext {
16
17
  readonly pipeline: Pipeline;
17
18
  locals: App.Locals;
18
19
  readonly middleware: MiddlewareHandler;
20
+ readonly actions: SSRActions;
19
21
  pathname: string;
20
22
  request: Request;
21
23
  routeData: RouteData;
@@ -36,7 +38,7 @@ export declare class RenderContext {
36
38
  * A safety net in case of loops
37
39
  */
38
40
  counter: number;
39
- static create({ locals, middleware, pathname, pipeline, request, routeData, clientAddress, status, props, partial, }: Pick<RenderContext, 'pathname' | 'pipeline' | 'request' | 'routeData' | 'clientAddress'> & Partial<Pick<RenderContext, 'locals' | 'middleware' | 'status' | 'props' | 'partial'>>): Promise<RenderContext>;
41
+ static create({ locals, middleware, pathname, pipeline, request, routeData, clientAddress, status, props, partial, actions, }: Pick<RenderContext, 'pathname' | 'pipeline' | 'request' | 'routeData' | 'clientAddress'> & Partial<Pick<RenderContext, 'locals' | 'middleware' | 'status' | 'props' | 'partial' | 'actions'>>): Promise<RenderContext>;
40
42
  /**
41
43
  * The main function of the RenderContext.
42
44
  *
@@ -49,9 +51,9 @@ export declare class RenderContext {
49
51
  * - fallback
50
52
  */
51
53
  render(componentInstance: ComponentInstance | undefined, slots?: Record<string, any>): Promise<Response>;
52
- createAPIContext(props: APIContext['props']): APIContext;
54
+ createAPIContext(props: APIContext['props'], context: ActionAPIContext): APIContext;
53
55
  createActionAPIContext(): ActionAPIContext;
54
- createResult(mod: ComponentInstance): Promise<SSRResult>;
56
+ createResult(mod: ComponentInstance, ctx: ActionAPIContext): Promise<SSRResult>;
55
57
  /**
56
58
  * The Astro global is sourced in 3 different phases:
57
59
  * - **Static**: `.generator` and `.glob` is printed by the compiler, instantiated once per process per astro file
@@ -60,8 +62,8 @@ export declare class RenderContext {
60
62
  *
61
63
  * The page level partial is used as the prototype of the user-visible `Astro` global object, which is instantiated once per use of a component.
62
64
  */
63
- createAstro(result: SSRResult, astroStaticPartial: AstroGlobalPartial, props: Record<string, any>, slotValues: Record<string, any> | null): AstroGlobal;
64
- createAstroPagePartial(result: SSRResult, astroStaticPartial: AstroGlobalPartial): Omit<AstroGlobal, 'props' | 'self' | 'slots'>;
65
+ createAstro(result: SSRResult, astroStaticPartial: AstroGlobalPartial, props: Record<string, any>, slotValues: Record<string, any> | null, apiContext: ActionAPIContext): AstroGlobal;
66
+ createAstroPagePartial(result: SSRResult, astroStaticPartial: AstroGlobalPartial, apiContext: ActionAPIContext): Omit<AstroGlobal, 'props' | 'self' | 'slots'>;
65
67
  getClientAddress(): string;
66
68
  computeCurrentLocale(): string | undefined;
67
69
  computePreferredLocale(): string | undefined;
@@ -1,3 +1,4 @@
1
+ import { getActionContext } from "../actions/runtime/virtual/server.js";
1
2
  import { deserializeActionResult } from "../actions/runtime/virtual/shared.js";
2
3
  import { createCallAction, createGetActionResult, hasActionPayload } from "../actions/utils.js";
3
4
  import {
@@ -29,10 +30,11 @@ import { copyRequest, getOriginPathname, setOriginPathname } from "./routing/rew
29
30
  import { AstroSession } from "./session.js";
30
31
  const apiContextRoutesSymbol = Symbol.for("context.routes");
31
32
  class RenderContext {
32
- constructor(pipeline, locals, middleware, pathname, request, routeData, status, clientAddress, cookies = new AstroCookies(request), params = getParams(routeData, pathname), url = new URL(request.url), props = {}, partial = void 0, session = pipeline.manifest.sessionConfig ? new AstroSession(cookies, pipeline.manifest.sessionConfig) : void 0) {
33
+ constructor(pipeline, locals, middleware, actions, pathname, request, routeData, status, clientAddress, cookies = new AstroCookies(request), params = getParams(routeData, pathname), url = new URL(request.url), props = {}, partial = void 0, session = pipeline.manifest.sessionConfig ? new AstroSession(cookies, pipeline.manifest.sessionConfig) : void 0) {
33
34
  this.pipeline = pipeline;
34
35
  this.locals = locals;
35
36
  this.middleware = middleware;
37
+ this.actions = actions;
36
38
  this.pathname = pathname;
37
39
  this.request = request;
38
40
  this.routeData = routeData;
@@ -63,14 +65,17 @@ class RenderContext {
63
65
  clientAddress,
64
66
  status = 200,
65
67
  props,
66
- partial = void 0
68
+ partial = void 0,
69
+ actions
67
70
  }) {
68
71
  const pipelineMiddleware = await pipeline.getMiddleware();
72
+ const pipelineActions = actions ?? await pipeline.getActions();
69
73
  setOriginPathname(request, pathname);
70
74
  return new RenderContext(
71
75
  pipeline,
72
76
  locals,
73
77
  sequence(...pipeline.internalMiddleware, middleware ?? pipelineMiddleware),
78
+ pipelineActions,
74
79
  pathname,
75
80
  request,
76
81
  routeData,
@@ -106,7 +111,8 @@ class RenderContext {
106
111
  serverLike,
107
112
  base: manifest.base
108
113
  });
109
- const apiContext = this.createAPIContext(props);
114
+ const actionApiContext = this.createActionAPIContext();
115
+ const apiContext = this.createAPIContext(props, actionApiContext);
110
116
  this.counter++;
111
117
  if (this.counter === 4) {
112
118
  return new Response("Loop Detected", {
@@ -153,6 +159,13 @@ class RenderContext {
153
159
  this.status = 200;
154
160
  }
155
161
  let response2;
162
+ if (!ctx.isPrerendered) {
163
+ const { action, setActionResult, serializeActionResult } = getActionContext(ctx);
164
+ if (action?.calledFrom === "form") {
165
+ const actionResult = await action.handler();
166
+ setActionResult(action.name, serializeActionResult(actionResult));
167
+ }
168
+ }
156
169
  switch (this.routeData.type) {
157
170
  case "endpoint": {
158
171
  response2 = await renderEndpoint(
@@ -166,7 +179,7 @@ class RenderContext {
166
179
  case "redirect":
167
180
  return renderRedirect(this);
168
181
  case "page": {
169
- const result = await this.createResult(componentInstance);
182
+ const result = await this.createResult(componentInstance, actionApiContext);
170
183
  try {
171
184
  response2 = await renderPage(
172
185
  result,
@@ -209,8 +222,7 @@ class RenderContext {
209
222
  attachCookiesToResponse(response, cookies);
210
223
  return response;
211
224
  }
212
- createAPIContext(props) {
213
- const context = this.createActionAPIContext();
225
+ createAPIContext(props, context) {
214
226
  const redirect = (path, status = 302) => new Response(null, { status, headers: { Location: path } });
215
227
  Reflect.set(context, apiContextRoutesSymbol, this.pipeline);
216
228
  return Object.assign(context, {
@@ -295,7 +307,7 @@ class RenderContext {
295
307
  session
296
308
  };
297
309
  }
298
- async createResult(mod) {
310
+ async createResult(mod, ctx) {
299
311
  const { cookies, pathname, pipeline, routeData, status } = this;
300
312
  const { clientDirectives, inlinedScripts, compressHTML, manifest, renderers, resolve } = pipeline;
301
313
  const { links, scripts, styles } = await pipeline.headElements(routeData);
@@ -324,7 +336,7 @@ class RenderContext {
324
336
  compressHTML,
325
337
  cookies,
326
338
  /** This function returns the `Astro` faux-global */
327
- createAstro: (astroGlobal, props, slots) => this.createAstro(result, astroGlobal, props, slots),
339
+ createAstro: (astroGlobal, props, slots) => this.createAstro(result, astroGlobal, props, slots, ctx),
328
340
  links,
329
341
  params: this.params,
330
342
  partial,
@@ -361,17 +373,19 @@ class RenderContext {
361
373
  *
362
374
  * The page level partial is used as the prototype of the user-visible `Astro` global object, which is instantiated once per use of a component.
363
375
  */
364
- createAstro(result, astroStaticPartial, props, slotValues) {
376
+ createAstro(result, astroStaticPartial, props, slotValues, apiContext) {
365
377
  let astroPagePartial;
366
378
  if (this.isRewriting) {
367
379
  astroPagePartial = this.#astroPagePartial = this.createAstroPagePartial(
368
380
  result,
369
- astroStaticPartial
381
+ astroStaticPartial,
382
+ apiContext
370
383
  );
371
384
  } else {
372
385
  astroPagePartial = this.#astroPagePartial ??= this.createAstroPagePartial(
373
386
  result,
374
- astroStaticPartial
387
+ astroStaticPartial,
388
+ apiContext
375
389
  );
376
390
  }
377
391
  const astroComponentPartial = { props, self: null };
@@ -394,7 +408,7 @@ class RenderContext {
394
408
  });
395
409
  return Astro;
396
410
  }
397
- createAstroPagePartial(result, astroStaticPartial) {
411
+ createAstroPagePartial(result, astroStaticPartial, apiContext) {
398
412
  const renderContext = this;
399
413
  const { cookies, locals, params, pipeline, url, session } = this;
400
414
  const { response } = result;
@@ -409,6 +423,7 @@ class RenderContext {
409
423
  const rewrite = async (reroutePayload) => {
410
424
  return await this.#executeRewrite(reroutePayload);
411
425
  };
426
+ const callAction = createCallAction(apiContext);
412
427
  return {
413
428
  generator: astroStaticPartial.generator,
414
429
  glob: astroStaticPartial.glob,
@@ -437,7 +452,7 @@ class RenderContext {
437
452
  site: pipeline.site,
438
453
  getActionResult: createGetActionResult(locals),
439
454
  get callAction() {
440
- return createCallAction(this);
455
+ return callAction;
441
456
  },
442
457
  url,
443
458
  get originPathname() {
@@ -1,4 +1,5 @@
1
1
  import { TRANSITION_AFTER_SWAP, doPreparation, doSwap } from "./events.js";
2
+ import { detectScriptExecuted } from "./swap-functions.js";
2
3
  const inBrowser = import.meta.env.SSR === false;
3
4
  const pushState = inBrowser && history.pushState.bind(history);
4
5
  const replaceState = inBrowser && history.replaceState.bind(history);
@@ -417,7 +418,7 @@ if (inBrowser) {
417
418
  }
418
419
  }
419
420
  for (const script of document.getElementsByTagName("script")) {
420
- script.dataset.astroExec = "";
421
+ detectScriptExecuted(script);
421
422
  }
422
423
  }
423
424
  async function prepareForClientOnlyComponents(newDocument, toLocation, signal) {
@@ -3,6 +3,7 @@ export type SavedFocus = {
3
3
  start?: number | null;
4
4
  end?: number | null;
5
5
  };
6
+ export declare function detectScriptExecuted(script: HTMLScriptElement): boolean;
6
7
  export declare function deselectScripts(doc: Document): void;
7
8
  export declare function swapRootAttributes(doc: Document): void;
8
9
  export declare function swapHeadElements(doc: Document): void;
@@ -1,16 +1,19 @@
1
1
  const PERSIST_ATTR = "data-astro-transition-persist";
2
+ const scriptsAlreadyRan = /* @__PURE__ */ new Set();
3
+ function detectScriptExecuted(script) {
4
+ const key = script.src ? new URL(script.src, location.href).href : script.textContent;
5
+ if (scriptsAlreadyRan.has(key)) return true;
6
+ scriptsAlreadyRan.add(key);
7
+ return false;
8
+ }
2
9
  function deselectScripts(doc) {
3
- for (const s1 of document.scripts) {
4
- for (const s2 of doc.scripts) {
5
- if (
6
- // Check if the script should be rerun regardless of it being the same
7
- !s2.hasAttribute("data-astro-rerun") && // Inline
8
- (!s1.src && s1.textContent === s2.textContent || // External
9
- s1.src && s1.type === s2.type && s1.src === s2.src)
10
- ) {
11
- s2.dataset.astroExec = "";
12
- break;
13
- }
10
+ for (const s2 of doc.scripts) {
11
+ if (
12
+ // Check if the script should be rerun regardless of it being the same
13
+ !s2.hasAttribute("data-astro-rerun") && // Check if the script has already been executed
14
+ detectScriptExecuted(s2)
15
+ ) {
16
+ s2.dataset.astroExec = "";
14
17
  }
15
18
  }
16
19
  }
@@ -106,6 +109,7 @@ const swap = (doc) => {
106
109
  };
107
110
  export {
108
111
  deselectScripts,
112
+ detectScriptExecuted,
109
113
  restoreFocus,
110
114
  saveFocus,
111
115
  swap,
@@ -29,5 +29,4 @@ export declare class DevPipeline extends Pipeline {
29
29
  getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
30
30
  tryRewrite(payload: RewritePayload, request: Request): Promise<TryRewriteResult>;
31
31
  setManifestData(manifestData: RoutesList): void;
32
- rewriteKnownRoute(route: string, sourceRoute: RouteData): ComponentInstance;
33
32
  }
@@ -154,16 +154,6 @@ class DevPipeline extends Pipeline {
154
154
  setManifestData(manifestData) {
155
155
  this.routesList = manifestData;
156
156
  }
157
- rewriteKnownRoute(route, sourceRoute) {
158
- if (this.serverLike && sourceRoute.prerender) {
159
- for (let def of this.defaultRoutes) {
160
- if (route === def.route) {
161
- return def.instance;
162
- }
163
- }
164
- }
165
- throw new Error("Unknown route");
166
- }
167
157
  }
168
158
  export {
169
159
  DevPipeline
@@ -29,7 +29,7 @@ function createVitePluginAstroServer({
29
29
  }) {
30
30
  return {
31
31
  name: "astro:server",
32
- configureServer(viteServer) {
32
+ async configureServer(viteServer) {
33
33
  const loader = createViteLoader(viteServer);
34
34
  const pipeline = DevPipeline.create(routesList, {
35
35
  loader,
@@ -1,3 +1,4 @@
1
+ import { loadActions } from "../actions/loadActions.js";
1
2
  import {
2
3
  DEFAULT_404_COMPONENT,
3
4
  NOOP_MIDDLEWARE_HEADER,
@@ -104,6 +105,8 @@ async function handleRoute({
104
105
  let renderContext;
105
106
  let mod = void 0;
106
107
  let route;
108
+ const actions = await loadActions(loader);
109
+ pipeline.setActions(actions);
107
110
  const middleware = (await loadMiddleware(loader)).onRequest;
108
111
  const locals = Reflect.get(incomingRequest, clientLocalsSymbol);
109
112
  const { preloadedComponent } = matchedRoute;
@@ -128,7 +131,8 @@ async function handleRoute({
128
131
  middleware: isDefaultPrerendered404(matchedRoute.route) ? void 0 : middleware,
129
132
  request,
130
133
  routeData: route,
131
- clientAddress: incomingRequest.socket.remoteAddress
134
+ clientAddress: incomingRequest.socket.remoteAddress,
135
+ actions
132
136
  });
133
137
  let response;
134
138
  let statusCode = 200;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "5.4.1",
3
+ "version": "5.4.2",
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",
@@ -133,7 +133,7 @@
133
133
  "kleur": "^4.1.5",
134
134
  "magic-string": "^0.30.17",
135
135
  "magicast": "^0.3.5",
136
- "mrmime": "^2.0.0",
136
+ "mrmime": "^2.0.1",
137
137
  "neotraverse": "^0.6.18",
138
138
  "p-limit": "^6.2.0",
139
139
  "p-queue": "^8.1.0",
@@ -145,23 +145,23 @@
145
145
  "shiki": "^1.29.2",
146
146
  "tinyexec": "^0.3.2",
147
147
  "tinyglobby": "^0.2.12",
148
- "tsconfck": "^3.1.4",
148
+ "tsconfck": "^3.1.5",
149
149
  "ultrahtml": "^1.5.3",
150
150
  "unist-util-visit": "^5.0.0",
151
- "unstorage": "^1.14.4",
151
+ "unstorage": "^1.15.0",
152
152
  "vfile": "^6.0.3",
153
153
  "vite": "^6.2.0",
154
- "vitefu": "^1.0.5",
154
+ "vitefu": "^1.0.6",
155
155
  "which-pm": "^3.0.1",
156
156
  "xxhash-wasm": "^1.1.0",
157
157
  "yargs-parser": "^21.1.1",
158
- "yocto-spinner": "^0.2.0",
159
- "zod": "^3.24.1",
160
- "zod-to-json-schema": "^3.24.1",
158
+ "yocto-spinner": "^0.2.1",
159
+ "zod": "^3.24.2",
160
+ "zod-to-json-schema": "^3.24.3",
161
161
  "zod-to-ts": "^1.2.0",
162
162
  "@astrojs/internal-helpers": "0.6.0",
163
- "@astrojs/telemetry": "3.2.0",
164
- "@astrojs/markdown-remark": "6.2.0"
163
+ "@astrojs/markdown-remark": "6.2.0",
164
+ "@astrojs/telemetry": "3.2.0"
165
165
  },
166
166
  "optionalDependencies": {
167
167
  "sharp": "^0.33.3"
@@ -186,8 +186,8 @@
186
186
  "cheerio": "1.0.0",
187
187
  "eol": "^0.10.0",
188
188
  "execa": "^8.0.1",
189
- "expect-type": "^1.1.0",
190
- "fs-fixture": "^2.7.0",
189
+ "expect-type": "^1.2.0",
190
+ "fs-fixture": "^2.7.1",
191
191
  "mdast-util-mdx": "^3.0.0",
192
192
  "mdast-util-mdx-jsx": "^3.2.0",
193
193
  "node-mocks-http": "^1.16.2",
@@ -196,11 +196,11 @@
196
196
  "rehype-slug": "^6.0.0",
197
197
  "rehype-toc": "^3.0.2",
198
198
  "remark-code-titles": "^0.1.2",
199
- "rollup": "^4.34.2",
200
- "sass": "^1.83.4",
201
- "undici": "^7.3.0",
199
+ "rollup": "^4.34.9",
200
+ "sass": "^1.85.1",
201
+ "undici": "^7.4.0",
202
202
  "unified": "^11.0.5",
203
- "vitest": "^3.0.5",
203
+ "vitest": "^3.0.7",
204
204
  "astro-scripts": "0.0.14"
205
205
  },
206
206
  "engines": {
@@ -2,10 +2,12 @@ import {
2
2
  ACTION_QUERY_PARAMS,
3
3
  ActionError,
4
4
  appendForwardSlash,
5
+ astroCalledServerError,
5
6
  deserializeActionResult,
6
7
  getActionQueryString,
7
8
  } from 'astro:actions';
8
9
 
10
+ const apiContextRoutesSymbol = Symbol.for('context.routes');
9
11
  const ENCODED_DOT = '%2E';
10
12
 
11
13
  function toActionProxy(actionCallback = {}, aggregatedPath = '') {
@@ -73,11 +75,13 @@ export function getActionPath(action) {
73
75
  */
74
76
  async function handleAction(param, path, context) {
75
77
  // When running server-side, import the action and call it.
76
- if (import.meta.env.SSR) {
77
- const { getAction } = await import('astro/actions/runtime/virtual/get-action.js');
78
- const action = await getAction(path);
78
+ if (import.meta.env.SSR && context) {
79
+ const pipeline = Reflect.get(context, apiContextRoutesSymbol);
80
+ if (!pipeline) {
81
+ throw astroCalledServerError();
82
+ }
83
+ const action = await pipeline.getAction(path);
79
84
  if (!action) throw new Error(`Action not found: ${path}`);
80
-
81
85
  return action.bind(context)(param);
82
86
  }
83
87
 
@@ -1 +0,0 @@
1
- export declare const onRequest: import("../../types/public/common.js").MiddlewareHandler;
@@ -1,14 +0,0 @@
1
- import { defineMiddleware } from "../../virtual-modules/middleware.js";
2
- import { getActionContext } from "./virtual/server.js";
3
- const onRequest = defineMiddleware(async (context, next) => {
4
- if (context.isPrerendered) return next();
5
- const { action, setActionResult, serializeActionResult } = getActionContext(context);
6
- if (action?.calledFrom === "form") {
7
- const actionResult = await action.handler();
8
- setActionResult(action.name, serializeActionResult(actionResult));
9
- }
10
- return next();
11
- });
12
- export {
13
- onRequest
14
- };
@@ -1,8 +0,0 @@
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>>;
@@ -1,29 +0,0 @@
1
- import { ActionNotFoundError } from "../../../core/errors/errors-data.js";
2
- import { AstroError } from "../../../core/errors/errors.js";
3
- async function getAction(path) {
4
- const pathKeys = path.split(".").map((key) => decodeURIComponent(key));
5
- let { server: actionLookup } = await import("astro:internal-actions");
6
- if (actionLookup == null || !(typeof actionLookup === "object")) {
7
- throw new TypeError(
8
- `Expected \`server\` export in actions file to be an object. Received ${typeof actionLookup}.`
9
- );
10
- }
11
- for (const key of pathKeys) {
12
- if (!(key in actionLookup)) {
13
- throw new AstroError({
14
- ...ActionNotFoundError,
15
- message: ActionNotFoundError.message(pathKeys.join("."))
16
- });
17
- }
18
- actionLookup = actionLookup[key];
19
- }
20
- if (typeof actionLookup !== "function") {
21
- throw new TypeError(
22
- `Expected handler for action ${pathKeys.join(".")} to be a function. Received ${typeof actionLookup}.`
23
- );
24
- }
25
- return actionLookup;
26
- }
27
- export {
28
- getAction
29
- };