Repository: withastro/astro
File: packages/astro/src/core/config/schemas/refined.ts (TypeScript)

Code before:
		});
	}

	if (config.experimental.fonts && config.experimental.fonts.length > 0) {
		for (let i = 0; i < config.experimental.fonts.length; i++) {
			const { cssVariable } = config.experimental.fonts[i];

			// Checks if the name starts with --, doesn't include a space nor a colon.
			// We are not trying to recreate the full CSS spec about indents:
			// https://developer.mozilla.org/en-US/docs/Web/CSS/ident
			if (!cssVariable.startsWith('--') || cssVariable.includes(' ') || cssVariable.includes(':')) {
				ctx.addIssue({
					code: z.ZodIssueCode.custom,
					message: `**cssVariable** property "${cssVariable}" contains invalid characters for CSS variable generation. It must start with -- and be a valid indent: https://developer.mozilla.org/en-US/docs/Web/CSS/ident.`,
					path: ['fonts', i, 'cssVariable'],
				});
			}
		}
	}

	if (config.experimental.csp && typeof config.experimental.csp === 'object') {
		const { scriptHashes, styleHashes } = config.experimental.csp;
		if (scriptHashes) {
			for (const hash of scriptHashes) {
				if (
					!(hash.startsWith('sha256-') || hash.startsWith('sha384-') || hash.startsWith('sha512-'))
				) {
					ctx.addIssue({
						code: z.ZodIssueCode.custom,
						message: `**scriptHashes** property "${hash}" must start with "sha256-", "sha384-" or "sha512-" to be valid.`,
						path: ['experimental', 'csp', 'scriptHashes'],
					});
				}
			}
		}

		if (styleHashes) {
			for (const hash of styleHashes) {
				if (
					!(hash.startsWith('sha256-') || hash.startsWith('sha384-') || hash.startsWith('sha512-'))
				) {
					ctx.addIssue({
						code: z.ZodIssueCode.custom,
						message: `**styleHashes** property "${hash}" must start with "sha256-", "sha384-" or "sha512-" to be valid.`,
						path: ['experimental', 'csp', 'styleHashes'],
					});
				}
			}
		}
	}
});

Review comment (suggestion):
Instead of harcoding, I think it could be beneficial to have a mapping object in `csp/config.ts` eg:
```ts
const ALGORITHMS = {
	"SHA-256": "sha256"
	// ...
} as const
```
And then use those values for the algorithm schema, types etc

Code after:
			path: ['experimental', 'responsiveImages'],
		});
	}

	if (config.experimental.fonts && config.experimental.fonts.length > 0) {
		for (let i = 0; i < config.experimental.fonts.length; i++) {
			const { cssVariable } = config.experimental.fonts[i];

			// Checks if the name starts with --, doesn't include a space nor a colon.
			// We are not trying to recreate the full CSS spec about indents:
			// https://developer.mozilla.org/en-US/docs/Web/CSS/ident
			if (!cssVariable.startsWith('--') || cssVariable.includes(' ') || cssVariable.includes(':')) {
				ctx.addIssue({
					code: z.ZodIssueCode.custom,
					message: `**cssVariable** property "${cssVariable}" contains invalid characters for CSS variable generation. It must start with -- and be a valid indent: https://developer.mozilla.org/en-US/docs/Web/CSS/ident.`,
					path: ['fonts', i, 'cssVariable'],
				});
			}
		}
	}

	if (config.experimental.csp && typeof config.experimental.csp === 'object') {
		const { scriptHashes, styleHashes } = config.experimental.csp;
		if (scriptHashes) {
			for (const hash of scriptHashes) {
				for (const allowedValue of ALGORITHM_VALUES) {
					if (!hash.startsWith(allowedValue)) {
						ctx.addIssue({
							code: z.ZodIssueCode.custom,
							message: `**scriptHashes** property "${hash}" must start with with one of following values: ${ALGORITHM_VALUES.join(', ')}.`,
							path: ['experimental', 'csp', 'scriptHashes'],
						});
					}
				}
			}
		}

		if (styleHashes) {
			for (const hash of styleHashes) {
				for (const allowedValue of ALGORITHM_VALUES) {
					if (!hash.startsWith(allowedValue)) {
						ctx.addIssue({
							code: z.ZodIssueCode.custom,
							message: `**styleHashes** property "${hash}" must start with with one of following values: ${ALGORITHM_VALUES.join(', ')}.`,
							path: ['experimental', 'csp', 'styleHashes'],
						});
					}
				}
			}
		}
	}