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