Repository: withastro/astro
File: packages/astro/src/assets/fonts/implementations/url-resolver.ts (TypeScript)

Code before:
import { fileExtension, joinPaths, prependForwardSlash } from '../../../core/path.js';
import type { AssetsPrefix } from '../../../types/public/index.js';
import { getAssetsPrefix } from '../../utils/getAssetsPrefix.js';
import type { UrlResolver } from '../definitions.js';

export function createDevUrlResolver({
	base,
	assetQueryParams,
}: {
	base: string;
	assetQueryParams?: URLSearchParams;
}): UrlResolver {
	let resolved = false;
	return {
		resolve(hash) {
			resolved ||= true;
			let url = prependForwardSlash(joinPaths(base, hash));

			// Append assetQueryParams if available (for adapter-level tracking like skew protection)
			if (assetQueryParams) {
				const assetQueryString = assetQueryParams.toString();
				if (assetQueryString) {
					url += '?' + assetQueryString;
				}
			}

			return url;
		},
		getCspResources() {
			return resolved ? ["'self'"] : [];
		},
	};
}

export function createBuildUrlResolver({
	base,
	assetsPrefix,
	assetQueryParams,
}: {
	base: string;
	assetsPrefix: AssetsPrefix;
	assetQueryParams?: URLSearchParams;
}): UrlResolver {
	const resources = new Set<string>();
	return {
		resolve(hash) {
			const prefix = assetsPrefix ? getAssetsPrefix(fileExtension(hash), assetsPrefix) : undefined;
			let url: string;
			if (prefix) {
				resources.add(prefix);

Review comment (suggestion):
```suggestion
export function createDevUrlResolver({
	base,
	searchParams,
}: {
	base: string;
	searchParams: URLSearchParams | undefined;
}): UrlResolver {
	let resolved = false;
	return {
		resolve(hash) {
			resolved ||= true;
			let url = prependForwardSlash(joinPaths(base, hash));

			// Append searchParams if available (for adapter-level tracking like skew protection)
			if (searchParams) {
				const queryString = searchParams.toString();
				if (assetQueryString) {
					url += `?${queryString}`;
				}
			}
```

Code after:
import { fileExtension, joinPaths, prependForwardSlash } from '../../../core/path.js';
import type { AssetsPrefix } from '../../../types/public/index.js';
import { getAssetsPrefix } from '../../utils/getAssetsPrefix.js';
import { createPlaceholderURL, stringifyPlaceholderURL } from '../../utils/url.js';
import type { UrlResolver } from '../definitions.js';

export function createDevUrlResolver({
	base,
	searchParams,
}: {
	base: string;
	searchParams: URLSearchParams;
}): UrlResolver {
	let resolved = false;
	return {
		resolve(hash) {
			resolved ||= true;
			const urlPath = prependForwardSlash(joinPaths(base, hash));
			const url = createPlaceholderURL(urlPath);

			// Append searchParams if available (for adapter-level tracking like skew protection)
			searchParams.forEach((value, key) => {
				url.searchParams.set(key, value);
			});

			return stringifyPlaceholderURL(url);
		},
		getCspResources() {
			return resolved ? ["'self'"] : [];
		},
	};
}

export function createBuildUrlResolver({
	base,
	assetsPrefix,
	searchParams,
}: {
	base: string;
	assetsPrefix: AssetsPrefix;
	searchParams: URLSearchParams;
}): UrlResolver {
	const resources = new Set<string>();
	return {
		resolve(hash) {
			const prefix = assetsPrefix ? getAssetsPrefix(fileExtension(hash), assetsPrefix) : undefined;
			let urlPath: string;
			if (prefix) {
				resources.add(prefix);
				urlPath = joinPaths(prefix, base, hash);