Repository: withastro/astro
File: packages/astro/src/env/schema.ts (TypeScript)

Code before:
	context: z.literal('client'),
	access: z.literal('public'),
});
const PublicServerEnvFieldMetadata = z.object({
	context: z.literal('server'),
	access: z.literal('public'),
});
const SecretServerEnvFieldMetadata = z.object({
	context: z.literal('server'),
	access: z.literal('secret'),
});
const _EnvFieldMetadata = z.union([
	PublicClientEnvFieldMetadata,
	PublicServerEnvFieldMetadata,
	SecretServerEnvFieldMetadata,
]);
const EnvFieldMetadata = z.custom<z.input<typeof _EnvFieldMetadata>>().superRefine((data, ctx) => {
	const result = _EnvFieldMetadata.safeParse(data);
	if (result.success) {
		return;
	}
	for (const issue of result.error.issues) {
		if (issue.code === z.ZodIssueCode.invalid_union) {
			ctx.addIssue({
				code: z.ZodIssueCode.custom,
				message: `**Invalid combination** of "access" and "context" options:\n  Review the configuration of \`env.schema.${ctx.path.at(-1)}\`.\n  Learn more at https://docs.astro.build/en/guides/environment-variables/#variable-types`,
				path: ['context', 'access'],
			});
		} else {
			ctx.addIssue(issue);
		}
	}
});

const EnvSchemaKey = z
	.string()
	.min(1)
	.refine(([firstChar]) => isNaN(Number.parseInt(firstChar)), {
		message: 'A valid variable name cannot start with a number.',
	})
	.refine((str) => /^[A-Z0-9_]+$/.test(str), {
		message: 'A valid variable name can only contain uppercase letters, numbers and underscores.',
	});

export const EnvSchema = z.record(EnvSchemaKey, z.intersection(EnvFieldMetadata, EnvFieldType));

// https://www.totaltypescript.com/concepts/the-prettify-helper
type Prettify<T> = {
	[K in keyof T]: T[K];
} & {};


Review comment (security):
```suggestion
				message: `**Invalid combination** of "access" and "context" options:\n  Review the configuration of \`env.schema.${ctx.path.at(-1)}\`.\n  Learn more at https://docs.astro.build/en/guides/environment-variables/#variable-types`,
```
Some questions!

- Do error messages usually have some bolded words? This jumps out to me as odd.
- If I remember correctly, isn't there only *one* invalid combination? So, instead of saying "invalid combination", is it possible to describe that "You can't have secret variables that are available on the client" or something like that? Or is the intention to highlight that this is fixed **by changing your configuration** (and the helpful part is supposed to be guiding you to config?)

Code after:
	context: z.literal('client'),
	access: z.literal('public'),
});
const PublicServerEnvFieldMetadata = z.object({
	context: z.literal('server'),
	access: z.literal('public'),
});
const SecretServerEnvFieldMetadata = z.object({
	context: z.literal('server'),
	access: z.literal('secret'),
});
const _EnvFieldMetadata = z.union([
	PublicClientEnvFieldMetadata,
	PublicServerEnvFieldMetadata,
	SecretServerEnvFieldMetadata,
]);
const EnvFieldMetadata = z.custom<z.input<typeof _EnvFieldMetadata>>().superRefine((data, ctx) => {
	const result = _EnvFieldMetadata.safeParse(data);
	if (result.success) {
		return;
	}
	for (const issue of result.error.issues) {
		if (issue.code === z.ZodIssueCode.invalid_union) {
			ctx.addIssue({
				code: z.ZodIssueCode.custom,
				message: `**Invalid combination** of "access" and "context" options:\n  Secret client variables are not supported. Please review the configuration of \`env.schema.${ctx.path.at(-1)}\`.\n  Learn more at https://docs.astro.build/en/guides/environment-variables/#variable-types`,
				path: ['context', 'access'],
			});
		} else {
			ctx.addIssue(issue);
		}
	}
});

const EnvSchemaKey = z
	.string()
	.min(1)
	.refine(([firstChar]) => isNaN(Number.parseInt(firstChar)), {
		message: 'A valid variable name cannot start with a number.',
	})
	.refine((str) => /^[A-Z0-9_]+$/.test(str), {
		message: 'A valid variable name can only contain uppercase letters, numbers and underscores.',
	});

export const EnvSchema = z.record(EnvSchemaKey, z.intersection(EnvFieldMetadata, EnvFieldType));

// https://www.totaltypescript.com/concepts/the-prettify-helper
type Prettify<T> = {
	[K in keyof T]: T[K];
} & {};