astro 7.2.2 → 7.2.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 (151) hide show
  1. package/dist/actions/handler.d.ts +6 -12
  2. package/dist/actions/handler.js +30 -40
  3. package/dist/actions/load.d.ts +12 -0
  4. package/dist/actions/load.js +55 -0
  5. package/dist/actions/runtime/entrypoints/server.js +5 -4
  6. package/dist/actions/runtime/server.js +8 -6
  7. package/dist/assets/fonts/providers/index.d.ts +1 -1
  8. package/dist/assets/fonts/providers/index.js +2 -12
  9. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  10. package/dist/container/environment.d.ts +20 -0
  11. package/dist/container/environment.js +106 -0
  12. package/dist/container/index.js +65 -32
  13. package/dist/content/content-layer.js +3 -3
  14. package/dist/content/data-store-writer.d.ts +7 -10
  15. package/dist/content/data-store-writer.js +62 -27
  16. package/dist/content/data-store.d.ts +20 -3
  17. package/dist/content/data-store.js +28 -7
  18. package/dist/content/mutable-data-store.js +15 -10
  19. package/dist/content/runtime.d.ts +3 -2
  20. package/dist/content/runtime.js +43 -26
  21. package/dist/core/app/app.d.ts +0 -2
  22. package/dist/core/app/app.js +0 -7
  23. package/dist/core/app/base.d.ts +32 -20
  24. package/dist/core/app/base.js +76 -66
  25. package/dist/core/app/dev-facade.d.ts +27 -0
  26. package/dist/core/app/dev-facade.js +51 -0
  27. package/dist/core/app/entrypoints/index.d.ts +0 -1
  28. package/dist/core/app/entrypoints/index.js +0 -2
  29. package/dist/core/app/entrypoints/virtual/dev.js +24 -19
  30. package/dist/core/app/node.js +13 -14
  31. package/dist/core/app/prepare-response.d.ts +1 -1
  32. package/dist/core/app/validate-headers.js +7 -3
  33. package/dist/core/build/app.d.ts +31 -6
  34. package/dist/core/build/app.js +31 -13
  35. package/dist/core/build/environment.d.ts +25 -0
  36. package/dist/core/build/environment.js +157 -0
  37. package/dist/core/cache/handler.d.ts +13 -7
  38. package/dist/core/cache/handler.js +55 -51
  39. package/dist/core/cache/provider.d.ts +4 -0
  40. package/dist/core/cache/provider.js +15 -0
  41. package/dist/core/constants.d.ts +0 -11
  42. package/dist/core/constants.js +1 -5
  43. package/dist/core/dev/dev.js +1 -1
  44. package/dist/core/environment/dev-nonrunnable.d.ts +6 -0
  45. package/dist/core/environment/dev-nonrunnable.js +170 -0
  46. package/dist/core/environment/index.d.ts +90 -0
  47. package/dist/core/environment/index.js +12 -0
  48. package/dist/core/environment/production.d.ts +6 -0
  49. package/dist/core/environment/production.js +117 -0
  50. package/dist/core/errors/build-handler.d.ts +5 -10
  51. package/dist/core/errors/build-handler.js +11 -17
  52. package/dist/core/errors/default-handler.d.ts +4 -9
  53. package/dist/core/errors/default-handler.js +92 -107
  54. package/dist/core/errors/dev-handler.d.ts +7 -11
  55. package/dist/core/errors/dev-handler.js +71 -69
  56. package/dist/core/errors/errors-data.d.ts +18 -0
  57. package/dist/core/errors/errors-data.js +7 -0
  58. package/dist/core/errors/handler.d.ts +22 -3
  59. package/dist/core/errors/handler.js +25 -0
  60. package/dist/core/fetch/default-handler.d.ts +15 -8
  61. package/dist/core/fetch/default-handler.js +12 -34
  62. package/dist/core/fetch/features.d.ts +23 -0
  63. package/dist/core/fetch/features.js +27 -0
  64. package/dist/core/fetch/fetch-state.d.ts +52 -7
  65. package/dist/core/fetch/fetch-state.js +92 -81
  66. package/dist/core/fetch/index.d.ts +9 -2
  67. package/dist/core/fetch/index.js +18 -79
  68. package/dist/core/fetch/vite-plugin.js +2 -1
  69. package/dist/core/i18n/handler.d.ts +28 -13
  70. package/dist/core/i18n/handler.js +100 -100
  71. package/dist/core/logger/manifest-logger.d.ts +31 -0
  72. package/dist/core/logger/manifest-logger.js +37 -0
  73. package/dist/core/manifest/ambient-source.d.ts +2 -0
  74. package/dist/core/manifest/ambient-source.js +4 -0
  75. package/dist/core/manifest/ambient.d.ts +16 -0
  76. package/dist/core/manifest/ambient.js +22 -0
  77. package/dist/core/manifest/derived.d.ts +3 -0
  78. package/dist/core/manifest/derived.js +10 -0
  79. package/dist/core/manifest/memo.d.ts +31 -0
  80. package/dist/core/manifest/memo.js +47 -0
  81. package/dist/core/messages/runtime.js +1 -1
  82. package/dist/core/middleware/astro-middleware.d.ts +25 -35
  83. package/dist/core/middleware/astro-middleware.js +61 -81
  84. package/dist/core/middleware/load.d.ts +19 -0
  85. package/dist/core/middleware/load.js +36 -0
  86. package/dist/core/middleware/sequence.js +15 -7
  87. package/dist/core/pages/handler.d.ts +18 -25
  88. package/dist/core/pages/handler.js +68 -84
  89. package/dist/core/redirects/render.js +3 -8
  90. package/dist/core/render/index.d.ts +0 -1
  91. package/dist/core/render/index.js +0 -2
  92. package/dist/core/render/route-cache.d.ts +6 -1
  93. package/dist/core/render/route-cache.js +11 -1
  94. package/dist/core/rewrites/handler.d.ts +4 -6
  95. package/dist/core/rewrites/handler.js +19 -20
  96. package/dist/core/routing/default.d.ts +2 -0
  97. package/dist/core/routing/default.js +7 -1
  98. package/dist/core/routing/dev.d.ts +1 -3
  99. package/dist/core/routing/dev.js +12 -5
  100. package/dist/core/routing/handler.d.ts +6 -16
  101. package/dist/core/routing/handler.js +126 -164
  102. package/dist/core/routing/helpers.d.ts +7 -1
  103. package/dist/core/routing/helpers.js +21 -0
  104. package/dist/core/routing/match-request.d.ts +10 -0
  105. package/dist/core/routing/match-request.js +57 -0
  106. package/dist/core/routing/route-table.d.ts +34 -0
  107. package/dist/core/routing/route-table.js +41 -0
  108. package/dist/core/routing/trailing-slash-handler.d.ts +4 -14
  109. package/dist/core/routing/trailing-slash-handler.js +39 -50
  110. package/dist/core/server-islands/mappings.d.ts +6 -0
  111. package/dist/core/server-islands/mappings.js +12 -0
  112. package/dist/core/session/driver.d.ts +4 -0
  113. package/dist/core/session/driver.js +14 -0
  114. package/dist/core/session/handler.d.ts +1 -1
  115. package/dist/core/session/handler.js +8 -8
  116. package/dist/core/session/provider-disabled.js +2 -2
  117. package/dist/entrypoints/prerender.js +5 -1
  118. package/dist/i18n/middleware.d.ts +4 -4
  119. package/dist/i18n/middleware.js +3 -3
  120. package/dist/manifest/serialized.d.ts +1 -0
  121. package/dist/manifest/serialized.js +9 -2
  122. package/dist/runtime/prerender/static-paths.d.ts +14 -2
  123. package/dist/runtime/prerender/static-paths.js +4 -3
  124. package/dist/types/public/config.d.ts +433 -241
  125. package/dist/vite-plugin-app/createAstroServerApp.js +22 -18
  126. package/dist/vite-plugin-app/environment.d.ts +18 -0
  127. package/dist/vite-plugin-app/environment.js +210 -0
  128. package/dist/vite-plugin-app/handle-request.d.ts +30 -0
  129. package/dist/vite-plugin-app/handle-request.js +129 -0
  130. package/dist/vite-plugin-app/index.d.ts +1 -1
  131. package/dist/vite-plugin-app/index.js +1 -1
  132. package/package.json +14 -7
  133. package/src/core/README.md +37 -0
  134. package/src/core/manifest/ambient-source.ts +9 -0
  135. package/src/types/README.md +5 -0
  136. package/dist/container/pipeline.d.ts +0 -14
  137. package/dist/container/pipeline.js +0 -78
  138. package/dist/core/app/dev/app.d.ts +0 -31
  139. package/dist/core/app/dev/app.js +0 -80
  140. package/dist/core/app/dev/pipeline.d.ts +0 -17
  141. package/dist/core/app/dev/pipeline.js +0 -133
  142. package/dist/core/app/pipeline.d.ts +0 -14
  143. package/dist/core/app/pipeline.js +0 -123
  144. package/dist/core/base-pipeline.d.ts +0 -183
  145. package/dist/core/base-pipeline.js +0 -293
  146. package/dist/core/build/pipeline.d.ts +0 -38
  147. package/dist/core/build/pipeline.js +0 -202
  148. package/dist/vite-plugin-app/app.d.ts +0 -62
  149. package/dist/vite-plugin-app/app.js +0 -237
  150. package/dist/vite-plugin-app/pipeline.d.ts +0 -25
  151. package/dist/vite-plugin-app/pipeline.js +0 -189
@@ -17,16 +17,10 @@ import type { FetchState } from '../core/fetch/fetch-state.js';
17
17
  * page dispatch. That placement preserves the existing behavior where
18
18
  * user middleware sees action requests and response finalization (cookies,
19
19
  * sessions, etc.) runs around the action response.
20
+ *
21
+ * Expects the APIContext that is already being used by the render pipeline.
22
+ * Returns a `Response` when the action fully handles the request (RPC),
23
+ * or `undefined` when the caller should continue processing the request
24
+ * (form actions or non-action requests).
20
25
  */
21
- export declare class ActionHandler {
22
- #private;
23
- /**
24
- * Run action handling for the current request. Expects the APIContext
25
- * that is already being used by the render pipeline.
26
- *
27
- * Returns a `Response` when the action fully handles the request (RPC),
28
- * or `undefined` when the caller should continue processing the
29
- * request (form actions or non-action requests).
30
- */
31
- handle(apiContext: APIContext, state: FetchState): Promise<Response | undefined> | undefined;
32
- }
26
+ export declare function handleAction(apiContext: APIContext, state: FetchState): Promise<Response | undefined> | undefined;
@@ -2,51 +2,41 @@ import {
2
2
  createCrossOriginForbiddenResponse,
3
3
  isForbiddenCrossOriginRequest
4
4
  } from "../core/app/origin-check.js";
5
- import { PipelineFeatures } from "../core/base-pipeline.js";
5
+ import { markFeatureUsed, FetchFeatures } from "../core/fetch/features.js";
6
6
  import { getActionContext, serializeActionResult } from "./runtime/server.js";
7
- class ActionHandler {
8
- /**
9
- * Run action handling for the current request. Expects the APIContext
10
- * that is already being used by the render pipeline.
11
- *
12
- * Returns a `Response` when the action fully handles the request (RPC),
13
- * or `undefined` when the caller should continue processing the
14
- * request (form actions or non-action requests).
15
- */
16
- handle(apiContext, state) {
17
- state.pipeline.usedFeatures |= PipelineFeatures.actions;
18
- if (apiContext.isPrerendered) {
19
- return void 0;
20
- }
21
- const { action, setActionResult } = getActionContext(apiContext);
22
- if (!action) {
23
- return void 0;
24
- }
25
- if (state.pipeline.manifest.checkOrigin && isForbiddenCrossOriginRequest(apiContext.request, apiContext.url, apiContext.isPrerendered)) {
26
- return Promise.resolve(createCrossOriginForbiddenResponse(apiContext.request));
27
- }
28
- return this.#executeAction(action, setActionResult);
7
+ function handleAction(apiContext, state) {
8
+ markFeatureUsed(state.manifest, FetchFeatures.actions);
9
+ if (apiContext.isPrerendered) {
10
+ return void 0;
29
11
  }
30
- async #executeAction(action, setActionResult) {
31
- const actionResult = await action.handler();
32
- const serialized = serializeActionResult(actionResult);
33
- if (action.calledFrom === "rpc") {
34
- if (serialized.type === "empty") {
35
- return new Response(null, {
36
- status: serialized.status
37
- });
38
- }
39
- return new Response(serialized.body, {
40
- status: serialized.status,
41
- headers: {
42
- "Content-Type": serialized.contentType
43
- }
12
+ const { action, setActionResult } = getActionContext(apiContext);
13
+ if (!action) {
14
+ return void 0;
15
+ }
16
+ if (state.manifest.checkOrigin && isForbiddenCrossOriginRequest(apiContext.request, apiContext.url, apiContext.isPrerendered)) {
17
+ return Promise.resolve(createCrossOriginForbiddenResponse(apiContext.request));
18
+ }
19
+ return executeAction(action, setActionResult);
20
+ }
21
+ async function executeAction(action, setActionResult) {
22
+ const actionResult = await action.handler();
23
+ const serialized = serializeActionResult(actionResult);
24
+ if (action.calledFrom === "rpc") {
25
+ if (serialized.type === "empty") {
26
+ return new Response(null, {
27
+ status: serialized.status
44
28
  });
45
29
  }
46
- setActionResult(action.name, serialized);
47
- return void 0;
30
+ return new Response(serialized.body, {
31
+ status: serialized.status,
32
+ headers: {
33
+ "Content-Type": serialized.contentType
34
+ }
35
+ });
48
36
  }
37
+ setActionResult(action.name, serialized);
38
+ return void 0;
49
39
  }
50
40
  export {
51
- ActionHandler
41
+ handleAction
52
42
  };
@@ -0,0 +1,12 @@
1
+ import type { $ZodType } from 'zod/v4/core';
2
+ import type { SSRActions, SSRManifest } from '../types/public/internal.js';
3
+ import type { ActionAccept, ActionClient } from './runtime/types.js';
4
+ /** Resolves the actions module from the manifest (a no-op module when none). */
5
+ export declare function getActions(manifest: SSRManifest): Promise<SSRActions>;
6
+ /**
7
+ * Clears the cached actions so they are re-resolved on the next request.
8
+ * Called via HMR when action files change during development.
9
+ */
10
+ export declare function clearActions(manifest: SSRManifest): void;
11
+ /** Looks up a single action handler by its dot-separated path. */
12
+ export declare function getAction(manifest: SSRManifest, path: string): Promise<ActionClient<unknown, ActionAccept, $ZodType>>;
@@ -0,0 +1,55 @@
1
+ import { FORBIDDEN_PATH_KEYS } from "@astrojs/internal-helpers/object";
2
+ import { ActionNotFoundError } from "../core/errors/errors-data.js";
3
+ import { AstroError } from "../core/errors/index.js";
4
+ import { createAsyncManifestMemo } from "../core/manifest/memo.js";
5
+ import { NOOP_ACTIONS_MOD } from "./noop-actions.js";
6
+ const actionsMemo = createAsyncManifestMemo(
7
+ async (manifest) => manifest.actions ? await manifest.actions() : NOOP_ACTIONS_MOD
8
+ );
9
+ function getActions(manifest) {
10
+ return actionsMemo.get(manifest);
11
+ }
12
+ function clearActions(manifest) {
13
+ actionsMemo.invalidate(manifest);
14
+ }
15
+ async function getAction(manifest, path) {
16
+ const pathKeys = path.split(".").map((key) => decodeURIComponent(key));
17
+ let { server } = await getActions(manifest);
18
+ if (!server || !(typeof server === "object")) {
19
+ throw new TypeError(
20
+ `Expected \`server\` export in actions file to be an object. Received ${typeof server}.`
21
+ );
22
+ }
23
+ for (const key of pathKeys) {
24
+ if (typeof server === "function") {
25
+ throw new AstroError({
26
+ ...ActionNotFoundError,
27
+ message: ActionNotFoundError.message(pathKeys.join("."))
28
+ });
29
+ }
30
+ if (FORBIDDEN_PATH_KEYS.has(key)) {
31
+ throw new AstroError({
32
+ ...ActionNotFoundError,
33
+ message: ActionNotFoundError.message(pathKeys.join("."))
34
+ });
35
+ }
36
+ if (!Object.hasOwn(server, key)) {
37
+ throw new AstroError({
38
+ ...ActionNotFoundError,
39
+ message: ActionNotFoundError.message(pathKeys.join("."))
40
+ });
41
+ }
42
+ server = server[key];
43
+ }
44
+ if (typeof server !== "function") {
45
+ throw new TypeError(
46
+ `Expected handler for action ${pathKeys.join(".")} to be a function. Received ${typeof server}.`
47
+ );
48
+ }
49
+ return server;
50
+ }
51
+ export {
52
+ clearActions,
53
+ getAction,
54
+ getActions
55
+ };
@@ -1,6 +1,7 @@
1
- import { pipelineSymbol } from "../../../core/constants.js";
1
+ import { fetchStateSymbol } from "../../../core/constants.js";
2
2
  import { ActionCalledFromServerError } from "../../../core/errors/errors-data.js";
3
3
  import { AstroError } from "../../../core/errors/errors.js";
4
+ import { getAction } from "../../load.js";
4
5
  import { createGetActionPath, createActionsProxy } from "../client.js";
5
6
  import { shouldAppendTrailingSlash } from "virtual:astro:actions/options";
6
7
  import { ACTION_QUERY_PARAMS } from "../../consts.js";
@@ -12,11 +13,11 @@ const getActionPath = createGetActionPath({
12
13
  });
13
14
  const actions = createActionsProxy({
14
15
  handleAction: async (param, path, context) => {
15
- const pipeline = context ? Reflect.get(context, pipelineSymbol) : void 0;
16
- if (!pipeline) {
16
+ const state = context ? Reflect.get(context, fetchStateSymbol) : void 0;
17
+ if (!state) {
17
18
  throw new AstroError(ActionCalledFromServerError);
18
19
  }
19
- const action = await pipeline.getAction(path);
20
+ const action = await getAction(state.manifest, path);
20
21
  if (!action) throw new Error(`Action not found: ${path}`);
21
22
  return action.bind(context)(param);
22
23
  }
@@ -1,7 +1,9 @@
1
1
  import { stringify as devalueStringify } from "devalue";
2
2
  import * as z from "zod/v4/core";
3
3
  import { shouldAppendForwardSlash } from "../../core/build/util.js";
4
- import { pipelineSymbol, REDIRECT_STATUS_CODES } from "../../core/constants.js";
4
+ import { REDIRECT_STATUS_CODES } from "../../core/constants.js";
5
+ import { getFetchStateFromAPIContext } from "../../core/fetch/fetch-state.js";
6
+ import { getAction } from "../load.js";
5
7
  import {
6
8
  ActionCalledFromServerError,
7
9
  ActionNotFoundError,
@@ -86,21 +88,21 @@ function getActionContext(context) {
86
88
  calledFrom: callerInfo.from,
87
89
  name: callerInfo.name,
88
90
  handler: async () => {
89
- const pipeline = Reflect.get(context, pipelineSymbol);
91
+ const { manifest } = getFetchStateFromAPIContext(context);
90
92
  const callerInfoName = shouldAppendForwardSlash(
91
- pipeline.manifest.trailingSlash,
92
- pipeline.manifest.buildFormat
93
+ manifest.trailingSlash,
94
+ manifest.buildFormat
93
95
  ) ? removeTrailingForwardSlash(callerInfo.name) : callerInfo.name;
94
96
  let baseAction;
95
97
  try {
96
- baseAction = await pipeline.getAction(callerInfoName);
98
+ baseAction = await getAction(manifest, callerInfoName);
97
99
  } catch (error) {
98
100
  if (error instanceof Error && "name" in error && typeof error.name === "string" && error.name === ActionNotFoundError.name) {
99
101
  return { data: void 0, error: new ActionError({ code: "NOT_FOUND" }) };
100
102
  }
101
103
  throw error;
102
104
  }
103
- const bodySizeLimit = pipeline.manifest.actionBodySizeLimit;
105
+ const bodySizeLimit = manifest.actionBodySizeLimit;
104
106
  let input;
105
107
  try {
106
108
  input = await parseRequestBody(context.request, bodySizeLimit);
@@ -26,7 +26,7 @@ declare function npm(options?: Omit<NpmProviderOptions, 'root' | 'readFile'>): F
26
26
  * - [Google](https://docs.astro.build/en/reference/font-provider-reference/#google)
27
27
  * - [Google Icons](https://docs.astro.build/en/reference/font-provider-reference/#google-icons)
28
28
  * - [Local](https://docs.astro.build/en/reference/font-provider-reference/#local)
29
- * - [NPM](TODO:)
29
+ * - [NPM](https://docs.astro.build/en/reference/font-provider-reference/#npm)
30
30
  */
31
31
  export declare const fontProviders: {
32
32
  adobe: typeof adobe;
@@ -94,18 +94,8 @@ function googleicons() {
94
94
  async init(context) {
95
95
  initializedProvider = await provider(context);
96
96
  },
97
- async resolveFont({ familyName, options, ...rest }) {
98
- const patchedOptions = options?.experimental?.glyphs && options.experimental.glyphs.length > 1 ? {
99
- ...options,
100
- experimental: {
101
- ...options.experimental,
102
- glyphs: [options.experimental.glyphs.join(",")]
103
- }
104
- } : options;
105
- return await initializedProvider?.resolveFont(familyName, {
106
- options: patchedOptions,
107
- ...rest
108
- });
97
+ async resolveFont({ familyName, ...rest }) {
98
+ return await initializedProvider?.resolveFont(familyName, rest);
109
99
  },
110
100
  async listFonts() {
111
101
  return await initializedProvider?.listFonts?.();
@@ -1,6 +1,6 @@
1
1
  class BuildTimeAstroVersionProvider {
2
2
  // Injected during the build through esbuild define
3
- version = "7.2.2";
3
+ version = "7.2.3";
4
4
  }
5
5
  export {
6
6
  BuildTimeAstroVersionProvider
@@ -0,0 +1,20 @@
1
+ import type { RouteData, SSRLoadedRenderer, SSRResult } from '../types/public/internal.js';
2
+ import type { SinglePageBuiltModule } from '../core/build/types.js';
3
+ import type { RenderEnvironment } from '../core/environment/index.js';
4
+ export interface ContainerEnvironmentOptions {
5
+ /**
6
+ * The route → module interner. Created by `experimental_AstroContainer`'s
7
+ * constructor and shared with its `insertRoute` writes; this record owns
8
+ * the lookups.
9
+ */
10
+ interner: WeakMap<RouteData, SinglePageBuiltModule>;
11
+ resolve: SSRResult['resolve'];
12
+ renderers: SSRLoadedRenderer[];
13
+ streaming: boolean;
14
+ }
15
+ /**
16
+ * The container environment. Registered by `experimental_AstroContainer`'s
17
+ * constructor on its fabricated manifest — the container never touches the
18
+ * ambient manifest, so multiple containers in one process stay isolated.
19
+ */
20
+ export declare function createContainerEnvironment({ interner, resolve, renderers, streaming, }: ContainerEnvironmentOptions): RenderEnvironment;
@@ -0,0 +1,106 @@
1
+ import { RedirectSinglePageBuiltModule } from "../core/redirects/index.js";
2
+ import {
3
+ createModuleScriptElement,
4
+ createStylesheetElementSet
5
+ } from "../core/render/ssr-element.js";
6
+ import { getDefaultRoutes } from "../core/routing/default.js";
7
+ import { findRouteToRewrite } from "../core/routing/rewrite.js";
8
+ async function getModuleForRoute(manifest, route) {
9
+ for (const defaultRoute of getDefaultRoutes(manifest)) {
10
+ if (route.component === defaultRoute.component) {
11
+ return {
12
+ page: () => Promise.resolve(defaultRoute.instance)
13
+ };
14
+ }
15
+ }
16
+ if (route.type === "redirect") {
17
+ return RedirectSinglePageBuiltModule;
18
+ } else {
19
+ if (manifest.pageMap) {
20
+ const importComponentInstance = manifest.pageMap.get(route.component);
21
+ if (!importComponentInstance) {
22
+ throw new Error(
23
+ `Unexpectedly unable to find a component instance for route ${route.route}`
24
+ );
25
+ }
26
+ return await importComponentInstance();
27
+ } else if (manifest.pageModule) {
28
+ return manifest.pageModule;
29
+ }
30
+ throw new Error(
31
+ "Astro couldn't find the correct page to render, probably because it wasn't correctly mapped for SSR usage. This is an internal error, please file an issue."
32
+ );
33
+ }
34
+ }
35
+ function createContainerEnvironment({
36
+ interner,
37
+ resolve,
38
+ renderers,
39
+ streaming
40
+ }) {
41
+ async function getComponentByRoute(_manifest, routeData) {
42
+ const page = interner.get(routeData);
43
+ if (page) {
44
+ return page.page();
45
+ }
46
+ throw new Error("Couldn't find component for route " + routeData.pathname);
47
+ }
48
+ return {
49
+ name: "container",
50
+ runtimeMode: "development",
51
+ defaultStreaming: () => streaming,
52
+ async resolve(_manifest, specifier) {
53
+ return resolve(specifier);
54
+ },
55
+ headElements(manifest, routeData) {
56
+ const routeInfo = manifest.routes.find((route) => route.routeData === routeData);
57
+ const links = /* @__PURE__ */ new Set();
58
+ const scripts = /* @__PURE__ */ new Set();
59
+ const styles = createStylesheetElementSet(routeInfo?.styles ?? []);
60
+ for (const script of routeInfo?.scripts ?? []) {
61
+ if ("stage" in script) {
62
+ if (script.stage === "head-inline") {
63
+ scripts.add({
64
+ props: {},
65
+ children: script.children
66
+ });
67
+ }
68
+ } else {
69
+ scripts.add(createModuleScriptElement(script));
70
+ }
71
+ }
72
+ return { links, styles, scripts };
73
+ },
74
+ componentMetadata() {
75
+ },
76
+ getComponentByRoute,
77
+ getModuleForRoute,
78
+ async tryRewrite(manifest, payload, request) {
79
+ const { newUrl, pathname, routeData } = findRouteToRewrite({
80
+ payload,
81
+ request,
82
+ // PER-CALL scan of the live manifest routes: the container inserts
83
+ // routes at runtime (`insertRoute` pushes into `manifest.routes`),
84
+ // and an uncompiled scan sees them immediately. Reading the derived
85
+ // route table here would miss them.
86
+ routes: manifest.routes.map((r) => r.routeData),
87
+ trailingSlash: manifest.trailingSlash,
88
+ buildFormat: manifest.buildFormat,
89
+ base: manifest.base,
90
+ outDir: manifest.outDir
91
+ });
92
+ const componentInstance = await getComponentByRoute(manifest, routeData);
93
+ return { componentInstance, routeData, newUrl, pathname };
94
+ },
95
+ getRenderers() {
96
+ return renderers;
97
+ },
98
+ errorStrategy: "default",
99
+ injectCspMetaTagsOnErrorPages: false,
100
+ logRequest() {
101
+ }
102
+ };
103
+ }
104
+ export {
105
+ createContainerEnvironment
106
+ };
@@ -2,16 +2,20 @@ import { getDefaultClientDirectives } from "../core/client-directive/default.js"
2
2
  import { ASTRO_CONFIG_DEFAULTS } from "../core/config/schemas/defaults.js";
3
3
  import { createKey } from "../core/encryption.js";
4
4
  import { FetchState } from "../core/fetch/fetch-state.js";
5
- import { AstroMiddleware } from "../core/middleware/astro-middleware.js";
5
+ import { handleMiddleware } from "../core/middleware/astro-middleware.js";
6
6
  import { NOOP_MIDDLEWARE_FN } from "../core/middleware/noop-middleware.js";
7
- import { PagesHandler } from "../core/pages/handler.js";
7
+ import { handlePages } from "../core/pages/handler.js";
8
8
  import { removeLeadingForwardSlash } from "../core/path.js";
9
9
  import { getParts } from "../core/routing/parts.js";
10
10
  import { getPattern } from "../core/routing/pattern.js";
11
11
  import { validateSegment } from "../core/routing/segment.js";
12
12
  import { SlotString } from "../runtime/server/render/slot.js";
13
- import { ContainerPipeline } from "./pipeline.js";
13
+ import { createContainerEnvironment } from "./environment.js";
14
+ import { setEnvironment } from "../core/environment/index.js";
14
15
  import { createConsoleLogger } from "../core/logger/impls/console.js";
16
+ import { setLogger } from "../core/logger/manifest-logger.js";
17
+ import { peekMiddleware } from "../core/middleware/load.js";
18
+ import { getRouteTable } from "../core/routing/route-table.js";
15
19
  function createManifest(manifest, renderers, middleware) {
16
20
  function middlewareInstance() {
17
21
  return {
@@ -69,9 +73,17 @@ function createManifest(manifest, renderers, middleware) {
69
73
  };
70
74
  }
71
75
  class experimental_AstroContainer {
72
- #pipeline;
73
- #astroMiddleware;
74
- #pagesHandler;
76
+ /**
77
+ * The container's fabricated manifest — the source of truth all the
78
+ * functional-core accessors key off. The container never touches the
79
+ * ambient manifest, so multiple containers in one process stay isolated.
80
+ */
81
+ #manifest;
82
+ /**
83
+ * The route → module interner, shared between the environment record
84
+ * (lookups) and the `insertRoute` writes below.
85
+ */
86
+ #interner;
75
87
  /**
76
88
  * Internally used to check if the container was created with a manifest.
77
89
  * @private
@@ -84,22 +96,29 @@ class experimental_AstroContainer {
84
96
  resolve
85
97
  }) {
86
98
  const ssrManifest = createManifest(manifest, renderers);
87
- this.#pipeline = ContainerPipeline.create({
88
- logger: createConsoleLogger({ level: "error" }),
89
- manifest: ssrManifest,
90
- streaming,
91
- renderers: renderers ?? manifest?.renderers ?? [],
92
- resolve: async (specifier) => {
93
- if (this.#withManifest) {
94
- return this.#containerResolve(specifier, ssrManifest);
95
- } else if (resolve) {
96
- return resolve(specifier);
97
- }
98
- return specifier;
99
+ const containerRenderers = renderers ?? manifest?.renderers ?? [];
100
+ const containerResolve = async (specifier) => {
101
+ if (this.#withManifest) {
102
+ return this.#containerResolve(specifier, ssrManifest);
103
+ } else if (resolve) {
104
+ return resolve(specifier);
99
105
  }
100
- });
101
- this.#astroMiddleware = new AstroMiddleware(this.#pipeline);
102
- this.#pagesHandler = new PagesHandler(this.#pipeline);
106
+ return specifier;
107
+ };
108
+ const interner = /* @__PURE__ */ new WeakMap();
109
+ setLogger(ssrManifest, createConsoleLogger({ level: "error" }));
110
+ setEnvironment(
111
+ ssrManifest,
112
+ createContainerEnvironment({
113
+ interner,
114
+ resolve: containerResolve,
115
+ renderers: containerRenderers,
116
+ streaming
117
+ })
118
+ );
119
+ getRouteTable(ssrManifest);
120
+ this.#manifest = ssrManifest;
121
+ this.#interner = interner;
103
122
  }
104
123
  async #containerResolve(specifier, manifest) {
105
124
  const found = manifest.entryModules[specifier];
@@ -153,12 +172,12 @@ class experimental_AstroContainer {
153
172
  );
154
173
  }
155
174
  if (isNamedRenderer(renderer)) {
156
- this.#pipeline.manifest.renderers.push({
175
+ this.#manifest.renderers.push({
157
176
  name: renderer.name,
158
177
  ssr: renderer
159
178
  });
160
179
  } else if ("name" in options) {
161
- this.#pipeline.manifest.renderers.push({
180
+ this.#manifest.renderers.push({
162
181
  name: options.name,
163
182
  ssr: renderer
164
183
  });
@@ -193,15 +212,15 @@ class experimental_AstroContainer {
193
212
  */
194
213
  addClientRenderer(options) {
195
214
  const { entrypoint, name } = options;
196
- const rendererIndex = this.#pipeline.manifest.renderers.findIndex((r) => r.name === name);
215
+ const rendererIndex = this.#manifest.renderers.findIndex((r) => r.name === name);
197
216
  if (rendererIndex === -1) {
198
217
  throw new Error(
199
218
  "You tried to add the " + name + " client renderer, but its server renderer wasn't added. You must add the server renderer first. Use the `addServerRenderer` function."
200
219
  );
201
220
  }
202
- const renderer = this.#pipeline.manifest.renderers[rendererIndex];
221
+ const renderer = this.#manifest.renderers[rendererIndex];
203
222
  renderer.clientEntrypoint = entrypoint;
204
- this.#pipeline.manifest.renderers[rendererIndex] = renderer;
223
+ this.#manifest.renderers[rendererIndex] = renderer;
205
224
  }
206
225
  // NOTE: we keep this private via TS instead via `#` so it's still available on the surface, so we can play with it.
207
226
  // @ts-expect-error @ematipico: I plan to use it for a possible integration that could help people
@@ -212,6 +231,20 @@ class experimental_AstroContainer {
212
231
  container.#withManifest = true;
213
232
  return container;
214
233
  }
234
+ /**
235
+ * Associates a runtime-inserted route with its component module in the
236
+ * interner shared with the container environment record. Snapshots the
237
+ * already-resolved middleware synchronously via `peekMiddleware` —
238
+ * `undefined` when `getMiddleware` has not settled yet.
239
+ */
240
+ #internRoute(routeData, componentInstance) {
241
+ this.#interner.set(routeData, {
242
+ page() {
243
+ return Promise.resolve(componentInstance);
244
+ },
245
+ onRequest: peekMiddleware(this.#manifest)
246
+ });
247
+ }
215
248
  #insertRoute({
216
249
  path,
217
250
  componentInstance,
@@ -220,14 +253,14 @@ class experimental_AstroContainer {
220
253
  }) {
221
254
  const pathUrl = new URL(path, "https://example.com");
222
255
  const routeData = this.#createRoute(pathUrl, params, type);
223
- this.#pipeline.manifest.routes.push({
256
+ this.#manifest.routes.push({
224
257
  routeData,
225
258
  file: "",
226
259
  links: [],
227
260
  styles: [],
228
261
  scripts: []
229
262
  });
230
- this.#pipeline.insertRoute(routeData, componentInstance);
263
+ this.#internRoute(routeData, componentInstance);
231
264
  return routeData;
232
265
  }
233
266
  /**
@@ -286,7 +319,7 @@ class experimental_AstroContainer {
286
319
  params: options.params,
287
320
  type: routeType
288
321
  });
289
- const state = new FetchState(this.#pipeline, request);
322
+ const state = new FetchState(this.#manifest, request);
290
323
  state.routeData = routeData;
291
324
  state.pathname = url.pathname;
292
325
  state.clientAddress = "";
@@ -300,7 +333,7 @@ class experimental_AstroContainer {
300
333
  if (options.props) {
301
334
  state.initialProps = options.props;
302
335
  }
303
- return this.#astroMiddleware.handle(state, this.#pagesHandler.handle.bind(this.#pagesHandler));
336
+ return handleMiddleware(state, handlePages);
304
337
  }
305
338
  /**
306
339
  * It stores an Astro **page** route. The first argument, `route`, gets associated to the `component`.
@@ -315,7 +348,7 @@ class experimental_AstroContainer {
315
348
  insertPageRoute(route, component, params) {
316
349
  const url = new URL(route, "https://example.com/");
317
350
  const routeData = this.#createRoute(url, params ?? {}, "page");
318
- this.#pipeline.manifest.routes.push({
351
+ this.#manifest.routes.push({
319
352
  routeData,
320
353
  file: "",
321
354
  links: [],
@@ -323,7 +356,7 @@ class experimental_AstroContainer {
323
356
  scripts: []
324
357
  });
325
358
  const componentInstance = this.#wrapComponent(component, params);
326
- this.#pipeline.insertRoute(routeData, componentInstance);
359
+ this.#internRoute(routeData, componentInstance);
327
360
  }
328
361
  #createRoute(url, params, type) {
329
362
  const segments = removeLeadingForwardSlash(url.pathname).split("/").filter(Boolean).map((s) => {
@@ -196,7 +196,7 @@ ${contentConfig.error.message}`
196
196
  logger.info("Content config changed");
197
197
  shouldClear = true;
198
198
  }
199
- if (previousAstroVersion && previousAstroVersion !== "7.2.2") {
199
+ if (previousAstroVersion && previousAstroVersion !== "7.2.3") {
200
200
  logger.info("Astro version changed");
201
201
  shouldClear = true;
202
202
  }
@@ -204,8 +204,8 @@ ${contentConfig.error.message}`
204
204
  logger.info("Clearing content store");
205
205
  this.#store.clearAll();
206
206
  }
207
- if ("7.2.2") {
208
- this.#store.metaStore().set("astro-version", "7.2.2");
207
+ if ("7.2.3") {
208
+ this.#store.metaStore().set("astro-version", "7.2.3");
209
209
  }
210
210
  if (currentConfigDigest) {
211
211
  this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -1,8 +1,5 @@
1
1
  import { type PathLike } from 'node:fs';
2
- /**
3
- * A chunked store manifest: each collection maps to the list of part file names
4
- * whose contents concatenate back into that collection's serialized string.
5
- */
2
+ /** A chunked store manifest: each collection maps to its content-addressed part files. */
6
3
  export type DataStoreManifest = Record<string, string[]>;
7
4
  /**
8
5
  * Persists the content collection data produced by the content layer. This is
@@ -26,9 +23,9 @@ export declare function serializeDataStore(collections: Map<string, Map<string,
26
23
  * on UTF-16 code-unit boundaries (e.g. `String.prototype.slice`) can cut a
27
24
  * surrogate pair in half; encoding a lone surrogate to UTF-8 substitutes U+FFFD,
28
25
  * so concatenating the re-read parts would corrupt any astral-plane character
29
- * (emoji, some CJK, etc.). Iterating with `for...of` yields whole code points,
30
- * so `str.slice` is only ever called on code-point boundaries and the parts
31
- * always rejoin to the exact original string.
26
+ * (emoji, some CJK, etc.). `getChunkEnd()` advances by Unicode code point, so
27
+ * `str.slice` is only ever called on code-point boundaries and the parts always
28
+ * rejoin to the exact original string.
32
29
  */
33
30
  export declare function chunkString(str: string, maxBytes: number): string[];
34
31
  /**
@@ -52,9 +49,9 @@ export declare class FileWriter implements DataStoreWriter {
52
49
  * A {@link DataStoreWriter} that splits the store across many content-addressed
53
50
  * files inside a directory, described by a manifest.
54
51
  *
55
- * Each collection is serialized to a string and split into parts no larger than
56
- * a fixed byte size, so no single file grows unbounded (platform file-size
57
- * limits). Each part file is named by the xxhash of its contents, so unchanged
52
+ * Entries are serialized independently into parts no larger than a fixed byte
53
+ * size, so writing a collection does not require a collection-wide serialized
54
+ * string. Each part file is named by the xxhash of its contents, so unchanged
58
55
  * parts keep the same name across builds and their writes are skipped, and two
59
56
  * identical parts are naturally deduplicated. The manifest is written last as
60
57
  * the commit point, and stale files are pruned afterwards. This is the inverse