astro 4.11.4 → 4.11.6

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.
@@ -4,7 +4,7 @@ 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 { Accept, ActionClient, InputSchema } from '../actions/runtime/virtual/server.js';
7
+ import type { ActionAccept, ActionClient, ActionInputSchema } from '../actions/runtime/virtual/server.js';
8
8
  import type { RemotePattern } from '../assets/utils/remotePattern.js';
9
9
  import type { AssetsPrefix, SerializedSSRManifest } from '../core/app/types.js';
10
10
  import type { PageBuildData } from '../core/build/types.js';
@@ -2025,6 +2025,36 @@ export interface AstroUserConfig {
2025
2025
  * ```
2026
2026
  */
2027
2027
  schema?: EnvSchema;
2028
+ /**
2029
+ * @docs
2030
+ * @name experimental.env.validateSecrets
2031
+ * @kind h4
2032
+ * @type {boolean}
2033
+ * @default `false`
2034
+ * @version 4.11.6
2035
+ * @description
2036
+ *
2037
+ * Whether or not to validate secrets on the server when starting the dev server or running a build.
2038
+ *
2039
+ * By default, only public variables are validated on the server when starting the dev server or a build, and private variables are validated at runtime only. If enabled, private variables will also be checked on start. This is useful in some continuous integration (CI) pipelines to make sure all your secrets are correctly set before deploying.
2040
+ *
2041
+ * ```js
2042
+ * // astro.config.mjs
2043
+ * import { defineConfig, envField } from "astro/config"
2044
+ *
2045
+ * export default defineConfig({
2046
+ * experimental: {
2047
+ * env: {
2048
+ * schema: {
2049
+ * // ...
2050
+ * },
2051
+ * validateSecrets: true
2052
+ * }
2053
+ * }
2054
+ * })
2055
+ * ```
2056
+ */
2057
+ validateSecrets?: boolean;
2028
2058
  };
2029
2059
  };
2030
2060
  }
@@ -2492,7 +2522,7 @@ interface AstroSharedContext<Props extends Record<string, any> = Record<string,
2492
2522
  /**
2493
2523
  * Get action result on the server when using a form POST.
2494
2524
  */
2495
- getActionResult: <TAccept extends Accept, TInputSchema extends InputSchema<TAccept>, TAction extends ActionClient<unknown, TAccept, TInputSchema>>(action: TAction) => Awaited<ReturnType<TAction['safe']>> | undefined;
2525
+ getActionResult: <TAccept extends ActionAccept, TInputSchema extends ActionInputSchema<TAccept>, TAction extends ActionClient<unknown, TAccept, TInputSchema>>(action: TAction) => Awaited<ReturnType<TAction['safe']>> | undefined;
2496
2526
  /**
2497
2527
  * Route parameters for this request if this is a dynamic route.
2498
2528
  */
@@ -7,3 +7,13 @@ export type MaybePromise<T> = T | Promise<T>;
7
7
  * the user's `src/actions/index.ts` file at build-time.
8
8
  */
9
9
  export declare function getAction(path: string): Promise<((param: unknown) => MaybePromise<unknown>) | undefined>;
10
+ /**
11
+ * Used to preserve the input schema type in the error object.
12
+ * This allows for type inference on the `fields` property
13
+ * when type narrowed to an `ActionInputError`.
14
+ *
15
+ * Example: Action has an input schema of `{ name: z.string() }`.
16
+ * When calling the action and checking `isInputError(result.error)`,
17
+ * `result.error.fields` will be typed with the `name` field.
18
+ */
19
+ export type ErrorInferenceObject = Record<string, any>;
@@ -1,23 +1,24 @@
1
1
  import { z } from 'zod';
2
2
  import { type ActionAPIContext, getApiContext as _getApiContext } from '../store.js';
3
- import { type MaybePromise } from '../utils.js';
4
- import { type ErrorInferenceObject, type SafeResult } from './shared.js';
3
+ import type { ErrorInferenceObject, MaybePromise } from '../utils.js';
4
+ import { type SafeResult } from './shared.js';
5
5
  export * from './shared.js';
6
6
  export { z } from 'zod';
7
7
  /** @deprecated Access context from the second `handler()` parameter. */
8
8
  export declare const getApiContext: typeof _getApiContext;
9
- export type Accept = 'form' | 'json';
10
- export type InputSchema<T extends Accept> = T extends 'form' ? z.AnyZodObject | z.ZodType<FormData> : z.ZodType;
11
- type Handler<TInputSchema, TOutput> = TInputSchema extends z.ZodType ? (input: z.infer<TInputSchema>, context: ActionAPIContext) => MaybePromise<TOutput> : (input: any, context: ActionAPIContext) => MaybePromise<TOutput>;
12
- export type ActionClient<TOutput, TAccept extends Accept, TInputSchema extends InputSchema<TAccept> | undefined> = TInputSchema extends z.ZodType ? ((input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<Awaited<TOutput>>) & {
9
+ export type ActionAccept = 'form' | 'json';
10
+ export type ActionInputSchema<T extends ActionAccept | undefined> = T extends 'form' ? z.AnyZodObject | z.ZodType<FormData> : z.ZodType;
11
+ export type ActionHandler<TInputSchema, TOutput> = TInputSchema extends z.ZodType ? (input: z.infer<TInputSchema>, context: ActionAPIContext) => MaybePromise<TOutput> : (input: any, context: ActionAPIContext) => MaybePromise<TOutput>;
12
+ export type ActionReturnType<T extends ActionHandler<any, any>> = Awaited<ReturnType<T>>;
13
+ 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<Awaited<TOutput>>) & {
13
14
  safe: (input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<SafeResult<z.input<TInputSchema> extends ErrorInferenceObject ? z.input<TInputSchema> : ErrorInferenceObject, Awaited<TOutput>>>;
14
15
  } : ((input?: any) => Promise<Awaited<TOutput>>) & {
15
16
  safe: (input?: any) => Promise<SafeResult<never, Awaited<TOutput>>>;
16
17
  };
17
- export declare function defineAction<TOutput, TAccept extends Accept = 'json', TInputSchema extends InputSchema<Accept> | undefined = TAccept extends 'form' ? z.ZodType<FormData> : undefined>({ accept, input: inputSchema, handler, }: {
18
+ 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, }: {
18
19
  input?: TInputSchema;
19
20
  accept?: TAccept;
20
- handler: Handler<TInputSchema, TOutput>;
21
+ handler: ActionHandler<TInputSchema, TOutput>;
21
22
  }): ActionClient<TOutput, TAccept, TInputSchema>;
22
23
  /** Transform form data to an object based on a Zod schema. */
23
24
  export declare function formDataToObject<T extends z.AnyZodObject>(formData: FormData, schema: T): Record<string, unknown>;
@@ -1,11 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { getApiContext as _getApiContext } from "../store.js";
3
- import {} from "../utils.js";
4
- import {
5
- ActionError,
6
- ActionInputError,
7
- callSafely
8
- } from "./shared.js";
3
+ import { ActionError, ActionInputError, callSafely } from "./shared.js";
9
4
  export * from "./shared.js";
10
5
  import { z as z2 } from "zod";
11
6
  const getApiContext = _getApiContext;
@@ -58,8 +53,8 @@ function formDataToObject(formData, schema) {
58
53
  const obj = {};
59
54
  for (const [key, baseValidator] of Object.entries(schema.shape)) {
60
55
  let validator = baseValidator;
61
- if (baseValidator instanceof z.ZodOptional || baseValidator instanceof z.ZodNullable) {
62
- validator = baseValidator._def.innerType;
56
+ while (validator instanceof z.ZodOptional || validator instanceof z.ZodNullable) {
57
+ validator = validator._def.innerType;
63
58
  }
64
59
  if (validator instanceof z.ZodBoolean) {
65
60
  obj[key] = formData.has(key);
@@ -1,7 +1,7 @@
1
1
  import type { z } from 'zod';
2
- import type { MaybePromise } from '../utils.js';
3
- type ActionErrorCode = '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
- export type ErrorInferenceObject = Record<string, any>;
2
+ import type { ErrorInferenceObject, MaybePromise } from '../utils.js';
3
+ 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
+ export type ActionErrorCode = (typeof ACTION_ERROR_CODES)[number];
5
5
  export declare class ActionError<T extends ErrorInferenceObject = ErrorInferenceObject> extends Error {
6
6
  type: string;
7
7
  code: ActionErrorCode;
@@ -16,6 +16,7 @@ export declare class ActionError<T extends ErrorInferenceObject = ErrorInference
16
16
  static fromResponse(res: Response): Promise<ActionError<ErrorInferenceObject>>;
17
17
  }
18
18
  export declare function isInputError<T extends ErrorInferenceObject>(error?: ActionError<T>): error is ActionInputError<T>;
19
+ export declare function isInputError(error?: unknown): error is ActionInputError<ErrorInferenceObject>;
19
20
  export type SafeResult<TInput extends ErrorInferenceObject, TOutput> = {
20
21
  data: TOutput;
21
22
  error: undefined;
@@ -35,4 +36,3 @@ export declare function getActionProps<T extends (args: FormData) => MaybePromis
35
36
  readonly name: "_astroAction";
36
37
  readonly value: string;
37
38
  };
38
- export {};
@@ -1,3 +1,18 @@
1
+ const ACTION_ERROR_CODES = [
2
+ "BAD_REQUEST",
3
+ "UNAUTHORIZED",
4
+ "FORBIDDEN",
5
+ "NOT_FOUND",
6
+ "TIMEOUT",
7
+ "CONFLICT",
8
+ "PRECONDITION_FAILED",
9
+ "PAYLOAD_TOO_LARGE",
10
+ "UNSUPPORTED_MEDIA_TYPE",
11
+ "UNPROCESSABLE_CONTENT",
12
+ "TOO_MANY_REQUESTS",
13
+ "CLIENT_CLOSED_REQUEST",
14
+ "INTERNAL_SERVER_ERROR"
15
+ ];
1
16
  const codeToStatusMap = {
2
17
  // Implemented from tRPC error code table
3
18
  // https://trpc.io/docs/server/error-handling#error-codes
@@ -103,6 +118,7 @@ function getActionProps(action) {
103
118
  };
104
119
  }
105
120
  export {
121
+ ACTION_ERROR_CODES,
106
122
  ActionError,
107
123
  ActionInputError,
108
124
  callSafely,
@@ -216,9 +216,9 @@ class App {
216
216
  }
217
217
  const pathname = this.#getPathnameFromRequest(request);
218
218
  const defaultStatus = this.#getDefaultStatusCode(routeData, pathname);
219
- const mod = await this.#pipeline.getModuleForRoute(routeData);
220
219
  let response;
221
220
  try {
221
+ const mod = await this.#pipeline.getModuleForRoute(routeData);
222
222
  const renderContext = RenderContext.create({
223
223
  pipeline: this.#pipeline,
224
224
  locals,
@@ -37,8 +37,8 @@ function vitePluginPages(opts, internals) {
37
37
  for (const pageData of pageDatas) {
38
38
  const resolvedPage = await this.resolve(pageData.moduleSpecifier);
39
39
  if (resolvedPage) {
40
- imports.push(`const page = () => import(${JSON.stringify(pageData.moduleSpecifier)});`);
41
- exports.push(`export { page }`);
40
+ imports.push(`import * as _page from ${JSON.stringify(pageData.moduleSpecifier)};`);
41
+ exports.push(`export const page = () => _page`);
42
42
  imports.push(`import { renderers } from "${RENDERERS_MODULE_ID}";`);
43
43
  exports.push(`export { renderers };`);
44
44
  return `${imports.join("\n")}${exports.join("\n")}`;
@@ -26,7 +26,7 @@ function getNonPrerenderOnlyChunks(bundle, internals) {
26
26
  const prerenderOnlyEntryChunks = /* @__PURE__ */ new Set();
27
27
  const nonPrerenderOnlyEntryChunks = /* @__PURE__ */ new Set();
28
28
  for (const chunk of chunks) {
29
- if (chunk.type === "chunk" && (chunk.isEntry || chunk.isDynamicEntry)) {
29
+ if (chunk.type === "chunk" && chunk.isEntry) {
30
30
  if (chunk.facadeModuleId?.startsWith(ASTRO_PAGE_RESOLVED_MODULE_ID)) {
31
31
  const pageDatas = getPagesFromVirtualModulePageName(
32
32
  internals,
@@ -58,6 +58,9 @@ export declare const ASTRO_CONFIG_DEFAULTS: {
58
58
  clientPrerender: false;
59
59
  globalRoutePriority: false;
60
60
  rewriting: false;
61
+ env: {
62
+ validateSecrets: false;
63
+ };
61
64
  };
62
65
  };
63
66
  export declare const AstroConfigSchema: z.ZodObject<{
@@ -398,7 +401,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
398
401
  globalRoutePriority: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
399
402
  rewriting: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
400
403
  env: z.ZodOptional<z.ZodObject<{
401
- schema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodIntersection<z.ZodUnion<[z.ZodObject<{
404
+ schema: z.ZodOptional<z.ZodRecord<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, z.ZodIntersection<z.ZodUnion<[z.ZodObject<{
402
405
  context: z.ZodLiteral<"client">;
403
406
  access: z.ZodLiteral<"public">;
404
407
  }, "strip", z.ZodTypeAny, {
@@ -523,7 +526,9 @@ export declare const AstroConfigSchema: z.ZodObject<{
523
526
  default?: string | undefined;
524
527
  optional?: boolean | undefined;
525
528
  }>]>>>>;
529
+ validateSecrets: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
526
530
  }, "strict", z.ZodTypeAny, {
531
+ validateSecrets: boolean;
527
532
  schema?: Record<string, ({
528
533
  context: "client";
529
534
  access: "public";
@@ -564,6 +569,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
564
569
  optional?: boolean | undefined;
565
570
  })> | undefined;
566
571
  }, {
572
+ validateSecrets?: boolean | undefined;
567
573
  schema?: Record<string, ({
568
574
  context: "client";
569
575
  access: "public";
@@ -613,6 +619,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
613
619
  globalRoutePriority: boolean;
614
620
  rewriting: boolean;
615
621
  env?: {
622
+ validateSecrets: boolean;
616
623
  schema?: Record<string, ({
617
624
  context: "client";
618
625
  access: "public";
@@ -662,6 +669,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
662
669
  globalRoutePriority?: boolean | undefined;
663
670
  rewriting?: boolean | undefined;
664
671
  env?: {
672
+ validateSecrets?: boolean | undefined;
665
673
  schema?: Record<string, ({
666
674
  context: "client";
667
675
  access: "public";
@@ -788,6 +796,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
788
796
  globalRoutePriority: boolean;
789
797
  rewriting: boolean;
790
798
  env?: {
799
+ validateSecrets: boolean;
791
800
  schema?: Record<string, ({
792
801
  context: "client";
793
802
  access: "public";
@@ -950,6 +959,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
950
959
  globalRoutePriority?: boolean | undefined;
951
960
  rewriting?: boolean | undefined;
952
961
  env?: {
962
+ validateSecrets?: boolean | undefined;
953
963
  schema?: Record<string, ({
954
964
  context: "client";
955
965
  access: "public";
@@ -1332,7 +1342,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1332
1342
  globalRoutePriority: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
1333
1343
  rewriting: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
1334
1344
  env: z.ZodOptional<z.ZodObject<{
1335
- schema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodIntersection<z.ZodUnion<[z.ZodObject<{
1345
+ schema: z.ZodOptional<z.ZodRecord<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, z.ZodIntersection<z.ZodUnion<[z.ZodObject<{
1336
1346
  context: z.ZodLiteral<"client">;
1337
1347
  access: z.ZodLiteral<"public">;
1338
1348
  }, "strip", z.ZodTypeAny, {
@@ -1457,7 +1467,9 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1457
1467
  default?: string | undefined;
1458
1468
  optional?: boolean | undefined;
1459
1469
  }>]>>>>;
1470
+ validateSecrets: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
1460
1471
  }, "strict", z.ZodTypeAny, {
1472
+ validateSecrets: boolean;
1461
1473
  schema?: Record<string, ({
1462
1474
  context: "client";
1463
1475
  access: "public";
@@ -1498,6 +1510,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1498
1510
  optional?: boolean | undefined;
1499
1511
  })> | undefined;
1500
1512
  }, {
1513
+ validateSecrets?: boolean | undefined;
1501
1514
  schema?: Record<string, ({
1502
1515
  context: "client";
1503
1516
  access: "public";
@@ -1547,6 +1560,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1547
1560
  globalRoutePriority: boolean;
1548
1561
  rewriting: boolean;
1549
1562
  env?: {
1563
+ validateSecrets: boolean;
1550
1564
  schema?: Record<string, ({
1551
1565
  context: "client";
1552
1566
  access: "public";
@@ -1596,6 +1610,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1596
1610
  globalRoutePriority?: boolean | undefined;
1597
1611
  rewriting?: boolean | undefined;
1598
1612
  env?: {
1613
+ validateSecrets?: boolean | undefined;
1599
1614
  schema?: Record<string, ({
1600
1615
  context: "client";
1601
1616
  access: "public";
@@ -1797,6 +1812,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1797
1812
  globalRoutePriority: boolean;
1798
1813
  rewriting: boolean;
1799
1814
  env?: {
1815
+ validateSecrets: boolean;
1800
1816
  schema?: Record<string, ({
1801
1817
  context: "client";
1802
1818
  access: "public";
@@ -1959,6 +1975,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1959
1975
  globalRoutePriority?: boolean | undefined;
1960
1976
  rewriting?: boolean | undefined;
1961
1977
  env?: {
1978
+ validateSecrets?: boolean | undefined;
1962
1979
  schema?: Record<string, ({
1963
1980
  context: "client";
1964
1981
  access: "public";
@@ -2086,6 +2103,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
2086
2103
  globalRoutePriority: boolean;
2087
2104
  rewriting: boolean;
2088
2105
  env?: {
2106
+ validateSecrets: boolean;
2089
2107
  schema?: Record<string, ({
2090
2108
  context: "client";
2091
2109
  access: "public";
@@ -2248,6 +2266,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
2248
2266
  globalRoutePriority?: boolean | undefined;
2249
2267
  rewriting?: boolean | undefined;
2250
2268
  env?: {
2269
+ validateSecrets?: boolean | undefined;
2251
2270
  schema?: Record<string, ({
2252
2271
  context: "client";
2253
2272
  access: "public";
@@ -2375,6 +2394,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
2375
2394
  globalRoutePriority: boolean;
2376
2395
  rewriting: boolean;
2377
2396
  env?: {
2397
+ validateSecrets: boolean;
2378
2398
  schema?: Record<string, ({
2379
2399
  context: "client";
2380
2400
  access: "public";
@@ -2537,6 +2557,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
2537
2557
  globalRoutePriority?: boolean | undefined;
2538
2558
  rewriting?: boolean | undefined;
2539
2559
  env?: {
2560
+ validateSecrets?: boolean | undefined;
2540
2561
  schema?: Record<string, ({
2541
2562
  context: "client";
2542
2563
  access: "public";
@@ -2664,6 +2685,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
2664
2685
  globalRoutePriority: boolean;
2665
2686
  rewriting: boolean;
2666
2687
  env?: {
2688
+ validateSecrets: boolean;
2667
2689
  schema?: Record<string, ({
2668
2690
  context: "client";
2669
2691
  access: "public";
@@ -2826,6 +2848,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
2826
2848
  globalRoutePriority?: boolean | undefined;
2827
2849
  rewriting?: boolean | undefined;
2828
2850
  env?: {
2851
+ validateSecrets?: boolean | undefined;
2829
2852
  schema?: Record<string, ({
2830
2853
  context: "client";
2831
2854
  access: "public";
@@ -47,7 +47,10 @@ const ASTRO_CONFIG_DEFAULTS = {
47
47
  contentCollectionJsonSchema: false,
48
48
  clientPrerender: false,
49
49
  globalRoutePriority: false,
50
- rewriting: false
50
+ rewriting: false,
51
+ env: {
52
+ validateSecrets: false
53
+ }
51
54
  }
52
55
  };
53
56
  const AstroConfigSchema = z.object({
@@ -328,7 +331,8 @@ const AstroConfigSchema = z.object({
328
331
  globalRoutePriority: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.globalRoutePriority),
329
332
  rewriting: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.rewriting),
330
333
  env: z.object({
331
- schema: EnvSchema.optional()
334
+ schema: EnvSchema.optional(),
335
+ validateSecrets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.env.validateSecrets)
332
336
  }).strict().optional()
333
337
  }).strict(
334
338
  `Invalid or outdated experimental feature.
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "4.11.4";
1
+ const ASTRO_VERSION = "4.11.6";
2
2
  const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
3
3
  const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
4
4
  const DEFAULT_404_COMPONENT = "astro-default-404.astro";
@@ -90,17 +90,8 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
90
90
  customLogger: createViteLogger(logger, settings.config.vite.logLevel),
91
91
  appType: "custom",
92
92
  optimizeDeps: {
93
- // Scan all files within `srcDir` except for known server-code (e.g endpoints)
94
- entries: [
95
- `${srcDirPattern}!(pages)/**/*`,
96
- // All files except for pages
97
- `${srcDirPattern}pages/**/!(*.js|*.mjs|*.ts|*.mts)`,
98
- // All pages except for endpoints
99
- `${srcDirPattern}pages/**/_*.{js,mjs,ts,mts}`,
100
- // Remaining JS/TS files prefixed with `_` (not endpoints)
101
- `${srcDirPattern}pages/**/_*/**/*.{js,mjs,ts,mts}`
102
- // Remaining JS/TS files within directories prefixed with `_` (not endpoints)
103
- ],
93
+ // Scan for component code within `srcDir`
94
+ entries: [`${srcDirPattern}**/*.{jsx,tsx,vue,svelte,html,astro}`],
104
95
  exclude: ["astro", "node-fetch"]
105
96
  },
106
97
  plugins: [
@@ -28,7 +28,9 @@ async function createContainer({
28
28
  base,
29
29
  server: { host, headers, open: serverOpen }
30
30
  } = settings.config;
31
- const open = typeof serverOpen == "string" ? serverOpen : serverOpen ? base : false;
31
+ const isServerOpenURL = typeof serverOpen == "string" && !isRestart;
32
+ const isServerOpenBoolean = serverOpen && !isRestart;
33
+ const open = isServerOpenURL ? serverOpen : isServerOpenBoolean ? base : false;
32
34
  const rendererClientEntries = settings.renderers.map((r) => r.clientEntrypoint).filter(Boolean);
33
35
  const viteConfig = await createVite(
34
36
  {
@@ -19,7 +19,7 @@ async function dev(inlineConfig) {
19
19
  await telemetry.record([]);
20
20
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
21
21
  const logger = restart.container.logger;
22
- const currentVersion = "4.11.4";
22
+ const currentVersion = "4.11.6";
23
23
  const isPrerelease = currentVersion.includes("-");
24
24
  if (!isPrerelease) {
25
25
  try {
@@ -1106,7 +1106,7 @@ export declare const RouteNotFound: {
1106
1106
  export declare const EnvInvalidVariables: {
1107
1107
  name: string;
1108
1108
  title: string;
1109
- message: (variables: string) => string;
1109
+ message: (errors: Array<string>) => string;
1110
1110
  };
1111
1111
  /**
1112
1112
  * @docs
@@ -399,9 +399,9 @@ const RouteNotFound = {
399
399
  const EnvInvalidVariables = {
400
400
  name: "EnvInvalidVariables",
401
401
  title: "Invalid Environment Variables",
402
- message: (variables) => `The following environment variables do not match the data type and/or properties defined in \`experimental.env.schema\`:
402
+ message: (errors) => `The following environment variables defined in \`experimental.env.schema\` are invalid:
403
403
 
404
- ${variables}
404
+ ${errors.map((err) => `- ${err}`).join("\n")}
405
405
  `
406
406
  };
407
407
  const EnvInvalidVariable = {
@@ -37,7 +37,7 @@ function serverStart({
37
37
  host,
38
38
  base
39
39
  }) {
40
- const version = "4.11.4";
40
+ const version = "4.11.6";
41
41
  const localPrefix = `${dim("\u2503")} Local `;
42
42
  const networkPrefix = `${dim("\u2503")} Network `;
43
43
  const emptyPrefix = " ".repeat(11);
@@ -269,7 +269,7 @@ function printHelp({
269
269
  message.push(
270
270
  linebreak(),
271
271
  ` ${bgGreen(black(` ${commandName} `))} ${green(
272
- `v${"4.11.4"}`
272
+ `v${"4.11.6"}`
273
273
  )} ${headline}`
274
274
  );
275
275
  }
@@ -220,7 +220,7 @@ declare const EnvFieldMetadata: z.ZodUnion<[z.ZodObject<{
220
220
  context: "server";
221
221
  access: "secret";
222
222
  }>]>;
223
- export declare const EnvSchema: z.ZodRecord<z.ZodString, z.ZodIntersection<z.ZodUnion<[z.ZodObject<{
223
+ export declare const EnvSchema: z.ZodRecord<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, z.ZodIntersection<z.ZodUnion<[z.ZodObject<{
224
224
  context: z.ZodLiteral<"client">;
225
225
  access: z.ZodLiteral<"public">;
226
226
  }, "strip", z.ZodTypeAny, {
@@ -69,13 +69,12 @@ const EnvFieldMetadata = z.union([
69
69
  PublicServerEnvFieldMetadata,
70
70
  SecretServerEnvFieldMetadata
71
71
  ]);
72
- const KEY_REGEX = /^[A-Z_]+$/;
73
- const EnvSchema = z.record(
74
- z.string().regex(KEY_REGEX, {
75
- message: "A valid variable name can only contain uppercase letters and underscores."
76
- }),
77
- z.intersection(EnvFieldMetadata, EnvFieldType)
78
- );
72
+ const EnvSchemaKey = z.string().min(1).refine(([firstChar]) => isNaN(Number.parseInt(firstChar)), {
73
+ message: "A valid variable name cannot start with a number."
74
+ }).refine((str) => /^[A-Z0-9_]+$/.test(str), {
75
+ message: "A valid variable name can only contain uppercase letters, numbers and underscores."
76
+ });
77
+ const EnvSchema = z.record(EnvSchemaKey, z.intersection(EnvFieldMetadata, EnvFieldType));
79
78
  export {
80
79
  EnvSchema
81
80
  };
@@ -1,12 +1,12 @@
1
1
  import type { EnvFieldType } from './schema.js';
2
2
  export type ValidationResultValue = EnvFieldType['default'];
3
+ export type ValidationResultErrors = ['missing'] | ['type'] | Array<string>;
3
4
  type ValidationResult = {
4
5
  ok: true;
5
- type: string;
6
6
  value: ValidationResultValue;
7
7
  } | {
8
8
  ok: false;
9
- type: string;
9
+ errors: ValidationResultErrors;
10
10
  };
11
11
  export declare function getEnvFieldType(options: EnvFieldType): string;
12
12
  export declare function validateEnvVariable(value: string | undefined, options: EnvFieldType): ValidationResult;
@@ -9,72 +9,106 @@ function getEnvFieldType(options) {
9
9
  return `${type}${optional ? " | undefined" : ""}`;
10
10
  }
11
11
  const stringValidator = ({ max, min, length, url, includes, startsWith, endsWith }) => (input) => {
12
- let valid = typeof input === "string";
13
- if (valid && max !== void 0) {
14
- valid = input.length <= max;
12
+ if (typeof input !== "string") {
13
+ return {
14
+ ok: false,
15
+ errors: ["type"]
16
+ };
15
17
  }
16
- if (valid && min !== void 0) {
17
- valid = input.length >= min;
18
+ const errors = [];
19
+ if (max !== void 0 && !(input.length <= max)) {
20
+ errors.push("max");
18
21
  }
19
- if (valid && length !== void 0) {
20
- valid = input.length === length;
22
+ if (min !== void 0 && !(input.length >= min)) {
23
+ errors.push("min");
21
24
  }
22
- if (valid && url !== void 0) {
23
- try {
24
- new URL(input);
25
- } catch (_) {
26
- valid = false;
27
- }
25
+ if (length !== void 0 && !(input.length === length)) {
26
+ errors.push("length");
27
+ }
28
+ if (url !== void 0 && !URL.canParse(input)) {
29
+ errors.push("url");
28
30
  }
29
- if (valid && includes !== void 0) {
30
- valid = input.includes(includes);
31
+ if (includes !== void 0 && !input.includes(includes)) {
32
+ errors.push("includes");
31
33
  }
32
- if (valid && startsWith !== void 0) {
33
- valid = input.startsWith(startsWith);
34
+ if (startsWith !== void 0 && !input.startsWith(startsWith)) {
35
+ errors.push("startsWith");
34
36
  }
35
- if (valid && endsWith !== void 0) {
36
- valid = input.endsWith(endsWith);
37
+ if (endsWith !== void 0 && !input.endsWith(endsWith)) {
38
+ errors.push("endsWith");
39
+ }
40
+ if (errors.length > 0) {
41
+ return {
42
+ ok: false,
43
+ errors
44
+ };
37
45
  }
38
46
  return {
39
- valid,
40
- parsed: input
47
+ ok: true,
48
+ value: input
41
49
  };
42
50
  };
43
51
  const numberValidator = ({ gt, min, lt, max, int }) => (input) => {
44
52
  const num = parseFloat(input ?? "");
45
- let valid = !isNaN(num);
46
- if (valid && gt !== void 0) {
47
- valid = num > gt;
53
+ if (isNaN(num)) {
54
+ return {
55
+ ok: false,
56
+ errors: ["type"]
57
+ };
48
58
  }
49
- if (valid && min !== void 0) {
50
- valid = num >= min;
59
+ const errors = [];
60
+ if (gt !== void 0 && !(num > gt)) {
61
+ errors.push("gt");
51
62
  }
52
- if (valid && lt !== void 0) {
53
- valid = num < lt;
63
+ if (min !== void 0 && !(num >= min)) {
64
+ errors.push("min");
54
65
  }
55
- if (valid && max !== void 0) {
56
- valid = num <= max;
66
+ if (lt !== void 0 && !(num < lt)) {
67
+ errors.push("lt");
57
68
  }
58
- if (valid && int !== void 0) {
69
+ if (max !== void 0 && !(num <= max)) {
70
+ errors.push("max");
71
+ }
72
+ if (int !== void 0) {
59
73
  const isInt = Number.isInteger(num);
60
- valid = int ? isInt : !isInt;
74
+ if (!(int ? isInt : !isInt)) {
75
+ errors.push("int");
76
+ }
77
+ }
78
+ if (errors.length > 0) {
79
+ return {
80
+ ok: false,
81
+ errors
82
+ };
61
83
  }
62
84
  return {
63
- valid,
64
- parsed: num
85
+ ok: true,
86
+ value: num
65
87
  };
66
88
  };
67
89
  const booleanValidator = (input) => {
68
90
  const bool = input === "true" ? true : input === "false" ? false : void 0;
91
+ if (typeof bool !== "boolean") {
92
+ return {
93
+ ok: false,
94
+ errors: ["type"]
95
+ };
96
+ }
69
97
  return {
70
- valid: typeof bool === "boolean",
71
- parsed: bool
98
+ ok: true,
99
+ value: bool
72
100
  };
73
101
  };
74
102
  const enumValidator = ({ values }) => (input) => {
103
+ if (!(typeof input === "string" ? values.includes(input) : false)) {
104
+ return {
105
+ ok: false,
106
+ errors: ["type"]
107
+ };
108
+ }
75
109
  return {
76
- valid: typeof input === "string" ? values.includes(input) : false,
77
- parsed: input
110
+ ok: true,
111
+ value: input
78
112
  };
79
113
  };
80
114
  function selectValidator(options) {
@@ -90,29 +124,20 @@ function selectValidator(options) {
90
124
  }
91
125
  }
92
126
  function validateEnvVariable(value, options) {
93
- const validator = selectValidator(options);
94
- const type = getEnvFieldType(options);
95
- if (options.optional || options.default !== void 0) {
96
- if (value === void 0) {
97
- return {
98
- ok: true,
99
- value: options.default,
100
- type
101
- };
102
- }
103
- }
104
- const { valid, parsed } = validator(value);
105
- if (valid) {
127
+ const isOptional = options.optional || options.default !== void 0;
128
+ if (isOptional && value === void 0) {
106
129
  return {
107
130
  ok: true,
108
- value: parsed,
109
- type
131
+ value: options.default
110
132
  };
111
133
  }
112
- return {
113
- ok: false,
114
- type
115
- };
134
+ if (!isOptional && value === void 0) {
135
+ return {
136
+ ok: false,
137
+ errors: ["missing"]
138
+ };
139
+ }
140
+ return selectValidator(options)(value);
116
141
  }
117
142
  export {
118
143
  getEnvFieldType,
@@ -6,7 +6,7 @@ import {
6
6
  VIRTUAL_MODULES_IDS,
7
7
  VIRTUAL_MODULES_IDS_VALUES
8
8
  } from "./constants.js";
9
- import { validateEnvVariable } from "./validators.js";
9
+ import { getEnvFieldType, validateEnvVariable } from "./validators.js";
10
10
  function astroEnv({
11
11
  settings,
12
12
  mode,
@@ -32,7 +32,11 @@ function astroEnv({
32
32
  process.env[key] = value;
33
33
  }
34
34
  }
35
- const validatedVariables = validatePublicVariables({ schema, loadedEnv });
35
+ const validatedVariables = validatePublicVariables({
36
+ schema,
37
+ loadedEnv,
38
+ validateSecrets: settings.config.experimental.env?.validateSecrets ?? false
39
+ });
36
40
  templates = {
37
41
  ...getTemplates(schema, fs, validatedVariables),
38
42
  internal: `export const schema = ${JSON.stringify(schema)};`
@@ -70,28 +74,38 @@ function resolveVirtualModuleId(id) {
70
74
  }
71
75
  function validatePublicVariables({
72
76
  schema,
73
- loadedEnv
77
+ loadedEnv,
78
+ validateSecrets
74
79
  }) {
75
80
  const valid = [];
76
81
  const invalid = [];
77
82
  for (const [key, options] of Object.entries(schema)) {
78
- if (options.access !== "public") {
83
+ const variable = loadedEnv[key] === "" ? void 0 : loadedEnv[key];
84
+ if (options.access === "secret" && !validateSecrets) {
79
85
  continue;
80
86
  }
81
- const variable = loadedEnv[key];
82
- const result = validateEnvVariable(variable === "" ? void 0 : variable, options);
83
- if (result.ok) {
84
- valid.push({ key, value: result.value, type: result.type, context: options.context });
85
- } else {
86
- invalid.push({ key, type: result.type });
87
+ const result = validateEnvVariable(variable, options);
88
+ const type = getEnvFieldType(options);
89
+ if (!result.ok) {
90
+ invalid.push({ key, type, errors: result.errors });
91
+ } else if (options.access === "public") {
92
+ valid.push({ key, value: result.value, type, context: options.context });
87
93
  }
88
94
  }
89
95
  if (invalid.length > 0) {
96
+ const _errors = [];
97
+ for (const { key, type, errors } of invalid) {
98
+ if (errors[0] === "missing") {
99
+ _errors.push(`${key} is missing`);
100
+ } else if (errors[0] === "type") {
101
+ _errors.push(`${key}'s type is invalid, expected: ${type}`);
102
+ } else {
103
+ _errors.push(`The following constraints for ${key} are not met: ${errors.join(", ")}`);
104
+ }
105
+ }
90
106
  throw new AstroError({
91
107
  ...AstroErrorData.EnvInvalidVariables,
92
- message: AstroErrorData.EnvInvalidVariables.message(
93
- invalid.map(({ key, type }) => `Variable ${key} is not of type: ${type}.`).join("\n")
94
- )
108
+ message: AstroErrorData.EnvInvalidVariables.message(_errors)
95
109
  });
96
110
  }
97
111
  return valid;
@@ -91,6 +91,7 @@ const aria_non_interactive_roles = [
91
91
  "rowheader",
92
92
  "search",
93
93
  "status",
94
+ "tabpanel",
94
95
  "term",
95
96
  "timer",
96
97
  "toolbar",
@@ -422,7 +423,7 @@ const a11y = [
422
423
  title: "Invalid `tabindex` on non-interactive element",
423
424
  description: 'The `tabindex` attribute should only be used on interactive elements, as it can be confusing for keyboard-only users to navigate through non-interactive elements. If your element is only conditionally interactive, consider using `tabindex="-1"` to make it focusable only when it is actually interactive.',
424
425
  message: (element) => `${element.localName} elements should not have \`tabindex\` attribute`,
425
- selector: "[tabindex]",
426
+ selector: '[tabindex]:not([role="tabpanel"])',
426
427
  match(element) {
427
428
  const isScrollable = element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
428
429
  if (isScrollable) return false;
@@ -2,6 +2,8 @@ import { clsx } from "clsx";
2
2
  import { HTMLString, markHTMLString } from "../escape.js";
3
3
  const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
4
4
  const htmlBooleanAttributes = /^(?:allowfullscreen|async|autofocus|autoplay|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|itemscope)$/i;
5
+ const htmlEnumAttributes = /^(?:contenteditable|draggable|spellcheck|value)$/i;
6
+ const svgEnumAttributes = /^(?:autoReverse|externalResourcesRequired|focusable|preserveAlpha)$/i;
5
7
  const AMPERSAND_REGEX = /&/g;
6
8
  const DOUBLE_QUOTE_REGEX = /"/g;
7
9
  const STATIC_DIRECTIVES = /* @__PURE__ */ new Set(["set:html", "set:text"]);
@@ -36,6 +38,12 @@ function addAttribute(value, key, shouldEscape = true) {
36
38
  if (value == null) {
37
39
  return "";
38
40
  }
41
+ if (value === false) {
42
+ if (htmlEnumAttributes.test(key) || svgEnumAttributes.test(key)) {
43
+ return markHTMLString(` ${key}="false"`);
44
+ }
45
+ return "";
46
+ }
39
47
  if (STATIC_DIRECTIVES.has(key)) {
40
48
  console.warn(`[astro] The "${key}" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute.
41
49
 
@@ -65,13 +73,11 @@ Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the
65
73
  if (typeof value === "string" && value.includes("&") && isHttpUrl(value)) {
66
74
  return markHTMLString(` ${key}="${toAttributeString(value, false)}"`);
67
75
  }
68
- if (htmlBooleanAttributes.test(key)) {
69
- return markHTMLString(value ? ` ${key}` : "");
70
- }
71
- if (value === "") {
76
+ if (value === true && (key.startsWith("data-") || htmlBooleanAttributes.test(key))) {
72
77
  return markHTMLString(` ${key}`);
78
+ } else {
79
+ return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
73
80
  }
74
- return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
75
81
  }
76
82
  function internalSpreadAttributes(values, shouldEscape = true) {
77
83
  let output = "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "4.11.4",
3
+ "version": "4.11.6",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -109,19 +109,19 @@
109
109
  "vendor"
110
110
  ],
111
111
  "dependencies": {
112
- "@astrojs/compiler": "^2.8.1",
113
- "@babel/core": "^7.24.7",
114
- "@babel/generator": "^7.24.7",
115
- "@babel/parser": "^7.24.7",
112
+ "@astrojs/compiler": "^2.8.2",
113
+ "@babel/core": "^7.24.9",
114
+ "@babel/generator": "^7.24.10",
115
+ "@babel/parser": "^7.24.8",
116
116
  "@babel/plugin-transform-react-jsx": "^7.24.7",
117
- "@babel/traverse": "^7.24.7",
118
- "@babel/types": "^7.24.7",
117
+ "@babel/traverse": "^7.24.8",
118
+ "@babel/types": "^7.24.9",
119
119
  "@types/babel__core": "^7.20.5",
120
120
  "@types/cookie": "^0.6.0",
121
- "acorn": "^8.12.0",
121
+ "acorn": "^8.12.1",
122
122
  "aria-query": "^5.3.0",
123
- "axobject-query": "^4.0.0",
124
- "boxen": "^7.1.1",
123
+ "axobject-query": "^4.1.0",
124
+ "boxen": "^8.0.0",
125
125
  "chokidar": "^3.6.0",
126
126
  "ci-info": "^4.0.0",
127
127
  "clsx": "^2.1.1",
@@ -149,22 +149,22 @@
149
149
  "magic-string": "^0.30.10",
150
150
  "mrmime": "^2.0.0",
151
151
  "ora": "^8.0.1",
152
- "p-limit": "^5.0.0",
152
+ "p-limit": "^6.1.0",
153
153
  "p-queue": "^8.0.1",
154
154
  "path-to-regexp": "^6.2.2",
155
- "preferred-pm": "^3.1.3",
155
+ "preferred-pm": "^4.0.0",
156
156
  "prompts": "^2.4.2",
157
157
  "rehype": "^13.0.1",
158
158
  "semver": "^7.6.2",
159
- "shiki": "^1.10.0",
159
+ "shiki": "^1.10.3",
160
160
  "string-width": "^7.2.0",
161
161
  "strip-ansi": "^7.1.0",
162
162
  "tsconfck": "^3.1.1",
163
163
  "unist-util-visit": "^5.0.0",
164
- "vfile": "^6.0.1",
165
- "vite": "^5.3.2",
164
+ "vfile": "^6.0.2",
165
+ "vite": "^5.3.4",
166
166
  "vitefu": "^0.2.5",
167
- "which-pm": "^2.2.0",
167
+ "which-pm": "^3.0.0",
168
168
  "yargs-parser": "^21.1.1",
169
169
  "zod": "^3.23.8",
170
170
  "zod-to-json-schema": "^3.23.1",
@@ -176,8 +176,8 @@
176
176
  "sharp": "^0.33.3"
177
177
  },
178
178
  "devDependencies": {
179
- "@astrojs/check": "^0.7.0",
180
- "@playwright/test": "^1.45.0",
179
+ "@astrojs/check": "^0.8.1",
180
+ "@playwright/test": "^1.45.2",
181
181
  "@types/aria-query": "^5.0.4",
182
182
  "@types/babel__generator": "^7.6.8",
183
183
  "@types/babel__traverse": "^7.20.6",
@@ -200,6 +200,7 @@
200
200
  "@types/yargs-parser": "^21.0.3",
201
201
  "cheerio": "1.0.0-rc.12",
202
202
  "eol": "^0.9.1",
203
+ "expect-type": "^0.19.0",
203
204
  "mdast-util-mdx": "^3.0.0",
204
205
  "mdast-util-mdx-jsx": "^3.1.2",
205
206
  "memfs": "^4.9.3",
@@ -209,8 +210,8 @@
209
210
  "rehype-slug": "^6.0.0",
210
211
  "rehype-toc": "^3.0.2",
211
212
  "remark-code-titles": "^0.1.2",
212
- "rollup": "^4.18.0",
213
- "sass": "^1.77.6",
213
+ "rollup": "^4.18.1",
214
+ "sass": "^1.77.8",
214
215
  "srcset-parse": "^1.1.0",
215
216
  "undici": "^6.19.2",
216
217
  "unified": "^11.0.5",
@@ -230,12 +231,13 @@
230
231
  "build:ci": "pnpm run prebuild && astro-scripts build \"src/**/*.{ts,js}\" && pnpm run postbuild",
231
232
  "dev": "astro-scripts dev --copy-wasm --prebuild \"src/runtime/server/astro-island.ts\" --prebuild \"src/runtime/client/{idle,load,media,only,visible}.ts\" \"src/**/*.{ts,js}\"",
232
233
  "postbuild": "astro-scripts copy \"src/**/*.astro\" && astro-scripts copy \"src/**/*.wasm\"",
233
- "test": "pnpm run test:node",
234
+ "test": "pnpm run test:node && pnpm run test:types",
234
235
  "test:match": "pnpm run test:node --match",
235
236
  "test:e2e": "pnpm test:e2e:chrome && pnpm test:e2e:firefox",
236
237
  "test:e2e:match": "playwright test -g",
237
238
  "test:e2e:chrome": "playwright test",
238
239
  "test:e2e:firefox": "playwright test --config playwright.firefox.config.js",
240
+ "test:types": "tsc --project tsconfig.tests.json",
239
241
  "test:node": "astro-scripts test \"test/**/*.test.js\""
240
242
  }
241
243
  }