astro 5.0.0-beta.6 → 5.0.0-beta.8
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/components/Welcome.astro +172 -0
- package/components/index.ts +3 -1
- package/dist/actions/consts.d.ts +1 -1
- package/dist/actions/consts.js +3 -2
- package/dist/actions/integration.js +2 -2
- package/dist/actions/runtime/middleware.d.ts +0 -8
- package/dist/actions/runtime/middleware.js +7 -111
- package/dist/actions/runtime/route.js +5 -26
- package/dist/actions/runtime/utils.d.ts +10 -0
- package/dist/actions/runtime/utils.js +8 -1
- package/dist/actions/runtime/virtual/client.d.ts +1 -0
- package/dist/actions/runtime/virtual/client.js +5 -1
- package/dist/actions/runtime/virtual/get-action.js +1 -1
- package/dist/actions/runtime/virtual/server.d.ts +32 -2
- package/dist/actions/runtime/virtual/server.js +82 -2
- package/dist/actions/runtime/virtual/shared.d.ts +0 -1
- package/dist/actions/utils.d.ts +1 -2
- package/dist/assets/types.d.ts +3 -3
- package/dist/cli/add/index.js +6 -4
- package/dist/cli/docs/open.d.ts +1 -1
- package/dist/cli/docs/open.js +1 -1
- package/dist/cli/exec.d.ts +5 -0
- package/dist/cli/exec.js +23 -0
- package/dist/cli/index.js +2 -2
- package/dist/cli/install-package.js +1 -1
- package/dist/cli/sync/index.d.ts +1 -1
- package/dist/cli/sync/index.js +1 -6
- package/dist/config/index.js +3 -7
- package/dist/content/content-layer.js +3 -3
- package/dist/content/runtime.d.ts +3 -3
- package/dist/core/app/index.js +4 -8
- package/dist/core/app/node.js +4 -2
- package/dist/core/build/generate.js +1 -0
- package/dist/core/build/static-build.js +11 -18
- package/dist/core/constants.d.ts +4 -0
- package/dist/core/constants.js +3 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/dev/restart.js +5 -1
- package/dist/core/errors/dev/vite.d.ts +1 -0
- package/dist/core/errors/dev/vite.js +1 -0
- package/dist/core/errors/errors-data.d.ts +12 -0
- package/dist/core/errors/errors-data.js +12 -1
- package/dist/core/messages.js +6 -2
- package/dist/core/middleware/index.js +5 -1
- package/dist/core/middleware/noop-middleware.js +5 -1
- package/dist/core/middleware/sequence.js +9 -0
- package/dist/core/module-loader/vite.js +18 -0
- package/dist/core/render-context.js +27 -6
- package/dist/core/routing/rewrite.d.ts +1 -1
- package/dist/core/routing/rewrite.js +1 -1
- package/dist/core/sync/index.js +31 -47
- package/dist/env/runtime.js +1 -5
- package/dist/env/vite-plugin-env.js +5 -2
- package/dist/prefetch/vite-plugin-prefetch.js +12 -2
- package/dist/runtime/client/dev-toolbar/apps/settings.js +2 -0
- package/dist/runtime/server/render/component.js +1 -0
- package/dist/runtime/server/render/util.js +1 -1
- package/dist/type-utils.d.ts +3 -0
- package/dist/types/public/context.d.ts +5 -0
- package/dist/vite-plugin-astro/index.js +3 -0
- package/dist/vite-plugin-astro-server/route.d.ts +1 -2
- package/dist/vite-plugin-astro-server/route.js +24 -14
- package/package.json +7 -7
- package/templates/actions.mjs +1 -10
- package/dist/env/runtime-constants.d.ts +0 -1
- package/dist/env/runtime-constants.js +0 -4
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
title?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const cards = [
|
|
7
|
+
{
|
|
8
|
+
href: 'https://docs.astro.build/',
|
|
9
|
+
title: 'Documentation',
|
|
10
|
+
body: 'Learn how Astro works and explore the official API docs.',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
href: 'https://astro.build/integrations/',
|
|
14
|
+
title: 'Integrations',
|
|
15
|
+
body: 'Supercharge your project with new frameworks and libraries.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
href: 'https://astro.build/themes/',
|
|
19
|
+
title: 'Themes',
|
|
20
|
+
body: 'Explore a galaxy of community-built starter themes.',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
href: 'https://astro.build/chat/',
|
|
24
|
+
title: 'Community',
|
|
25
|
+
body: 'Come say hi to our amazing Discord community. ❤️',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const { title = 'Welcome to Astro' } = Astro.props;
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<main>
|
|
33
|
+
<svg
|
|
34
|
+
class="astro-a"
|
|
35
|
+
width="495"
|
|
36
|
+
height="623"
|
|
37
|
+
viewBox="0 0 495 623"
|
|
38
|
+
fill="none"
|
|
39
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
40
|
+
aria-hidden="true"
|
|
41
|
+
>
|
|
42
|
+
<path
|
|
43
|
+
fill-rule="evenodd"
|
|
44
|
+
clip-rule="evenodd"
|
|
45
|
+
d="M167.19 364.254C83.4786 364.254 0 404.819 0 404.819C0 404.819 141.781 19.4876 142.087 18.7291C146.434 7.33701 153.027 0 162.289 0H332.441C341.703 0 348.574 7.33701 352.643 18.7291C352.92 19.5022 494.716 404.819 494.716 404.819C494.716 404.819 426.67 364.254 327.525 364.254L264.41 169.408C262.047 159.985 255.147 153.581 247.358 153.581C239.569 153.581 232.669 159.985 230.306 169.408L167.19 364.254ZM160.869 530.172C160.877 530.18 160.885 530.187 160.894 530.195L160.867 530.181C160.868 530.178 160.868 530.175 160.869 530.172ZM136.218 411.348C124.476 450.467 132.698 504.458 160.869 530.172C160.997 529.696 161.125 529.242 161.248 528.804C161.502 527.907 161.737 527.073 161.917 526.233C165.446 509.895 178.754 499.52 195.577 500.01C211.969 500.487 220.67 508.765 223.202 527.254C224.141 534.12 224.23 541.131 224.319 548.105C224.328 548.834 224.337 549.563 224.347 550.291C224.563 566.098 228.657 580.707 237.264 593.914C245.413 606.426 256.108 615.943 270.749 622.478C270.593 621.952 270.463 621.508 270.35 621.126C270.045 620.086 269.872 619.499 269.685 618.911C258.909 585.935 266.668 563.266 295.344 543.933C298.254 541.971 301.187 540.041 304.12 538.112C310.591 533.854 317.059 529.599 323.279 525.007C345.88 508.329 360.09 486.327 363.431 457.844C364.805 446.148 363.781 434.657 359.848 423.275C358.176 424.287 356.587 425.295 355.042 426.275C351.744 428.366 348.647 430.33 345.382 431.934C303.466 452.507 259.152 455.053 214.03 448.245C184.802 443.834 156.584 436.019 136.218 411.348Z"
|
|
46
|
+
fill="url(#paint0_linear_1805_24383)"></path>
|
|
47
|
+
<defs>
|
|
48
|
+
<linearGradient
|
|
49
|
+
id="paint0_linear_1805_24383"
|
|
50
|
+
x1="247.358"
|
|
51
|
+
y1="0"
|
|
52
|
+
x2="247.358"
|
|
53
|
+
y2="622.479"
|
|
54
|
+
gradientUnits="userSpaceOnUse"
|
|
55
|
+
>
|
|
56
|
+
<stop stop-opacity="0.9"></stop>
|
|
57
|
+
<stop offset="1" stop-opacity="0.2"></stop>
|
|
58
|
+
</linearGradient>
|
|
59
|
+
</defs>
|
|
60
|
+
</svg>
|
|
61
|
+
<h1>{title}</h1>
|
|
62
|
+
<p class="instructions">
|
|
63
|
+
To get started, open the directory <code>src/pages</code> in your project.<br />
|
|
64
|
+
<strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above.
|
|
65
|
+
</p>
|
|
66
|
+
<ul role="list" class="link-card-grid">
|
|
67
|
+
{
|
|
68
|
+
cards.map((card) => (
|
|
69
|
+
<li class="link-card">
|
|
70
|
+
<a href={card.href}>
|
|
71
|
+
<h2>
|
|
72
|
+
{card.title}
|
|
73
|
+
<span>→</span>
|
|
74
|
+
</h2>
|
|
75
|
+
<p>{card.body}</p>
|
|
76
|
+
</a>
|
|
77
|
+
</li>
|
|
78
|
+
))
|
|
79
|
+
}
|
|
80
|
+
</ul>
|
|
81
|
+
</main>
|
|
82
|
+
|
|
83
|
+
<style>
|
|
84
|
+
main {
|
|
85
|
+
margin: auto;
|
|
86
|
+
padding: 1rem;
|
|
87
|
+
width: 800px;
|
|
88
|
+
max-width: calc(100% - 2rem);
|
|
89
|
+
color: white;
|
|
90
|
+
font-size: 20px;
|
|
91
|
+
line-height: 1.6;
|
|
92
|
+
}
|
|
93
|
+
.astro-a {
|
|
94
|
+
position: absolute;
|
|
95
|
+
top: -32px;
|
|
96
|
+
left: 50%;
|
|
97
|
+
transform: translatex(-50%);
|
|
98
|
+
width: 220px;
|
|
99
|
+
height: auto;
|
|
100
|
+
z-index: -1;
|
|
101
|
+
}
|
|
102
|
+
h1 {
|
|
103
|
+
font-size: 4rem;
|
|
104
|
+
font-weight: 700;
|
|
105
|
+
line-height: 1;
|
|
106
|
+
text-align: center;
|
|
107
|
+
margin-bottom: 1em;
|
|
108
|
+
}
|
|
109
|
+
.instructions {
|
|
110
|
+
margin-bottom: 2rem;
|
|
111
|
+
border: 1px solid rgba(var(--accent-light), 25%);
|
|
112
|
+
background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
|
|
113
|
+
padding: 1.5rem;
|
|
114
|
+
border-radius: 8px;
|
|
115
|
+
}
|
|
116
|
+
.instructions code {
|
|
117
|
+
font-size: 0.8em;
|
|
118
|
+
font-weight: bold;
|
|
119
|
+
background: rgba(var(--accent-light), 12%);
|
|
120
|
+
color: rgb(var(--accent-light));
|
|
121
|
+
border-radius: 4px;
|
|
122
|
+
padding: 0.3em 0.4em;
|
|
123
|
+
}
|
|
124
|
+
.instructions strong {
|
|
125
|
+
color: rgb(var(--accent-light));
|
|
126
|
+
}
|
|
127
|
+
.link-card-grid {
|
|
128
|
+
display: grid;
|
|
129
|
+
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
|
|
130
|
+
gap: 2rem;
|
|
131
|
+
padding: 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.link-card {
|
|
135
|
+
list-style: none;
|
|
136
|
+
display: flex;
|
|
137
|
+
padding: 1px;
|
|
138
|
+
background-color: #23262d;
|
|
139
|
+
background-image: none;
|
|
140
|
+
background-size: 400%;
|
|
141
|
+
border-radius: 7px;
|
|
142
|
+
background-position: 100%;
|
|
143
|
+
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
|
144
|
+
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
145
|
+
}
|
|
146
|
+
.link-card > a {
|
|
147
|
+
width: 100%;
|
|
148
|
+
text-decoration: none;
|
|
149
|
+
line-height: 1.4;
|
|
150
|
+
padding: calc(1.5rem - 1px);
|
|
151
|
+
border-radius: 8px;
|
|
152
|
+
color: white;
|
|
153
|
+
background-color: #23262d;
|
|
154
|
+
opacity: 0.8;
|
|
155
|
+
}
|
|
156
|
+
h2 {
|
|
157
|
+
margin: 0;
|
|
158
|
+
font-size: 1.25rem;
|
|
159
|
+
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
|
160
|
+
}
|
|
161
|
+
p {
|
|
162
|
+
margin-top: 0.5rem;
|
|
163
|
+
margin-bottom: 0;
|
|
164
|
+
}
|
|
165
|
+
.link-card:is(:hover, :focus-within) {
|
|
166
|
+
background-position: 0;
|
|
167
|
+
background-image: var(--accent-gradient);
|
|
168
|
+
}
|
|
169
|
+
.link-card:is(:hover, :focus-within) h2 {
|
|
170
|
+
color: rgb(var(--accent-light));
|
|
171
|
+
}
|
|
172
|
+
</style>
|
package/components/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// The `ts-ignore` comments here are necessary because we're importing this file inside the `astro:components`
|
|
2
|
-
// virtual module's types, which means that `tsc` will try to resolve these imports.
|
|
2
|
+
// virtual module's types, which means that `tsc` will try to resolve these imports.
|
|
3
3
|
// @ts-ignore
|
|
4
4
|
export { default as Code } from './Code.astro';
|
|
5
5
|
// @ts-ignore
|
|
6
6
|
export { default as Debug } from './Debug.astro';
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
export { default as Welcome } from './Welcome.astro';
|
package/dist/actions/consts.d.ts
CHANGED
package/dist/actions/consts.js
CHANGED
|
@@ -6,12 +6,13 @@ const RESOLVED_VIRTUAL_INTERNAL_MODULE_ID = "\0astro:internal-actions";
|
|
|
6
6
|
const NOOP_ACTIONS = "\0noop-actions";
|
|
7
7
|
const ACTION_QUERY_PARAMS = {
|
|
8
8
|
actionName: "_astroAction",
|
|
9
|
-
actionPayload: "_astroActionPayload"
|
|
10
|
-
actionRedirect: "_astroActionRedirect"
|
|
9
|
+
actionPayload: "_astroActionPayload"
|
|
11
10
|
};
|
|
11
|
+
const ACTION_RPC_ROUTE_PATTERN = "/_actions/[...path]";
|
|
12
12
|
export {
|
|
13
13
|
ACTIONS_TYPES_FILE,
|
|
14
14
|
ACTION_QUERY_PARAMS,
|
|
15
|
+
ACTION_RPC_ROUTE_PATTERN,
|
|
15
16
|
NOOP_ACTIONS,
|
|
16
17
|
RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
|
|
17
18
|
RESOLVED_VIRTUAL_MODULE_ID,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ActionsWithoutServerOutputError } from "../core/errors/errors-data.js";
|
|
2
2
|
import { AstroError } from "../core/errors/errors.js";
|
|
3
3
|
import { viteID } from "../core/util.js";
|
|
4
|
-
import { ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID } from "./consts.js";
|
|
4
|
+
import { ACTIONS_TYPES_FILE, ACTION_RPC_ROUTE_PATTERN, VIRTUAL_MODULE_ID } from "./consts.js";
|
|
5
5
|
function astroIntegrationActionsRouteHandler({
|
|
6
6
|
settings
|
|
7
7
|
}) {
|
|
@@ -10,7 +10,7 @@ function astroIntegrationActionsRouteHandler({
|
|
|
10
10
|
hooks: {
|
|
11
11
|
async "astro:config:setup"(params) {
|
|
12
12
|
params.injectRoute({
|
|
13
|
-
pattern:
|
|
13
|
+
pattern: ACTION_RPC_ROUTE_PATTERN,
|
|
14
14
|
entrypoint: "astro/actions/runtime/route.js",
|
|
15
15
|
prerender: false
|
|
16
16
|
});
|
|
@@ -1,9 +1 @@
|
|
|
1
|
-
import { type SerializedActionResult } from './virtual/shared.js';
|
|
2
|
-
export type ActionPayload = {
|
|
3
|
-
actionResult: SerializedActionResult;
|
|
4
|
-
actionName: string;
|
|
5
|
-
};
|
|
6
|
-
export type Locals = {
|
|
7
|
-
_actionPayload: ActionPayload;
|
|
8
|
-
};
|
|
9
1
|
export declare const onRequest: import("../../types/public/common.js").MiddlewareHandler;
|
|
@@ -1,118 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { defineMiddleware } from "../../core/middleware/index.js";
|
|
4
|
-
import { getOriginPathname } from "../../core/routing/rewrite.js";
|
|
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";
|
|
11
|
-
const decoder = new TextDecoder();
|
|
12
|
-
const encoder = new TextEncoder();
|
|
1
|
+
import { defineMiddleware } from "../../virtual-modules/middleware.js";
|
|
2
|
+
import { getActionContext } from "./virtual/server.js";
|
|
13
3
|
const onRequest = defineMiddleware(async (context, next) => {
|
|
14
|
-
if (context.isPrerendered)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
return next();
|
|
22
|
-
}
|
|
23
|
-
const locals = context.locals;
|
|
24
|
-
if (locals._actionPayload) return next();
|
|
25
|
-
const actionPayloadCookie = context.cookies.get(ACTION_QUERY_PARAMS.actionPayload)?.value;
|
|
26
|
-
if (actionPayloadCookie) {
|
|
27
|
-
const actionPayload = JSON.parse(decoder.decode(decodeBase64(actionPayloadCookie)));
|
|
28
|
-
if (!isActionPayload(actionPayload)) {
|
|
29
|
-
throw new Error("Internal: Invalid action payload in cookie.");
|
|
30
|
-
}
|
|
31
|
-
return renderResult({ context, next, ...actionPayload });
|
|
32
|
-
}
|
|
33
|
-
const actionName = context.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
|
|
34
|
-
if (context.request.method === "POST" && actionName) {
|
|
35
|
-
return handlePost({ context, next, actionName });
|
|
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));
|
|
36
9
|
}
|
|
37
10
|
return next();
|
|
38
11
|
});
|
|
39
|
-
async function renderResult({
|
|
40
|
-
context,
|
|
41
|
-
next,
|
|
42
|
-
actionResult,
|
|
43
|
-
actionName
|
|
44
|
-
}) {
|
|
45
|
-
const locals = context.locals;
|
|
46
|
-
locals._actionPayload = { actionResult, actionName };
|
|
47
|
-
const response = await next();
|
|
48
|
-
context.cookies.delete(ACTION_QUERY_PARAMS.actionPayload);
|
|
49
|
-
if (actionResult.type === "error") {
|
|
50
|
-
return new Response(response.body, {
|
|
51
|
-
status: actionResult.status,
|
|
52
|
-
statusText: actionResult.type,
|
|
53
|
-
headers: response.headers
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
return response;
|
|
57
|
-
}
|
|
58
|
-
async function handlePost({
|
|
59
|
-
context,
|
|
60
|
-
next,
|
|
61
|
-
actionName
|
|
62
|
-
}) {
|
|
63
|
-
const { request } = context;
|
|
64
|
-
const baseAction = await getAction(actionName);
|
|
65
|
-
const contentType = request.headers.get("content-type");
|
|
66
|
-
let formData;
|
|
67
|
-
if (contentType && hasContentType(contentType, formContentTypes)) {
|
|
68
|
-
formData = await request.clone().formData();
|
|
69
|
-
}
|
|
70
|
-
const { getActionResult, callAction, props, redirect, ...actionAPIContext } = context;
|
|
71
|
-
const action = baseAction.bind(actionAPIContext);
|
|
72
|
-
const actionResult = await action(formData);
|
|
73
|
-
if (context.url.searchParams.get(ACTION_QUERY_PARAMS.actionRedirect) === "false") {
|
|
74
|
-
return renderResult({
|
|
75
|
-
context,
|
|
76
|
-
next,
|
|
77
|
-
actionName,
|
|
78
|
-
actionResult: serializeActionResult(actionResult)
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
return redirectWithResult({ context, actionName, actionResult });
|
|
82
|
-
}
|
|
83
|
-
async function redirectWithResult({
|
|
84
|
-
context,
|
|
85
|
-
actionName,
|
|
86
|
-
actionResult
|
|
87
|
-
}) {
|
|
88
|
-
const cookieValue = encodeBase64(
|
|
89
|
-
encoder.encode(
|
|
90
|
-
JSON.stringify({
|
|
91
|
-
actionName,
|
|
92
|
-
actionResult: serializeActionResult(actionResult)
|
|
93
|
-
})
|
|
94
|
-
)
|
|
95
|
-
);
|
|
96
|
-
context.cookies.set(ACTION_QUERY_PARAMS.actionPayload, cookieValue);
|
|
97
|
-
if (actionResult.error) {
|
|
98
|
-
const referer2 = context.request.headers.get("Referer");
|
|
99
|
-
if (!referer2) {
|
|
100
|
-
throw new Error("Internal: Referer unexpectedly missing from Action POST request.");
|
|
101
|
-
}
|
|
102
|
-
return context.redirect(referer2);
|
|
103
|
-
}
|
|
104
|
-
const referer = getOriginPathname(context.request);
|
|
105
|
-
if (referer) {
|
|
106
|
-
return context.redirect(referer);
|
|
107
|
-
}
|
|
108
|
-
return context.redirect(context.url.pathname);
|
|
109
|
-
}
|
|
110
|
-
function isActionPayload(json) {
|
|
111
|
-
if (typeof json !== "object" || json == null) return false;
|
|
112
|
-
if (!("actionResult" in json) || typeof json.actionResult !== "object") return false;
|
|
113
|
-
if (!("actionName" in json) || typeof json.actionName !== "string") return false;
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
12
|
export {
|
|
117
13
|
onRequest
|
|
118
14
|
};
|
|
@@ -1,31 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getAction } from "./virtual/get-action.js";
|
|
3
|
-
import { serializeActionResult } from "./virtual/shared.js";
|
|
1
|
+
import { getActionContext } from "./virtual/server.js";
|
|
4
2
|
const POST = async (context) => {
|
|
5
|
-
const {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
baseAction = await getAction(url.pathname);
|
|
9
|
-
} catch (e) {
|
|
10
|
-
if (import.meta.env.DEV) throw e;
|
|
11
|
-
console.error(e);
|
|
12
|
-
return new Response(e instanceof Error ? e.message : null, { status: 404 });
|
|
3
|
+
const { action, serializeActionResult } = getActionContext(context);
|
|
4
|
+
if (action?.calledFrom !== "rpc") {
|
|
5
|
+
return new Response("Not found", { status: 404 });
|
|
13
6
|
}
|
|
14
|
-
const
|
|
15
|
-
const contentLength = request.headers.get("Content-Length");
|
|
16
|
-
let args;
|
|
17
|
-
if (!contentType || contentLength === "0") {
|
|
18
|
-
args = void 0;
|
|
19
|
-
} else if (contentType && hasContentType(contentType, formContentTypes)) {
|
|
20
|
-
args = await request.clone().formData();
|
|
21
|
-
} else if (contentType && hasContentType(contentType, ["application/json"])) {
|
|
22
|
-
args = await request.clone().json();
|
|
23
|
-
} else {
|
|
24
|
-
return new Response(null, { status: 415 });
|
|
25
|
-
}
|
|
26
|
-
const { getActionResult, callAction, props, redirect, ...actionAPIContext } = context;
|
|
27
|
-
const action = baseAction.bind(actionAPIContext);
|
|
28
|
-
const result = await action(args);
|
|
7
|
+
const result = await action.handler();
|
|
29
8
|
const serialized = serializeActionResult(result);
|
|
30
9
|
if (serialized.type === "empty") {
|
|
31
10
|
return new Response(null, {
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { APIContext } from '../../types/public/context.js';
|
|
2
|
+
import type { SerializedActionResult } from './virtual/shared.js';
|
|
3
|
+
export type ActionPayload = {
|
|
4
|
+
actionResult: SerializedActionResult;
|
|
5
|
+
actionName: string;
|
|
6
|
+
};
|
|
7
|
+
export type Locals = {
|
|
8
|
+
_actionPayload: ActionPayload;
|
|
9
|
+
};
|
|
10
|
+
export declare const ACTION_API_CONTEXT_SYMBOL: unique symbol;
|
|
2
11
|
export declare const formContentTypes: string[];
|
|
3
12
|
export declare function hasContentType(contentType: string, expected: string[]): boolean;
|
|
4
13
|
export type ActionAPIContext = Omit<APIContext, 'getActionResult' | 'callAction' | 'props' | 'redirect'>;
|
|
@@ -13,3 +22,4 @@ export type MaybePromise<T> = T | Promise<T>;
|
|
|
13
22
|
* `result.error.fields` will be typed with the `name` field.
|
|
14
23
|
*/
|
|
15
24
|
export type ErrorInferenceObject = Record<string, any>;
|
|
25
|
+
export declare function isActionAPIContext(ctx: ActionAPIContext): boolean;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
const ACTION_API_CONTEXT_SYMBOL = Symbol.for("astro.actionAPIContext");
|
|
1
2
|
const formContentTypes = ["application/x-www-form-urlencoded", "multipart/form-data"];
|
|
2
3
|
function hasContentType(contentType, expected) {
|
|
3
4
|
const type = contentType.split(";")[0].toLowerCase();
|
|
4
5
|
return expected.some((t) => type === t);
|
|
5
6
|
}
|
|
7
|
+
function isActionAPIContext(ctx) {
|
|
8
|
+
const symbol = Reflect.get(ctx, ACTION_API_CONTEXT_SYMBOL);
|
|
9
|
+
return symbol === true;
|
|
10
|
+
}
|
|
6
11
|
export {
|
|
12
|
+
ACTION_API_CONTEXT_SYMBOL,
|
|
7
13
|
formContentTypes,
|
|
8
|
-
hasContentType
|
|
14
|
+
hasContentType,
|
|
15
|
+
isActionAPIContext
|
|
9
16
|
};
|
|
@@ -2,6 +2,10 @@ export * from "./shared.js";
|
|
|
2
2
|
function defineAction() {
|
|
3
3
|
throw new Error("[astro:action] `defineAction()` unexpectedly used on the client.");
|
|
4
4
|
}
|
|
5
|
+
function getActionContext() {
|
|
6
|
+
throw new Error("[astro:action] `getActionContext()` unexpectedly used on the client.");
|
|
7
|
+
}
|
|
5
8
|
export {
|
|
6
|
-
defineAction
|
|
9
|
+
defineAction,
|
|
10
|
+
getActionContext
|
|
7
11
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ActionNotFoundError } from "../../../core/errors/errors-data.js";
|
|
2
2
|
import { AstroError } from "../../../core/errors/errors.js";
|
|
3
3
|
async function getAction(path) {
|
|
4
|
-
const pathKeys = path.
|
|
4
|
+
const pathKeys = path.split(".").map((key) => decodeURIComponent(key));
|
|
5
5
|
let { server: actionLookup } = await import("astro:internal-actions");
|
|
6
6
|
if (actionLookup == null || !(typeof actionLookup === "object")) {
|
|
7
7
|
throw new TypeError(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import type {
|
|
3
|
-
import { type
|
|
2
|
+
import type { APIContext } from '../../../types/public/index.js';
|
|
3
|
+
import { type ActionAPIContext, type ErrorInferenceObject, type MaybePromise } from '../utils.js';
|
|
4
|
+
import { type SafeResult, type SerializedActionResult, deserializeActionResult, serializeActionResult } from './shared.js';
|
|
4
5
|
export * from './shared.js';
|
|
5
6
|
export type ActionAccept = 'form' | 'json';
|
|
6
7
|
export type ActionHandler<TInputSchema, TOutput> = TInputSchema extends z.ZodType ? (input: z.infer<TInputSchema>, context: ActionAPIContext) => MaybePromise<TOutput> : (input: any, context: ActionAPIContext) => MaybePromise<TOutput>;
|
|
@@ -18,3 +19,32 @@ export declare function defineAction<TOutput, TAccept extends ActionAccept | und
|
|
|
18
19
|
}): ActionClient<TOutput, TAccept, TInputSchema> & string;
|
|
19
20
|
/** Transform form data to an object based on a Zod schema. */
|
|
20
21
|
export declare function formDataToObject<T extends z.AnyZodObject>(formData: FormData, schema: T): Record<string, unknown>;
|
|
22
|
+
export type ActionMiddlewareContext = {
|
|
23
|
+
/** Information about an incoming action request. */
|
|
24
|
+
action?: {
|
|
25
|
+
/** Whether an action was called using an RPC function or by using an HTML form action. */
|
|
26
|
+
calledFrom: 'rpc' | 'form';
|
|
27
|
+
/** The name of the action. Useful to track the source of an action result during a redirect. */
|
|
28
|
+
name: string;
|
|
29
|
+
/** Programatically call the action to get the result. */
|
|
30
|
+
handler: () => Promise<SafeResult<any, any>>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Manually set the action result accessed via `getActionResult()`.
|
|
34
|
+
* Calling this function from middleware will disable Astro's own action result handling.
|
|
35
|
+
*/
|
|
36
|
+
setActionResult(actionName: string, actionResult: SerializedActionResult): void;
|
|
37
|
+
/**
|
|
38
|
+
* Serialize an action result to stored in a cookie or session.
|
|
39
|
+
* Also used to pass a result to Astro templates via `setActionResult()`.
|
|
40
|
+
*/
|
|
41
|
+
serializeActionResult: typeof serializeActionResult;
|
|
42
|
+
/**
|
|
43
|
+
* Deserialize an action result to access data and error objects.
|
|
44
|
+
*/
|
|
45
|
+
deserializeActionResult: typeof deserializeActionResult;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Access information about Action requests from middleware.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getActionContext(context: APIContext): ActionMiddlewareContext;
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { ActionCalledFromServerError } from "../../../core/errors/errors-data.js";
|
|
3
3
|
import { AstroError } from "../../../core/errors/errors.js";
|
|
4
|
-
import {
|
|
4
|
+
import { ACTION_RPC_ROUTE_PATTERN } from "../../consts.js";
|
|
5
|
+
import {
|
|
6
|
+
formContentTypes,
|
|
7
|
+
hasContentType
|
|
8
|
+
} from "../utils.js";
|
|
9
|
+
import { getAction } from "./get-action.js";
|
|
10
|
+
import {
|
|
11
|
+
ACTION_QUERY_PARAMS,
|
|
12
|
+
ActionError,
|
|
13
|
+
ActionInputError,
|
|
14
|
+
callSafely,
|
|
15
|
+
deserializeActionResult,
|
|
16
|
+
serializeActionResult
|
|
17
|
+
} from "./shared.js";
|
|
5
18
|
export * from "./shared.js";
|
|
6
19
|
function defineAction({
|
|
7
20
|
accept,
|
|
@@ -119,7 +132,74 @@ function unwrapBaseObjectSchema(schema, unparsedInput) {
|
|
|
119
132
|
}
|
|
120
133
|
return schema;
|
|
121
134
|
}
|
|
135
|
+
function getActionContext(context) {
|
|
136
|
+
const callerInfo = getCallerInfo(context);
|
|
137
|
+
const actionResultAlreadySet = Boolean(context.locals._actionPayload);
|
|
138
|
+
let action = void 0;
|
|
139
|
+
if (callerInfo && context.request.method === "POST" && !actionResultAlreadySet) {
|
|
140
|
+
action = {
|
|
141
|
+
calledFrom: callerInfo.from,
|
|
142
|
+
name: callerInfo.name,
|
|
143
|
+
handler: async () => {
|
|
144
|
+
const baseAction = await getAction(callerInfo.name);
|
|
145
|
+
let input;
|
|
146
|
+
try {
|
|
147
|
+
input = await parseRequestBody(context.request);
|
|
148
|
+
} catch (e) {
|
|
149
|
+
if (e instanceof TypeError) {
|
|
150
|
+
return { data: void 0, error: new ActionError({ code: "UNSUPPORTED_MEDIA_TYPE" }) };
|
|
151
|
+
}
|
|
152
|
+
throw e;
|
|
153
|
+
}
|
|
154
|
+
const {
|
|
155
|
+
props: _props,
|
|
156
|
+
getActionResult: _getActionResult,
|
|
157
|
+
callAction: _callAction,
|
|
158
|
+
redirect: _redirect,
|
|
159
|
+
...actionAPIContext
|
|
160
|
+
} = context;
|
|
161
|
+
const handler = baseAction.bind(actionAPIContext);
|
|
162
|
+
return handler(input);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function setActionResult(actionName, actionResult) {
|
|
167
|
+
context.locals._actionPayload = {
|
|
168
|
+
actionResult,
|
|
169
|
+
actionName
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
action,
|
|
174
|
+
setActionResult,
|
|
175
|
+
serializeActionResult,
|
|
176
|
+
deserializeActionResult
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function getCallerInfo(ctx) {
|
|
180
|
+
if (ctx.routePattern === ACTION_RPC_ROUTE_PATTERN) {
|
|
181
|
+
return { from: "rpc", name: ctx.url.pathname.replace(/^.*\/_actions\//, "") };
|
|
182
|
+
}
|
|
183
|
+
const queryParam = ctx.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
|
|
184
|
+
if (queryParam) {
|
|
185
|
+
return { from: "form", name: queryParam };
|
|
186
|
+
}
|
|
187
|
+
return void 0;
|
|
188
|
+
}
|
|
189
|
+
async function parseRequestBody(request) {
|
|
190
|
+
const contentType = request.headers.get("content-type");
|
|
191
|
+
const contentLength = request.headers.get("Content-Length");
|
|
192
|
+
if (!contentType) return void 0;
|
|
193
|
+
if (hasContentType(contentType, formContentTypes)) {
|
|
194
|
+
return await request.clone().formData();
|
|
195
|
+
}
|
|
196
|
+
if (hasContentType(contentType, ["application/json"])) {
|
|
197
|
+
return contentLength === "0" ? void 0 : await request.clone().json();
|
|
198
|
+
}
|
|
199
|
+
throw new TypeError("Unsupported content type");
|
|
200
|
+
}
|
|
122
201
|
export {
|
|
123
202
|
defineAction,
|
|
124
|
-
formDataToObject
|
|
203
|
+
formDataToObject,
|
|
204
|
+
getActionContext
|
|
125
205
|
};
|
|
@@ -4,7 +4,6 @@ export type ActionAPIContext = _ActionAPIContext;
|
|
|
4
4
|
export declare const ACTION_QUERY_PARAMS: {
|
|
5
5
|
actionName: string;
|
|
6
6
|
actionPayload: string;
|
|
7
|
-
actionRedirect: string;
|
|
8
7
|
};
|
|
9
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"];
|
|
10
9
|
export type ActionErrorCode = (typeof ACTION_ERROR_CODES)[number];
|
package/dist/actions/utils.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type fsMod from 'node:fs';
|
|
2
2
|
import type { APIContext } from '../types/public/context.js';
|
|
3
|
-
import type { Locals } from './runtime/
|
|
4
|
-
import type { ActionAPIContext } from './runtime/utils.js';
|
|
3
|
+
import type { ActionAPIContext, Locals } from './runtime/utils.js';
|
|
5
4
|
export declare function hasActionPayload(locals: APIContext['locals']): locals is Locals;
|
|
6
5
|
export declare function createGetActionResult(locals: APIContext['locals']): APIContext['getActionResult'];
|
|
7
6
|
export declare function createCallAction(context: ActionAPIContext): APIContext['callAction'];
|
package/dist/assets/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithRequired } from '../type-utils.js';
|
|
1
|
+
import type { OmitPreservingIndexSignature, Simplify, WithRequired } from '../type-utils.js';
|
|
2
2
|
import type { VALID_INPUT_FORMATS, VALID_OUTPUT_FORMATS } from './consts.js';
|
|
3
3
|
import type { ImageService } from './services/service.js';
|
|
4
4
|
export type ImageQualityPreset = 'low' | 'mid' | 'high' | 'max' | (string & {});
|
|
@@ -48,12 +48,12 @@ export type SrcSetValue = UnresolvedSrcSetValue & {
|
|
|
48
48
|
/**
|
|
49
49
|
* A yet to be resolved image transform. Used by `getImage`
|
|
50
50
|
*/
|
|
51
|
-
export type UnresolvedImageTransform =
|
|
51
|
+
export type UnresolvedImageTransform = Simplify<OmitPreservingIndexSignature<ImageTransform, 'src'> & {
|
|
52
52
|
src: ImageMetadata | string | Promise<{
|
|
53
53
|
default: ImageMetadata;
|
|
54
54
|
}>;
|
|
55
55
|
inferSize?: boolean;
|
|
56
|
-
} & {
|
|
56
|
+
}> & {
|
|
57
57
|
[isESMImport]?: never;
|
|
58
58
|
};
|
|
59
59
|
/**
|