Buckets:
| import { | |
| TEMPLATE_EXPRESSION_REGEX, | |
| URI_SCHEME_SEPARATOR, | |
| URI_TEMPLATE_OPERATORS, | |
| URI_TEMPLATE_SEPARATORS, | |
| VARIABLE_EXPLODE_MODIFIER_REGEX, | |
| VARIABLE_PREFIX_MODIFIER_REGEX, | |
| LEADING_SLASHES_REGEX | |
| } from '../constants'; | |
| /** | |
| * Normalize a resource URI for comparison. | |
| * | |
| * URI template expansion (especially with path operators like {/var}) | |
| * can produce URIs that differ from listed resource URIs in slash placement. | |
| * For example, the template `svelte://{/slug*}.md` with slug="svelte/$effect" | |
| * expands to `svelte:///svelte/$effect.md`, while the listed resource URI is | |
| * `svelte://svelte/$effect.md`. | |
| * | |
| * This function strips extra leading slashes after the scheme to normalize | |
| * both forms to the same string for comparison purposes. | |
| * | |
| * @param uri - The URI to normalize | |
| * @returns Normalized URI string | |
| */ | |
| export function normalizeResourceUri(uri: string): string { | |
| const schemeEnd = uri.indexOf(URI_SCHEME_SEPARATOR); | |
| if (schemeEnd === -1) return uri; | |
| const scheme = uri.substring(0, schemeEnd); | |
| const rest = uri | |
| .substring(schemeEnd + URI_SCHEME_SEPARATOR.length) | |
| .replace(LEADING_SLASHES_REGEX, ''); | |
| return `${scheme}${URI_SCHEME_SEPARATOR}${rest}`; | |
| } | |
| /** | |
| * A parsed variable from a URI template expression. | |
| */ | |
| export interface UriTemplateVariable { | |
| /** Variable name */ | |
| name: string; | |
| /** Operator prefix (+, #, /, etc.) or empty string */ | |
| operator: string; | |
| } | |
| /** | |
| * Extract all variable names from a URI template string. | |
| * | |
| * @param template - URI template string (RFC 6570) | |
| * @returns Array of unique variable descriptors | |
| * | |
| * @example | |
| * ```ts | |
| * extractTemplateVariables("file:///{path}") | |
| * // => [{ name: "path", operator: "" }] | |
| * | |
| * extractTemplateVariables("db://{schema}/{table}") | |
| * // => [{ name: "schema", operator: "" }, { name: "table", operator: "" }] | |
| * ``` | |
| */ | |
| export function extractTemplateVariables(template: string): UriTemplateVariable[] { | |
| const variables: UriTemplateVariable[] = []; | |
| const seen = new Set<string>(); | |
| let match; | |
| TEMPLATE_EXPRESSION_REGEX.lastIndex = 0; | |
| while ((match = TEMPLATE_EXPRESSION_REGEX.exec(template)) !== null) { | |
| const operator = match[1] || ''; | |
| const varList = match[2]; | |
| // RFC 6570 allows comma-separated variable lists: {x,y,z} | |
| for (const varSpec of varList.split(',')) { | |
| // Strip explode modifier (*) and prefix modifier (:N) | |
| const name = varSpec | |
| .replace(VARIABLE_EXPLODE_MODIFIER_REGEX, '') | |
| .replace(VARIABLE_PREFIX_MODIFIER_REGEX, '') | |
| .trim(); | |
| if (name && !seen.has(name)) { | |
| seen.add(name); | |
| variables.push({ name, operator }); | |
| } | |
| } | |
| } | |
| return variables; | |
| } | |
| /** | |
| * Expand a URI template with the given variable values. | |
| * Implements a simplified RFC 6570 Level 2 expansion. | |
| * | |
| * @param template - URI template string | |
| * @param values - Map of variable name to value | |
| * @returns Expanded URI string | |
| * | |
| * @example | |
| * ```ts | |
| * expandTemplate("file:///{path}", { path: "src/main.rs" }) | |
| * // => "file:///src/main.rs" | |
| * ``` | |
| */ | |
| export function expandTemplate(template: string, values: Record<string, string>): string { | |
| TEMPLATE_EXPRESSION_REGEX.lastIndex = 0; | |
| return template.replace( | |
| TEMPLATE_EXPRESSION_REGEX, | |
| (_match, operator: string, varList: string) => { | |
| const varNames = varList | |
| .split(',') | |
| .map((v: string) => | |
| v | |
| .replace(VARIABLE_EXPLODE_MODIFIER_REGEX, '') | |
| .replace(VARIABLE_PREFIX_MODIFIER_REGEX, '') | |
| .trim() | |
| ); | |
| const expandedParts = varNames | |
| .map((name: string) => values[name] ?? '') | |
| .filter((v: string) => v !== ''); | |
| if (expandedParts.length === 0) return ''; | |
| switch (operator) { | |
| case URI_TEMPLATE_OPERATORS.RESERVED: | |
| // Reserved expansion: no encoding | |
| return expandedParts.join(URI_TEMPLATE_SEPARATORS.COMMA); | |
| case URI_TEMPLATE_OPERATORS.FRAGMENT: | |
| // Fragment expansion | |
| return ( | |
| URI_TEMPLATE_OPERATORS.FRAGMENT + expandedParts.join(URI_TEMPLATE_SEPARATORS.COMMA) | |
| ); | |
| case URI_TEMPLATE_OPERATORS.PATH_SEGMENT: | |
| // Path segments | |
| return URI_TEMPLATE_SEPARATORS.SLASH + expandedParts.join(URI_TEMPLATE_SEPARATORS.SLASH); | |
| case URI_TEMPLATE_OPERATORS.LABEL: | |
| // Label expansion | |
| return ( | |
| URI_TEMPLATE_SEPARATORS.PERIOD + expandedParts.join(URI_TEMPLATE_SEPARATORS.PERIOD) | |
| ); | |
| case URI_TEMPLATE_OPERATORS.PATH_PARAM: | |
| // Path-style parameters | |
| return varNames | |
| .filter((_: string, i: number) => expandedParts[i]) | |
| .map( | |
| (name: string, i: number) => | |
| `${URI_TEMPLATE_SEPARATORS.SEMICOLON}${name}=${expandedParts[i]}` | |
| ) | |
| .join(''); | |
| case URI_TEMPLATE_OPERATORS.FORM_QUERY: | |
| // Form-style query | |
| return ( | |
| URI_TEMPLATE_SEPARATORS.QUERY_PREFIX + | |
| varNames | |
| .filter((_: string, i: number) => expandedParts[i]) | |
| .map( | |
| (name: string, i: number) => | |
| `${encodeURIComponent(name)}=${encodeURIComponent(expandedParts[i])}` | |
| ) | |
| .join(URI_TEMPLATE_SEPARATORS.QUERY_CONTINUATION) | |
| ); | |
| case URI_TEMPLATE_OPERATORS.FORM_CONTINUATION: | |
| // Form-style query continuation | |
| return ( | |
| URI_TEMPLATE_SEPARATORS.QUERY_CONTINUATION + | |
| varNames | |
| .filter((_: string, i: number) => expandedParts[i]) | |
| .map( | |
| (name: string, i: number) => | |
| `${encodeURIComponent(name)}=${encodeURIComponent(expandedParts[i])}` | |
| ) | |
| .join(URI_TEMPLATE_SEPARATORS.COMMA) | |
| ); | |
| default: | |
| // Simple string expansion (default operator) | |
| return expandedParts | |
| .map((v: string) => encodeURIComponent(v)) | |
| .join(URI_TEMPLATE_SEPARATORS.COMMA); | |
| } | |
| } | |
| ); | |
| } | |
| /** | |
| * Check whether all required variables in a template have been provided. | |
| * | |
| * @param template - URI template string | |
| * @param values - Map of variable name to value | |
| * @returns true if all variables have non-empty values | |
| */ | |
| export function isTemplateComplete(template: string, values: Record<string, string>): boolean { | |
| const variables = extractTemplateVariables(template); | |
| return variables.every((v) => (values[v.name] ?? '').trim() !== ''); | |
| } | |
Xet Storage Details
- Size:
- 6.05 kB
- Xet hash:
- 7aac3c7de505d49fd050024cc21c6d7f4eea37fd3292ed288d3b43593b78b184
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.