astro 4.14.3 → 4.14.5
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.
- package/dist/@types/astro.d.ts +5 -4
- package/dist/actions/runtime/middleware.js +9 -8
- package/dist/actions/runtime/virtual/server.d.ts +2 -3
- package/dist/actions/runtime/virtual/server.js +17 -3
- package/dist/actions/runtime/virtual/shared.d.ts +2 -1
- package/dist/content/loaders/file.js +3 -6
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/errors/errors-data.d.ts +1 -0
- package/dist/core/messages.js +2 -2
- package/dist/core/render-context.d.ts +1 -1
- package/dist/core/render-context.js +9 -3
- package/package.json +1 -1
- package/templates/actions.mjs +3 -1
package/dist/@types/astro.d.ts
CHANGED
|
@@ -4,7 +4,8 @@ import type { MarkdownHeading, MarkdownVFile, RehypePlugins, RemarkPlugins, Rema
|
|
|
4
4
|
import type * as babel from '@babel/core';
|
|
5
5
|
import type * as rollup from 'rollup';
|
|
6
6
|
import type * as vite from 'vite';
|
|
7
|
-
import type {
|
|
7
|
+
import type { z } from 'zod';
|
|
8
|
+
import type { ActionAccept, ActionClient, ActionReturnType } from '../actions/runtime/virtual/server.js';
|
|
8
9
|
import type { RemotePattern } from '../assets/utils/remotePattern.js';
|
|
9
10
|
import type { DataEntry, RenderedContent } from '../content/data-store.js';
|
|
10
11
|
import type { AssetsPrefix, SSRManifest, SerializedSSRManifest } from '../core/app/types.js';
|
|
@@ -2128,7 +2129,7 @@ export interface AstroUserConfig {
|
|
|
2128
2129
|
* ```
|
|
2129
2130
|
*
|
|
2130
2131
|
* :::note
|
|
2131
|
-
* Loaders will not automatically [exclude files prefaced with an `_`](/en/guides/routing/#excluding-pages). Use a regular expression such as `pattern: '**\/[^_]*.md` in your loader to ignore these files.
|
|
2132
|
+
* Loaders will not automatically [exclude files prefaced with an `_`](/en/guides/routing/#excluding-pages). Use a regular expression such as `pattern: '**\/[^_]*.md'` in your loader to ignore these files.
|
|
2132
2133
|
* :::
|
|
2133
2134
|
*
|
|
2134
2135
|
* #### Querying and rendering with the Content Layer API
|
|
@@ -2757,11 +2758,11 @@ interface AstroSharedContext<Props extends Record<string, any> = Record<string,
|
|
|
2757
2758
|
/**
|
|
2758
2759
|
* Get action result on the server when using a form POST.
|
|
2759
2760
|
*/
|
|
2760
|
-
getActionResult: <TAccept extends ActionAccept, TInputSchema extends
|
|
2761
|
+
getActionResult: <TAccept extends ActionAccept, TInputSchema extends z.ZodType, TAction extends ActionClient<unknown, TAccept, TInputSchema>>(action: TAction) => ActionReturnType<TAction> | undefined;
|
|
2761
2762
|
/**
|
|
2762
2763
|
* Call action handler from the server.
|
|
2763
2764
|
*/
|
|
2764
|
-
callAction: <TAccept extends ActionAccept, TInputSchema extends
|
|
2765
|
+
callAction: <TAccept extends ActionAccept, TInputSchema extends z.ZodType, TOutput, TAction extends ActionClient<TOutput, TAccept, TInputSchema> | ActionClient<TOutput, TAccept, TInputSchema>['orThrow']>(action: TAction, input: Parameters<TAction>[0]) => Promise<ActionReturnType<TAction>>;
|
|
2765
2766
|
/**
|
|
2766
2767
|
* Route parameters for this request if this is a dynamic route.
|
|
2767
2768
|
*/
|
|
@@ -9,8 +9,16 @@ import {
|
|
|
9
9
|
serializeActionResult
|
|
10
10
|
} from "./virtual/shared.js";
|
|
11
11
|
const onRequest = defineMiddleware(async (context, next) => {
|
|
12
|
+
if (context._isPrerendered) {
|
|
13
|
+
if (context.request.method === "POST") {
|
|
14
|
+
console.warn(
|
|
15
|
+
yellow("[astro:actions]"),
|
|
16
|
+
'POST requests should not be sent to prerendered pages. If you\'re using Actions, disable prerendering with `export const prerender = "false".'
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return next();
|
|
20
|
+
}
|
|
12
21
|
const locals = context.locals;
|
|
13
|
-
const { request } = context;
|
|
14
22
|
if (locals._actionPayload) return next();
|
|
15
23
|
const actionPayload = context.cookies.get(ACTION_QUERY_PARAMS.actionPayload)?.json();
|
|
16
24
|
if (actionPayload) {
|
|
@@ -19,13 +27,6 @@ const onRequest = defineMiddleware(async (context, next) => {
|
|
|
19
27
|
}
|
|
20
28
|
return renderResult({ context, next, ...actionPayload });
|
|
21
29
|
}
|
|
22
|
-
if (import.meta.env.DEV && request.method === "POST" && request.body === null) {
|
|
23
|
-
console.warn(
|
|
24
|
-
yellow("[astro:actions]"),
|
|
25
|
-
'POST requests should not be sent to prerendered pages. If you\'re using Actions, disable prerendering with `export const prerender = "false".'
|
|
26
|
-
);
|
|
27
|
-
return next();
|
|
28
|
-
}
|
|
29
30
|
const actionName = context.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
|
|
30
31
|
if (context.request.method === "POST" && actionName) {
|
|
31
32
|
return handlePost({ context, next, actionName });
|
|
@@ -4,16 +4,15 @@ import { type SafeResult } from './shared.js';
|
|
|
4
4
|
export * from './shared.js';
|
|
5
5
|
export { z } from 'zod';
|
|
6
6
|
export type ActionAccept = 'form' | 'json';
|
|
7
|
-
export type ActionInputSchema<T extends ActionAccept | undefined> = T extends 'form' ? z.AnyZodObject | z.ZodType<FormData> : z.ZodType;
|
|
8
7
|
export type ActionHandler<TInputSchema, TOutput> = TInputSchema extends z.ZodType ? (input: z.infer<TInputSchema>, context: ActionAPIContext) => MaybePromise<TOutput> : (input: any, context: ActionAPIContext) => MaybePromise<TOutput>;
|
|
9
8
|
export type ActionReturnType<T extends ActionHandler<any, any>> = Awaited<ReturnType<T>>;
|
|
10
|
-
export type ActionClient<TOutput, TAccept extends ActionAccept | undefined, TInputSchema extends
|
|
9
|
+
export type ActionClient<TOutput, TAccept extends ActionAccept | undefined, TInputSchema extends z.ZodType | undefined> = TInputSchema extends z.ZodType ? ((input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<SafeResult<z.input<TInputSchema> extends ErrorInferenceObject ? z.input<TInputSchema> : ErrorInferenceObject, Awaited<TOutput>>>) & {
|
|
11
10
|
queryString: string;
|
|
12
11
|
orThrow: (input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<Awaited<TOutput>>;
|
|
13
12
|
} : ((input?: any) => Promise<SafeResult<never, Awaited<TOutput>>>) & {
|
|
14
13
|
orThrow: (input?: any) => Promise<Awaited<TOutput>>;
|
|
15
14
|
};
|
|
16
|
-
export declare function defineAction<TOutput, TAccept extends ActionAccept | undefined = undefined, TInputSchema extends
|
|
15
|
+
export declare function defineAction<TOutput, TAccept extends ActionAccept | undefined = undefined, TInputSchema extends z.ZodType | undefined = TAccept extends 'form' ? z.ZodType<FormData> : undefined>({ accept, input: inputSchema, handler, }: {
|
|
17
16
|
input?: TInputSchema;
|
|
18
17
|
accept?: TAccept;
|
|
19
18
|
handler: ActionHandler<TInputSchema, TOutput>;
|
|
@@ -34,8 +34,11 @@ function getFormServerHandler(handler, inputSchema) {
|
|
|
34
34
|
message: "This action only accepts FormData."
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
-
if (!
|
|
38
|
-
const
|
|
37
|
+
if (!inputSchema) return await handler(unparsedInput, context);
|
|
38
|
+
const baseSchema = unwrapSchemaEffects(inputSchema);
|
|
39
|
+
const parsed = await inputSchema.safeParseAsync(
|
|
40
|
+
baseSchema instanceof z.ZodObject ? formDataToObject(unparsedInput, baseSchema) : unparsedInput
|
|
41
|
+
);
|
|
39
42
|
if (!parsed.success) {
|
|
40
43
|
throw new ActionInputError(parsed.error.issues);
|
|
41
44
|
}
|
|
@@ -59,7 +62,7 @@ function getJsonServerHandler(handler, inputSchema) {
|
|
|
59
62
|
};
|
|
60
63
|
}
|
|
61
64
|
function formDataToObject(formData, schema) {
|
|
62
|
-
const obj = {};
|
|
65
|
+
const obj = schema._def.unknownKeys === "passthrough" ? Object.fromEntries(formData.entries()) : {};
|
|
63
66
|
for (const [key, baseValidator] of Object.entries(schema.shape)) {
|
|
64
67
|
let validator = baseValidator;
|
|
65
68
|
while (validator instanceof z.ZodOptional || validator instanceof z.ZodNullable || validator instanceof z.ZodDefault) {
|
|
@@ -98,6 +101,17 @@ function handleFormDataGet(key, formData, validator, baseValidator) {
|
|
|
98
101
|
}
|
|
99
102
|
return validator instanceof z.ZodNumber ? Number(value) : value;
|
|
100
103
|
}
|
|
104
|
+
function unwrapSchemaEffects(schema) {
|
|
105
|
+
while (schema instanceof z.ZodEffects || schema instanceof z.ZodPipeline) {
|
|
106
|
+
if (schema instanceof z.ZodEffects) {
|
|
107
|
+
schema = schema._def.schema;
|
|
108
|
+
}
|
|
109
|
+
if (schema instanceof z.ZodPipeline) {
|
|
110
|
+
schema = schema._def.in;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return schema;
|
|
114
|
+
}
|
|
101
115
|
export {
|
|
102
116
|
defineAction,
|
|
103
117
|
formDataToObject,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { z } from 'zod';
|
|
2
|
-
import type { ErrorInferenceObject, MaybePromise } from '../utils.js';
|
|
2
|
+
import type { ErrorInferenceObject, MaybePromise, ActionAPIContext as _ActionAPIContext } from '../utils.js';
|
|
3
|
+
export type ActionAPIContext = _ActionAPIContext;
|
|
3
4
|
export declare const ACTION_QUERY_PARAMS: {
|
|
4
5
|
actionName: string;
|
|
5
6
|
actionPayload: string;
|
|
@@ -15,6 +15,7 @@ function file(fileName) {
|
|
|
15
15
|
logger.debug(error.message);
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
+
const normalizedFilePath = posixRelative(fileURLToPath(settings.config.root), filePath);
|
|
18
19
|
if (Array.isArray(json)) {
|
|
19
20
|
if (json.length === 0) {
|
|
20
21
|
logger.warn(`No items found in ${fileName}`);
|
|
@@ -28,11 +29,7 @@ function file(fileName) {
|
|
|
28
29
|
continue;
|
|
29
30
|
}
|
|
30
31
|
const data = await parseData({ id, data: rawItem, filePath });
|
|
31
|
-
store.set({
|
|
32
|
-
id,
|
|
33
|
-
data,
|
|
34
|
-
filePath: posixRelative(fileURLToPath(settings.config.root), filePath)
|
|
35
|
-
});
|
|
32
|
+
store.set({ id, data, filePath: normalizedFilePath });
|
|
36
33
|
}
|
|
37
34
|
} else if (typeof json === "object") {
|
|
38
35
|
const entries = Object.entries(json);
|
|
@@ -40,7 +37,7 @@ function file(fileName) {
|
|
|
40
37
|
store.clear();
|
|
41
38
|
for (const [id, rawItem] of entries) {
|
|
42
39
|
const data = await parseData({ id, data: rawItem, filePath });
|
|
43
|
-
store.set({ id, data });
|
|
40
|
+
store.set({ id, data, filePath: normalizedFilePath });
|
|
44
41
|
}
|
|
45
42
|
} else {
|
|
46
43
|
logger.error(`Invalid data in ${fileName}. Must be an array or object.`);
|
package/dist/core/constants.js
CHANGED
package/dist/core/dev/dev.js
CHANGED
|
@@ -23,7 +23,7 @@ async function dev(inlineConfig) {
|
|
|
23
23
|
await telemetry.record([]);
|
|
24
24
|
const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
|
|
25
25
|
const logger = restart.container.logger;
|
|
26
|
-
const currentVersion = "4.14.
|
|
26
|
+
const currentVersion = "4.14.5";
|
|
27
27
|
const isPrerelease = currentVersion.includes("-");
|
|
28
28
|
if (!isPrerelease) {
|
|
29
29
|
try {
|
|
@@ -1485,6 +1485,7 @@ export declare const ActionsWithoutServerOutputError: {
|
|
|
1485
1485
|
* - [Actions RFC](https://github.com/withastro/roadmap/blob/actions/proposals/0046-actions.md)
|
|
1486
1486
|
* @description
|
|
1487
1487
|
* 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.
|
|
1488
|
+
* @deprecated Deprecated since version 4.13.2.
|
|
1488
1489
|
*/
|
|
1489
1490
|
export declare const ActionsUsedWithForGetError: {
|
|
1490
1491
|
name: string;
|
package/dist/core/messages.js
CHANGED
|
@@ -38,7 +38,7 @@ function serverStart({
|
|
|
38
38
|
host,
|
|
39
39
|
base
|
|
40
40
|
}) {
|
|
41
|
-
const version = "4.14.
|
|
41
|
+
const version = "4.14.5";
|
|
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.14.
|
|
273
|
+
`v${"4.14.5"}`
|
|
274
274
|
)} ${headline}`
|
|
275
275
|
);
|
|
276
276
|
}
|
|
@@ -42,7 +42,7 @@ export declare class RenderContext {
|
|
|
42
42
|
* - fallback
|
|
43
43
|
*/
|
|
44
44
|
render(componentInstance: ComponentInstance | undefined, slots?: Record<string, any>): Promise<Response>;
|
|
45
|
-
createAPIContext(props: APIContext['props']): APIContext;
|
|
45
|
+
createAPIContext(props: APIContext['props'], isPrerendered: boolean): APIContext;
|
|
46
46
|
createActionAPIContext(): ActionAPIContext;
|
|
47
47
|
createResult(mod: ComponentInstance): Promise<SSRResult>;
|
|
48
48
|
/**
|
|
@@ -87,6 +87,7 @@ class RenderContext {
|
|
|
87
87
|
async render(componentInstance, slots = {}) {
|
|
88
88
|
const { cookies, middleware, pipeline } = this;
|
|
89
89
|
const { logger, serverLike, streaming } = pipeline;
|
|
90
|
+
const isPrerendered = !serverLike || this.routeData.prerender;
|
|
90
91
|
const props = Object.keys(this.props).length > 0 ? this.props : await getProps({
|
|
91
92
|
mod: componentInstance,
|
|
92
93
|
routeData: this.routeData,
|
|
@@ -95,7 +96,7 @@ class RenderContext {
|
|
|
95
96
|
logger,
|
|
96
97
|
serverLike
|
|
97
98
|
});
|
|
98
|
-
const apiContext = this.createAPIContext(props);
|
|
99
|
+
const apiContext = this.createAPIContext(props, isPrerendered);
|
|
99
100
|
this.counter++;
|
|
100
101
|
if (this.counter === 4) {
|
|
101
102
|
return new Response("Loop Detected", {
|
|
@@ -166,12 +167,17 @@ class RenderContext {
|
|
|
166
167
|
attachCookiesToResponse(response, cookies);
|
|
167
168
|
return response;
|
|
168
169
|
}
|
|
169
|
-
createAPIContext(props) {
|
|
170
|
+
createAPIContext(props, isPrerendered) {
|
|
170
171
|
const context = this.createActionAPIContext();
|
|
171
172
|
return Object.assign(context, {
|
|
172
173
|
props,
|
|
173
174
|
getActionResult: createGetActionResult(context.locals),
|
|
174
|
-
callAction: createCallAction(context)
|
|
175
|
+
callAction: createCallAction(context),
|
|
176
|
+
// Used internally by Actions middleware.
|
|
177
|
+
// TODO: discuss exposing this information from APIContext.
|
|
178
|
+
// middleware runs on prerendered routes in the dev server,
|
|
179
|
+
// so this is useful information to have.
|
|
180
|
+
_isPrerendered: isPrerendered
|
|
175
181
|
});
|
|
176
182
|
}
|
|
177
183
|
async #executeRewrite(reroutePayload) {
|
package/package.json
CHANGED
package/templates/actions.mjs
CHANGED
|
@@ -93,7 +93,9 @@ async function handleAction(param, path, context) {
|
|
|
93
93
|
body,
|
|
94
94
|
headers,
|
|
95
95
|
});
|
|
96
|
-
if (rawResult.status === 204)
|
|
96
|
+
if (rawResult.status === 204) {
|
|
97
|
+
return deserializeActionResult({ type: 'empty', status: 204 });
|
|
98
|
+
}
|
|
97
99
|
|
|
98
100
|
return deserializeActionResult({
|
|
99
101
|
type: rawResult.ok ? 'data' : 'error',
|