const windowsReservedName = /^(?:con|prn|aux|nul|clock\$|com[1-9]|lpt[1-9])(?:\..*)?$/i; const unsafeUnicode = /[\u0000-\u001f\u007f\u202a-\u202e\u2066-\u2069\u2044\u2215\u29f8]/; export function normalizeArtifactRelativePath(value: string): string | null { if (!value || value.length > 4096 || value !== value.normalize('NFC') || unsafeUnicode.test(value)) return null; if (value.startsWith('/') || value.startsWith('\\') || /^[a-z]:/i.test(value)) return null; const normalized = value.replace(/\\/g, '/'); const segments = normalized.split('/'); if (segments.some(segment => !segment || segment === '.' || segment === '..' || segment.length > 255 || segment !== segment.trim() || segment.endsWith('.') || segment.includes(':') || windowsReservedName.test(segment))) return null; return segments.join('/'); } export function artifactPathCollisionKey(value: string): string | null { const normalized = normalizeArtifactRelativePath(value); return normalized?.toLowerCase() ?? null; }