Refactor routing in App component to enhance navigation and improve error handling by integrating dynamic routes and updating the NotFound route.
This commit is contained in:
2
node_modules/astro/dist/actions/runtime/virtual/client.d.ts
generated
vendored
Normal file
2
node_modules/astro/dist/actions/runtime/virtual/client.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './shared.js';
|
||||
export declare function defineAction(): void;
|
7
node_modules/astro/dist/actions/runtime/virtual/client.js
generated
vendored
Normal file
7
node_modules/astro/dist/actions/runtime/virtual/client.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from "./shared.js";
|
||||
function defineAction() {
|
||||
throw new Error("[astro:action] `defineAction()` unexpectedly used on the client.");
|
||||
}
|
||||
export {
|
||||
defineAction
|
||||
};
|
8
node_modules/astro/dist/actions/runtime/virtual/get-action.d.ts
generated
vendored
Normal file
8
node_modules/astro/dist/actions/runtime/virtual/get-action.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { ZodType } from 'zod';
|
||||
import type { ActionAccept, ActionClient } from './server.js';
|
||||
/**
|
||||
* Get server-side action based on the route path.
|
||||
* Imports from the virtual module `astro:internal-actions`, which maps to
|
||||
* the user's `src/actions/index.ts` file at build-time.
|
||||
*/
|
||||
export declare function getAction(path: string): Promise<ActionClient<unknown, ActionAccept, ZodType>>;
|
29
node_modules/astro/dist/actions/runtime/virtual/get-action.js
generated
vendored
Normal file
29
node_modules/astro/dist/actions/runtime/virtual/get-action.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
import { ActionNotFoundError } from "../../../core/errors/errors-data.js";
|
||||
import { AstroError } from "../../../core/errors/errors.js";
|
||||
async function getAction(path) {
|
||||
const pathKeys = path.replace(/^.*\/_actions\//, "").split(".").map((key) => decodeURIComponent(key));
|
||||
let { server: actionLookup } = await import("astro:internal-actions");
|
||||
if (actionLookup == null || !(typeof actionLookup === "object")) {
|
||||
throw new TypeError(
|
||||
`Expected \`server\` export in actions file to be an object. Received ${typeof actionLookup}.`
|
||||
);
|
||||
}
|
||||
for (const key of pathKeys) {
|
||||
if (!(key in actionLookup)) {
|
||||
throw new AstroError({
|
||||
...ActionNotFoundError,
|
||||
message: ActionNotFoundError.message(pathKeys.join("."))
|
||||
});
|
||||
}
|
||||
actionLookup = actionLookup[key];
|
||||
}
|
||||
if (typeof actionLookup !== "function") {
|
||||
throw new TypeError(
|
||||
`Expected handler for action ${pathKeys.join(".")} to be a function. Received ${typeof actionLookup}.`
|
||||
);
|
||||
}
|
||||
return actionLookup;
|
||||
}
|
||||
export {
|
||||
getAction
|
||||
};
|
20
node_modules/astro/dist/actions/runtime/virtual/server.d.ts
generated
vendored
Normal file
20
node_modules/astro/dist/actions/runtime/virtual/server.d.ts
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import { z } from 'zod';
|
||||
import { type ActionAPIContext, type ErrorInferenceObject, type MaybePromise } from '../utils.js';
|
||||
import { type SafeResult } from './shared.js';
|
||||
export * from './shared.js';
|
||||
export type ActionAccept = 'form' | 'json';
|
||||
export type ActionHandler<TInputSchema, TOutput> = TInputSchema extends z.ZodType ? (input: z.infer<TInputSchema>, context: ActionAPIContext) => MaybePromise<TOutput> : (input: any, context: ActionAPIContext) => MaybePromise<TOutput>;
|
||||
export type ActionReturnType<T extends ActionHandler<any, any>> = Awaited<ReturnType<T>>;
|
||||
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>>>) & {
|
||||
queryString: string;
|
||||
orThrow: (input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<Awaited<TOutput>>;
|
||||
} : ((input?: any) => Promise<SafeResult<never, Awaited<TOutput>>>) & {
|
||||
orThrow: (input?: any) => Promise<Awaited<TOutput>>;
|
||||
};
|
||||
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, }: {
|
||||
input?: TInputSchema;
|
||||
accept?: TAccept;
|
||||
handler: ActionHandler<TInputSchema, TOutput>;
|
||||
}): ActionClient<TOutput, TAccept, TInputSchema> & string;
|
||||
/** Transform form data to an object based on a Zod schema. */
|
||||
export declare function formDataToObject<T extends z.AnyZodObject>(formData: FormData, schema: T): Record<string, unknown>;
|
128
node_modules/astro/dist/actions/runtime/virtual/server.js
generated
vendored
Normal file
128
node_modules/astro/dist/actions/runtime/virtual/server.js
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
import { z } from "zod";
|
||||
import { ActionCalledFromServerError } from "../../../core/errors/errors-data.js";
|
||||
import { AstroError } from "../../../core/errors/errors.js";
|
||||
import {
|
||||
isActionAPIContext
|
||||
} from "../utils.js";
|
||||
import { ActionError, ActionInputError, callSafely } from "./shared.js";
|
||||
export * from "./shared.js";
|
||||
function defineAction({
|
||||
accept,
|
||||
input: inputSchema,
|
||||
handler
|
||||
}) {
|
||||
const serverHandler = accept === "form" ? getFormServerHandler(handler, inputSchema) : getJsonServerHandler(handler, inputSchema);
|
||||
async function safeServerHandler(unparsedInput) {
|
||||
if (typeof this === "function" || !isActionAPIContext(this)) {
|
||||
throw new AstroError(ActionCalledFromServerError);
|
||||
}
|
||||
return callSafely(() => serverHandler(unparsedInput, this));
|
||||
}
|
||||
Object.assign(safeServerHandler, {
|
||||
orThrow(unparsedInput) {
|
||||
if (typeof this === "function") {
|
||||
throw new AstroError(ActionCalledFromServerError);
|
||||
}
|
||||
return serverHandler(unparsedInput, this);
|
||||
}
|
||||
});
|
||||
return safeServerHandler;
|
||||
}
|
||||
function getFormServerHandler(handler, inputSchema) {
|
||||
return async (unparsedInput, context) => {
|
||||
if (!(unparsedInput instanceof FormData)) {
|
||||
throw new ActionError({
|
||||
code: "UNSUPPORTED_MEDIA_TYPE",
|
||||
message: "This action only accepts FormData."
|
||||
});
|
||||
}
|
||||
if (!inputSchema) return await handler(unparsedInput, context);
|
||||
const baseSchema = unwrapBaseObjectSchema(inputSchema, unparsedInput);
|
||||
const parsed = await inputSchema.safeParseAsync(
|
||||
baseSchema instanceof z.ZodObject ? formDataToObject(unparsedInput, baseSchema) : unparsedInput
|
||||
);
|
||||
if (!parsed.success) {
|
||||
throw new ActionInputError(parsed.error.issues);
|
||||
}
|
||||
return await handler(parsed.data, context);
|
||||
};
|
||||
}
|
||||
function getJsonServerHandler(handler, inputSchema) {
|
||||
return async (unparsedInput, context) => {
|
||||
if (unparsedInput instanceof FormData) {
|
||||
throw new ActionError({
|
||||
code: "UNSUPPORTED_MEDIA_TYPE",
|
||||
message: "This action only accepts JSON."
|
||||
});
|
||||
}
|
||||
if (!inputSchema) return await handler(unparsedInput, context);
|
||||
const parsed = await inputSchema.safeParseAsync(unparsedInput);
|
||||
if (!parsed.success) {
|
||||
throw new ActionInputError(parsed.error.issues);
|
||||
}
|
||||
return await handler(parsed.data, context);
|
||||
};
|
||||
}
|
||||
function formDataToObject(formData, schema) {
|
||||
const obj = schema._def.unknownKeys === "passthrough" ? Object.fromEntries(formData.entries()) : {};
|
||||
for (const [key, baseValidator] of Object.entries(schema.shape)) {
|
||||
let validator = baseValidator;
|
||||
while (validator instanceof z.ZodOptional || validator instanceof z.ZodNullable || validator instanceof z.ZodDefault) {
|
||||
if (validator instanceof z.ZodDefault && !formData.has(key)) {
|
||||
obj[key] = validator._def.defaultValue();
|
||||
}
|
||||
validator = validator._def.innerType;
|
||||
}
|
||||
if (!formData.has(key) && key in obj) {
|
||||
continue;
|
||||
} else if (validator instanceof z.ZodBoolean) {
|
||||
const val = formData.get(key);
|
||||
obj[key] = val === "true" ? true : val === "false" ? false : formData.has(key);
|
||||
} else if (validator instanceof z.ZodArray) {
|
||||
obj[key] = handleFormDataGetAll(key, formData, validator);
|
||||
} else {
|
||||
obj[key] = handleFormDataGet(key, formData, validator, baseValidator);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
function handleFormDataGetAll(key, formData, validator) {
|
||||
const entries = Array.from(formData.getAll(key));
|
||||
const elementValidator = validator._def.type;
|
||||
if (elementValidator instanceof z.ZodNumber) {
|
||||
return entries.map(Number);
|
||||
} else if (elementValidator instanceof z.ZodBoolean) {
|
||||
return entries.map(Boolean);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
function handleFormDataGet(key, formData, validator, baseValidator) {
|
||||
const value = formData.get(key);
|
||||
if (!value) {
|
||||
return baseValidator instanceof z.ZodOptional ? void 0 : null;
|
||||
}
|
||||
return validator instanceof z.ZodNumber ? Number(value) : value;
|
||||
}
|
||||
function unwrapBaseObjectSchema(schema, unparsedInput) {
|
||||
while (schema instanceof z.ZodEffects || schema instanceof z.ZodPipeline) {
|
||||
if (schema instanceof z.ZodEffects) {
|
||||
schema = schema._def.schema;
|
||||
}
|
||||
if (schema instanceof z.ZodPipeline) {
|
||||
schema = schema._def.in;
|
||||
}
|
||||
}
|
||||
if (schema instanceof z.ZodDiscriminatedUnion) {
|
||||
const typeKey = schema._def.discriminator;
|
||||
const typeValue = unparsedInput.get(typeKey);
|
||||
if (typeof typeValue !== "string") return schema;
|
||||
const objSchema = schema._def.optionsMap.get(typeValue);
|
||||
if (!objSchema) return schema;
|
||||
return objSchema;
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
export {
|
||||
defineAction,
|
||||
formDataToObject
|
||||
};
|
57
node_modules/astro/dist/actions/runtime/virtual/shared.d.ts
generated
vendored
Normal file
57
node_modules/astro/dist/actions/runtime/virtual/shared.d.ts
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
import type { z } from 'zod';
|
||||
import type { ErrorInferenceObject, MaybePromise, ActionAPIContext as _ActionAPIContext } from '../utils.js';
|
||||
export type ActionAPIContext = _ActionAPIContext;
|
||||
export declare const ACTION_QUERY_PARAMS: {
|
||||
actionName: string;
|
||||
actionPayload: string;
|
||||
actionRedirect: string;
|
||||
};
|
||||
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"];
|
||||
export type ActionErrorCode = (typeof ACTION_ERROR_CODES)[number];
|
||||
export declare class ActionError<_T extends ErrorInferenceObject = ErrorInferenceObject> extends Error {
|
||||
type: string;
|
||||
code: ActionErrorCode;
|
||||
status: number;
|
||||
constructor(params: {
|
||||
message?: string;
|
||||
code: ActionErrorCode;
|
||||
stack?: string;
|
||||
});
|
||||
static codeToStatus(code: ActionErrorCode): number;
|
||||
static statusToCode(status: number): ActionErrorCode;
|
||||
static fromJson(body: any): ActionError<ErrorInferenceObject>;
|
||||
}
|
||||
export declare function isActionError(error?: unknown): error is ActionError;
|
||||
export declare function isInputError<T extends ErrorInferenceObject>(error?: ActionError<T>): error is ActionInputError<T>;
|
||||
export declare function isInputError(error?: unknown): error is ActionInputError<ErrorInferenceObject>;
|
||||
export type SafeResult<TInput extends ErrorInferenceObject, TOutput> = {
|
||||
data: TOutput;
|
||||
error: undefined;
|
||||
} | {
|
||||
data: undefined;
|
||||
error: ActionError<TInput>;
|
||||
};
|
||||
export declare class ActionInputError<T extends ErrorInferenceObject> extends ActionError {
|
||||
type: string;
|
||||
issues: z.ZodIssue[];
|
||||
fields: z.ZodError<T>['formErrors']['fieldErrors'];
|
||||
constructor(issues: z.ZodIssue[]);
|
||||
}
|
||||
export declare function callSafely<TOutput>(handler: () => MaybePromise<TOutput>): Promise<SafeResult<z.ZodType, TOutput>>;
|
||||
export declare function getActionQueryString(name: string): string;
|
||||
export type SerializedActionResult = {
|
||||
type: 'data';
|
||||
contentType: 'application/json+devalue';
|
||||
status: 200;
|
||||
body: string;
|
||||
} | {
|
||||
type: 'error';
|
||||
contentType: 'application/json';
|
||||
status: number;
|
||||
body: string;
|
||||
} | {
|
||||
type: 'empty';
|
||||
status: 204;
|
||||
};
|
||||
export declare function serializeActionResult(res: SafeResult<any, any>): SerializedActionResult;
|
||||
export declare function deserializeActionResult(res: SerializedActionResult): SafeResult<any, any>;
|
226
node_modules/astro/dist/actions/runtime/virtual/shared.js
generated
vendored
Normal file
226
node_modules/astro/dist/actions/runtime/virtual/shared.js
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
import { parse as devalueParse, stringify as devalueStringify } from "devalue";
|
||||
import { REDIRECT_STATUS_CODES } from "../../../core/constants.js";
|
||||
import { ActionsReturnedInvalidDataError } from "../../../core/errors/errors-data.js";
|
||||
import { AstroError } from "../../../core/errors/errors.js";
|
||||
import { ACTION_QUERY_PARAMS as _ACTION_QUERY_PARAMS } from "../../consts.js";
|
||||
const ACTION_QUERY_PARAMS = _ACTION_QUERY_PARAMS;
|
||||
const ACTION_ERROR_CODES = [
|
||||
"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"
|
||||
];
|
||||
const codeToStatusMap = {
|
||||
// Implemented from tRPC error code table
|
||||
// https://trpc.io/docs/server/error-handling#error-codes
|
||||
BAD_REQUEST: 400,
|
||||
UNAUTHORIZED: 401,
|
||||
FORBIDDEN: 403,
|
||||
NOT_FOUND: 404,
|
||||
TIMEOUT: 405,
|
||||
CONFLICT: 409,
|
||||
PRECONDITION_FAILED: 412,
|
||||
PAYLOAD_TOO_LARGE: 413,
|
||||
UNSUPPORTED_MEDIA_TYPE: 415,
|
||||
UNPROCESSABLE_CONTENT: 422,
|
||||
TOO_MANY_REQUESTS: 429,
|
||||
CLIENT_CLOSED_REQUEST: 499,
|
||||
INTERNAL_SERVER_ERROR: 500
|
||||
};
|
||||
const statusToCodeMap = Object.entries(codeToStatusMap).reduce(
|
||||
// reverse the key-value pairs
|
||||
(acc, [key, value]) => ({ ...acc, [value]: key }),
|
||||
{}
|
||||
);
|
||||
class ActionError extends Error {
|
||||
type = "AstroActionError";
|
||||
code = "INTERNAL_SERVER_ERROR";
|
||||
status = 500;
|
||||
constructor(params) {
|
||||
super(params.message);
|
||||
this.code = params.code;
|
||||
this.status = ActionError.codeToStatus(params.code);
|
||||
if (params.stack) {
|
||||
this.stack = params.stack;
|
||||
}
|
||||
}
|
||||
static codeToStatus(code) {
|
||||
return codeToStatusMap[code];
|
||||
}
|
||||
static statusToCode(status) {
|
||||
return statusToCodeMap[status] ?? "INTERNAL_SERVER_ERROR";
|
||||
}
|
||||
static fromJson(body) {
|
||||
if (isInputError(body)) {
|
||||
return new ActionInputError(body.issues);
|
||||
}
|
||||
if (isActionError(body)) {
|
||||
return new ActionError(body);
|
||||
}
|
||||
return new ActionError({
|
||||
code: "INTERNAL_SERVER_ERROR"
|
||||
});
|
||||
}
|
||||
}
|
||||
function isActionError(error) {
|
||||
return typeof error === "object" && error != null && "type" in error && error.type === "AstroActionError";
|
||||
}
|
||||
function isInputError(error) {
|
||||
return typeof error === "object" && error != null && "type" in error && error.type === "AstroActionInputError" && "issues" in error && Array.isArray(error.issues);
|
||||
}
|
||||
class ActionInputError extends ActionError {
|
||||
type = "AstroActionInputError";
|
||||
// We don't expose all ZodError properties.
|
||||
// Not all properties will serialize from server to client,
|
||||
// and we don't want to import the full ZodError object into the client.
|
||||
issues;
|
||||
fields;
|
||||
constructor(issues) {
|
||||
super({
|
||||
message: `Failed to validate: ${JSON.stringify(issues, null, 2)}`,
|
||||
code: "BAD_REQUEST"
|
||||
});
|
||||
this.issues = issues;
|
||||
this.fields = {};
|
||||
for (const issue of issues) {
|
||||
if (issue.path.length > 0) {
|
||||
const key = issue.path[0].toString();
|
||||
this.fields[key] ??= [];
|
||||
this.fields[key]?.push(issue.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async function callSafely(handler) {
|
||||
try {
|
||||
const data = await handler();
|
||||
return { data, error: void 0 };
|
||||
} catch (e) {
|
||||
if (e instanceof ActionError) {
|
||||
return { data: void 0, error: e };
|
||||
}
|
||||
return {
|
||||
data: void 0,
|
||||
error: new ActionError({
|
||||
message: e instanceof Error ? e.message : "Unknown error",
|
||||
code: "INTERNAL_SERVER_ERROR"
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
function getActionQueryString(name) {
|
||||
const searchParams = new URLSearchParams({ [_ACTION_QUERY_PARAMS.actionName]: name });
|
||||
return `?${searchParams.toString()}`;
|
||||
}
|
||||
function serializeActionResult(res) {
|
||||
if (res.error) {
|
||||
if (import.meta.env?.DEV) {
|
||||
actionResultErrorStack.set(res.error.stack);
|
||||
}
|
||||
return {
|
||||
type: "error",
|
||||
status: res.error.status,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
...res.error,
|
||||
message: res.error.message
|
||||
})
|
||||
};
|
||||
}
|
||||
if (res.data === void 0) {
|
||||
return {
|
||||
type: "empty",
|
||||
status: 204
|
||||
};
|
||||
}
|
||||
let body;
|
||||
try {
|
||||
body = devalueStringify(res.data, {
|
||||
// Add support for URL objects
|
||||
URL: (value) => value instanceof URL && value.href
|
||||
});
|
||||
} catch (e) {
|
||||
let hint = ActionsReturnedInvalidDataError.hint;
|
||||
if (res.data instanceof Response) {
|
||||
hint = REDIRECT_STATUS_CODES.includes(res.data.status) ? "If you need to redirect when the action succeeds, trigger a redirect where the action is called. See the Actions guide for server and client redirect examples: https://docs.astro.build/en/guides/actions." : "If you need to return a Response object, try using a server endpoint instead. See https://docs.astro.build/en/guides/endpoints/#server-endpoints-api-routes";
|
||||
}
|
||||
throw new AstroError({
|
||||
...ActionsReturnedInvalidDataError,
|
||||
message: ActionsReturnedInvalidDataError.message(String(e)),
|
||||
hint
|
||||
});
|
||||
}
|
||||
return {
|
||||
type: "data",
|
||||
status: 200,
|
||||
contentType: "application/json+devalue",
|
||||
body
|
||||
};
|
||||
}
|
||||
function deserializeActionResult(res) {
|
||||
if (res.type === "error") {
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(res.body);
|
||||
} catch {
|
||||
return {
|
||||
data: void 0,
|
||||
error: new ActionError({
|
||||
message: res.body,
|
||||
code: "INTERNAL_SERVER_ERROR"
|
||||
})
|
||||
};
|
||||
}
|
||||
if (import.meta.env?.PROD) {
|
||||
return { error: ActionError.fromJson(json), data: void 0 };
|
||||
} else {
|
||||
const error = ActionError.fromJson(json);
|
||||
error.stack = actionResultErrorStack.get();
|
||||
return {
|
||||
error,
|
||||
data: void 0
|
||||
};
|
||||
}
|
||||
}
|
||||
if (res.type === "empty") {
|
||||
return { data: void 0, error: void 0 };
|
||||
}
|
||||
return {
|
||||
data: devalueParse(res.body, {
|
||||
URL: (href) => new URL(href)
|
||||
}),
|
||||
error: void 0
|
||||
};
|
||||
}
|
||||
const actionResultErrorStack = /* @__PURE__ */ function actionResultErrorStackFn() {
|
||||
let errorStack;
|
||||
return {
|
||||
set(stack) {
|
||||
errorStack = stack;
|
||||
},
|
||||
get() {
|
||||
return errorStack;
|
||||
}
|
||||
};
|
||||
}();
|
||||
export {
|
||||
ACTION_ERROR_CODES,
|
||||
ACTION_QUERY_PARAMS,
|
||||
ActionError,
|
||||
ActionInputError,
|
||||
callSafely,
|
||||
deserializeActionResult,
|
||||
getActionQueryString,
|
||||
isActionError,
|
||||
isInputError,
|
||||
serializeActionResult
|
||||
};
|
Reference in New Issue
Block a user