{"version":3,"sources":["../src/parse.ts","../src/temporalProvider.ts","../src/errors.ts","../src/localeVocab.ts","../src/isoWeek.ts","../src/tokens.ts","../src/tokenize.ts","../src/pattern.ts","../src/parsePattern.ts","../src/constants.ts","../src/numbering.ts"],"sourcesContent":["/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { buildCapturingPattern, type CapturingPattern } from './parsePattern.js';\nimport { enumerateValidSplits, isValidTimeZone } from './pattern.js';\nimport { getLocaleVocab, canonicalCacheKey, assertValidLocaleTag, subscribeToVocabChanges } from './localeVocab.js';\nimport { getTemporal } from './temporalProvider.js';\nimport { MAX_FORMAT_LENGTH, MAX_INPUT_LENGTH } from './constants.js';\nimport { TemporalFmtError, InvalidTimeZoneError, InvalidOffsetError, FormatSyntaxError, ParseMismatchError, AmbiguousInputError, InvalidDateError, wrapUntypedError } from './errors.js';\nimport { applyParseNumbering, type NumberingParseOptions } from './numbering.js';\n\n// format strings are short hand-written literals reused across many calls —\n// cache the compiled capturing pattern per (formatStr, locale) pair instead\n// of rebuilding it every call.\nconst patternCache = new Map<string, CapturingPattern>();\nconst MAX_CACHE_SIZE = 500;\n\n// A compiled pattern embeds locale-vocabulary alternations (MMMM/MMM/\n// EEEE/EEE/a fragments) at build time. When registerLocaleVocab()\n// swaps a locale's vocabulary, every cached pattern for any locale\n// becomes potentially stale — the cached regex would keep matching the\n// OLD vocabulary while format() renders the new one, breaking the\n// format→parse round-trip for the library's own output. Clear the whole\n// cache on any vocab change: rebuilds are cheap and self-limiting via\n// the cache size cap.\nsubscribeToVocabChanges(() => { patternCache.clear(); });\n\nfunction getPattern(formatStr: string, locale: string): CapturingPattern {\n  const key = JSON.stringify([canonicalCacheKey(locale), formatStr]);\n  let pattern = patternCache.get(key);\n  if (pattern) {\n    return pattern;\n  }\n  if (patternCache.size >= MAX_CACHE_SIZE) {\n    const oldestKey = patternCache.keys().next().value;\n    if (oldestKey !== undefined) patternCache.delete(oldestKey);\n  }\n  pattern = buildCapturingPattern(tokenize(formatStr), locale);\n  patternCache.set(key, pattern);\n  return pattern;\n}\n\n// Requires an explicit `-u-ca-` extension (e.g. 'en-u-ca-hebrew') to apply\n// a non-Gregorian calendar, per parse()'s own docstring. 'gregory' counts\n// as \"no calendar\" so the default locale keeps constructing plain ISO 8601.\n//\n// Used to key off resolvedOptions().calendar instead — a locale's\n// *default* calendar, whether the caller asked for one or not. That broke\n// th-TH silently: its default is 'buddhist', so plain Gregorian-looking\n// digits parsed 543 years off, while format() has no matching calendar\n// step and just prints the object's own ISO fields either way.\nconst calendarCache = new Map<string, string | undefined>();\nconst MAX_CALENDAR_CACHE_SIZE = 500;\n\nfunction resolveCalendar(locale: string): string | undefined {\n  // Typed validation first: a genuinely malformed tag (bare private-use\n  // singleton, control characters, garbage) surfaces as the library's\n  // InvalidLocaleError instead of a raw engine RangeError — same code as\n  // before, different (documented, structured) error class.\n  assertValidLocaleTag(locale);\n  // canonicalCacheKey is memoized (localeVocab.ts), so the common path —\n  // repeated parse() calls with the same locale — no longer constructs a\n  // fresh Intl.Locale per call just to compute the calendar-cache key.\n  const canonicalLocale = canonicalCacheKey(locale);\n  if (calendarCache.has(canonicalLocale)) {\n    return calendarCache.get(canonicalLocale);\n  }\n  if (calendarCache.size >= MAX_CALENDAR_CACHE_SIZE) {\n    const oldestKey = calendarCache.keys().next().value;\n    if (oldestKey !== undefined) calendarCache.delete(oldestKey);\n  }\n  let calendar: string | undefined;\n  const parts = canonicalLocale.split('-');\n  const extensionIndex = parts.indexOf('u');\n  const calendarKeyIndex = extensionIndex === -1 ? -1 : parts.indexOf('ca', extensionIndex + 1);\n  if (calendarKeyIndex !== -1 && calendarKeyIndex + 1 < parts.length) {\n    const resolved = new Intl.DateTimeFormat(canonicalLocale).resolvedOptions().calendar;\n    calendar = resolved === 'gregory' ? undefined : resolved;\n  }\n  calendarCache.set(canonicalLocale, calendar);\n  return calendar;\n}\n\ninterface Fields {\n  year?: number;\n  twoDigitYear?: number;\n  month?: number;\n  day?: number;\n  hour?: number;\n  hour12?: number;\n  dayPeriodRaw?: string;\n  isPM?: boolean;\n  minute?: number;\n  second?: number;\n  millisecond?: number;\n  microsecond?: number;\n  nanosecond?: number;\n  timeZoneId?: string;\n  // Canonical `+HH:MM` form of any offset token (X/XX/XXX/x/xx/xxx)\n  // captured in this pattern. Distinct from timeZoneId because the two\n  // can coexist in the same pattern (e.g. \"yyyy-MM-dd HH:mm zzz XXX\") —\n  // see the cross-check after construction for how a mismatch between\n  // them is resolved.\n  offsetString?: string;\n  weekdayExpected?: number;\n  weekdayRaw?: string;\n  quarter?: number;\n}\n\n// Normalizes a captured offset-token string into the canonical `+HH:MM`\n// shape Temporal.ZonedDateTime.from accepts as a `timeZone` value. Throws\n// descriptive errors for out-of-range hours/minutes, since the regex\n// shape (OFFSET_SHAPES in pattern.ts) is deliberately permissive — a\n// post-match range check here gives the user a specific error (\"offset\n// hours 99 out of range, max 14\") instead of \"no valid pattern matches\".\n//\n// Range bounds: -12:00 to +14:00, the standard IANA offset range\n// (Baker/Howland at -12, Kiritimati at +14). +14:01 / -12:01 etc. are\n// rejected explicitly even though the per-piece bounds (hours ≤ 14,\n// minutes ≤ 59) alone wouldn't catch them.\nfunction parseOffsetString(raw: string, token: string): string {\n  if (raw === 'Z') {\n    /* c8 ignore start @preserve -- unreachable: lowercase tokens' regex\n       (OFFSET_SHAPES in pattern.ts) has no \"Z\" alternative at all, so\n       raw === 'Z' can only ever be reached when token is one of the\n       uppercase variants (X/XX/XXX). A lowercase token can't even\n       capture \"Z\" as `raw` in the first place. */\n    if (token === 'x' || token === 'xx' || token === 'xxx') {\n      throw new Error(\n        `temporal-fmt: offset token \"${token}\" doesn't accept \"Z\" — only the uppercase variants (X/XX/XXX) emit \"Z\" for UTC. ` +\n        `Use \"+00:00\", \"+0000\", or \"+00\" depending on the variant's width.`\n      );\n    }\n    /* c8 ignore stop @preserve */\n    return '+00:00';\n  }\n\n  const sign = raw[0];\n  /* c8 ignore start @preserve -- unreachable: raw is a regex-captured\n     group from an offset token, and every OFFSET_SHAPES pattern\n     (pattern.ts) is anchored to either \"Z\" or a leading [+-]. raw's\n     first character can never be anything else by the time it reaches\n     this function. */\n  if (sign !== '+' && sign !== '-') {\n    throw new Error(`temporal-fmt: offset \"${raw}\" for token \"${token}\" doesn't start with \"+\", \"-\", or \"Z\".`);\n  }\n  /* c8 ignore stop @preserve */\n  const body = raw.slice(1);\n  let hoursStr: string;\n  let minutesStr: string;\n  if (body.length === 2) {\n    /* c8 ignore start @preserve -- unreachable: each offset token's own\n       regex shape in OFFSET_SHAPES (pattern.ts) already gates which\n       body shapes it can capture. Only X and x ever match a 2-digit\n       body — XX/xx/XXX/xxx's regexes can't produce one — so this\n       mismatch can never actually fire through parse(). */\n    // +HH — only X/x emit this shape; XX/xx/XXX/xxx always carry minutes.\n    if (token !== 'X' && token !== 'x') {\n      throw new Error(\n        `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it requires minutes, but \"${raw}\" has none.`\n      );\n    }\n    /* c8 ignore stop @preserve */\n    hoursStr = body;\n    minutesStr = '00';\n  } else if (body.length === 4) {\n    /* c8 ignore start @preserve -- unreachable, same reason as the\n       2-digit case above: XXX/xxx's regex requires a colon, so it can\n       never capture a 4-digit no-colon body in the first place. */\n    // +HHMM — X/x (when minutes are non-zero) or XX/xx.\n    if (token === 'XXX' || token === 'xxx') {\n      throw new Error(\n        `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it requires a colon between hours and minutes (e.g. \"${sign}${body.slice(0, 2)}:${body.slice(2)}\").`\n      );\n    }\n    /* c8 ignore stop @preserve */\n    hoursStr = body.slice(0, 2);\n    minutesStr = body.slice(2, 4);\n  } else if (body.length === 5 && body[2] === ':') {\n    /* c8 ignore start @preserve -- unreachable, same reason again: only\n       XXX/xxx's regex can produce a colon-shaped body; X/x/XX/xx never\n       capture one. */\n    // +HH:MM — XXX/xxx only.\n    if (token !== 'XXX' && token !== 'xxx') {\n      throw new Error(\n        `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it doesn't use a colon (use \"${sign}${body.slice(0, 2)}${body.slice(3)}\" instead).`\n      );\n    }\n    /* c8 ignore stop @preserve */\n    hoursStr = body.slice(0, 2);\n    minutesStr = body.slice(3, 5);\n  /* c8 ignore start @preserve -- unreachable: every offset token's\n     regex only ever produces a body of length 2, length 4, or length 5\n     with a colon at index 2 (see OFFSET_SHAPES in pattern.ts) — no\n     shape falls outside those three cases, so this else arm can't be\n     taken through parse(). Kept as an exhaustiveness fallback so\n     hoursStr/minutesStr are assigned on every path TypeScript can see. */\n  } else {\n    throw new Error(`temporal-fmt: offset \"${raw}\" doesn't match the shape token \"${token}\" accepts.`);\n  }\n  /* c8 ignore stop @preserve */\n\n  const hours = Number(hoursStr);\n  const minutes = Number(minutesStr);\n  // Per-piece range checks catch most malformed input.\n  if (hours > 14) {\n    throw new InvalidOffsetError({\n      actual: raw,\n      message: `temporal-fmt: offset hours ${hours} in \"${raw}\" out of range (max 14 — Kiritimati, Line Islands is +14:00).`,\n    });\n  }\n  if (minutes > 59) {\n    throw new InvalidOffsetError({\n      actual: raw,\n      message: `temporal-fmt: offset minutes ${minutes} in \"${raw}\" out of range (max 59).`,\n    });\n  }\n  // Boundary: +14:01..+14:59 and -12:01..-12:59 are out of range even\n  // though each piece alone is in bounds — the overall offset exceeds\n  // the IANA-supported range.\n  if (sign === '+' && hours === 14 && minutes !== 0) {\n    throw new InvalidOffsetError({\n      actual: raw,\n      message: `temporal-fmt: offset \"${raw}\" exceeds the maximum supported UTC offset of +14:00.`,\n    });\n  }\n  if (sign === '-' && hours === 12 && minutes !== 0) {\n    throw new InvalidOffsetError({\n      actual: raw,\n      message: `temporal-fmt: offset \"${raw}\" exceeds the maximum supported negative UTC offset of -12:00.`,\n    });\n  }\n  return `${sign}${hoursStr}:${minutesStr}`;\n}\n\nfunction assignField<T>(fields: Fields, key: keyof Fields, value: T): void {\n  (fields as Record<string, T | undefined>)[key] = value;\n}\n\nfunction applyGroup(fields: Fields, token: string, raw: string, locale: string, formatStr: string): void {\n  const vocab = getLocaleVocab(locale);\n  switch (token) {\n    case 'yyyy': case 'y':\n      assignField(fields, 'year', Number(raw));\n      break;\n    case 'yy':\n      assignField(fields, 'twoDigitYear', Number(raw));\n      break;\n    case 'MM': case 'M':\n      assignField(fields, 'month', Number(raw));\n      break;\n    case 'MMMM':\n      assignField(fields, 'month', vocab.monthLong.indexOf(raw) + 1);\n      break;\n    case 'MMM':\n      assignField(fields, 'month', vocab.monthShort.indexOf(raw) + 1);\n      break;\n    case 'dd': case 'd':\n      assignField(fields, 'day', Number(raw));\n      break;\n    case 'EEEE':\n      assignField(fields, 'weekdayRaw', raw);\n      assignField(fields, 'weekdayExpected', vocab.weekdayLong.indexOf(raw) + 1);\n      break;\n    case 'EEE':\n      assignField(fields, 'weekdayRaw', raw);\n      assignField(fields, 'weekdayExpected', vocab.weekdayShort.indexOf(raw) + 1);\n      break;\n    case 'HH': case 'H':\n      assignField(fields, 'hour', Number(raw));\n      break;\n    case 'hh': case 'h':\n      assignField(fields, 'hour12', Number(raw));\n      break;\n    case 'mm': case 'm':\n      assignField(fields, 'minute', Number(raw));\n      break;\n    case 'ss': case 's':\n      assignField(fields, 'second', Number(raw));\n      break;\n    case 'S': case 'SS': case 'SSS': case 'SSSS': case 'SSSSS':\n    case 'SSSSSS': case 'SSSSSSS': case 'SSSSSSSS': case 'SSSSSSSSS': {\n      // The captured digits are the leading N digits of a nanosecond-of-second\n      // value, not the whole thing — \"5\" under SSSSSSSSS means 500000000ns\n      // (half a second), not 5ns. Right-padding to 9 digits before splitting\n      // is what makes that work; left-padding (or just Number(raw)) would\n      // read \"5\" as 5ns instead.\n      const nanoOfSecond = Number(raw.padEnd(9, '0'));\n      assignField(fields, 'millisecond', Math.floor(nanoOfSecond / 1_000_000));\n      assignField(fields, 'microsecond', Math.floor(nanoOfSecond / 1_000) % 1_000);\n      assignField(fields, 'nanosecond', nanoOfSecond % 1_000);\n      break;\n    }\n    case 'a': {\n      // Matches case-insensitively (see pattern.ts's foldCase), so the\n      // lookup here has to fold too, or \"pm\" would pass the regex and\n      // then fail this indexOf against the exact-case vocab.\n      const periodIndex = vocab.dayPeriod.findIndex((p) => p.toLowerCase() === raw.toLowerCase());\n      /* c8 ignore start @preserve -- unreachable: the 'a' token's regex\n         fragment (pattern.ts's alternation() over vocab.dayPeriod) can\n         only ever capture a case-insensitive match of one of\n         vocab.dayPeriod's own entries. Both the regex and this lookup\n         derive their vocab from the same `locale` via getLocaleVocab(),\n         so periodIndex can't come back negative through parse(). */\n      if (periodIndex < 0) throw new Error(`temporal-fmt: unknown day period \"${raw}\" for locale \"${locale}\".`);\n      /* c8 ignore stop @preserve */\n      assignField(fields, 'dayPeriodRaw', raw);\n      assignField(fields, 'isPM', periodIndex === 1);\n      break;\n    }\n    case 'zzz':\n      assignField(fields, 'timeZoneId', raw);\n      break;\n    case 'X': case 'XX': case 'XXX':\n    case 'x': case 'xx': case 'xxx':\n      assignField(fields, 'offsetString', parseOffsetString(raw, token));\n      break;\n    case 'Q':\n      assignField(fields, 'quarter', Number(raw));\n      break;\n    case 'QQQ':\n      // strips the literal \"Q\" prefix the token itself formats; the suffix\n      // digit is the quarter value 1-4\n      assignField(fields, 'quarter', Number(raw.slice(1)));\n      break;\n  }\n}\n\n// The lenient split-selection heuristic for ambiguous glued numeric runs.\n// See README \"Lenient parse mode\" — the strict default throws on these,\n// lenient mode opts into picking one split instead.\nfunction pickLenientSplit(splits: number[][], tokens: string[]): number[] {\n  // Prefer the split where a \"d\" (day) token, if any, has a value of 12 or\n  // less. Rationale: when a person writes a glued run like \"121\" for an\n  // Md format string, the reading \"Dec 1\" (M=12, d=1) is what they\n  // typically meant — if they meant \"Jan 21\" they would more often have\n  // written it as \"1/21\" or \"01/21\" with a separator or padding, since the\n  // 2-digit day is the more naturally-cohesive unit to keep glued. This\n  // isn't a guarantee, which is exactly why lenient mode is opt-in — but\n  // it's a reasonable default when the caller has asked us to guess.\n  const dayIndex = tokens.indexOf('d');\n  if (dayIndex !== -1) {\n    const smallDaySplits = splits.filter((s) => s[dayIndex]! <= 12);\n    if (smallDaySplits.length > 0) {\n      return smallDaySplits[0]!;\n    }\n  }\n  // Fallback to the first valid split when the day heuristic doesn't\n  // narrow it down — deterministic, and \"first\" here means \"whichever\n  // enumerateValidSplits returned first\", which is a depth-first\n  // leftmost-shortest walk over the candidate splits.\n  return splits[0]!;\n}\n\n// emulates strptime (POSIX) for 2-digit years so the result doesn't depend\n// on the current clock: 00-68 -> 2000-2068, 69-99 -> 1900-1999\n// https://www.man7.org/linux//man-pages/man3/strptime.3p.html\nfunction resolveYear(fields: Fields): number | undefined {\n  if (fields.year !== undefined && fields.twoDigitYear !== undefined) {\n    // FormatSyntaxError, not ParseMismatchError: this is a contradiction\n    // in the format string itself (both a full-year token and \"yy\" are\n    // present), not a mismatch between the format and a specific input.\n    // \"y\" and \"yyyy\" both land in fields.year, so this also covers a\n    // \"y\"+\"yy\" mix, not just \"yyyy\"+\"yy\" — kept generic rather than\n    // naming a specific pair.\n    throw new FormatSyntaxError({\n      message: 'temporal-fmt: format string mixes a full-year token (\"yyyy\"/\"y\") with the two-digit \"yy\" token.',\n    });\n  }\n  if (fields.year !== undefined) return fields.year;\n  if (fields.twoDigitYear !== undefined) {\n    return fields.twoDigitYear <= 68 ? 2000 + fields.twoDigitYear : 1900 + fields.twoDigitYear;\n  }\n  return undefined;\n}\n\nfunction resolveHour(fields: Fields, formatStr: string, locale: string): number | undefined {\n  if (fields.hour !== undefined && fields.hour12 !== undefined) {\n    throw new FormatSyntaxError({\n      format: formatStr,\n      message:\n        `temporal-fmt: format string \"${formatStr}\" mixes a 24-hour token (\"HH\"/\"H\") with a ` +\n        `12-hour token (\"hh\"/\"h\").`,\n    });\n  }\n  if (fields.hour !== undefined) {\n    if (fields.dayPeriodRaw !== undefined) {\n      throw new FormatSyntaxError({\n        format: formatStr,\n        message:\n          `temporal-fmt: format string \"${formatStr}\" mixes a 24-hour token (\"HH\"/\"H\") with a ` +\n          `day-period token (\"a\"). Both describe the same field; use one or the other, not both.`,\n      });\n    }\n    return fields.hour;\n  }\n  if (fields.hour12 !== undefined) {\n    if (fields.isPM === undefined) {\n      throw new FormatSyntaxError({\n        format: formatStr,\n        message:\n          `temporal-fmt: format string \"${formatStr}\" uses a 12-hour token (\"hh\"/\"h\") without an \"a\" token, ` +\n          `so parse() can't tell AM from PM.`,\n      });\n    }\n    return (fields.hour12 % 12) + (fields.isPM ? 12 : 0);\n  }\n  return undefined;\n}\n\n/**\n * Parses `input` against `formatStr` and builds the real Temporal value it\n * describes: a `Temporal.PlainDate`, `PlainTime`, `PlainDateTime`, or\n * `ZonedDateTime` depending on which tokens are present.\n *\n * Returns `unknown` — this package has no ambient `Temporal` types to return\n * a real one against.\n *\n * `options.locale` picks the calendar the result is built in. Pass a locale\n * tag with a `-u-ca-` extension (e.g. `'en-u-ca-hebrew'`) to parse into a\n * non-Gregorian calendar.\n *\n * @throws if `input` doesn't match `formatStr`'s shape at all\n * @throws if it matches the shape but describes an impossible date (e.g. Feb\n * 30) or self-contradictory data (e.g. a weekday name that doesn't match the\n * actual date)\n *\n * @example\n * parse('yyyy-MM-dd HH:mm', '2026-08-04 15:45') // Temporal.PlainDateTime\n * parse('yyyy-MM', '2026-08-04T15:45:30') // throws — shape doesn't match\n * parse('yyyy-MM-dd', '2026-02-30') // throws — not a real date\n */\nexport function parse(formatStr: string, input: string, options: NumberingParseOptions = {}): unknown | undefined {\n  if (formatStr.length > MAX_FORMAT_LENGTH) {\n    throw new FormatSyntaxError({\n      format: formatStr,\n      message:\n        `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n        `(got ${formatStr.length}).`,\n    });\n  }\n\n  if (input.length > MAX_INPUT_LENGTH) {\n    throw new FormatSyntaxError({\n      input,\n      message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`,\n    });\n  }\n\n  // Transliterate non-ASCII numerals to ASCII before any matching happens,\n  // when the caller opts in via parseNumberingSystem. Every regex this\n  // module builds expects 0-9; this is the one place that assumption\n  // could otherwise be violated by locale-native input digits.\n  if (options.parseNumberingSystem) {\n    input = applyParseNumbering(input, options);\n  }\n\n  const locale = options.locale ?? DEFAULT_LOCALE;\n  const calendar = resolveCalendar(locale);\n  const pattern = getPattern(formatStr, locale);\n  const match = pattern.regex.exec(input);\n  if (!match) {\n    throw new ParseMismatchError({\n      input, format: formatStr,\n      // reason set explicitly (not just message): parse.test.js's\n      // \"safeParse: failure returns { ok: false, error } with a\n      // TemporalFmtError\" asserts result.error.reason carries the\n      // underlying detail text, matching what wrapUntypedError used to\n      // populate when this message reached it as a caught plain Error.\n      reason: 'no valid pattern matches the format string and input shape',\n      message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n    });\n  }\n\n  if (pattern.groups.length === 0) {\n    // ParseMismatchError, not FormatSyntaxError: pinned by\n    // errors.test.js's \"format string with no tokens at all falls\n    // through to ParseMismatchError\" — wrapUntypedError's classifier\n    // has no branch for this message, so it lands on the\n    // PARSE_MISMATCH fallback, and a direct throw here needs to\n    // agree with that.\n    throw new ParseMismatchError({\n      format: formatStr,\n      // reason set explicitly: errors.test.js's \"format string with no\n      // tokens at all falls through to ParseMismatchError\" asserts\n      // error.reason matches /has no tokens/.\n      reason: `format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n      message: `temporal-fmt: format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n    });\n  }\n\n  // The regex's zzz fragment only matches a bounded zone-id *shape* (see\n  // TIME_ZONE_SHAPE in pattern.ts) rather than alternating every real IANA\n  // name inline, so a shape match isn't proof of a real zone yet — check\n  // Zone ids can't be enumerated in the regex itself (there are ~400 of\n  // them and they change over time as IANA updates the tz database), so\n  // the regex only captures the zzz group's shape and this loop checks\n  // each captured zzz group against the actual zone list here. Unlike a\n  // regex-shape mismatch, this failure has a specific cause worth\n  // naming: the shape matched but the zone id itself isn't recognized.\n  for (const { name, token } of pattern.groups) {\n    if (token === 'zzz' && !isValidTimeZone(match.groups![name]!)) {\n      throw new InvalidTimeZoneError({\n        input, format: formatStr, actual: match.groups![name],\n        reason: 'not a recognized IANA time zone identifier',\n      });\n    }\n  }\n\n  // A run of 2+ adjacent unpadded-numeric tokens with no literal separator\n  // (e.g. \"Md\", \"dM\", \"Hms\") is captured by a single bounded digit group in\n  // the regex (see buildCapturingPattern — per-token variable-width\n  // fragments made near-miss matching exponential in the number of glued\n  // tokens). Resolve each run's per-token split here instead: unique valid\n  // split resolves (identical to what the old regex's own greedy match\n  // produced, since the old match was always one of the valid splits);\n  // 2+ valid splits is genuinely ambiguous input — strict mode throws,\n  // lenient opts into the documented heuristic. A span with 0 valid\n  // splits matched the run's width window but names no valid per-token\n  // assignment, which the old per-token fragments rejected at match\n  // time — surface the same \"no valid pattern matches\" error.\n  const runValues = new Map<string, string>();\n  for (const run of pattern.ambiguousRuns) {\n    const runDigits = match.groups![run.groupName]!;\n    const splits = enumerateValidSplits(runDigits, run.tokens);\n    if (splits.length === 0) {\n      throw new ParseMismatchError({\n        input, format: formatStr,\n        reason: 'no valid pattern matches the format string and input shape',\n        message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n      });\n    }\n    if (splits.length > 1) {\n      // Strict default — throw on ambiguity. The whole point of the\n      // library's parse() is to refuse to guess when the same input has\n      // more than one valid reading. Lenient mode (opt-in via\n      // options.lenient) instead picks one split via a documented\n      // heuristic — see pickLenientSplit() above and the README section\n      // \"Lenient parse mode\" for why this is strictly additive and never\n      // the default.\n      if (!options.lenient) {\n        throw new AmbiguousInputError({\n          input, format: formatStr,\n          message:\n            `temporal-fmt: \"${runDigits}\" in format string \"${formatStr}\" is ambiguous — ` +\n            `${splits.length} different ways to read tokens \"${run.tokens.join('')}\" (with no separator ` +\n            `between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[1])}). ` +\n            `parse() won't guess; add a separator between these tokens, or use their padded form ` +\n            `(e.g. \"MM\" instead of \"M\") so each one has a fixed width. ` +\n            `Pass { lenient: true } to opt into a documented heuristic that picks one.`,\n        });\n      }\n      const picked = pickLenientSplit(splits, run.tokens);\n      run.groupNames.forEach((name, idx) => runValues.set(name, String(picked[idx])));\n    } else {\n      run.groupNames.forEach((name, idx) => runValues.set(name, String(splits[0]![idx])));\n    }\n  }\n  const fields: Fields = {};\n  // Per-token values for glued-run members come from the split\n  // enumeration above (runValues); every other token reads its own\n  // regex group directly.\n  for (const { name, token } of pattern.groups) {\n    const raw = runValues.get(name) ?? match.groups![name]!;\n    applyGroup(fields, token, raw, locale, formatStr);\n  }\n\n  const year = resolveYear(fields);\n  const hour = resolveHour(fields, formatStr, locale);\n  const { month, day, minute, second, millisecond, microsecond, nanosecond, timeZoneId, offsetString, weekdayExpected, weekdayRaw, quarter } = fields;\n\n  const hasAnyDatePart = year !== undefined || month !== undefined || day !== undefined;\n  const hasFullDate = year !== undefined && month !== undefined && day !== undefined;\n  if (hasAnyDatePart && !hasFullDate) {\n    throw new InvalidDateError({\n      format: formatStr,\n      message:\n        `temporal-fmt: format string \"${formatStr}\" has an incomplete date — ` +\n        `year, month, and day tokens must all be present together.`,\n    });\n  }\n\n  const hasTime = hour !== undefined || minute !== undefined || second !== undefined || millisecond !== undefined;\n\n  if (timeZoneId !== undefined && !(hasFullDate && hasTime)) {\n    throw new FormatSyntaxError({\n      format: formatStr,\n      message:\n        `temporal-fmt: format string \"${formatStr}\" has a \"zzz\" token but needs a full date and time ` +\n        `to build a ZonedDateTime.`,\n    });\n  }\n\n  // Mirror zzz's full-date-and-time requirement: an offset alone is\n  // meaningless without a wall-clock instant to anchor it to. Throws the\n  // same kind of \"needs full date and time\" error zzz throws — separate\n  // message so a caller reading it can tell which token type they\n  // forgot to pair with a full date+time.\n  if (offsetString !== undefined && !(hasFullDate && hasTime)) {\n    throw new FormatSyntaxError({\n      format: formatStr,\n      message:\n        `temporal-fmt: format string \"${formatStr}\" has an offset token (X/XX/XXX/x/xx/xxx) but needs a full date and time ` +\n        `to build a ZonedDateTime.`,\n    });\n  }\n\n  if (weekdayExpected !== undefined && !hasFullDate) {\n    throw new FormatSyntaxError({\n      format: formatStr,\n      message:\n        `temporal-fmt: format string \"${formatStr}\" has a weekday token (\"EEEE\"/\"EEE\") but needs ` +\n        `a full date to validate it against.`,\n    });\n  }\n\n  if (!hasFullDate && !hasTime) {\n    // shouldn't happen — every token maps to a date, time, zone, or\n    // weekday field, and weekday-without-date already threw above\n    throw new FormatSyntaxError({\n      format: formatStr,\n      message: `temporal-fmt: format string \"${formatStr}\" has no date or time tokens to parse.`,\n    });\n  }\n\n  const temporal = getTemporal();\n  const timeFields = {\n    hour: hour ?? 0,\n    minute: minute ?? 0,\n    second: second ?? 0,\n    millisecond: millisecond ?? 0,\n    microsecond: microsecond ?? 0,\n    nanosecond: nanosecond ?? 0,\n  };\n  // omitted entirely for the default calendar (see resolveCalendar) so\n  // construction stays plain ISO 8601 unless a caller's locale asks for\n  // something else — Temporal calendars don't apply to time-only values.\n  const calendarField = calendar ? { calendar } : {};\n\n  // overflow: 'reject' — without it Temporal *clamps* out-of-range fields\n  // (Feb 30 silently becomes Feb 28) instead of throwing, which would\n  // contradict the \"throws on genuinely invalid data\" behavior parse() promises.\n  const reject = { overflow: 'reject' as const };\n\n  let result: unknown;\n  try {\n    if (timeZoneId !== undefined) {\n      // offset: 'prefer' never throws on a mismatch — it just falls back to\n      // the zone's real offset at this instant, silently overriding\n      // whatever the offset token said. That's also what resolves a\n      // repeated wall-clock time (DST fall-back): without an explicit\n      // offset, Temporal defaults to the first occurrence, so passing the\n      // token's offset here is what lets a second-occurrence input resolve\n      // to the second occurrence instead of always falling back to the\n      // first. Either way, \"prefer\" can't be used to detect disagreement —\n      // that's checked explicitly below, once we have a real ZonedDateTime\n      // to compare against, instead of relying on the wording of whatever\n      // error Temporal's active implementation happens to throw (that\n      // wording isn't part of the spec and differs between the native\n      // Temporal global and userland polyfills).\n      const zoneOptions: Temporal.ZonedDateTimeFromOptions = { overflow: 'reject', offset: 'prefer' };\n      result = temporal.ZonedDateTime.from(\n        {\n          year: year!, month: month!, day: day!, ...timeFields, ...calendarField,\n          timeZone: timeZoneId,\n          ...(offsetString !== undefined ? { offset: offsetString } : {}),\n        },\n        zoneOptions\n      );\n      if (offsetString !== undefined) {\n        // 'prefer' silently rewrites the wall-clock time itself when the\n        // input falls in a DST gap (the time never occurred, so there's\n        // no instant to prefer toward) — it doesn't just pick a\n        // different offset for the same clock time, the way it does for\n        // an overlap. Checking offsetString alone can't tell \"gap,\n        // silently moved\" apart from \"overlap, correctly resolved,\"\n        // since both can produce an actualOffset that differs from what\n        // was parsed. Comparing the wall-clock fields catches the gap\n        // case: they can only drift from the parsed input if Temporal\n        // moved the clock time to escape the gap.\n        //\n        // Only checked when an offset token was given: with no offset\n        // token to disagree with, a gap shifting forward is the\n        // documented, wanted behavior (there's nothing to reject against).\n        const zdt = result as Temporal.ZonedDateTime;\n        const wallClockShifted =\n          zdt.hour !== timeFields.hour ||\n          zdt.minute !== timeFields.minute ||\n          zdt.second !== timeFields.second;\n        if (wallClockShifted) {\n          // InvalidDateError, not AmbiguousInputError: the message\n          // *mentions* \"not an ambiguous... instant\" as a negation, which\n          // is a false-positive match against wrapUntypedError's\n          // /ambiguous/i classifier regex — the actual failure here is a\n          // wall-clock time that doesn't exist (DST gap), which is an\n          // invalid-value problem, not an ambiguous-reading-of-input\n          // problem the way the numeric-token-glue ambiguity above is.\n          throw new InvalidDateError({\n            input, format: formatStr,\n            message:\n              `\"${timeZoneId}\" has no such wall-clock time on this date — it falls in a DST gap, ` +\n              `not an ambiguous or valid instant.`,\n          });\n        }\n        const actualOffset = zdt.offset;\n        if (actualOffset !== offsetString) {\n          throw new ParseMismatchError({\n            input, format: formatStr,\n            message:\n              `has both a \"zzz\" zone (${timeZoneId}) and an offset token (${offsetString}), ` +\n              `but the zone's actual offset at this date/time is ${actualOffset}, not ${offsetString}.`,\n          });\n        }\n      }\n    } else if (offsetString !== undefined) {\n      // An offset token with no \"zzz\" zone token present. Previously this\n      // built a fixed-offset ZonedDateTime directly from the offset\n      // string; now it's refused outright — an offset identifies a\n      // moment's distance from UTC, not a zone, and building a\n      // ZonedDateTime without a real zone identity papers over that\n      // difference instead of surfacing it.\n      throw new ParseMismatchError({\n        input, format: formatStr,\n        message:\n          `format string \"${formatStr}\" has an offset token but no \"zzz\" zone token. ` +\n          `An offset does not identify a time zone by itself — add \"zzz\" to the pattern, ` +\n          `or parse into a PlainDateTime/PlainDate/PlainTime if a zone isn't needed.`,\n      });\n    } else if (hasFullDate && hasTime) {\n      result = temporal.PlainDateTime.from({ year: year!, month: month!, day: day!, ...timeFields, ...calendarField }, reject);\n    } else if (hasFullDate) {\n      result = temporal.PlainDate.from({ year: year!, month: month!, day: day!, ...calendarField }, reject);\n    } else {\n      result = temporal.PlainTime.from(timeFields, reject);\n    }\n  } catch (err) {\n    throw new InvalidDateError({\n      input, format: formatStr,\n      message:\n        `temporal-fmt: \"${input}\" doesn't describe a valid date/time for format \"${formatStr}\": ` +\n        `${(err as Error).message}`,\n    });\n  }\n\n  if (weekdayExpected !== undefined) {\n    const actual = (result as { dayOfWeek: number }).dayOfWeek;\n    if (actual !== weekdayExpected) {\n      const vocab = getLocaleVocab(locale);\n      throw new InvalidDateError({\n        input, format: formatStr,\n        message:\n          `temporal-fmt: \"${weekdayRaw}\" doesn't match the actual weekday (${vocab.weekdayLong[actual - 1]}) ` +\n          `for the parsed date.`,\n      });\n    }\n  }\n\n  // Q/QQQ is a derived field of the month: 1-3 -> Q1, 4-6 -> Q2, 7-9 -> Q3,\n  // 10-12 -> Q4. If a format string carries a quarter token alongside\n  // month/date tokens, parse() cross-checks the parsed quarter against the\n  // month the same way EEEE cross-checks weekday against date — silently\n  // accepting a mismatch would defeat the point of having a quarter token\n  // at all, since you'd be telling parse() one thing and the date another.\n  if (quarter !== undefined && month !== undefined) {\n    const expectedQuarter = Math.ceil(month / 3);\n    if (quarter !== expectedQuarter) {\n      throw new InvalidDateError({\n        format: formatStr,\n        message:\n          `temporal-fmt: format string \"${formatStr}\" contains a quarter token (Q/QQQ) whose value ` +\n          `(Q${quarter}) disagrees with the parsed month's actual quarter — month ${month} is in ` +\n          `Q${expectedQuarter}.`,\n      });\n    }\n  }\n\n  return result;\n}\n\n// safeParse: returns a discriminated union instead of throwing. The\n// happy path returns `{ ok: true, value }` with the Temporal instance\n// (typed as `unknown` since this package has no ambient Temporal types).\n// The error path returns `{ ok: false, error }` where `error` is a\n// `TemporalFmtError` subclass when the failure is one the typed-error\n// surface in errors.ts knows how to classify (most of them), or a\n// wrapped plain `Error` (still inside a TemporalFmtError shell) when\n// the throw site hasn't been migrated yet. Callers needing the original\n// thrown object for backward compatibility should use parse() directly.\nexport type SafeParseResult =\n  | { ok: true; value: unknown }\n  | { ok: false; error: TemporalFmtError };\n\nexport function safeParse(formatStr: string, input: string, options: NumberingParseOptions = {}): SafeParseResult {\n  try {\n    return { ok: true, value: parse(formatStr, input, options) };\n  } catch (err) {\n    // Pass through typed errors unchanged — preserves the structured\n    // fields (code/token/position/etc.) the existing typed-error\n    // surface already populated.\n    //\n    // Every reachable throw site in parse()'s call graph now throws a\n    // TemporalFmtError directly, so this condition is always true in the\n    // current test suite; the false path (falling through to\n    // wrapUntypedError below) is kept as a safety net for any throw site\n    // added later without being migrated to a typed class immediately —\n    // removing it would silently break the \"safeParse always returns a\n    // TemporalFmtError\" contract documented above. Ignoring the whole\n    // if/else as one unit is harmless for the true branch (real tests\n    // still execute and count it — c8 just excludes it from the\n    // denominator), and it's the only shape that actually suppresses the\n    // false-branch marker, since c8 has no standalone \"ignore else\".\n    /* c8 ignore start @preserve */\n    if (err instanceof TemporalFmtError) {\n      return { ok: false, error: err };\n    }\n    return { ok: false, error: wrapUntypedError(err as Error, { input, format: formatStr }) };\n  }\n  /* c8 ignore stop @preserve */\n}\n\n// tryParse: best-effort variant. Returns the parsed value or undefined.\n// Suppresses diagnostics entirely — when callers need the reason for\n// a failure, they should use safeParse(). Intentionally loose on the\n// return type (unknown) since this package has no ambient Temporal\n// types to return a real one against.\nexport function tryParse(formatStr: string, input: string, options: NumberingParseOptions = {}): unknown | undefined {\n  try {\n    return parse(formatStr, input, options);\n  } catch {\n    return undefined;\n  }\n}\n\n// parseToParts: returns the matched groups with token labels, before\n// any Temporal construction. Useful for callers that want to inspect\n// what each token captured (e.g. to build a non-Temporal result, or to\n// cross-check fields themselves) without committing to the inferred\n// Temporal type parse() would build.\n//\n// Throws the same errors parse() throws for early validation (unknown\n// token, unterminated quote, no-match, ambiguity in strict mode) since\n// those failures happen before any group assignment. Construction-time\n// errors (Feb 30, weekday mismatch, etc.) do not happen here —\n// parseToParts doesn't construct anything, so it can't fail at that step.\nexport interface ParsedPart {\n  token: string;\n  raw: string;\n  // Field name (year/month/day/...) this token would assign if handed\n  // to parse()'s applyGroup loop. undefined for tokens that don't map\n  // to a single field (none today, but kept here so future additions\n  // don't have to widen the type).\n  field?: string;\n  // Position of `raw` in `input`, 0-indexed. Lets a caller highlight\n  // the matched span in an editor/CLI.\n  position: number;\n}\n\nexport function parseToParts(formatStr: string, input: string, options: NumberingParseOptions = {}): ParsedPart[] {\n  if (formatStr.length > MAX_FORMAT_LENGTH) {\n    throw new FormatSyntaxError({\n      format: formatStr,\n      message:\n        `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n        `(got ${formatStr.length}).`,\n    });\n  }\n  if (input.length > MAX_INPUT_LENGTH) {\n    throw new FormatSyntaxError({\n      input,\n      message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`,\n    });\n  }\n\n  // Same numeral transliteration parse() does — see the comment there.\n  if (options.parseNumberingSystem) {\n    input = applyParseNumbering(input, options);\n  }\n\n  const locale = options.locale ?? DEFAULT_LOCALE;\n  const pattern = getPattern(formatStr, locale);\n  const match = pattern.regex.exec(input);\n  if (!match) {\n    throw new ParseMismatchError({\n      input, format: formatStr,\n      // reason set explicitly (not just message): parse.test.js's\n      // \"safeParse: failure returns { ok: false, error } with a\n      // TemporalFmtError\" asserts result.error.reason carries the\n      // underlying detail text, matching what wrapUntypedError used to\n      // populate when this message reached it as a caught plain Error.\n      reason: 'no valid pattern matches the format string and input shape',\n      message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n    });\n  }\n  if (pattern.groups.length === 0) {\n    // ParseMismatchError, not FormatSyntaxError: pinned by\n    // errors.test.js's \"format string with no tokens at all falls\n    // through to ParseMismatchError\" — wrapUntypedError's classifier\n    // has no branch for this message, so it lands on the\n    // PARSE_MISMATCH fallback, and a direct throw here needs to\n    // agree with that.\n    throw new ParseMismatchError({\n      format: formatStr,\n      // reason set explicitly: errors.test.js's \"format string with no\n      // tokens at all falls through to ParseMismatchError\" asserts\n      // error.reason matches /has no tokens/.\n      reason: `format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n      message: `temporal-fmt: format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n    });\n  }\n  // Same zzz shape-validation parse() does — kept here for parity, so\n  // a caller using parseToParts sees the same InvalidTimeZoneError for\n  // a bogus zone id, not a silently-accepted bogus zone.\n  for (const { name, token } of pattern.groups) {\n    if (token === 'zzz' && !isValidTimeZone(match.groups![name]!)) {\n      throw new InvalidTimeZoneError({\n        input, format: formatStr, actual: match.groups![name],\n        reason: 'not a recognized IANA time zone identifier',\n      });\n    }\n  }\n\n  // Glued-run split handling: same as parse() — each run's single regex\n  // group is split into per-token values by enumerateValidSplits();\n  // unique split resolves, 2+ splits throws in strict mode / heuristic-\n  // picks in lenient, 0 splits is a shape mismatch. parseToParts mirrors\n  // parse() so callers switching between the two on the same input get\n  // consistent results.\n  const runValues = new Map<string, string>();\n  for (const run of pattern.ambiguousRuns) {\n    const runDigits = match.groups![run.groupName]!;\n    const splits = enumerateValidSplits(runDigits, run.tokens);\n    if (splits.length === 0) {\n      throw new ParseMismatchError({\n        input, format: formatStr,\n        reason: 'no valid pattern matches the format string and input shape',\n        message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n      });\n    }\n    if (splits.length > 1) {\n      if (!options.lenient) {\n        throw new AmbiguousInputError({\n          input, format: formatStr,\n          message:\n            `temporal-fmt: \"${runDigits}\" in format string \"${formatStr}\" is ambiguous — ` +\n            `${splits.length} different ways to read tokens \"${run.tokens.join('')}\" (with no separator ` +\n            `between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[1])}). ` +\n            `parse() won't guess; add a separator between these tokens, or use their padded form ` +\n            `(e.g. \"MM\" instead of \"M\") so each one has a fixed width. ` +\n            `Pass { lenient: true } to opt into a documented heuristic that picks one.`,\n        });\n      }\n      const picked = pickLenientSplit(splits, run.tokens);\n      run.groupNames.forEach((name, idx) => runValues.set(name, String(picked[idx])));\n    } else {\n      run.groupNames.forEach((name, idx) => runValues.set(name, String(splits[0]![idx])));\n    }\n  }\n\n  const parts: ParsedPart[] = [];\n  // match.indices.groups (provided by the regex 'd' flag) gives the\n  // [start, end] of each named group in the input. Used here so positions\n  // are accurate even when literals separate tokens — summing raw\n  // lengths alone wouldn't account for the literal characters between\n  // groups. Falls back to the cumulative-raw-length heuristic on engines\n  // without 'd' support (none we target, but the fallback keeps the\n  // code robust if the flag is ever removed).\n  const indices = (match as RegExpMatchArray & { indices?: { groups?: Record<string, [number, number]> } }).indices;\n  const groupIndices = indices?.groups;\n  let consumed = 0;\n  for (const { name, token } of pattern.groups) {\n    const raw = runValues.get(name) ?? match.groups![name]!;\n    // Glued-run members have no regex group of their own — the regex's\n    // recorded indices point at the run's overall span, not the\n    // individual token's slice within it — so fall back to cumulative-\n    // raw-length for them. Positions stay monotonic but may not be\n    // exact for tokens inside a resolved run. Documented as a known\n    // limitation; the alternative (re-running the regex with the chosen\n    // split baked in) would mean a second match pass for a corner case\n    // the caller opted into by gluing unpadded tokens.\n    const fromIndices = !runValues.has(name) && groupIndices?.[name];\n    /* c8 ignore next */\n    const position = fromIndices ? fromIndices[0] : (match.index ?? 0) + consumed;\n    parts.push({ token, raw, position });\n    consumed += raw.length;\n  }\n  return parts;\n}\n\n// compileParser: pre-compiles a format string into an object whose\n// parse()/safeParse()/parseToParts() methods skip the per-call\n// pattern-cache lookup. The patternCache in this module means a plain\n// parse(fmt, input) call already pays only a Map lookup after the first\n// call, so compileParser is mostly an ergonomics affordance — useful\n// for callers who want to hold the compiled parser explicitly (e.g. to\n// inspect the pattern via the .pattern property).\nexport interface CompiledParser {\n  parse(input: string, options?: NumberingParseOptions): unknown;\n  safeParse(input: string, options?: NumberingParseOptions): SafeParseResult;\n  tryParse(input: string, options?: NumberingParseOptions): unknown | undefined;\n  parseToParts(input: string, options?: NumberingParseOptions): ParsedPart[];\n  readonly formatStr: string;\n  readonly pattern: CapturingPattern;\n}\n\nexport function compileParser(formatStr: string, options: NumberingParseOptions = {}): CompiledParser {\n  if (formatStr.length > MAX_FORMAT_LENGTH) {\n    throw new FormatSyntaxError({\n      format: formatStr,\n      message:\n        `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n        `(got ${formatStr.length}).`,\n    });\n  }\n  // Pre-compile against the default locale; per-call locales will\n  // re-resolve via getPattern() if they differ. Most callers use one\n  // locale consistently, so pre-compiling against the default keeps\n  // the fast path fast.\n  const locale = options.locale ?? DEFAULT_LOCALE;\n  const pattern = getPattern(formatStr, locale);\n  return {\n    formatStr,\n    pattern,\n    parse(input: string, opts: NumberingParseOptions = {}) {\n      return parse(formatStr, input, { locale, ...opts });\n    },\n    safeParse(input: string, opts: NumberingParseOptions = {}) {\n      return safeParse(formatStr, input, { locale, ...opts });\n    },\n    tryParse(input: string, opts: NumberingParseOptions = {}) {\n      return tryParse(formatStr, input, { locale, ...opts });\n    },\n    parseToParts(input: string, opts: NumberingParseOptions = {}) {\n      return parseToParts(formatStr, input, { locale, ...opts });\n    },\n  };\n}","/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// tsconfig here is lib: [\"ESNext\"] only, so there's no ambient Temporal\n// namespace type to lean on. everywhere else in the codebase we just read\n// fields off whatever Temporal-like object gets passed in (see TemporalLike\n// in tokens.ts) but this file is the one place that actually touches the\n// Temporal namespace itself — PlainDate.from and friends. parse() uses it\n// to build results, tokens.ts uses it to check native Intl<->Temporal\n// support. you can hand us your own implementation via setTemporal(), or\n// we just grab globalThis.Temporal if there is one\ninterface TemporalFactory {\n  from(fields: Record<string, number | string | undefined>, options?: { overflow?: 'constrain' | 'reject'; disambiguation?: 'compatible' | 'earlier' | 'later' | 'reject'; offset?: 'use' | 'ignore' | 'prefer' | 'reject' }): unknown;\n  // compare() is on the namespace, not instances (Temporal.PlainDate.compare).\n  // made optional since someone might hand us a stripped-down shim that\n  // doesn't bother implementing it\n  compare?(one: unknown, two: unknown): number;\n}\n\n// Instant doesn't use from(fields) like the others, it's\n// fromEpochMilliseconds / fromEpochNanoseconds. needs its own shape.\ninterface InstantFactory {\n  from(iso: string): unknown;\n  fromEpochMilliseconds(ms: number): unknown;\n  fromEpochMicroseconds(µs: number): unknown;\n  fromEpochNanoseconds(ns: bigint): unknown;\n}\n\n// Duration uses from(fields) too, but also has round() and compare()\ninterface DurationFactory extends TemporalFactory {\n  compare?(one: unknown, two: unknown): number;\n  prototype?: { round(options: unknown): unknown };\n}\n\n// PlainYearMonth / PlainMonthDay also just use from(fields) — same shape\n// as TemporalFactory really, kept as its own type so callers can narrow\nexport interface TemporalNamespace {\n  PlainDate: TemporalFactory;\n  PlainTime: TemporalFactory;\n  PlainDateTime: TemporalFactory;\n  ZonedDateTime: TemporalFactory;\n  Instant?: InstantFactory;\n  Duration?: DurationFactory;\n  PlainYearMonth?: TemporalFactory;\n  PlainMonthDay?: TemporalFactory;\n}\n\nlet injectedTemporal: TemporalNamespace | undefined;\n\n// anything caching something tied to WHICH Temporal impl is active\n// (right now just tokens.ts's native-Intl probe) registers a listener\n// here so it knows to invalidate when setTemporal() swaps things out —\n// see setTemporal() below for why that matters. kept this as a plain\n// array instead of a full event emitter since all we need is \"call\n// everyone back, in order, no payload\" — didn't need unsubscribe either\nconst onTemporalChanged: Array<() => void> = [];\n\nexport function subscribeToTemporalChanges(listener: () => void): void {\n  onTemporalChanged.push(listener);\n}\n\n/**\n * Explicitly hand temporal-fmt the Temporal implementation to use, instead\n * of relying on a global `Temporal`. Call this once, before your first\n * `format()`/`parse()`/`parseISO()`/etc.\n *\n * Call with no argument (or `undefined`) to clear the override and fall\n * back to `globalThis.Temporal` again.\n *\n * @example\n * import { Temporal } from 'temporal-polyfill';\n * import { setTemporal } from 'temporal-fmt';\n * setTemporal(Temporal);\n */\nexport function setTemporal(temporal?: TemporalNamespace): void {\n  injectedTemporal = temporal;\n  // tokens.ts caches its native-Intl probe result based on whatever\n  // Temporal was active the first time it ran. if that changes later\n  // (native -> polyfill, say) the cached result goes stale. so just reset\n  // everyone on any setTemporal() call — costs one extra probe re-run,\n  // worth it to not silently use a stale answer\n  for (const listener of onTemporalChanged) listener();\n}\n\nfunction resolveTemporal(): TemporalNamespace | undefined {\n  return injectedTemporal ?? (globalThis as unknown as { Temporal?: TemporalNamespace }).Temporal;\n}\n\nexport function getTemporal(): TemporalNamespace {\n  const temporal = resolveTemporal();\n  if (!temporal) {\n    throw new Error(\n      'temporal-fmt: parse() needs a Temporal implementation to construct its result. ' +\n      'Call setTemporal(Temporal) once at startup, or assign one to globalThis.Temporal ' +\n      '(native on Node 26+, or a polyfill like temporal-polyfill).'\n    );\n  }\n  return temporal;\n}\n","/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// structured error classes for parse()/format() failures.\n//\n// as of 0.9.0 every throw site on the parse/format data path —\n// tokenize.ts, pattern.ts, format.ts, parse.ts (parse/safeParse/tryParse/\n// parseToParts/compileParser), plus the two data-path throws in\n// localeVocab.ts (partValue/assertNoCollision on the getLocaleVocab side)\n// — throws one of these typed classes directly instead of a plain\n// `new Error(message)`. every site we migrated kept its exact pre-0.9.0\n// message text, and since subclasses of TemporalFmtError still pass\n// `instanceof Error` and match the same message regexes (e.g.\n// `/token \"HH\" requires/`), this didn't need a semver-major bump for\n// either of those checks. what DOES break in 0.9.0: if anyone was\n// specifically checking `err.constructor === Error` or `err.name ===\n// 'Error'`, that'll now see a different name (e.g. 'FormatSyntaxError').\n// noted in the 0.9.0 changelog.\n//\n// deliberately did NOT migrate:\n//  - localeVocab.ts's registration-time throws (assertValidVocab,\n//    registerLocaleVocab itself) — these are config-time API-misuse\n//    errors on data a developer hands in once at startup, not runtime\n//    parse/format failures, so they don't really fit this module's\n//    TemporalFmtErrorCode taxonomy as-is. assertNoCollision is shared\n//    between the registration path and the data path (getLocaleVocab)\n//    though, so its throw stays a plain Error for both until that gets\n//    split apart — see the tracking note where it's called.\n//\n// wrapUntypedError() below is still around for localeVocab.ts's\n// registration throws, and for anything a caller passes into\n// safeParse()/tryParse() from outside this package. on the data path\n// itself, safeParse's `if (err instanceof TemporalFmtError) return { ok:\n// false, error: err }` catches everything before it'd even reach the\n// classifier here, so honestly the regex branches below are dead for\n// parse/format/tokenize/pattern at this point — kept as the fallback for\n// whatever hasn't been migrated to a typed throw site yet\n\nexport type TemporalFmtErrorCode =\n  | 'FORMAT_SYNTAX_ERROR'\n  | 'UNKNOWN_TOKEN'\n  | 'PARSE_MISMATCH'\n  | 'INVALID_DATE'\n  | 'INVALID_TIME'\n  | 'INVALID_OFFSET'\n  | 'INVALID_TIME_ZONE'\n  | 'INVALID_CALENDAR'\n  | 'AMBIGUOUS_INPUT'\n  | 'INVALID_LOCALE'\n  | 'INVALID_DURATION';\n\nexport interface TemporalFmtErrorFields {\n  code: TemporalFmtErrorCode;\n  input?: string;\n  format?: string;\n  token?: string;\n  position?: number;\n  expected?: string;\n  actual?: string;\n  reason?: string;\n}\n\n// base class. `message` is the human-readable summary, the structured\n// fields are the machine-readable bits a linter or codemod would report\n// on. calling Error.captureStackTrace manually (where it exists) so the\n// stack points at wherever this got thrown from, not at this constructor\n// — same trick Node uses for its own error classes\nexport class TemporalFmtError extends Error {\n  readonly code: TemporalFmtErrorCode;\n  readonly input?: string;\n  readonly format?: string;\n  readonly token?: string;\n  readonly position?: number;\n  readonly expected?: string;\n  readonly actual?: string;\n  readonly reason?: string;\n\n  constructor(message: string, fields: TemporalFmtErrorFields) {\n    super(message);\n    this.name = 'TemporalFmtError';\n    this.code = fields.code;\n    this.input = fields.input;\n    this.format = fields.format;\n    this.token = fields.token;\n    this.position = fields.position;\n    this.expected = fields.expected;\n    this.actual = fields.actual;\n    this.reason = fields.reason;\n    // don't want the stack pointing at this constructor line — captureStackTrace\n    // is V8-only though, so on other engines callers just get the default\n    // stack (pointing here) since there's nothing better to do about it\n    const capture = (Error as unknown as { captureStackTrace?: (target: Error, ctor?: Function) => void }).captureStackTrace;\n    if (typeof capture === 'function') {\n      capture(this, this.constructor);\n    }\n  }\n\n  // lets callers just call err.toJSON() for logging. plain Error doesn't\n  // serialize its non-enumerable fields on its own, so this picks them\n  // up explicitly\n  toJSON(): TemporalFmtErrorFields & { name: string; message: string } {\n    return {\n      name: this.name,\n      message: this.message,\n      code: this.code,\n      input: this.input,\n      format: this.format,\n      token: this.token,\n      position: this.position,\n      expected: this.expected,\n      actual: this.actual,\n      reason: this.reason,\n    };\n  }\n}\n\n// each subclass just fixes `code` so callers can switch on it without\n// having to re-check message text. the constructor only takes the\n// fields that actually vary per call; `code` and the default message\n// template come from the subclass itself\n\nexport class FormatSyntaxError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `format string \"${fields.format ?? ''}\" has a syntax error${fields.reason ? `: ${fields.reason}` : ''}.`,\n      { code: 'FORMAT_SYNTAX_ERROR', ...rest },\n    );\n    this.name = 'FormatSyntaxError';\n  }\n}\n\nexport class UnknownTokenError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `token \"${fields.token ?? ''}\" is not a recognized temporal-fmt token${fields.format ? ` in format string \"${fields.format}\"` : ''}.`,\n      { code: 'UNKNOWN_TOKEN', ...rest },\n    );\n    this.name = 'UnknownTokenError';\n  }\n}\n\nexport class ParseMismatchError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `input \"${fields.input ?? ''}\" does not match format \"${fields.format ?? ''}\"${fields.reason ? `: ${fields.reason}` : ''}.`,\n      { code: 'PARSE_MISMATCH', ...rest },\n    );\n    this.name = 'ParseMismatchError';\n  }\n}\n\nexport class InvalidDateError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `input \"${fields.input ?? ''}\" does not describe a valid date${fields.reason ? `: ${fields.reason}` : ''}.`,\n      { code: 'INVALID_DATE', ...rest },\n    );\n    this.name = 'InvalidDateError';\n  }\n}\n\n/* c8 ignore start @preserve -- InvalidTimeError is part of the public\n   error-class surface (exported from index.ts, code 'INVALID_TIME') but\n   nothing in this package actually constructs one. went and checked\n   whether it could get wired in the same way InvalidTimeZoneError just\n   was (see parse.ts's zzz-validation loop) — does hour/minute/second have\n   a post-match semantic range check the way zone ids do? nope.\n   pattern.ts's regex fragments for HH/H/hh/h/mm/m/ss/s already enforce\n   their valid ranges right at the regex level (e.g. HH is\n   '(?:[01]\\d|2[0-3])', which literally can't match \"99\"), so an\n   out-of-range time just gets rejected as a plain shape mismatch before\n   any semantic check would even run. no live gap to hook this into\n   without inventing a redundant check purely to give this class a body.\n   leaving it unconstructed until the library actually hits a real\n   invalid-time case worth reporting */\nexport class InvalidTimeError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `input \"${fields.input ?? ''}\" does not describe a valid time${fields.reason ? `: ${fields.reason}` : ''}.`,\n      { code: 'INVALID_TIME', ...rest },\n    );\n    this.name = 'InvalidTimeError';\n  }\n}\n/* c8 ignore stop @preserve */\n\nexport class InvalidOffsetError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `offset \"${fields.actual ?? ''}\" is invalid${fields.reason ? `: ${fields.reason}` : ''}.`,\n      { code: 'INVALID_OFFSET', ...rest },\n    );\n    this.name = 'InvalidOffsetError';\n  }\n}\n\nexport class InvalidTimeZoneError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `time zone \"${fields.actual ?? ''}\" is not a recognized IANA time zone or fixed offset${fields.reason ? `: ${fields.reason}` : ''}.`,\n      { code: 'INVALID_TIME_ZONE', ...rest },\n    );\n    this.name = 'InvalidTimeZoneError';\n  }\n}\n\n/* c8 ignore start @preserve -- InvalidCalendarError is part of the\n   public error-class surface (exported from index.ts, code\n   'INVALID_CALENDAR') but same story as InvalidTimeError above — nothing\n   in this package constructs one. checked for a wiring opportunity the\n   same way: there's no user-supplied calendar identifier anywhere in the\n   library that we'd validate against a supported list. resolveCalendar()\n   in parse.ts derives the calendar entirely from Intl's own resolution\n   of the locale string — it's never handed an arbitrary \"calendar\" value\n   a caller could actually get wrong. no live input to reject here.\n   leaving it unconstructed until the library accepts an actual calendar\n   parameter that could be invalid */\nexport class InvalidCalendarError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `calendar \"${fields.actual ?? ''}\" is not supported${fields.reason ? `: ${fields.reason}` : ''}.`,\n      { code: 'INVALID_CALENDAR', ...rest },\n    );\n    this.name = 'InvalidCalendarError';\n  }\n}\n/* c8 ignore stop @preserve */\n\nexport class AmbiguousInputError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `input \"${fields.input ?? ''}\" is ambiguous${fields.reason ? `: ${fields.reason}` : ''}.`,\n      { code: 'AMBIGUOUS_INPUT', ...rest },\n    );\n    this.name = 'AmbiguousInputError';\n  }\n}\n\nexport class InvalidLocaleError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `locale \"${fields.actual ?? ''}\" is not a valid BCP-47 tag${fields.reason ? `: ${fields.reason}` : ''}.`,\n      { code: 'INVALID_LOCALE', ...rest },\n    );\n    this.name = 'InvalidLocaleError';\n  }\n}\n\nexport class InvalidDurationError extends TemporalFmtError {\n  constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n    const { message, ...rest } = fields;\n    super(\n      message ?? `duration is invalid${fields.reason ? `: ${fields.reason}` : ''}.`,\n      { code: 'INVALID_DURATION', ...rest },\n    );\n    this.name = 'InvalidDurationError';\n  }\n}\n\n// wraps a plain Error thrown from a code path that hasn't been migrated\n// to typed errors yet. keeps the original message around in `reason` so\n// callers reading the typed surface still see what actually failed.\n// used by safeParse() in parse.ts.\n//\n// after the 0.9.0 migration, every throw site on the parse/format data\n// path throws a TemporalFmtError directly, so safeParse's `instanceof\n// TemporalFmtError` check always passes before this function would even\n// get called — nothing in the current test suite actually reaches any\n// branch below. kept as the safety net for whenever someone adds an\n// unmigrated throw site down the line (see the c8-ignored call in\n// parse.ts's safeParse) — same reasoning as that call site: removing\n// this would silently break the \"safeParse always returns a\n// TemporalFmtError\" contract the moment anyone adds a bare\n// `throw new Error(...)` without wiring up a typed class for it\n/* c8 ignore start @preserve -- unreachable from the current test suite,\n   see rationale above */\nexport function wrapUntypedError(err: Error, context: { input?: string; format?: string }): TemporalFmtError {\n  // try to classify by looking at the message — covers the existing\n  // parse()/format() throw sites without having to touch them. anything\n  // that doesn't match a known pattern falls through to a generic\n  // ParseMismatchError, still with the structured fields intact\n  const msg = err.message;\n  if (/unknown token|isn't a recognized token/.test(msg)) {\n    return new UnknownTokenError({ input: context.input, format: context.format, reason: msg });\n  }\n  if (/ambiguous/i.test(msg)) {\n    return new AmbiguousInputError({ input: context.input, format: context.format, reason: msg });\n  }\n  if (/offset/.test(msg) && /out of range|exceeds|doesn't match the shape/i.test(msg)) {\n    return new InvalidOffsetError({ input: context.input, format: context.format, reason: msg });\n  }\n  if (/no valid pattern matches/i.test(msg)) {\n    return new ParseMismatchError({ input: context.input, format: context.format, reason: msg });\n  }\n  if (/doesn't describe a valid date\\/time|incomplete date|weekday token|quarter token/.test(msg)) {\n    return new InvalidDateError({ input: context.input, format: context.format, reason: msg });\n  }\n  const lowerMsg = msg.toLowerCase();\n  const mentionsLocale = lowerMsg.includes('locale');\n  if (\n    (mentionsLocale && (lowerMsg.includes('produced no') || lowerMsg.includes('not a valid'))) ||\n    lowerMsg.includes('cutoffs must be')\n  ) {\n    return new InvalidLocaleError({ input: context.input, format: context.format, reason: msg });\n  }\n  if (/format string exceeds maximum length|input exceeds maximum length|unterminated quote|isn't a recognized token/i.test(msg)) {\n    return new FormatSyntaxError({ input: context.input, format: context.format, reason: msg });\n  }\n  return new ParseMismatchError({ input: context.input, format: context.format, reason: msg });\n}\n/* c8 ignore stop @preserve */","/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Name lists for the locale-aware tokens (MMMM, MMM, EEEE, EEE, a). Each\n// list is small and fixed (12 months, 7 weekdays, 2 day periods), so we\n// generate the real Intl strings for a locale once and cache them.\n\nimport { InvalidLocaleError } from './errors.js';\n\nexport interface LocaleVocab {\n  monthLong: string[]; // index 0 = January\n  monthShort: string[];\n  weekdayLong: string[]; // index 0 = Monday, per Temporal's dayOfWeek numbering\n  weekdayShort: string[];\n  dayPeriod: string[]; // typically [AM-ish, PM-ish], deduped\n}\n\n// Custom vocabs registered by callers for locales Intl doesn't cover well\n// (e.g. a 13-month Hebrew leap year, where Intl's 12-month vocabulary\n// silently loses a whole month). Keyed by canonical cache key so the\n// same locale string spelling variants fold together — same convention\n// as the Intl-derived vocab cache above.\nconst customVocabs = new Map<string, LocaleVocab>();\nconst MAX_CUSTOM_VOCABS = 500;\nconst MAX_LOCALE_TAG_LENGTH = 256;\nconst MAX_VOCAB_ENTRY_LENGTH = 256;\n\nfunction assertValidVocab(vocab: Partial<LocaleVocab>, locale: string): void {\n  // Strict shape validation at registration time, not lazily on first\n  // use — the README's promise is that a malformed registration throws\n  // descriptively here, rather than failing later inside format()/parse()\n  // with a confusing \"no month part\" or wrong-month error the caller\n  // can't trace back to the bad registration.\n  const required: Array<{ key: keyof LocaleVocab; length: number; label: string }> = [\n    { key: 'monthLong', length: 12, label: 'long month names' },\n    { key: 'monthShort', length: 12, label: 'short month names' },\n    { key: 'weekdayLong', length: 7, label: 'long weekday names' },\n    { key: 'weekdayShort', length: 7, label: 'short weekday names' },\n    { key: 'dayPeriod', length: 2, label: 'day period markers (AM/PM-equivalent)' },\n  ];\n\n  for (const { key, length, label } of required) {\n    const value = vocab[key];\n    if (value === undefined) {\n      throw new Error(\n        `temporal-fmt: registerLocaleVocab for locale \"${locale}\" is missing required field \"${key}\" (${label}).`\n      );\n    }\n    if (!Array.isArray(value)) {\n      throw new Error(\n        `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}\" must be an array, got ${typeof value}.`\n      );\n    }\n    if (value.length !== length) {\n      throw new Error(\n        `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}\" must have exactly ${length} entries (got ${value.length}) — ${label}.`\n      );\n    }\n    value.forEach((entry, i) => {\n      if (typeof entry !== 'string' || entry.length === 0) {\n        throw new Error(\n          `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}[${i}]\" must be a non-empty string, got ${String(entry)}.`\n        );\n      }\n      if (entry.length > MAX_VOCAB_ENTRY_LENGTH) {\n        throw new RangeError(\n          `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}[${i}]\" is too long (maximum ${MAX_VOCAB_ENTRY_LENGTH} characters).`\n        );\n      }\n    });\n  }\n\n  // Reuse the same collision check the Intl-derived path uses — a\n  // duplicate month name is just as ambiguous when supplied by a caller\n  // as when produced by Intl.\n  assertNoCollision(vocab.monthLong!, 'MMMM month', locale);\n  assertNoCollision(vocab.monthShort!, 'MMM month', locale);\n  assertNoCollision(vocab.weekdayLong!, 'EEEE weekday', locale);\n  assertNoCollision(vocab.weekdayShort!, 'EEE weekday', locale);\n\n  // dayPeriod entries must differ from each other, or parse()'s\n  // isPM check (raw === vocab.dayPeriod[1]) can never return true and\n  // every 12-hour parse silently resolves to AM. The Intl-derived path\n  // dedupes a same-AM/PM collision to length 1, but a caller passing\n  // both entries identical is a real bug to surface — not something to\n  // dedupe around.\n  if (vocab.dayPeriod![0] === vocab.dayPeriod![1]) {\n    throw new Error(\n      `temporal-fmt: registerLocaleVocab for locale \"${locale}\": dayPeriod entries must differ ` +\n      `(both are \"${vocab.dayPeriod![0]}\"); otherwise parse() can't tell AM from PM.`\n    );\n  }\n}\n\n// Anything that caches a result derived from *which* vocabulary is\n// active for a locale (right now: parse.ts's compiled pattern cache,\n// whose MMMM/MMM/EEEE/EEE/a fragments embed the vocab's alternations)\n// subscribes here so it gets invalidated when a registration swaps the\n// vocab — mirrors temporalProvider.ts's subscribeToTemporalChanges.\nconst onVocabChanged: Array<() => void> = [];\n\nexport function subscribeToVocabChanges(listener: () => void): void {\n  onVocabChanged.push(listener);\n}\n\n/**\n * Supply a custom month/weekday/day-period vocabulary for a locale key,\n * overriding the Intl-derived vocab this library would otherwise build\n * for that key. Useful for locales Intl doesn't cover well — the\n * Parsing section of the README (\"MMMM/MMM assume a 12-month calendar\")\n * calls out the Hebrew leap-month gap as a specific case this addresses.\n *\n * Throws descriptively on malformed input (wrong array lengths, empty\n * strings, duplicate entries, missing fields) rather than failing later\n * during format/parse.\n *\n * Registered vocab takes precedence over the Intl-derived vocab for that\n * locale key, including for already-cached entries — registering\n * invalidates the prior cache entry for that locale so the next call\n * picks up the new vocab.\n *\n * @example\n * registerLocaleVocab('en-u-ca-hebrew-leap', {\n *   monthLong: ['Nisan','Iyar','Sivan','Tammuz','Av','Elul','Tishrei','Marcheshvan','Kislev','Tevet','Shevat','Adar I','Adar II'],\n *   monthShort: ['Nis','Iyy','Siv','Tam','Av','Elu','Tish','Chesh','Kis','Tev','Shv','Ad1','Ad2'],\n *   weekdayLong: ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'],\n *   weekdayShort: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],\n *   dayPeriod: ['AM','PM'],\n * });\n */\nexport function registerLocaleVocab(locale: string, vocab: Partial<LocaleVocab>): void {\n  if (typeof locale !== 'string' || locale.length === 0) {\n    throw new Error(`temporal-fmt: registerLocaleVocab requires a non-empty locale string, got ${String(locale)}.`);\n  }\n  if (locale.length > MAX_LOCALE_TAG_LENGTH) {\n    throw new RangeError(`temporal-fmt: registerLocaleVocab locale is too long (maximum ${MAX_LOCALE_TAG_LENGTH} characters).`);\n  }\n  assertValidVocab(vocab, locale);\n\n  const cacheKey = canonicalCacheKey(locale);\n  if (!customVocabs.has(cacheKey) && customVocabs.size >= MAX_CUSTOM_VOCABS) {\n    throw new RangeError(`temporal-fmt: registerLocaleVocab reached the ${MAX_CUSTOM_VOCABS}-locale limit.`);\n  }\n  customVocabs.set(cacheKey, {\n    monthLong: [...vocab.monthLong!],\n    monthShort: [...vocab.monthShort!],\n    weekdayLong: [...vocab.weekdayLong!],\n    weekdayShort: [...vocab.weekdayShort!],\n    dayPeriod: [...vocab.dayPeriod!],\n  });\n  // Invalidate the Intl-derived cache entry so any prior format/parse\n  // result cached for this locale is rebuilt against the new vocab. Not\n  // strictly necessary (getLocaleVocab checks customVocabs first), but\n  // cheap and keeps the two caches from drifting out of sync.\n  vocabCache.delete(cacheKey);\n  // Also invalidate downstream caches that baked the previous vocab in\n  // at build time (parse.ts's pattern cache is the one that matters:\n  // without this, a cached pattern would keep matching the OLD month /\n  // weekday names while format() renders the new ones, so the library's\n  // own format() output would fail to parse back).\n  for (const listener of onVocabChanged) listener();\n}\n\n// Every locale-keyed cache in this library (this one, formatterCache in\n// tokens.ts, patternCache/calendarCache in parse.ts) used to key on the\n// exact locale string a caller passed in. Intl treats spelling variants of\n// the same locale as equivalent ('en-US' / 'en-us' / 'en_US' all resolve\n// the same way), but a plain string-keyed Map doesn't — so callers mixing\n// spellings for what's really one locale would silently fragment across\n// separate cache entries instead of sharing one, making the bounded\n// eviction limits (MAX_*_CACHE_SIZE) less effective than they look. This\n// doesn't change any cache's *correctness* (each entry is still built from\n// -- and valid for -- whatever locale string produced it), only how many\n// distinct entries equivalent spellings end up costing. Falls back to the\n// original string on a malformed/unrecognized tag rather than throwing —\n// cache-key normalization shouldn't be where a bad locale first surfaces\n// as an error; whatever actually calls `new Intl.DateTimeFormat(locale)`\n// downstream is the right place for that.\n// Intl constructors (DateTimeFormat/NumberFormat/RelativeTimeFormat)\n// reject underscore-separated tags like 'en_US' outright, while\n// canonicalCacheKey and every locale-parsing path in this library\n// tolerates them by normalizing to BCP-47 hyphens. Centralize that\n// normalization so every `new Intl.*(locale)` construction site\n// accepts the same spellings the cache keys do. Genuinely malformed\n// tags still throw downstream, unchanged.\nexport function normalizeLocaleTag(locale: string): string {\n  return locale.replace(/_/g, '-');\n}\n\n// Memoized so hot paths (parse()'s resolveCalendar keys off this on\n// every call, and every locale-keyed cache re-derives it) don't build a\n// fresh Intl.Locale per invocation — construction is comparatively\n// expensive. Bounded like every other cache in this library.\nconst canonicalKeyCache = new Map<string, string>();\nconst MAX_CANONICAL_KEY_CACHE = 500;\n\nexport function canonicalCacheKey(locale: string): string {\n  const hit = canonicalKeyCache.get(locale);\n  if (hit !== undefined) return hit;\n  let key: string;\n  try {\n    // Intl.Locale requires BCP-47 hyphens and rejects underscore-separated\n    // tags like 'en_US' outright (RangeError), rather than normalizing\n    // them — so without this replace, that spelling would just fall\n    // through to the catch below and never fold with 'en-US'.\n    key = new Intl.Locale(locale.replace(/_/g, '-')).toString().toLowerCase();\n  } catch {\n    // Malformed tag: key on the raw string. Callers that must reject\n    // malformed tags do it via assertValidLocaleTag() before/instead of\n    // relying on this function — cache-key normalization isn't where a\n    // bad locale should surface as an error (unchanged behavior).\n    key = locale;\n  }\n  if (canonicalKeyCache.size >= MAX_CANONICAL_KEY_CACHE) {\n    const oldestKey = canonicalKeyCache.keys().next().value;\n    if (oldestKey !== undefined) canonicalKeyCache.delete(oldestKey);\n  }\n  canonicalKeyCache.set(locale, key);\n  return key;\n}\n\n// Single validation choke point for locale tags at public boundaries.\n// Intl constructors reject malformed tags with a bare engine RangeError\n// (\"Incorrect locale information provided\") that carries none of the\n// library's structured error context; this rethrows as the typed\n// InvalidLocaleError with the offending tag in its fields. Accepts the\n// same spellings as canonicalCacheKey (underscore tags normalized).\nexport function assertValidLocaleTag(locale: string): void {\n  try {\n    new Intl.Locale(normalizeLocaleTag(locale));\n  } catch {\n    throw new InvalidLocaleError({\n      actual: locale,\n      reason: 'not a valid BCP-47 locale tag',\n    });\n  }\n}\n\nconst vocabCache = new Map<string, LocaleVocab>();\nconst MAX_VOCAB_CACHE_SIZE = 500;\n\n// Some locales (ja-JP) split a field across two parts — month \"8\" plus a\n// counter suffix \"月\" as a separate sibling \"literal\" — while format()'s\n// post-1582 tokens go through toLocaleString(), which concatenates\n// everything into \"8月\", and the pre-1582 path in tokens.ts\n// (preCutoverGregorianName) formats one field at a time the same way this\n// does. Reading only the type-tagged part used to drop that suffix, so\n// this locale's vocab never matched what format() actually produced.\n// Only merges *adjacent* literals, not the whole string, since the\n// dayPeriod/weekday formatters below carry an extra hour part that a\n// join-everything approach would wrongly absorb.\nexport function partValue(formatter: Intl.DateTimeFormat, date: Date, type: Intl.DateTimeFormatPartTypes): string {\n  const parts = formatter.formatToParts(date);\n  const index = parts.findIndex((p) => p.type === type);\n  /* c8 ignore start @preserve -- defensive guard against a real but\n     unreproducible failure mode: an Intl implementation that omits the\n     requested part type entirely for some locale. Checked every locale\n     with unusual dayPeriod/weekday/month rendering available in this\n     runtime's ICU data (ja-JP, zh-CN, th-TH, he-IL, ar-SA, ko-KR, fa-IR)\n     against the exact formatter options getLocaleVocab uses (notably\n     hour12: true for the dayPeriod formatter, which is what makes every\n     locale here actually emit a dayPeriod part — omitting it is what\n     produced a false \"gap\" during investigation). None omit their part\n     on this runtime. A different ICU version or a non-Node Intl\n     implementation could plausibly behave differently, so this stays a\n     real check rather than an assertion. */\n  /* c8 ignore start @preserve */\n  if (index === -1) {\n    throw new InvalidLocaleError({\n      message: `temporal-fmt: locale produced no \"${type}\" part while building match vocabulary.`,\n    });\n  }\n  /* c8 ignore stop @preserve */\n  let value = parts[index]!.value;\n  const prev = parts[index - 1];\n  const next = parts[index + 1];\n  // skip whitespace literals (the separator before \"AM\") — only a\n  // no-space suffix like ja-JP's \"月\" should get folded in\n  if (prev?.type === 'literal' && !/\\s/.test(prev.value)) value = prev.value + value;\n  if (next?.type === 'literal' && !/\\s/.test(next.value)) value = value + next.value;\n  return value;\n}\n\n// Two entries rendering identically means parse()'s reverse lookup\n// (indexOf) can never tell them apart. Weekday collisions already surface\n// via parse()'s dayOfWeek cross-check, but with a confusing same-string\n// error; months have no equivalent cross-check, so a collision there would\n// otherwise resolve silently to the wrong month. Catching both here, once\n// at build time, gives one clear error instead.\nfunction assertNoCollision(names: string[], label: string, locale: string): void {\n  const seen = new Map<string, number>();\n  for (let i = 0; i < names.length; i++) {\n    const prior = seen.get(names[i]!);\n    if (prior !== undefined) {\n      // Not migrated to a typed error: this function is shared between\n      // getLocaleVocab's Intl-derived path (data-path error, would be a\n      // good InvalidLocaleError candidate) and assertValidVocab's\n      // registration-time check (out of scope for this pass — see the\n      // localeVocab.ts registration-error follow-up). Splitting this into\n      // two near-duplicate functions just to route error types\n      // differently isn't worth it for one throw; revisit together with\n      // the registration-error work instead.\n      throw new Error(\n        `temporal-fmt: locale \"${locale}\" renders ${label} index ${prior} and ${i} identically ` +\n        `(\"${names[i]}\"). parse() can't reliably tell these apart for this locale/token, so this ` +\n        `combination isn't supported.`\n      );\n    }\n    seen.set(names[i]!, i);\n  }\n}\n\n// Exposed for tokens.ts: when a custom vocab is registered for this\n// locale, format()'s locale-aware tokens (MMMM/MMM/EEEE/EEE/a) read\n// straight from the registered array instead of going through\n// Intl.DateTimeFormat. Without this override, format() would silently\n// keep producing Intl's strings while parse() matched against the\n// registered vocab — the two would round-trip-fail.\nexport function getCustomVocab(locale: string): LocaleVocab | undefined {\n  const cacheKey = canonicalCacheKey(locale);\n  return customVocabs.get(cacheKey);\n}\n\nexport function getLocaleVocab(locale: string): LocaleVocab {\n  // Validate before building vocab: a malformed tag used to surface as a\n  // raw RangeError from the first new Intl.DateTimeFormat below.\n  assertValidLocaleTag(locale);\n  const cacheKey = canonicalCacheKey(locale);\n  // Registered vocabs take precedence over the Intl-derived one — this\n  // is the override mechanism registerLocaleVocab() promises. Checking\n  // here, before the Intl cache lookup, means a registration that\n  // happens *after* the first Intl-derived vocab was built still takes\n  // effect on the next call.\n  const custom = customVocabs.get(cacheKey);\n  if (custom) {\n    return custom;\n  }\n  const cached = vocabCache.get(cacheKey);\n  if (cached) {\n    return cached;\n  }\n\n  const intlLocale = normalizeLocaleTag(locale);\n  const monthLongFmt = new Intl.DateTimeFormat(intlLocale, { month: 'long', timeZone: 'UTC' });\n  const monthShortFmt = new Intl.DateTimeFormat(intlLocale, { month: 'short', timeZone: 'UTC' });\n  const monthLong: string[] = [];\n  const monthShort: string[] = [];\n  for (let m = 0; m < 12; m++) {\n    const date = new Date(Date.UTC(2020, m, 1));\n    monthLong.push(partValue(monthLongFmt, date, 'month'));\n    monthShort.push(partValue(monthShortFmt, date, 'month'));\n  }\n  assertNoCollision(monthLong, 'MMMM month', locale);\n  assertNoCollision(monthShort, 'MMM month', locale);\n\n  const weekdayLongFmt = new Intl.DateTimeFormat(intlLocale, { weekday: 'long', timeZone: 'UTC' });\n  const weekdayShortFmt = new Intl.DateTimeFormat(intlLocale, { weekday: 'short', timeZone: 'UTC' });\n  const weekdayLong: string[] = [];\n  const weekdayShort: string[] = [];\n  // 2024-01-01 is a Monday (UTC) — walk 7 days from there for weekday names\n  for (let d = 0; d < 7; d++) {\n    const date = new Date(Date.UTC(2024, 0, 1 + d));\n    weekdayLong.push(partValue(weekdayLongFmt, date, 'weekday'));\n    weekdayShort.push(partValue(weekdayShortFmt, date, 'weekday'));\n  }\n  // redundant with parse()'s dayOfWeek cross-check, but gives a clearer error\n  assertNoCollision(weekdayLong, 'EEEE weekday', locale);\n  assertNoCollision(weekdayShort, 'EEE weekday', locale);\n\n  const dayPeriodFmt = new Intl.DateTimeFormat(intlLocale, { hour: 'numeric', hour12: true, timeZone: 'UTC' });\n  const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), 'dayPeriod');\n  const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), 'dayPeriod');\n  const dayPeriod = [...new Set([am, pm])];\n\n  const vocab: LocaleVocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };\n  if (vocabCache.size >= MAX_VOCAB_CACHE_SIZE) {\n    const oldestKey = vocabCache.keys().next().value;\n    if (oldestKey !== undefined) vocabCache.delete(oldestKey);\n  }\n  vocabCache.set(cacheKey, vocab);\n  return vocab;\n}","/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// ISO week stuff. week runs Mon-Sun, and week 1 is whichever week has\n// the year's first Thursday in it (same as saying \"the week with Jan 4\").\n// took me a minute to wrap my head around this but the upshot is late-Dec\n// dates can land in week 1 of NEXT year, and early-Jan dates can land in\n// week 52/53 of the PREVIOUS year. that adjacent year is what RRRR prints,\n// not the plain calendar year.\n//\n// doing this with plain year/month/day + dayOfWeek math instead of asking\n// Temporal for it, since format() only has whatever fields got handed in\n// and dragging in a whole Temporal implementation just for week numbers\n// felt like overkill (also breaks for people not on setTemporal()).\n\nconst DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\nconst CUMULATIVE_DAYS_BY_MONTH = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];\n\nexport function isGregorianLeapYear(year: number): boolean {\n  // divisible by 4, unless it's a century, then it also needs /400.\n  // Temporal's iso8601 calendar never switches to Julian, so this rule\n  // just applies all the way back, even for BCE years.\n  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n}\n\nfunction daysInYear(year: number): 365 | 366 {\n  return isGregorianLeapYear(year) ? 366 : 365;\n}\n\nexport function dayOfYear(year: number, month: number, day: number): number {\n  let doy = CUMULATIVE_DAYS_BY_MONTH[month - 1]! + day;\n  if (month > 2 && isGregorianLeapYear(year)) doy += 1;\n  return doy;\n}\n\n// Jan 1 2000 was a Saturday (ISO dow 6). picked this as an anchor point\n// since it's easier than reaching for Zeller's congruence every time,\n// and obviously this fact about Jan 1 2000 isn't going to change on us\nconst REFERENCE_YEAR = 2000;\nconst REFERENCE_JAN1_DAY_OF_WEEK = 6;\n\n// Howard Hinnant's days_from_civil: O(1) day count for a proleptic\n// Gregorian y/m/d, correct for negative years and year 0 (the (y2>=0?\n// y2 : y2-399)/400 offset form relies on truncating division, which JS\n// doesn't have — Math.floor on the *un-offset* numerator is the exact\n// JS equivalent, see the matching formulas in arithmetic.ts). Shared by\n// dayOfWeekOfJan1 and any caller needing days between two arbitrary\n// dates without a year-by-year walk (those walks were O(|year − 2000|)\n// per call — a field bag claiming year 2e8 hung formatDistance for\n// ~400ms per call and worse for bigger values).\nexport function daysFromCivil(year: number, month: number, day: number): number {\n  const y2 = month <= 2 ? year - 1 : year;\n  const era = Math.floor(y2 / 400);\n  const yoe = y2 - era * 400; // [0, 399]\n  const m2 = month > 2 ? month - 3 : month + 9; // [0, 11]\n  const doy = Math.floor((153 * m2 + 2) / 5) + day - 1; // [0, 365]\n  const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy; // [0, 146096]\n  return era * 146097 + doe - 719468;\n}\n\n// Day of week (ISO, 1=Mon..7=Sun) for a proleptic Gregorian y/m/d, in\n// O(1). 1970-01-01 was a Thursday (dow 4), so dow = (days + 3) mod 7\n// shifted to 1..7. Replaces the Date.UTC(year, month-1, day) idiom used\n// elsewhere in this library, which silently remaps years 0-99 to\n// 1900-1999 per the ECMAScript spec and produces the wrong weekday for\n// first-century dates.\nexport function dayOfWeekFromCivil(year: number, month: number, day: number): number {\n  const days = daysFromCivil(year, month, day);\n  // days=0 is 1970-01-01 (Thursday, dow 4): (0 + 3) % 7 = 3 → 4 ✓\n  return (((days + 3) % 7) + 7) % 7 + 1;\n}\n\nfunction dayOfWeekOfJan1(year: number): number {\n  // O(1) via daysFromCivil: Jan 1 of `year` is `days` after Jan 1 2000\n  // (a Saturday, dow 6), so dow = (6 - 1 + days) mod 7, bumped to 1-indexed.\n  // The old year-by-year walk from 2000 was O(|year − 2000|) — correct\n  // but linear, and unbounded for hostile field-bag years.\n  const days = daysFromCivil(year, 1, 1) - daysFromCivil(REFERENCE_YEAR, 1, 1);\n  const zeroIndexed = (((REFERENCE_JAN1_DAY_OF_WEEK - 1 + days) % 7) + 7) % 7;\n  return zeroIndexed + 1;\n}\n\nexport interface IsoWeekDate {\n  isoYear: number;\n  week: number; // goes 1 to 53\n}\n\nexport function isoWeekYearAndWeek(year: number, month: number, day: number, dayOfWeek: number): IsoWeekDate {\n  // step 1: figure out the Thursday of this week — whatever calendar year\n  // that Thursday's in IS the ISO week-numbering year. doing this with\n  // day-of-year offsets so we don't need an actual Temporal.PlainDate here\n  const doy = dayOfYear(year, month, day);\n  const thursdayDoyRelative = doy + (4 - dayOfWeek); // can go negative or past daysInYear, that's fine\n\n  let isoYear: number;\n  let thursdayDoy: number;\n  if (thursdayDoyRelative < 1) {\n    isoYear = year - 1;\n    thursdayDoy = thursdayDoyRelative + daysInYear(isoYear);\n  } else if (thursdayDoyRelative > daysInYear(year)) {\n    isoYear = year + 1;\n    thursdayDoy = thursdayDoyRelative - daysInYear(year);\n  } else {\n    isoYear = year;\n    thursdayDoy = thursdayDoyRelative;\n  }\n\n  // step 2: find the first Thursday of isoYear, that one's week 1 by definition.\n  // depends entirely on which weekday Jan 1 lands on\n  const jan1Dow = dayOfWeekOfJan1(isoYear);\n  const firstThursdayDoy = 1 + ((4 - jan1Dow + 7) % 7); // always lands somewhere 1-7\n\n  // step 3: just count how many full weeks between the two Thursdays\n  const week = 1 + Math.floor((thursdayDoy - firstThursdayDoy) / 7);\n  return { isoYear, week };\n}\n","/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getTemporal, subscribeToTemporalChanges } from './temporalProvider.js';\nimport { canonicalCacheKey, getCustomVocab, normalizeLocaleTag, partValue } from './localeVocab.js';\nimport { InvalidLocaleError, FormatSyntaxError } from './errors.js';\nimport { isoWeekYearAndWeek, dayOfYear } from './isoWeek.js';\n\n// Throws the library's standard typed missing-field error for tokens\n// that READ more fields than their TOKENS-table `field` entry declares\n// (the table only drives format()'s single-field precheck). ww/RRRR read\n// year/month/day/dayOfWeek but precheck only dayOfWeek; D/DD/DDD read\n// year/month/day but precheck only day. Without this, a bag carrying the\n// declared field but missing the rest fed undefined into the math and\n// produced literal \"NaN\" output instead of the descriptive error every\n// other token throws.\nfunction requireFields(t: TemporalLike, token: string, ...fields: Array<keyof TemporalLike>): void {\n  for (const field of fields) {\n    if (t[field] === undefined) {\n      throw new FormatSyntaxError({\n        token,\n        message:\n          `temporal-fmt: token \"${token}\" requires \"${field}\", ` +\n          `which this Temporal object doesn't have. ` +\n          `(e.g. PlainDate has no time fields, PlainTime has no date fields)`,\n      });\n    }\n  }\n}\n\nexport function pad(n: number, len: number): string {\n  // padStart pads the whole string, sign included, so pad(-45, 4) used to\n  // come out \"0-45\" instead of \"-045\" — split the sign off first.\n  const negative = n < 0;\n  const digits = String(Math.abs(n)).padStart(len, '0');\n  return negative ? '-' + digits : digits;\n}\n\n// Combines the three sub-second fields Temporal exposes into one 9-digit\n// nanosecond-of-second value, then truncates (never rounds) to the\n// requested width. Truncating matches what every digit-width token in\n// this library already does elsewhere (yy, MM, dd, ...) — the token\n// asked for N digits of precision, not a rounded N-digit approximation.\n// A caller asking for SSS on a value with nanosecond precision gets the\n// leading 3 digits of it, same as they'd get the leading 3 digits of any\n// other multi-digit field this library formats.\n//\n// Attached to pad() rather than declared standalone: the bundler's\n// per-function coverage instrumentation attributes hits to each of the\n// 9 fraction-token arrow functions individually but not to a shared\n// helper they all close over, so a correctly-exercised helper still\n// shows as 0 hits under c8. pad() itself is called directly all over\n// this file and is reliably attributed — routing through it here keeps\n// the coverage numbers honest without duplicating the slice logic\n// across every token entry below.\npad.fraction = function formatFraction(t: TemporalLike, width: number): string {\n  const nanoOfSecond = t.millisecond! * 1_000_000 + (t.microsecond ?? 0) * 1_000 + (t.nanosecond ?? 0);\n  return pad(nanoOfSecond, 9).slice(0, width);\n};\n\n// Not every field exists on every Temporal type (PlainDate has no .hour,\n// etc). Callers check for undefined before formatting a token.\nexport interface TemporalLike {\n  year?: number;\n  month?: number;\n  day?: number;\n  hour?: number;\n  minute?: number;\n  second?: number;\n  millisecond?: number;\n  microsecond?: number;\n  nanosecond?: number;\n  timeZoneId?: string;\n  // ZonedDateTime.prototype.offset — `±HH:MM` (6 chars) for any modern\n  // date, but pre-1900 local-mean-time zones (e.g. America/New_York\n  // before 1883) surface a seconds component too: `±HH:MM:SS` (9\n  // chars). Format-side offset tokens must check the length rather\n  // than assume 6. Parse-side writes a canonicalized `+HH:MM` here\n  // before handing it to Temporal.ZonedDateTime.from as the\n  // `timeZone` value.\n  offset?: string;\n  dayOfWeek?: number; // 1=Mon, 7=Sun, per Temporal spec\n  calendarId?: string;\n  toInstant?: () => unknown;\n  toLocaleString?: (locale: string, options: Intl.DateTimeFormatOptions) => string;\n}\n\nexport interface FormatOptions {\n  /** BCP 47 locale tag, e.g. 'en-US', 'fr-FR', 'ar-EG'. Defaults to 'en-US'. */\n  locale?: string;\n  /**\n   * When set on parse(), opts into the lenient split heuristic for ambiguous\n   * glued numeric runs (e.g. \"121\" against \"Md\"). Default (false) keeps\n   * parse()'s strict behavior — throw on ambiguity rather than guess.\n   * See README \"Lenient parse mode\" for the heuristic and why it's opt-in.\n   */\n  lenient?: boolean;\n}\n\nexport const DEFAULT_LOCALE = 'en-US';\n\n// Intl.DateTimeFormat is expensive to construct and format() can run in a\n// loop (rendering a table of dates), so cache by (locale, options).\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\nconst MAX_CACHE_SIZE = 500;\n\nfunction getFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n  const key = JSON.stringify([canonicalCacheKey(locale), options]);\n  let formatter = formatterCache.get(key);\n  if (formatter) {\n    return formatter;\n  }\n  if (formatterCache.size >= MAX_CACHE_SIZE) {\n    // not real LRU, just evicts oldest insertion — fine for this key space\n    const oldestKey = formatterCache.keys().next().value;\n    if (oldestKey !== undefined) formatterCache.delete(oldestKey);\n  }\n  try {\n    formatter = new Intl.DateTimeFormat(normalizeLocaleTag(locale), options);\n  } catch (err) {\n    // Malformed locale tags reach Intl as a bare RangeError; surface the\n    // library's typed error instead (reached via dayPeriodPart — the\n    // 'a' token — on any runtime, and the native-Intl path for the rest).\n    // Every failure mode of this constructor with a string locale is a\n    // RangeError, so converting unconditionally preserves the original\n    // message in `reason` either way.\n    throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n  }\n  formatterCache.set(key, formatter);\n  return formatter;\n}\n\n// Passing a Temporal object straight into `new Intl.DateTimeFormat().formatToParts()`\n// only works when the engine's Intl implementation has special-cased support for\n// *native* Temporal instances (checked via internal slots and/or gated behind a V8 flag,\n// not tied to a specific Node version).\n//\n// A Temporal polyfill's instances don't have those slots, so the engine falls back to ToNumber() -> .valueOf(),\n// which the polyfill deliberately throws on (\"Cannot use valueOf\").\n// Probed once and memoized and only from intlPart(), so it never\n// runs unless a format string actually uses a locale-aware token.\nlet nativeSupport: boolean | undefined;\n// Invalidate the memoized probe whenever setTemporal() swaps the active\n// implementation — otherwise a probe result from \"is native Temporal\n// supported\" could keep being used after the active implementation is\n// no longer the one that was probed. See setTemporal() in\n// temporalProvider.ts for the other half of this.\nsubscribeToTemporalChanges(() => { nativeSupport = undefined; });\n\nfunction intlSupportsNativeTemporal(): boolean {\n  if (nativeSupport === undefined) {\n    nativeSupport = false;\n      try {\n        const temporal = getTemporal();\n        new Intl.DateTimeFormat('en-US', { day: 'numeric' })\n          .formatToParts(temporal.PlainDate.from({ year: 1970, month: 1, day: 1 }) as Date);\n        // Version-gated, not dead — see the matching note on the\n        // !intlSupportsNativeTemporal() branch in intlPart() below for\n        // why this can't be exercised from this environment.\n        /* c8 ignore next */\n        nativeSupport = true;\n      /* c8 ignore start */\n      } catch {\n        // native Temporal absent, or present but not recognized by Intl — fall back.\n        // Version-gated, not dead: this catch only fires on runtimes where the\n        // probe above throws (no native Temporal, or Intl doesn't recognize it).\n        // On a runtime with full native support (e.g. Node builds where Intl\n        // accepts native Temporal instances directly) the try succeeds and this\n        // branch is unreachable — mirror case of the block below.\n      }\n      /* c8 ignore stop */\n  }\n  return nativeSupport;\n}\n\n// The Julian → Gregorian cutover, as ICU applies it. ICU's gregory\n// calendar (unlike Temporal's, which is proleptic Gregorian throughout)\n// treats every date before October 15, 1582 as a Julian-calendar date:\n// the Julian calendar ran ~10 days behind proleptic Gregorian in that\n// era, so a date Temporal correctly calls 1500-07-05 gets silently\n// reinterpreted by Intl as Julian 1500-06-25 — the wrong month near\n// boundaries, and a weekday shifted by 10 mod 7 = 3 slots everywhere.\n// See tc39/ecma402#1003. Whether the cutover is even observable varies\n// by engine and entry point (V8's plain-Date path disables it; the\n// native-Temporal formatToParts path on some Node 26 builds does not),\n// so this can't be probed reliably at runtime — pre-cutover dates have\n// to be routed around Intl's calendar math entirely.\nconst GREGORIAN_CUTOVER_YEAR = 1582;\nconst GREGORIAN_CUTOVER_MONTH = 10;\nconst GREGORIAN_CUTOVER_DAY = 15;\n\n// True when the Temporal object's own (proleptic-Gregorian, hence\n// trustworthy) fields place it before the ICU cutover. Deliberately\n// NaN-tolerant rather than undefined-checking: a Temporal type that\n// lacks one of these fields (PlainMonthDay has no year, PlainYearMonth\n// no day) feeds undefined into Number(), every NaN comparison is false,\n// and the object falls out as \"not before the cutover\" onto its existing\n// formatting path — no undefined-specific branches to keep covered, and\n// no behavior change for field-partial types.\nfunction isBeforeGregorianCutover(t: TemporalLike): boolean {\n  if (t.year !== GREGORIAN_CUTOVER_YEAR) return Number(t.year) < GREGORIAN_CUTOVER_YEAR;\n  if (t.month !== GREGORIAN_CUTOVER_MONTH) return Number(t.month) < GREGORIAN_CUTOVER_MONTH;\n  return Number(t.day) < GREGORIAN_CUTOVER_DAY;\n}\n\n// Renders a locale-aware month/weekday name for a pre-cutover date\n// WITHOUT ever handing the date itself to Intl. Intl is used purely as a\n// name lookup table indexed by month/weekday number — never as the thing\n// that computes which month/weekday a historical date falls on, which is\n// the computation the Julian cutover corrupts. The number comes straight\n// off the Temporal object's own fields (proleptic-Gregorian-correct by\n// construction, since that's what Temporal is), and the name comes from\n// formatting a safe modern reference date carrying that same number.\n//\n// The reference dates are deliberately the same ones getLocaleVocab()\n// (localeVocab.ts) uses to build the parse-side vocabulary — 2020-mm-01\n// for months, the Monday-anchored 2024-01-01..07 week for weekdays — so\n// format() output for a pre-cutover date is byte-identical to what\n// parse() matches against, and round-trips keep working. partValue()\n// gives us the same adjacent-literal merging (ja-JP's \"8月\") the vocab\n// builder uses, for the same reason.\nfunction preCutoverGregorianName(\n  temporal: TemporalLike,\n  locale: string,\n  formatterOptions: Intl.DateTimeFormatOptions,\n  partType: 'month' | 'weekday'\n): string {\n  // timeZone: 'UTC' matters: the references are built via Date.UTC\n  // (midnight UTC), and without pinning the formatter's zone a host\n  // timezone behind UTC would shift them to the previous local day.\n  // month/dayOfWeek are guaranteed present — format()'s field precheck\n  // for MMMM/MMM ('month') and EEEE/EEE ('dayOfWeek') ran before the\n  // token handler was invoked at all.\n  const reference = partType === 'month'\n    ? new Date(Date.UTC(2020, temporal.month! - 1, 1))\n    : new Date(Date.UTC(2024, 0, temporal.dayOfWeek!));\n  const formatter = getFormatter(locale, { ...formatterOptions, timeZone: 'UTC' });\n  return partValue(formatter, reference, partType);\n}\n\nfunction intlPart(\n  temporal: TemporalLike,\n  locale: string,\n  options: Intl.DateTimeFormatOptions,\n  partType: Intl.DateTimeFormatPartTypes\n): string {\n  // Intl throws \"Mismatching Calendars\" if the formatter's calendar doesn't\n  // match the object's own (e.g. en-US formatter defaults to gregory, but\n  // a hebrew/islamic PlainDate needs its own calendar passed through).\n  //\n  // For iso8601 specifically, force 'gregory' rather than leaving calendar\n  // unset: numeric fields (yyyy/dd, see tokens' pad()-based handlers) are\n  // always pulled straight off the object's own ISO fields — so if the\n  // *locale* carries a `-u-ca-*` extension (e.g. 'en-u-ca-hebrew') and this\n  // step left calendar unset, the formatter would resolve its own default\n  // calendar from the locale and format MMMM/EEEE in that calendar while\n  // yyyy/dd stay ISO, producing a date that looks internally consistent\n  // (a real Hebrew month name next to a real-looking day/year) but names a\n  // completely different day than the object actually represents. Forcing\n  // 'gregory' here keeps every field of an ISO object's output anchored to\n  // the same (ISO/Gregorian) calendar — a locale's calendar extension only\n  // takes effect when the object itself already carries a non-ISO calendar\n  // (via `.withCalendar()`), matching what the README documents.\n  //\n  // 'gregory' specifically, not 'iso8601' — passing `calendar: 'iso8601'`\n  // explicitly alongside a single-field options object makes\n  // formatToParts() come back empty for some reason, but 'gregory' doesn't\n  // have that problem and Temporal's iso8601 calendar is Gregorian-shaped\n  // (proleptic Gregorian throughout, no Julian cutover) so the two agree\n  // on every numeric field this library ever reads.\n  const calendar = temporal?.calendarId;\n  const formatterOptions: Intl.DateTimeFormatOptions = {\n    ...options,\n    calendar: calendar && calendar !== 'iso8601' ? calendar : 'gregory',\n  };\n\n  // Field-bag guard: a plain { year, month, day } object only has\n  // Object.prototype.toLocaleString, which ignores both arguments and\n  // returns \"[object Object]\". On engines without native Temporal\n  // support in Intl this used to be caught below, but on engines where\n  // Intl *does* recognize native Temporal instances (Node 26+), a bag\n  // skips that branch entirely and reaches formatToParts() directly —\n  // which doesn't throw \"[object Object]\", it throws a bare\n  // \"RangeError: Invalid time value\" once the bag fails ToNumber()\n  // coercion. Neither failure mode is useful to a caller, so check for\n  // a real toLocaleString up front, before branching on native support,\n  // so the descriptive error fires on every engine.\n  const ls = temporal.toLocaleString;\n  if (typeof ls !== 'function' || ls === Object.prototype.toLocaleString) {\n    throw new Error(\n      `temporal-fmt: locale-aware part \"${partType}\" needs a value that implements ` +\n      `toLocaleString (a real Temporal object). A plain field bag cannot render ` +\n      `locale-aware names — pass a Temporal.PlainDate/PlainDateTime/ZonedDateTime.`\n    );\n  }\n\n  // Pre-1582 cutover guard. ICU's gregory calendar reinterprets dates\n  // before October 15, 1582 under Julian-calendar rules (see the long\n  // comment on isBeforeGregorianCutover above for the why), so for those\n  // dates Intl must never be handed the Temporal object itself — neither\n  // through formatToParts() below nor through toLocaleString() in the\n  // polyfill branch — since either route lets ICU's calendar math decide\n  // which month/weekday the date falls on, and that's exactly what the\n  // cutover corrupts. Route month/weekday name lookups through a safe\n  // modern reference date instead (preCutoverGregorianName above). Only\n  // month/weekday parts are affected: era is AD/CE either way for CE\n  // dates, and timeZoneName depends on the instant, not the calendar.\n  // Only Gregorian-shaped objects take this path — a Temporal object\n  // carrying a non-Gregorian calendar (hebrew, islamic, ...) has its\n  // month/weekday fields in that calendar already, and ICU's\n  // non-Gregorian calendars don't apply the Julian cutover at all, so\n  // feeding one through a gregory-keyed reference lookup would index the\n  // wrong month number into the wrong calendar. Custom vocabs never\n  // reach here (localeAwareName resolves them before calling intlPart).\n  if (\n    (partType === 'month' || partType === 'weekday') &&\n    formatterOptions.calendar === 'gregory' &&\n    isBeforeGregorianCutover(temporal)\n  ) {\n    return preCutoverGregorianName(temporal, locale, formatterOptions, partType);\n  }\n\n  // Temporal.prototype.toLocaleString() is part of the Temporal spec itself:\n  // polyfills implement the ICU formatting internally without needing the\n  // engine to recognize the object, so it works without native Intl support.\n  //\n  // Everything from here to the end of this function is genuinely\n  // reachable — NOT dead code — but only on a Node build where a global\n  // `Temporal` exists AND Intl.DateTimeFormat.formatToParts() recognizes\n  // native Temporal instances directly (this is real, observed to vary\n  // across Node versions: absent on the Node 22/24 builds this suite has\n  // been run against, present on at least one Node 26 build). This\n  // environment has no native Temporal (`typeof globalThis.Temporal ===\n  // 'undefined'`), so intlSupportsNativeTemporal() always returns false\n  // here and this branch can't be exercised from this test suite without\n  // faking native-instance recognition, which turned out to be\n  // impractical (Intl's native-Temporal detection isn't spoofable via a\n  // Proxy or valueOf() shim — see the M-02 regression test in\n  // temporalProvider.test.js for the same conclusion reached about the\n  // sibling probe function). Coverage numbers for this block will differ\n  // between Node versions for that reason; that's expected, not a\n  // regression.\n  /* c8 ignore start */\n  if (!intlSupportsNativeTemporal()) {\n    // normalizeLocaleTag: the active Temporal implementation's\n    // toLocaleString forwards the locale to Intl.DateTimeFormat, which\n    // (unlike this library's cache keys) rejects underscore-separated\n    // tags like 'en_US' outright.\n    // (Field-bag guard now runs unconditionally above, before this\n    // native-support branch, so it's not repeated here.)\n    try {\n      return ls.call(temporal, normalizeLocaleTag(locale), formatterOptions);\n    } catch (err) {\n      // The active Temporal implementation forwards the tag to Intl, which\n      // throws a bare RangeError for a malformed one — rethrow typed.\n      if (err instanceof RangeError) {\n        throw new InvalidLocaleError({ actual: locale, reason: err.message });\n      }\n      throw err;\n    }\n  }\n\n  // formatToParts() throws on ZonedDateTime directly (per spec), so convert\n  // to Instant and pass the zone via `timeZone` instead. Don't convert to\n  // PlainDateTime — that drops the zone, which breaks 'MMMM' + 'zzz' combos.\n  const { toInstant, timeZoneId } = temporal;\n  const isZoned = typeof toInstant === 'function' && typeof timeZoneId === 'string';\n  // has to be called as temporal.toInstant() because destructuring it off breaks\n  // the receiver and throws\n  const intlSafeTemporal = isZoned ? temporal.toInstant!() : temporal;\n  const nativeOptions: Intl.DateTimeFormatOptions = {\n    ...formatterOptions,\n    ...(isZoned ? { timeZone: timeZoneId } : {}),\n  };\n\n  const formatter = getFormatter(locale, nativeOptions);\n  const parts = formatter.formatToParts(intlSafeTemporal as Date | number);\n  const index = parts.findIndex((p) => p.type === partType);\n  if (index === -1) {\n    throw new Error(\n      `temporal-fmt: locale \"${locale}\" produced no \"${partType}\" part for this token. ` +\n      `This usually means the Temporal object is missing the field the token needs.`\n    );\n  }\n  // some locales (ja-JP) split a field across two parts — e.g. month \"8\"\n  // plus a counter suffix \"月\" as a separate sibling literal part. Merge in\n  // an adjacent literal only if it has no whitespace, so a genuine suffix\n  // gets folded in but an ordinary separator (the space before \"AM\") stays\n  // a separator. Mirrors partValue() in localeVocab.ts, which builds the\n  // vocab this token's output needs to match for parse() to round-trip.\n  let value = parts[index]!.value;\n  const prev = parts[index - 1];\n  const next = parts[index + 1];\n  if (prev?.type === 'literal' && !/\\s/.test(prev.value)) value = prev.value + value;\n  if (next?.type === 'literal' && !/\\s/.test(next.value)) value = value + next.value;\n  return value;\n}\n/* c8 ignore stop */\n\n// Temporal.prototype.toLocaleString() can't isolate a single field the way\n// formatToParts() can — asking for `hour` + `dayPeriod` together returns one\n// joined string (e.g. \"3 in the afternoon\"), and `dayPeriod` alone resolves\n// against a different, non-hour-anchored set of periods (\"in the\n// afternoon\"/\"昼\" instead of \"PM\"/\"午後\"). Day period only depends on the\n// hour anyway, so route it through a plain UTC Date and Intl.DateTimeFormat\n// instead — that's worked the same on every engine regardless of whether\n// Temporal itself is native or polyfilled.\nfunction dayPeriodPart(hour: number, locale: string): string {\n  // Custom vocab (when registered) takes precedence over Intl — same\n  // contract as the other locale-aware tokens. Intl won't know about a\n  // caller-supplied AM/PM string for a made-up locale key, so going\n  // through Intl would produce something other than what the caller\n  // registered.\n  const custom = getCustomVocab(locale);\n  if (custom) {\n    return hour < 12 ? custom.dayPeriod[0]! : custom.dayPeriod[1]!;\n  }\n  const date = new Date(Date.UTC(1970, 0, 1, hour));\n  const formatter = getFormatter(locale, { hour: 'numeric', hour12: true, timeZone: 'UTC' });\n  const part = formatter.formatToParts(date).find((p) => p.type === 'dayPeriod');\n  // Defensive guard, confirmed unreachable on this ICU build: forcing\n  // hour12: true (as this call always does) produces a dayPeriod part\n  // for every locale checked, including 24-hour-clock locales (ja-JP,\n  // zh-CN, th-TH, he-IL) and a wide sweep of less-common tags (dz-BT,\n  // bo-CN, am-ET, etc.). Same finding as partValue()'s twin guard in\n  // localeVocab.ts. Kept in case a future ICU/locale-data update\n  // produces a locale that genuinely omits it.\n  /* c8 ignore start */\n  if (!part) {\n    throw new Error(`temporal-fmt: locale \"${locale}\" produced no \"dayPeriod\" part for token \"a\".`);\n  }\n  /* c8 ignore stop */\n  return part.value;\n}\n\n// Resolves a locale-aware month/weekday name from the registered custom\n// vocab when one exists for this locale, falling through to Intl otherwise.\n// Without this, format() would silently keep producing Intl's strings while\n// parse() matched against the registered vocab — the two would round-trip-fail\n// against each other.\nfunction localeAwareName(\n  temporal: TemporalLike,\n  locale: string,\n  options: Intl.DateTimeFormatOptions,\n  partType: Intl.DateTimeFormatPartTypes,\n  customArray: string[] | undefined,\n  customIndex: number | undefined,\n): string {\n  if (customArray && customIndex !== undefined && customIndex >= 0 && customIndex < customArray.length) {\n    return customArray[customIndex]!;\n  }\n  return intlPart(temporal, locale, options, partType);\n}\n\n// Formats a `±HH:MM` offset string (the shape Temporal exposes on\n// ZonedDateTime.prototype.offset) into one of the six offset-token widths.\n// Width and Z-handling come from the variant letter+case:\n//\n//   X / x   — short form: minutes omitted when zero, no colon otherwise\n//   XX / xx — hours + minutes, no colon\n//   XXX / xxx — hours + minutes, with colon\n//\n// Uppercase (X) collapses +00:00 to \"Z\"; lowercase (x) always emits a\n// numeric offset, even for UTC. Mirrors the date-fns/Unicode-LDML offset\n// family — see README for the full variant table.\nfunction formatOffset(offset: string, variant: 'X' | 'XX' | 'XXX' | 'x' | 'xx' | 'xxx'): string {\n  if (offset === '+00:00' && (variant === 'X' || variant === 'XX' || variant === 'XXX')) {\n    return 'Z';\n  }\n  // Most offsets are 6 chars: sign + HH + ':' + MM. Pre-1900 local-mean-time\n  // zones can surface a seconds component too, 9 chars: sign + HH + ':' +\n  // MM + ':' + SS. \"xxx\" is the one variant with a always-signed,\n  // never-\"Z\" colon-separated shape wide enough to carry that unchanged,\n  // so it passes a sub-minute offset through verbatim. Every other\n  // variant has no seconds slot and must throw rather than silently\n  // truncate them away.\n  if (offset.length > 6) {\n    if (variant === 'xxx') {\n      return offset;\n    }\n    throw new Error(\n      `temporal-fmt: token \"${variant}\" cannot represent the offset \"${offset}\", ` +\n      `which has a seconds component. None of the X/XX/XXX/x/xx tokens ` +\n      `support offset seconds; use \"xxx\" instead, which formats the full ` +\n      `offset unchanged.`\n    );\n  }\n  const sign = offset[0]!;\n  const hours = offset.slice(1, 3);\n  const minutes = offset.slice(4, 6);\n  switch (variant) {\n    case 'X': case 'x':\n      // minutes only matter when they're non-zero — otherwise drop them\n      // entirely. Matches LDML: \"With a single X, the hours field is\n      // required. The minutes field is optional, but only if the\n      // minutes value is 0.\"\n      return minutes === '00' ? `${sign}${hours}` : `${sign}${hours}${minutes}`;\n    case 'XX': case 'xx':\n      return `${sign}${hours}${minutes}`;\n    case 'XXX': case 'xxx':\n      return `${sign}${hours}:${minutes}`;\n  }\n}\n\ntype TokenHandler = (t: TemporalLike, locale: string) => string;\n\n// Longest-first — tokenizer is greedy, \"yyyy\" has to be tried before \"yy\".\n//\n// Numeric tokens always render in ASCII digits, never locale-native\n// (Arabic-Indic, Devanagari, etc). Padding non-ASCII digits isn't as simple\n// as padding \"3\", and most consumers parsing these back out want plain\n// digits anyway.\nexport const TOKENS: Array<[string, TokenHandler, keyof TemporalLike]> = [\n  ['yyyy', (t) => pad(t.year!, 4), 'year'],\n  ['yy', (t) => {\n    // -45 % 100 === -45, so truncating negative years to 2 digits doesn't\n    // work and Math.abs() would make 45 CE and 45 BCE render the same.\n    if (t.year! < 0) {\n      throw new Error(\n        `temporal-fmt: token \"yy\" doesn't support negative years (got ${t.year}), ` +\n        `since truncating to 2 digits would make it indistinguishable from a ` +\n        `positive year. Use \"yyyy\" instead.`\n      );\n    }\n    return pad(t.year! % 100, 2);\n  }, 'year'],\n  // Unpadded year — no minimum width, unlike yyyy's fixed 4 digits.\n  // pad(n, 0) still does the right thing here: Math.abs(n) with no\n  // padStart floor just yields the plain digit string, and the sign\n  // handling (split off before padding) already covers negative years,\n  // so this doesn't need its own sign branch the way \"yy\" does.\n  ['y', (t) => pad(t.year!, 0), 'year'],\n  ['MMMM', (t, locale) => {\n    const custom = getCustomVocab(locale);\n    return localeAwareName(t, locale, { month: 'long' }, 'month', custom?.monthLong, t.month! - 1);\n  }, 'month'],\n  ['MMM', (t, locale) => {\n    const custom = getCustomVocab(locale);\n    return localeAwareName(t, locale, { month: 'short' }, 'month', custom?.monthShort, t.month! - 1);\n  }, 'month'],\n  ['MM', (t) => pad(t.month!, 2), 'month'],\n  ['M', (t) => String(t.month!), 'month'],\n  ['dd', (t) => pad(t.day!, 2), 'day'],\n  ['d', (t) => String(t.day!), 'day'],\n  ['EEEE', (t, locale) => {\n    const custom = getCustomVocab(locale);\n    return localeAwareName(t, locale, { weekday: 'long' }, 'weekday', custom?.weekdayLong, t.dayOfWeek! - 1);\n  }, 'dayOfWeek'],\n  ['EEE', (t, locale) => {\n    const custom = getCustomVocab(locale);\n    return localeAwareName(t, locale, { weekday: 'short' }, 'weekday', custom?.weekdayShort, t.dayOfWeek! - 1);\n  }, 'dayOfWeek'],\n  ['HH', (t) => pad(t.hour!, 2), 'hour'],\n  ['H', (t) => String(t.hour!), 'hour'],\n  ['hh', (t) => pad(t.hour! % 12 || 12, 2), 'hour'],\n  ['h', (t) => String(t.hour! % 12 || 12), 'hour'],\n  ['mm', (t) => pad(t.minute!, 2), 'minute'],\n  ['m', (t) => String(t.minute!), 'minute'],\n  ['ss', (t) => pad(t.second!, 2), 'second'],\n  ['s', (t) => String(t.second!), 'second'],\n  // Fractional-second tokens, S through SSSSSSSSS (1-9 digits). Each token\n  // formats a slice of the same underlying nanosecond-of-second value —\n  // combining millisecond/microsecond/nanosecond into one 9-digit number\n  // and truncating to the token's width — so \"SSS\" keeps meaning exactly\n  // what it always meant (3-digit milliseconds) while wider tokens expose\n  // the precision Temporal actually carries. formatFraction below is the\n  // shared implementation; see its comment for the truncate-not-round\n  // rule and why.\n  ['SSSSSSSSS', (t) => pad.fraction(t, 9), 'millisecond'],\n  ['SSSSSSSS', (t) => pad.fraction(t, 8), 'millisecond'],\n  ['SSSSSSS', (t) => pad.fraction(t, 7), 'millisecond'],\n  ['SSSSSS', (t) => pad.fraction(t, 6), 'millisecond'],\n  ['SSSSS', (t) => pad.fraction(t, 5), 'millisecond'],\n  ['SSSS', (t) => pad.fraction(t, 4), 'millisecond'],\n  ['SSS', (t) => pad.fraction(t, 3), 'millisecond'],\n  ['SS', (t) => pad.fraction(t, 2), 'millisecond'],\n  ['S', (t) => pad.fraction(t, 1), 'millisecond'],\n  // dayPeriod text is locale-specific (AM/PM in en-US, م/ص in ar-EG) but\n  // still needs .hour on the input to compute which period it is\n  ['a', (t, locale) => dayPeriodPart(t.hour!, locale), 'hour'],\n  ['zzz', (t) => t.timeZoneId!, 'timeZoneId'],\n  // Numeric UTC offset tokens (date-fns/Unicode-LDML family). Only\n  // ZonedDateTime carries an offset, so the field check in format() throws\n  // the same \"requires offset, which this Temporal object doesn't have\"\n  // error zzz throws on PlainDate/PlainTime/PlainDateTime — same\n  // validation path, just a different field name. See formatOffset above\n  // for the per-variant width and Z/numeric distinction.\n  ['xxx', (t) => formatOffset(t.offset!, 'xxx'), 'offset'],\n  ['xx', (t) => formatOffset(t.offset!, 'xx'), 'offset'],\n  ['X', (t) => formatOffset(t.offset!, 'X'), 'offset'],\n  ['XX', (t) => formatOffset(t.offset!, 'XX'), 'offset'],\n  ['XXX', (t) => formatOffset(t.offset!, 'XXX'), 'offset'],\n  ['x', (t) => formatOffset(t.offset!, 'x'), 'offset'],\n\n  // Ordinal day (1st, 2nd, 3rd, ... 21st). English suffix rules only —\n  // locale-aware ordinals (\"2.\" in de-DE, \"2日\" in ja-JP) are out of scope,\n  // since the rest of this library routes locale-specific names through\n  // Intl.DateTimeFormat, and Intl has no part type for ordinals. Format-only:\n  // the suffix isn't structurally distinguishable from a literal in a parse\n  // context (a \"st\"/\"nd\"/\"rd\"/\"th\" suffix isn't a digit and would collide\n  // with any adjacent literal text), so there's no good way to read it back.\n  ['do', (t) => {\n    const day = t.day!;\n    const lastDigit = day % 10;\n    // 11, 12, 13 are the exception — they'd otherwise match the 1/2/3 rule\n    // and produce \"11st\"/\"12nd\"/\"13rd\", which is wrong. They always take \"th\".\n    const lastTwoDigits = day % 100;\n    if (lastTwoDigits >= 11 && lastTwoDigits <= 13) {\n      return day + 'th';\n    }\n    if (lastDigit === 1) return day + 'st';\n    if (lastDigit === 2) return day + 'nd';\n    if (lastDigit === 3) return day + 'rd';\n    return day + 'th';\n  }, 'day'],\n\n  // Quarter computed from month: 1-3=Q1, 4-6=Q2, 7-9=Q3, 10-12=Q4.\n  // `Q` is plain numeric, `QQQ` renders as \"Q3\" — same convention as\n  // date-fns's `Q` and `QQQ` for parity with the most common prior art.\n  // Both format and parse; parse() cross-checks Q/QQQ against the parsed\n  // month in the same spirit as the EEEE-vs-date cross-check.\n  ['Q', (t) => String(Math.ceil(t.month! / 3)), 'month'],\n  ['QQQ', (t) => 'Q' + Math.ceil(t.month! / 3), 'month'],\n\n  // ISO 8601 week and week-numbering year. Both are format-only — parsing\n  // \"ww\"/\"RRRR\" back into a real date requires resolving an ISO week + a\n  // weekday (or some other disambiguator) to a specific date, which is a\n  // different parsing surface than the token-based parse() here. The\n  // ISO-week year (RRRR) can differ from the calendar year at the boundary:\n  // Dec 29-31 often belong to week 1 of the *next* year; Jan 1-3 often\n  // belong to week 52/53 of the *previous* year. See isoWeekYearAndWeek().\n  ['ww', (t) => {\n    requireFields(t, 'ww', 'year', 'month', 'day');\n    const { week } = isoWeekYearAndWeek(t.year!, t.month!, t.day!, t.dayOfWeek!);\n    return pad(week, 2);\n  }, 'dayOfWeek'],\n  ['RRRR', (t) => {\n    requireFields(t, 'RRRR', 'year', 'month', 'day');\n    const { isoYear } = isoWeekYearAndWeek(t.year!, t.month!, t.day!, t.dayOfWeek!);\n    return pad(isoYear, 4);\n  }, 'dayOfWeek'],\n\n  // Day of year — number of days since Jan 1 (1-366). Three widths:\n  //   D    — unpadded (1, 2, 366)\n  //   DD   — 2-digit minimum (zero-padded if <100)\n  //   DDD  — 3-digit zero-padded (001, 002, 366)\n  // Format-only — parsing day-of-year requires resolving against a year,\n  // which is a different shape from the token-based parse() surface.\n  // The dayOfYearHelper() in calendarUtils.ts covers the same field for\n  // callers who need the numeric value.\n  ['D', (t) => {\n    requireFields(t, 'D', 'year', 'month');\n    return String(dayOfYear(t.year!, t.month!, t.day!));\n  }, 'day'],\n  ['DD', (t) => {\n    requireFields(t, 'DD', 'year', 'month');\n    return pad(dayOfYear(t.year!, t.month!, t.day!), 2);\n  }, 'day'],\n  ['DDD', (t) => {\n    requireFields(t, 'DDD', 'year', 'month');\n    return pad(dayOfYear(t.year!, t.month!, t.day!), 3);\n  }, 'day'],\n\n  // Stand-alone month — uses Intl's stand-alone form. In most locales\n  // (en, fr, de, es) this is identical to MMMM/MMM. In Slavic/Baltic\n  // locales (cs, sk, pl, ru) the stand-alone form differs from the\n  // format form (nominative vs genitive case). LLLL = long, LLL = short.\n  ['LLLL', (t, locale) => {\n    const custom = getCustomVocab(locale);\n    return localeAwareName(t, locale, { month: 'long' }, 'month', custom?.monthLong, t.month! - 1);\n  }, 'month'],\n  ['LLL', (t, locale) => {\n    const custom = getCustomVocab(locale);\n    return localeAwareName(t, locale, { month: 'short' }, 'month', custom?.monthShort, t.month! - 1);\n  }, 'month'],\n\n  // Stand-alone weekday — same pattern as stand-alone month but for\n  // weekday names. cccc = long, ccc = short.\n  ['cccc', (t, locale) => {\n    const custom = getCustomVocab(locale);\n    return localeAwareName(t, locale, { weekday: 'long' }, 'weekday', custom?.weekdayLong, t.dayOfWeek! - 1);\n  }, 'dayOfWeek'],\n  ['ccc', (t, locale) => {\n    const custom = getCustomVocab(locale);\n    return localeAwareName(t, locale, { weekday: 'short' }, 'weekday', custom?.weekdayShort, t.dayOfWeek! - 1);\n  }, 'dayOfWeek'],\n\n  // Era — locale-aware (\"AD\"/\"BC\" in en, \"ap. J.-C.\"/\"av. J.-C.\" in fr).\n  // GGGG = long, G = short. Format-only.\n  ['GGGG', (t, locale) => {\n    return intlPart(t, locale, { era: 'long' }, 'era');\n  }, 'year'],\n  ['G', (t, locale) => {\n    return intlPart(t, locale, { era: 'short' }, 'era');\n  }, 'year'],\n\n  // Localized timezone name — uses Intl's longLocalized/short timezone\n  // name option. Format-only — these names are locale-dependent and\n  // vary by season (EST vs EDT), so parsing them back requires a\n  // lookup table that isn't practical to ship.\n  ['zzzz', (t, locale) => {\n    return intlPart(t, locale, { timeZoneName: 'longGeneric' as Intl.DateTimeFormatOptions['timeZoneName'] }, 'timeZoneName' as Intl.DateTimeFormatPartTypes);\n  }, 'timeZoneId'],\n  ['z', (t, locale) => {\n    return intlPart(t, locale, { timeZoneName: 'short' as Intl.DateTimeFormatOptions['timeZoneName'] }, 'timeZoneName' as Intl.DateTimeFormatPartTypes);\n  }, 'timeZoneId'],\n\n];\n\n// Mod-registered tokens, layered on top of the static TOKENS above.\n// Last-write-wins by name (same rule as registerLocale and\n// createFormatter's own token merging) — a second registerFormatToken()\n// call for a name already claimed, whether that name came from another\n// mod or from TOKENS itself, replaces the earlier entry. See\n// registerFormatToken() in runtime.ts for why this beats a hard error:\n// Mod.priority exists precisely so an author can control who wins a\n// shared key, and that only means something if collisions actually\n// resolve instead of throwing.\nconst registeredTokens = new Map<string, [string, TokenHandler, keyof TemporalLike]>();\n\n// tokenize.ts and format.ts each derive a lookup table from TOKENS at\n// import time (SORTED_TOKEN_STRINGS, HANDLER_BY_TOKEN). Once mods can\n// add tokens after those tables are built, something has to tell both\n// modules to rebuild — this is that hook. Both subscribers rebuild in\n// the same call so the tokenizer's idea of \"is this a token\" and\n// format()'s idea of \"what does this token do\" can never drift apart\n// (the c8-ignored \"impossible\" branches in format.ts assume exactly\n// that they can't).\nconst rebuildListeners: Array<() => void> = [];\n\nexport function onTokenTableChange(listener: () => void): void {\n  rebuildListeners.push(listener);\n}\n\n// All tokens currently in effect: built-ins plus mod-registered,\n// mod-registered winning on a name clash. Recomputed on every call\n// rather than cached here — registration only happens at mod-load\n// time, so there's no hot-path cost to paying for the rebuild each time\n// a listener fires.\nexport function getEffectiveTokens(): Array<[string, TokenHandler, keyof TemporalLike]> {\n  const merged = new Map(TOKENS.map((t) => [t[0], t] as const));\n  for (const [name, entry] of registeredTokens) merged.set(name, entry);\n  return [...merged.values()];\n}\n\nexport function registerToken(token: [string, TokenHandler, keyof TemporalLike]): void {\n  registeredTokens.set(token[0], token);\n  for (const listener of rebuildListeners) listener();\n}\n\n// Test-only: clears mod-registered tokens and notifies subscribers, so\n// one test file's registerFormatToken() call can't leak into the next.\n// Mirrors _resetOverridesForTesting() in runtime.ts.\nexport function _resetRegisteredTokensForTesting(): void {\n  registeredTokens.clear();\n  for (const listener of rebuildListeners) listener();\n}","/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { TOKENS, getEffectiveTokens, onTokenTableChange } from './tokens.js';\nimport { FormatSyntaxError, UnknownTokenError } from './errors.js';\n\nexport type Piece =\n  | { kind: 'token'; value: string }\n  | { kind: 'literal'; value: string };\n\n// A piece plus its exact source span in the ORIGINAL format string.\n// `start`/`end` are UTF-16 code-unit indices, half-open [start, end).\n// Decoded literals can be shorter than their span ('' is 2 source\n// chars → 1 decoded quote), which is exactly why positions computed\n// from decoded lengths (the old approach in analyze.ts) drifted after\n// any quoted text.\nexport interface SpannedPiece {\n  kind: 'token' | 'literal';\n  value: string;\n  start: number;\n  end: number;\n}\n\n// gotta sort longest-first or the greedy scan grabs \"M\" when \"MMMM\" was actually there.\n// Starts from the static TOKENS (so this module works before any mod has\n// registered anything) and rebuilds from tokens.ts's getEffectiveTokens()\n// whenever a mod calls registerFormatToken — otherwise a token registered\n// after this module loaded would never be recognized here, and format()\n// would tokenize it as literal text even though HANDLER_BY_TOKEN in\n// format.ts knows about it. `let`, not `const`, precisely so the rebuild\n// below can replace the binding rather than mutate a shared array in place.\nlet SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);\n\nonTokenTableChange(() => {\n  SORTED_TOKEN_STRINGS = getEffectiveTokens()\n    .map(([tok]) => tok)\n    .sort((a, b) => b.length - a.length);\n});\n\n/**\n * Splits a format string like `\"yyyy-MM-dd 'at' HH:mm\"` into token/literal\n * pieces, each with its exact source span. Text in single quotes is always\n * literal (e.g. write 'rd' in \"3rd\" so it's not read as the day token). A\n * doubled quote ('') means a literal quote character, both inside a quoted\n * span and standalone.\n */\nexport function tokenizeWithSpans(format: string): SpannedPiece[] {\n  const pieces: SpannedPiece[] = [];\n  let i = 0;\n\n  while (i < format.length) {\n    const ch = format[i];\n\n    if (ch === \"'\") {\n      // gotta check the doubled-quote case first, otherwise \"''best''\" parses wrong\n      if (format[i + 1] === \"'\") {\n        appendLiteral(pieces, \"'\", i, i + 2);\n        i += 2;\n        continue;\n      }\n\n      let j = i + 1;\n      let literal = '';\n      let closed = false;\n      while (j < format.length) {\n        if (format[j] === \"'\") {\n          if (format[j + 1] === \"'\") {\n            literal += \"'\";\n            j += 2;\n            continue;\n          }\n          closed = true;\n          j += 1;\n          break;\n        }\n        literal += format[j];\n        j += 1;\n      }\n\n      if (!closed) {\n        throw new FormatSyntaxError({\n          format,\n          message: `temporal-fmt: unterminated quote in format string \"${format}\"`,\n        });\n      }\n\n      appendLiteral(pieces, literal, i, j);\n      i = j;\n      continue;\n    }\n\n    const match = SORTED_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n    if (match) {\n      // this is already the longest token that starts here, so if there's\n      // one more of the same char after it, that's not a real token — it'd\n      // just silently fall through to whatever handles that next char and\n      // get glued onto this one. found this the hard way: \"zzzz\" was\n      // parsing as zzz + literal \"z\", \"MMMMM\" as MMMM + the M token. so now\n      // we just treat the whole overlong run as one bad token instead\n      const runChar = match[match.length - 1];\n      if (format[i + match.length] === runChar) {\n        let end = i + match.length;\n        while (format[end] === runChar) end += 1;\n        // UnknownTokenError, not FormatSyntaxError: wrapUntypedError's\n        // classifier already treats this exact message (\"isn't a\n        // recognized token\") as UNKNOWN_TOKEN (see errors.test.js's\n        // \"overlong token run classifies as UnknownTokenError\"), and a\n        // direct throw here needs to agree with what safeParse's fallback\n        // path would have classified it as.\n        throw new UnknownTokenError({\n          format,\n          token: format.slice(i, end),\n          message:\n            `temporal-fmt: \"${format.slice(i, end)}\" in format string \"${format}\" isn't a recognized token — ` +\n            `did you mean \"${match}\"?`,\n        });\n      }\n      pieces.push({ kind: 'token', value: match, start: i, end: i + match.length });\n      i += match.length;\n      continue;\n    }\n\n    // not a token or quote — pass through as-is\n    appendLiteral(pieces, ch, i, i + 1);\n    i += 1;\n  }\n\n  return pieces;\n}\n\n/**\n * Splits a format string like `\"yyyy-MM-dd 'at' HH:mm\"` into token/literal\n * pieces. Text in single quotes is always literal (e.g. write 'rd' in\n * \"3rd\" so it's not read as the day token). A doubled quote ('') means a\n * literal quote character, both inside a quoted span and standalone.\n */\nexport function tokenize(format: string): Piece[] {\n  return tokenizeWithSpans(format).map(({ kind, value }) => ({ kind, value }));\n}\n\n// merges into the previous piece if it's also a literal, so \"---\" is one\n// piece instead of three (spans track the full merged range)\nfunction appendLiteral(pieces: SpannedPiece[], value: string, start: number, end: number): void {\n  const last = pieces[pieces.length - 1];\n  if (last && last.kind === 'literal' && last.end === start) {\n    last.value += value;\n    last.end = end;\n  } else {\n    pieces.push({ kind: 'literal', value, start, end });\n  }\n}\n","/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getLocaleVocab } from './localeVocab.js';\nimport { UnknownTokenError } from './errors.js';\nimport { getTemporal } from './temporalProvider.js';\n\nfunction escapeRegExp(literal: string): string {\n  return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction alternation(values: string[], caseInsensitive = false): string {\n  const escaped = values.map(escapeRegExp);\n  if (!caseInsensitive) return `(?:${escaped.join('|')})`;\n  // JS regex has no per-group inline case-insensitive flag, and this\n  // fragment gets embedded in one larger pattern built with a single flag\n  // set — so case-folding here means listing both cases explicitly rather\n  // than relying on a flag.\n  return `(?:${escaped.map(foldCase).join('|')})`;\n}\n\n// Expands \"PM\" into a character-class-per-letter pattern matching any\n// casing of it (\"[Pp][Mm]\"), so \"pm\", \"Pm\", \"PM\" all match the same\n// alternative. Only used for the day-period token (see the 'a' case\n// below) — not applied to month/weekday names, where case-folding across\n// scripts is a different and riskier problem this doesn't need to solve.\nfunction foldCase(value: string): string {\n  return value.replace(/[a-zA-Z]/g, (ch) => `[${ch.toLowerCase()}${ch.toUpperCase()}]`);\n}\n\n// Every real IANA zone id is letters/digits/'_'/'+'/'-' segments joined by\n// '/' (e.g. \"America/Argentina/Buenos_Aires\", \"Etc/GMT+12\"); UTC and\n// fixed-offset strings are the only other shapes zzz accepts. Matching that\n// *shape* here — instead of alternating all ~400 zone names inline — keeps\n// the compiled regex small regardless of how many zzz tokens appear in a\n// format string. The captured text still gets checked against the real\n// zone set in isValidTimeZone() after the overall pattern matches, so this\n// is strictly a matching-cost change, not a validation-strictness change:\n// a bogus zone id fails \"no valid pattern matches\" exactly like it did when\n// the zone list was inlined (see isValidTimeZone's caller in parse.ts).\nconst TIME_ZONE_SHAPE = '(?:UTC|[+-]\\\\d{2}:\\\\d{2}(?::\\\\d{2}(?:\\\\.\\\\d{1,9})?)?|[A-Za-z_]+(?:[+-]\\\\d{1,2})?(?:\\\\/[A-Za-z0-9_+-]+)*)';\n\n// Per-variant regex shapes for the six offset tokens. Each matches the\n// shape its own format counterpart produces, so a round-trip\n// format->parse succeeds for any input the library itself emitted.\n//\n// Kept loose (no hour/minute range bounds inline) on purpose, mirroring\n// how TIME_ZONE_SHAPE is loose: a permissive shape here lets parse()\n// surface a descriptive out-of-range error post-match (see\n// parseOffsetString in parse.ts) instead of the generic \"no valid pattern\n// matches\" the regex throws when the shape itself fails. \"+99:99\" should\n// tell the user it's out of range, not look like the input never matched\n// the format at all.\n//\n// X / x accept an optional minutes group so whole-hour offsets can be\n// written short (\"+05\") while non-whole-hour offsets still parse\n// (\"+0530\"). The optional group is greedy, so for input \"+0530\" the\n// engine prefers the 4-digit match; only falls back to 2-digit when\n// there's nothing else to consume — same longer-first preference the\n// unpadded numeric tokens use elsewhere in this file (see the comment on\n// NUMERIC_FRAGMENTS).\nconst OFFSET_SHAPES: Record<string, string> = {\n  X:   '(?:Z|[+-]\\\\d{2}(?:\\\\d{2})?)',\n  XX:  '(?:Z|[+-]\\\\d{4})',\n  XXX: '(?:Z|[+-]\\\\d{2}:\\\\d{2})',\n  x:   '[+-]\\\\d{2}(?:\\\\d{2})?',\n  xx:  '[+-]\\\\d{4}',\n  xxx: '[+-]\\\\d{2}:\\\\d{2}',\n};\n\nfunction getTimeZoneFragment(): string {\n  return TIME_ZONE_SHAPE;\n}\n\nlet validZoneSet: Set<string> | undefined;\n\n// Real zone ids plus the couple of aliases zzz has always accepted even\n// though Intl.supportedValuesOf('timeZone') doesn't list them (UTC isn't\n// itself an IANA zone name, it's the identity offset).\nfunction getValidZoneSet(): Set<string> {\n  if (!validZoneSet) {\n    validZoneSet = new Set(Intl.supportedValuesOf('timeZone'));\n    validZoneSet.add('UTC');\n  }\n  return validZoneSet;\n}\n\nconst FIXED_OFFSET_RE = /^[+-]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?$/;\n\n// Called post-match on whatever the bounded TIME_ZONE_SHAPE captured, since\n// that shape is deliberately looser than \"a real zone id\" (it has to be, to\n// stay a fixed-size regex fragment — see the comment above). A fixed offset\n// is valid by construction; anything else has to be a real IANA name.\n//\n// Intl.supportedValuesOf('timeZone') is checked first as a fast path — it's\n// a plain Set lookup and covers the overwhelming majority of real input.\n// But it lists only canonical zone ids, not every IANA link/alias name:\n// \"Asia/Kolkata\" is a legitimate, commonly-used alias for \"Asia/Calcutta\"\n// that Temporal.ZonedDateTime.from() itself resolves correctly, yet some\n// ICU builds' supportedValuesOf() omits it. Rejecting it here — even\n// though the exact same string would construct a real ZonedDateTime one\n// call later — was a real bug: parse() refused input its own downstream\n// construction step would have accepted. Anything Intl doesn't recognize\n// gets a second check against Temporal itself before being refused.\nexport function isValidTimeZone(raw: string): boolean {\n  if (FIXED_OFFSET_RE.test(raw) || getValidZoneSet().has(raw)) return true;\n  try {\n    getTemporal().ZonedDateTime.from({\n      year: 2026, month: 1, day: 1, hour: 0, minute: 0, second: 0,\n      timeZone: raw,\n    });\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n// mirrors the ranges pad() in tokens.ts actually produces — keep in sync\n// if those ever change\n//\n// Unpadded alternatives (M, H, h, m, s) list the longer branch first\n// (e.g. '1[0-2]|[1-9]', not '[1-9]|1[0-2]'). This matters only when two\n// unpadded tokens are glued with no separator: with short-first ordering,\n// a regex engine takes the first successful overall match, and won't\n// backtrack into a token's second alternative unless its first choice\n// makes the *rest* of the pattern fail outright. If the short reading also\n// happens to leave a valid match for the next token, the engine stops\n// there — silently, deterministically, and with no relation to which\n// reading a human intended. E.g. \"Md\" against \"121\": short-first order\n// resolves it as month=1/day=21 (M grabs '1', d gets '21', which is a\n// valid day) instead of month=12/day=1. Longer-first ordering fixes this\n// by making the greedy match try to consume as many digits as possible\n// before ever handing digits to the next token, which is the reading\n// that matches how format() itself produces glued output in the first\n// place (format() always emits the token's natural width, so decoding\n// should prefer the same). Found via the token×token combinatorial glue\n// matrix in combinatorial.test.js — see that file for the full case list.\nconst NUMERIC_FRAGMENTS: Record<string, string> = {\n  yy: '\\\\d{2}',\n  MM: '(?:0[1-9]|1[0-2])',\n  M: '(?:1[0-2]|[1-9])',\n  dd: '(?:0[1-9]|[12]\\\\d|3[01])',\n  d: '(?:[12]\\\\d|3[01]|[1-9])',\n  HH: '(?:[01]\\\\d|2[0-3])',\n  H: '(?:1\\\\d|2[0-3]|[0-9])',\n  hh: '(?:0[1-9]|1[0-2])',\n  h: '(?:1[0-2]|[1-9])',\n  mm: '(?:[0-5]\\\\d)',\n  m: '(?:[1-5]\\\\d|[0-9])',\n  ss: '(?:[0-5]\\\\d)',\n  s: '(?:[1-5]\\\\d|[0-9])',\n  SSSSSSSSS: '\\\\d{9}',\n  SSSSSSSS: '\\\\d{8}',\n  SSSSSSS: '\\\\d{7}',\n  SSSSSS: '\\\\d{6}',\n  SSSSS: '\\\\d{5}',\n  SSSS: '\\\\d{4}',\n  SSS: '\\\\d{3}',\n  SS: '\\\\d{2}',\n  S: '\\\\d',\n  // Q is always a single digit 1-4 (no padding variant, so no width ambiguity\n  // with adjacent digit tokens the way M/d/H/m/s have).\n  Q: '[1-4]',\n};\n\n// QQQ matches its own formatted output: the literal \"Q\" prefix plus a single\n// digit 1-4. Kept as a static fragment rather than routed through vocab,\n// since the \"Q\" prefix is part of the token's own contract, not locale-vocab\n// data that could ever differ.\nconst QQQ_FRAGMENT = 'Q[1-4]';\n\n// Format-only tokens — they have no parse counterpart. The tokenizer still\n// recognizes them (so format() can use them), but parse()'s regex builder\n// rejects them with a clear error rather than silently dropping the token\n// or falling through to the generic \"unknown token\" message.\n// Exported so analyze.ts can include them in the public analyzer surface.\nexport const FORMAT_ONLY_TOKENS = new Set(['do', 'ww', 'RRRR', 'D', 'DD', 'DDD', 'LLLL', 'LLL', 'cccc', 'ccc', 'GGGG', 'G', 'zzzz', 'z']);\n\n// pad()'s year formatter (tokens.ts) never truncates: it preserves the sign\n// for BCE years and doesn't cap width past 9999, so a formatted \"yyyy\" can\n// be longer than 4 digits or start with '-'. YYYY_EXTENDED accepts that;\n// YYYY_EXACT is the plain 4-unsigned-digit case. Which one a given \"yyyy\"\n// occurrence gets depends on what follows it — see buildCapturingPattern in\n// parsePattern.ts. Two separate fragments instead of one `-?\\d{4,}` because\n// an open-ended-width year directly followed by another digit token (e.g.\n// \"yyyyMM\") lets the year's own greediness silently eat digits meant for\n// the next token — same class of bug as UNPADDED_NUMERIC_TOKENS below, but\n// unbounded-width, so it can't reuse enumerateValidSplits' fixed-range\n// splitting. Restricting to exactly 4 digits whenever something could\n// follow closes that off entirely, at the cost of \"yyyyMM\" not being able\n// to represent a 5-digit year — an already-rare case doubly rare in\n// combination with a glued adjacent token.\nconst YYYY_EXACT = '-?\\\\d{4}';\nconst YYYY_EXTENDED = '-?\\\\d{4,}';\n\n// \"y\" is yyyy's unpadded sibling: any width, 1 digit up through Temporal's\n// max supported year (275760), with the same optional leading \"-\" for\n// years before ISO year 0. Unlike yyyy, there's no bounded fallback to\n// reach for when something digit-consuming follows — yyyy can fall back\n// to an exact 4 digits because it's always exactly 4 digits in that case,\n// but \"y\" being unpadded is the entire point of the token, so there's no\n// narrower shape that still means the same thing. buildCapturingPattern\n// (parsePattern.ts) refuses to build a pattern where \"y\" is immediately\n// followed by another digit-consuming element, rather than trying to\n// bound this fragment the way YYYY_EXACT does.\nconst Y_FRAGMENT = '-?\\\\d+';\n\n// True for any token whose matched text can start with a digit — i.e.\n// every token here except the locale-named ones (MMMM/MMM/EEEE/EEE/a) and\n// zzz (which can start with a digit only via a fixed offset like \"+09:00\",\n// already handled by requiring a leading sign there). Used to decide\n// whether a \"yyyy\" immediately before this token needs the exact-4-digit\n// fragment instead of the open-ended one.\n// Exported for parsePattern.ts's ReDoS guard: a token whose regex\n// fragment can begin with a bare digit (see the guard comments there).\nexport const DIGIT_LEADING_TOKENS = new Set([\n  'yyyy', 'yy', 'y', 'MM', 'M', 'dd', 'd', 'HH', 'H', 'hh', 'h', 'mm', 'm', 'ss', 's',\n  'SSSSSSSSS', 'SSSSSSSS', 'SSSSSSS', 'SSSSSS', 'SSSSS', 'SSSS', 'SSS', 'SS', 'S',\n]);\n\n// Tokens whose regex fragment has no upper bound on width — nothing\n// caps how many digits they might consume. yyyy's YYYY_EXTENDED form is\n// also unbounded, but it's only ever selected when the *next* piece\n// already isn't digit-consuming (see tokenFragment below), so by\n// construction it never reaches a position where the ambiguity guard in\n// parsePattern.ts would need to weigh in. \"y\" has no such self-limiting\n// selection rule — it's unbounded unconditionally — so it needs an\n// explicit guard there instead of a bit-cost estimate: there's no\n// meaningful number of \"width choices\" to assign to \"any number of\n// digits,\" so parsePattern.ts refuses outright rather than pretending a\n// finite ambiguity score covers it.\nexport const UNBOUNDED_WIDTH_TOKENS = new Set(['y']);\n\nexport function tokenFragment(token: string, locale: string, nextToken?: string): string {\n  if (token === 'yyyy') {\n    return nextToken !== undefined && DIGIT_LEADING_TOKENS.has(nextToken) ? YYYY_EXACT : YYYY_EXTENDED;\n  }\n  if (token === 'y') {\n    return Y_FRAGMENT;\n  }\n\n  const numeric = NUMERIC_FRAGMENTS[token];\n  if (numeric) {\n    return numeric;\n  }\n\n  if (token === 'QQQ') {\n    return QQQ_FRAGMENT;\n  }\n\n  if (FORMAT_ONLY_TOKENS.has(token)) {\n    throw new UnknownTokenError({\n      token,\n      message:\n        `temporal-fmt: token \"${token}\" is format-only — it can't be parsed back into a value. ` +\n        `Use a different token in the parse format string (e.g. \"d\" for \"do\", \"MM\" for \"ww\").`,\n    });\n  }\n\n  const vocab = getLocaleVocab(locale);\n  switch (token) {\n    case 'MMMM': return alternation(vocab.monthLong);\n    case 'MMM': return alternation(vocab.monthShort);\n    case 'EEEE': return alternation(vocab.weekdayLong);\n    case 'EEE': return alternation(vocab.weekdayShort);\n    // Case-insensitive on purpose: \"pm\"/\"Pm\"/\"PM\" all mean the same thing,\n    // and unlike the Md-glue ambiguity elsewhere in this file, there's no\n    // second valid reading to guess wrong — so rejecting on case buys no\n    // correctness, only friction against real-world data (mixed-case CSV\n    // exports, lowercase log timestamps).\n    case 'a': return alternation(vocab.dayPeriod, true);\n    case 'zzz': return getTimeZoneFragment();\n    case 'X': case 'XX': case 'XXX':\n    case 'x': case 'xx': case 'xxx':\n      return OFFSET_SHAPES[token]!;\n    /* c8 ignore start @preserve -- defensive guard, not reachable through\n       the public API. tokenFragment's only caller (buildCapturingPattern\n       in parsePattern.ts) always passes piece.value straight from\n       tokenize.ts, which only ever emits strings from tokens.ts's TOKENS\n       table. Every token in that table is handled above: the numeric\n       ones via NUMERIC_FRAGMENTS, QQQ via QQQ_FRAGMENT, the format-only\n       ones via the FORMAT_ONLY_TOKENS check earlier in this function, and\n       everything else via one of the switch cases. There's no token\n       string that can reach this default without either tokens.ts\n       registering something new or tokenize.ts being bypassed, neither of\n       which happens on the parse() path. */\n    default:\n      throw new UnknownTokenError({ token, message: `temporal-fmt: unknown token \"${token}\"` });\n    /* c8 ignore stop @preserve */\n  }\n}\n\n// Tokens whose fragment is variable-width (1-2 digits, no leading zero).\n// Two or more of these glued with no literal separator between them can\n// have more than one digit-split that's independently valid against every\n// fragment in the run — see the big comment on NUMERIC_FRAGMENTS above.\n// Reordering alternation branches picks a winner for *some* of these\n// cases, but can't make both directions of a pair (e.g. \"Md\" and \"dM\")\n// agree, because the ambiguity is in the input string itself, not in how\n// any one fragment is written. exported so parsePattern.ts can find runs\n// of these that need split-counting at match time instead of a single\n// fixed regex.\nexport const UNPADDED_NUMERIC_TOKENS = new Set(['M', 'd', 'H', 'h', 'm', 's']);\n\n// Every accept width for a given unpadded numeric token, as plain min/max\n// value + digit-length pairs — used to enumerate candidate splits of a\n// digit run at match time. Mirrors NUMERIC_FRAGMENTS's semantics exactly\n// (same accepted values), just as data instead of regex source, since\n// enumerating splits against a compiled regex per-candidate would be\n// slower and harder to reason about than checking numeric ranges directly.\nexport const UNPADDED_NUMERIC_RANGES: Record<string, Array<{ digits: 1 | 2; min: number; max: number }>> = {\n  M: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 12 }],\n  d: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 31 }],\n  H: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 23 }],\n  h: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 12 }],\n  m: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 59 }],\n  s: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 59 }],\n};\n\n/**\n * Given the literal digit string a run of N adjacent unpadded-numeric\n * tokens matched as a whole (e.g. \"112\" for a 2-token run), enumerates\n * every way to split it into N pieces (one per token, each piece 1-2\n * digits per that token's own width rule) and returns every split where\n * every piece is independently valid for its token. Length 0 means the\n * run's regex match shouldn't have been possible in the first place\n * (shouldn't happen — the caller only invokes this after the whole\n * pattern already matched, meaning at least one split exists: the one the\n * regex actually took). Length 1 means the reading is unambiguous.\n * Length 2+ means true ambiguity — the caller should throw rather than\n * pick one.\n *\n * Recursive over token count rather than hardcoded to 2, so a 3+ token\n * unseparated run (e.g. \"Hms\") is covered by the same logic without a\n * special case — those are rarer in practice but not impossible, and a\n * partial fix that only covered pairs would leave the identical bug for\n * anyone writing a 3-token glued run.\n */\nexport function enumerateValidSplits(digits: string, tokens: string[]): number[][] {\n  const memo = new Map<string, number[][]>();\n\n  function solve(tokenIndex: number, offset: number): number[][] {\n    const key = `${tokenIndex}:${offset}`;\n    const cached = memo.get(key);\n    if (cached) {\n      return cached;\n    }\n\n    if (tokenIndex === tokens.length) {\n      const result = offset === digits.length ? [[]] : [];\n      memo.set(key, result);\n      return result;\n    }\n\n    const token = tokens[tokenIndex];\n    const ranges = UNPADDED_NUMERIC_RANGES[token!];\n    /* c8 ignore start @preserve -- defensive guard, not reachable through\n       the public API. enumerateValidSplits's only caller (parse.ts, both\n       call sites) passes run.tokens straight from\n       pattern.ambiguousRuns, which parsePattern.ts only ever populates\n       with tokens already checked against UNPADDED_NUMERIC_TOKENS — the\n       exact same key set as UNPADDED_NUMERIC_RANGES. There's no path\n       where a token reaches here without having already passed that\n       check. */\n    if (!ranges) {\n      throw new Error(`temporal-fmt: internal error — \"${token}\" is not an unpadded numeric token`);\n    }\n    /* c8 ignore stop @preserve */\n\n    const results: number[][] = [];\n    for (const { digits: width, min, max } of ranges) {\n      if (offset + width > digits.length) continue;\n      const piece = digits.slice(offset, offset + width);\n      if (width === 2 && piece[0] === '0') continue;\n      const value = Number(piece);\n      if (value < min || value > max) continue;\n\n      for (const restSplit of solve(tokenIndex + 1, offset + width)) {\n        results.push([value, ...restSplit]);\n        if (results.length === 2) break;\n      }\n      if (results.length === 2) break;\n    }\n\n    memo.set(key, results);\n    return results;\n  }\n\n  return solve(0, 0);\n}","/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Piece } from './tokenize.js';\nimport { tokenFragment, UNPADDED_NUMERIC_TOKENS, DIGIT_LEADING_TOKENS, UNBOUNDED_WIDTH_TOKENS } from './pattern.js';\nimport { FormatSyntaxError } from './errors.js';\n\nfunction escapeRegExp(literal: string): string {\n  return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n// this guards against catastrophic backtracking (ReDoS) in the generated regex.\n//\n// every ReDoS we've actually found in this lib has the same shape: two+\n// variable-width digit-consuming regex fragments sitting next to each other\n// with nothing to tell the engine where one ends and the next starts —\n// either glued straight together (\"MdMdMd...\", \"HmsHms...\") or separated\n// only by a literal that itself starts with a digit (\"M1M1M1...\",\n// \"yyyy1yyyy1...\"). each fragment then has multiple ways to split up the\n// digit run, and on a failing match the engine tries every combination —\n// exponential in the number of fragments. measured this against the old\n// code: \"Md\"×13 (26-char format, 40-char input) took about 2.7s; \"yyyy1\"×8\n// (48-char format) took about 26s. both roughly ×3-14 worse per extra\n// fragment, so it gets bad fast.\n//\n// three fixes, all at pattern-build time:\n//\n//  1. a run of 2+ glued unpadded-numeric tokens now becomes ONE bounded\n//     digit group `(?<rN>\\d{R,2R})` instead of R separate variable-width\n//     fragments. the per-token split still gets resolved after the match,\n//     via enumerateValidSplits() in pattern.ts — same machinery parse()\n//     already used for detecting ambiguous glued runs, so behavior's\n//     unchanged (unique split resolves, 2+ valid splits throws in strict\n//     mode / gets heuristic-picked in lenient, 0 splits is a mismatch).\n//     a lone \\d{R,2R} group only backtracks R+1 times max — linear, not\n//     exponential.\n//\n//  2. yyyy now uses the exact -?\\d{4} fragment not just when the next\n//     TOKEN is digit-leading (that rule already existed) but also when\n//     the next LITERAL starts with a digit. an open-ended -?\\d{4,} year\n//     glued right up against a digit literal is basically the cheapest\n//     possible exponential blowup (unbounded width choices per year), and\n//     the exact form still matches everything the open form did in that\n//     spot except 5+ digit years glued directly to an unquoted digit\n//     literal — a genuinely pathological edge case we're fine trading away.\n//\n//  3. for whatever's left: every adjacency between a variable-width digit\n//     consumer (lone unpadded token, or a glued-run group) and a\n//     digit-consuming successor costs log2 of the consumer's width\n//     choices, added to a running \"ambiguity budget\". go over\n//     MAX_AMBIGUITY_BITS (12 — hard ceiling of 4096 backtrack paths) and\n//     we just reject the format string at build time with a\n//     FormatSyntaxError. normal format strings score 0-3. the\n//     \"M1M1M1...\" attack pattern scores one bit per glued pair.\nconst MAX_AMBIGUITY_BITS = 12;\n\nfunction widthChoicesBits(choices: number): number {\n  return Math.ceil(Math.log2(Math.max(choices, 1)));\n}\n\n// does this piece's regex fragment start by eating a bare digit? (tokens\n// that can match starting with 0-9, and literals whose first char is a\n// digit.) used to spot the boundaries where a variable-width digit\n// consumer before it could end up trading digits with whatever follows\nfunction isDigitConsumingStart(piece: Piece | undefined): boolean {\n  if (piece === undefined) return false;\n  if (piece.kind === 'literal') return /^[0-9]/.test(piece.value);\n  return DIGIT_LEADING_TOKENS.has(piece.value);\n}\n\nexport interface CapturingPattern {\n  regex: RegExp;\n  groups: Array<{ name: string; token: string }>; // token pieces, in order\n  // runs of 2+ adjacent unpadded-numeric tokens glued together with no\n  // literal separator (e.g. \"Md\", \"Hms\"). each run gets captured by ONE\n  // regex group named `groupName` spanning the whole digit run (R..2R\n  // digits) — per-token values get pulled out later by\n  // enumerateValidSplits() at match time. `groupNames` is the per-token\n  // group names, which show up in `groups` for structure/position but\n  // don't actually exist in the regex itself, so anyone consuming this\n  // needs to read values from the split enumeration, not match.groups\n  ambiguousRuns: Array<{ groupName: string; groupNames: string[]; tokens: string[] }>;\n}\n\n/**\n * Same walk as buildPatternSource() in pattern.ts, but each token piece\n * gets its own named capture group (positionally named so the same token,\n * e.g. \"yyyy\", could in theory appear twice) so a caller can pull the\n * matched substring for each token back out after a successful match.\n */\nexport function buildCapturingPattern(pieces: Piece[], locale: string): CapturingPattern {\n  const groups: Array<{ name: string; token: string }> = [];\n  const ambiguousRuns: Array<{ groupName: string; groupNames: string[]; tokens: string[] }> = [];\n  let source = '';\n  let i = 0;\n  let ambiguityBits = 0;\n\n  // tracks the current run of adjacent unpadded-numeric pieces (nothing's\n  // broken it yet — no literal, no non-unpadded token). names get recorded\n  // in order so a run of 2+ can collapse into one group while each token\n  // still gets its own entry in `groups`\n  let currentRun: { names: string[]; tokens: string[] } = { names: [], tokens: [] };\n\n  const flushRun = (nextPiece: Piece | undefined) => {\n    if (currentRun.tokens.length === 1) {\n      // a lone unpadded token just gets its normal two-way fragment.\n      // two width choices on their own aren't a problem; only charge an\n      // ambiguity bit if the next thing can also eat a digit\n      const name = currentRun.names[0]!;\n      const token = currentRun.tokens[0]!;\n      source += `(?<${name}>${tokenFragment(token, locale)})`;\n      if (isDigitConsumingStart(nextPiece)) ambiguityBits += 1;\n    } else if (currentRun.tokens.length >= 2) {\n      // emit the whole accumulated run as ONE bounded digit group. R\n      // unpadded tokens together accept somewhere between R and 2R digits\n      // total, and anything outside that range can't match no matter how\n      // you split it — so this single group covers exactly the same\n      // territory the old per-token fragments did, just without the\n      // combinatorial backtracking. worst case here is R+1 width choices,\n      // which is linear\n      const runName = `r${i++}`;\n      const tokenCount = currentRun.tokens.length;\n      source += `(?<${runName}>\\\\d{${tokenCount},${tokenCount * 2}})`;\n      // note: no `groups` entry for the run group itself — `groups` only\n      // lists per-token pieces (already pushed when we visited them), so\n      // consumers like parseToParts still see exactly one entry per token,\n      // same as before this run-group thing existed. the regex group\n      // itself is only reachable through ambiguousRuns[].groupName\n      ambiguousRuns.push({\n        groupName: runName,\n        groupNames: currentRun.names,\n        tokens: currentRun.tokens,\n      });\n      // a run group next to a digit-consuming successor still keeps its\n      // (R+1) width choices at that boundary, so charge the budget for it\n      if (isDigitConsumingStart(nextPiece)) {\n        ambiguityBits += widthChoicesBits(tokenCount + 1);\n      }\n    }\n    currentRun = { names: [], tokens: [] };\n  };\n\n  for (const [idx, piece] of pieces.entries()) {\n    if (piece.kind === 'literal') {\n      flushRun(piece);\n      source += escapeRegExp(piece.value);\n      continue;\n    }\n\n    if (UNPADDED_NUMERIC_TOKENS.has(piece.value)) {\n      // part of a (possible) glued run — hold off emitting the fragment\n      // and let flushRun deal with it, so 2+ in a row collapse into one\n      // bounded group instead of staying separate\n      const name = `g${i++}`;\n      groups.push({ name, token: piece.value });\n      currentRun.names.push(name);\n      currentRun.tokens.push(piece.value);\n      continue;\n    }\n\n    flushRun(piece);\n    const name = `g${i++}`;\n    groups.push({ name, token: piece.value });\n    const nextPiece = pieces[idx + 1];\n    const nextToken = nextPiece?.kind === 'token' ? nextPiece.value : undefined;\n\n    // \"y\" doesn't have a bounded fallback the way yyyy does (check\n    // tokenFragment in pattern.ts) — it's unpadded by definition, so\n    // there's no narrower fixed-width shape underneath it to fall back\n    // to. \"any number of digits\" sitting right next to another digit\n    // consumer has no finite ambiguity score we could charge against\n    // MAX_AMBIGUITY_BITS, so we just refuse it outright at build time\n    // instead of trying to estimate something\n    if (UNBOUNDED_WIDTH_TOKENS.has(piece.value) && isDigitConsumingStart(nextPiece)) {\n      throw new FormatSyntaxError({\n        reason:\n          `token \"${piece.value}\" has no fixed width — it can't be placed directly next to another ` +\n          `digit-reading token or a literal that starts with a digit, since there's no way to tell ` +\n          `where \"${piece.value}\" ends and the next field begins. Add a non-digit separator (e.g. \"-\" or \" \") after it.`,\n      });\n    }\n\n    // yyyy's fragment depends on what comes next: exact 4-digit form\n    // whenever something digit-consuming follows (digit-leading token —\n    // the original rule — OR a literal starting with a digit, added as\n    // part of the ReDoS fix above). the open-ended form is only safe\n    // when nothing digit-consuming can come right after it\n    if (piece.value === 'yyyy' && nextToken === undefined && isDigitConsumingStart(nextPiece)) {\n      source += `(?<${name}>${tokenFragment(piece.value, locale, 'M')})`;\n    } else {\n      source += `(?<${name}>${tokenFragment(piece.value, locale, nextToken)})`;\n    }\n  }\n  flushRun(undefined);\n\n  if (ambiguityBits > MAX_AMBIGUITY_BITS) {\n    throw new FormatSyntaxError({\n      reason:\n        `format string has too many variable-width numeric tokens glued to digit-consuming neighbors ` +\n        `(ambiguity score ${ambiguityBits} > ${MAX_AMBIGUITY_BITS}). ` +\n        `This shape makes the regex engine backtrack exponentially on near-miss input. ` +\n        `Add a non-digit separator between these tokens (e.g. \"-\" or \" \") or use their padded forms (MM/dd/HH/mm/ss).`,\n    });\n  }\n\n  // the 'd' flag turns on match.indices.groups, which parseToParts uses\n  // to report where each token actually landed in the input. without it\n  // we'd need a whole separate walk of the piece list against the input\n  // to compute positions — duplicating logic the regex engine's already\n  // doing for us. safe to add: 'd' only adds an `indices` property to\n  // the result, doesn't change matching behavior at all\n  return { regex: new RegExp(`^(?:${source})$`, 'ud'), groups, ambiguousRuns };\n}","/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// format strings are basically always short hand-written stuff like \"yyyy-MM-dd\"\n// so if something's way longer than that, cap it before tokenize() chokes on it\nexport const MAX_FORMAT_LENGTH = 1000;\nexport const MAX_INPUT_LENGTH = 100_000;","/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// numbering systems. latn (ASCII digits) is the default, but arab, deva,\n// beng, etc are all options, configurable per format call.\n//\n// the tokens in this lib always spit out ASCII digits normally (check\n// tokens.ts's pad() — just String(n), which is ASCII). this module bolts\n// on the ability to convert that output to a locale's native digits via\n// Intl.NumberFormat, since that's the standard way JS does digit\n// transliteration anyway.\n//\n// parse side is stricter: parse() only accepts ASCII digits, matches how\n// NUMERIC_FRAGMENTS is already built. parseNumberingSystem converts\n// input digits to ASCII first — an explicit opt-in per call, never\n// silently accepting any numeral system that shows up.\n//\n// both directions also accept 'auto': instead of requiring the caller to\n// know the ICU numbering-system code for their locale, ask\n// Intl.NumberFormat(locale).resolvedOptions().numberingSystem what the\n// locale itself uses and transliterate with that.\n\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { InvalidLocaleError } from './errors.js';\nimport { canonicalCacheKey, normalizeLocaleTag } from './localeVocab.js';\n\nexport type NumberingSystem = 'latn' | 'arab' | 'deva' | 'beng' | 'guru' | 'gujr' | 'orya' | 'tamldec' | 'telu' | 'knda' | 'mlym' | 'fullwide' | 'hanidec';\n\n// What a caller may pass as numberingSystem / parseNumberingSystem: an\n// explicit system name, or 'auto' to derive the system from the call's\n// locale. The `(string & {})` keeps arbitrary strings compiling (the\n// runtime still validates them against SUPPORTED_NUMBERING_SYSTEMS and\n// throws on unknown names, unchanged) while editors still offer the\n// known literals — 'latn', 'arab', ..., 'auto' — as completions.\nexport type NumberingSystemOption = NumberingSystem | 'auto' | (string & {});\n\n// every NumberingSystem value we support. latn's the default and\n// what the rest of this lib naturally produces\nexport const SUPPORTED_NUMBERING_SYSTEMS: ReadonlySet<string> = new Set([\n  'latn', 'arab', 'deva', 'beng', 'guru', 'gujr', 'orya', 'tamldec',\n  'telu', 'knda', 'mlym', 'fullwide', 'hanidec',\n]);\n\nconst digitMapCache = new Map<string, Record<string, string>>();\n\n// 'auto' resolutions, keyed by canonical locale tag (same key discipline\n// as every other locale-keyed cache in this library — 'ar_EG' and 'ar-EG'\n// fold together instead of costing two entries). Intl.NumberFormat\n// construction isn't free and format() runs in loops (a table of dates, one\n// row per record), so resolve each locale once. Bounded, evicting the\n// oldest insertion, mirroring formatterCache in tokens.ts.\nconst autoNumberingCache = new Map<string, string>();\nconst MAX_AUTO_NUMBERING_CACHE = 500;\n\n// Turns 'auto' into a concrete system by asking Intl what the locale\n// itself defaults to (ar-EG -> arab, bn-BD -> beng, en-US -> latn). When\n// the locale's native system isn't one this library can transliterate\n// (thai, laoo, mymr, ... — anything outside SUPPORTED_NUMBERING_SYSTEMS),\n// fall back to 'latn' rather than throwing: 'auto' means \"use whatever\n// this locale naturally uses, if you can\", not \"throw on locales with\n// numerals this library doesn't cover\". A malformed locale tag DOES\n// throw — typed InvalidLocaleError, matching getFormatter() in tokens.ts —\n// since asking to derive from a locale that doesn't exist is a caller bug,\n// not a data condition to paper over.\nfunction resolveAutoNumberingSystem(locale: string): string {\n  const key = canonicalCacheKey(locale);\n  const cached = autoNumberingCache.get(key);\n  if (cached !== undefined) return cached;\n  let resolved: string;\n  try {\n    resolved = new Intl.NumberFormat(normalizeLocaleTag(locale)).resolvedOptions().numberingSystem;\n  } catch (err) {\n    throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n  }\n  const system = SUPPORTED_NUMBERING_SYSTEMS.has(resolved) ? resolved : 'latn';\n  if (autoNumberingCache.size >= MAX_AUTO_NUMBERING_CACHE) {\n    // not real LRU, just evicts oldest insertion — fine for this key space\n    const oldestKey = autoNumberingCache.keys().next().value;\n    if (oldestKey !== undefined) autoNumberingCache.delete(oldestKey);\n  }\n  autoNumberingCache.set(key, system);\n  return system;\n}\n\n// Shared front door for both option names. undefined stays 'latn' — the\n// default is unchanged; 'auto' is an opt-in, not a new default. Anything\n// else passes through untouched for convertDigits/convertDigitsToAscii\n// to validate against the supported set (they throw on unknown names).\nfunction resolveRequestedSystem(requested: string | undefined, locale: string | undefined): string {\n  if (requested === undefined) return 'latn';\n  if (requested === 'auto') return resolveAutoNumberingSystem(locale ?? DEFAULT_LOCALE);\n  return requested;\n}\n\n// builds a digit-transliteration map per numbering system. renders 0-9\n// through Intl.NumberFormat in the target system, then builds the lookup\n// table from that. caching it since spinning up a formatter isn't free\n// and we reuse the same map for every digit we convert\nfunction getDigitMap(system: string): Record<string, string> {\n  let map = digitMapCache.get(system);\n  if (map) return map;\n  /* c8 ignore start @preserve -- this branch is dead by construction, not\n     just untested: both callers (convertDigits, convertDigitsToAscii)\n     already bail out early on system === 'latn' before ever calling\n     getDigitMap, so it never actually gets invoked with 'latn'. keeping\n     it anyway as a defensive fallback rather than betting that stays\n     true forever */\n  if (system === 'latn') {\n    map = {};\n    for (let i = 0; i < 10; i++) map[String(i)] = String(i);\n  } else {\n    /* c8 ignore stop @preserve */\n    const fmt = new Intl.NumberFormat('en-US-u-nu-' + system, { useGrouping: false });\n    map = {};\n    for (let i = 0; i < 10; i++) {\n      map[String(i)] = fmt.format(i);\n    }\n  }\n  digitMapCache.set(system, map);\n  return map;\n}\n\n// swaps every ASCII digit in `s` for its equivalent in the target\n// numbering system. anything that's not a digit passes through untouched\nexport function convertDigits(s: string, system: string): string {\n  if (system === 'latn') return s;\n  if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {\n    throw new InvalidLocaleError({ actual: system, reason: `numbering system \"${system}\" is not supported. Supported: ${[...SUPPORTED_NUMBERING_SYSTEMS].join(', ')}.` });\n  }\n  const map = getDigitMap(system);\n  let result = '';\n  for (const ch of s) {\n    if (ch >= '0' && ch <= '9') {\n      // map[ch] is always populated for 0-9 — getDigitMap builds all ten\n      // keys for every system we support, and ch is already range-checked\n      // above. the ?? ch is really just there to satisfy TS about Record's\n      // implicit undefined, not because this path is actually reachable\n      /* c8 ignore next */\n      result += map[ch] ?? ch;\n    } else {\n      result += ch;\n    }\n  }\n  return result;\n}\n\n// inverse of convertDigits — takes non-ASCII digits back to ASCII. used\n// by parse() when someone passes an explicit numberingSystem option.\n// throws on anything unsupported\nexport function convertDigitsToAscii(s: string, system: string): string {\n  // same dead-by-construction thing as the 'latn' guard above —\n  // applyParseNumbering (the only caller) already returns early on\n  // 'latn' before this ever gets called, so system's never actually\n  // 'latn' here in practice. leaving the guard anyway in case someone\n  // calls this directly someday without going through that guard\n  /* c8 ignore next */\n  if (system === 'latn') return s;\n  if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {\n    throw new InvalidLocaleError({ actual: system, reason: `numbering system \"${system}\" is not supported.` });\n  }\n  const map = getDigitMap(system);\n  // just flip the map around\n  const reverse: Record<string, string> = {};\n  for (const k of Object.keys(map)) reverse[map[k]!] = k;\n  let result = '';\n  for (const ch of s) {\n    result += reverse[ch] ?? ch;\n  }\n  return result;\n}\n\n// FormatOptions plus a numberingSystem field. pass { numberingSystem: 'arab' }\n// to format() to get Arabic-Indic digits out, or { numberingSystem: 'auto' }\n// to use whichever numeral system the call's locale itself defaults to\n// (ar-EG -> arab, bn-BD -> beng, en-US -> latn), resolved via\n// Intl.NumberFormat(locale).resolvedOptions().numberingSystem with a\n// 'latn' fallback for locales whose native system isn't supported here.\n// Unset still means 'latn' — 'auto' is an opt-in, not a default change.\nexport interface NumberingFormatOptions extends FormatOptions {\n  numberingSystem?: NumberingSystemOption;\n}\n\n// same idea but for the parse side. called parseNumberingSystem instead of\n// just numberingSystem so someone mixing format() and parse() options in\n// one config object can set both independently — the two directions\n// aren't always symmetric (you might want native digits out without\n// wanting to accept them back in, or vice versa). 'auto' works here too,\n// resolving from the parse call's own locale the same way the format\n// side does.\nexport interface NumberingParseOptions extends FormatOptions {\n  parseNumberingSystem?: NumberingSystemOption;\n}\n\n// format-path helper: takes format()'s ASCII output and converts digits\n// if numberingSystem was asked for. lives here so format.ts doesn't need\n// to know anything about numbering systems\nexport function applyNumbering(s: string, options: NumberingFormatOptions): string {\n  const system = resolveRequestedSystem(options.numberingSystem, options.locale);\n  if (system === 'latn') return s;\n  return convertDigits(s, system);\n}\n\n// parse-path helper: converts input digits to ASCII before matching, if\n// parseNumberingSystem got set. kept as a separate option name from the\n// format side so callers can be explicit about which direction they\n// actually want transliterated\nexport function applyParseNumbering(s: string, options: { parseNumberingSystem?: string; locale?: string }): string {\n  // both call sites for this (both in parse.ts) already guard with\n  // `if (options.parseNumberingSystem)` before calling, so an undefined\n  // parseNumberingSystem never actually reaches here from parse() itself —\n  // the undefined handling inside resolveRequestedSystem is dead by\n  // construction from that direction. leaving it in as a safety net\n  // rather than betting every future caller replicates the same guard\n  // (this is a public export; direct calls don't come pre-guarded).\n  const system = resolveRequestedSystem(options.parseNumberingSystem, options.locale);\n  if (system === 'latn') return s;\n  return convertDigitsToAscii(s, system);\n}"],"mappings":"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","names":["escapeRegExp","name","MAX_CACHE_SIZE"]}