{"version":3,"sources":["../src/relativeTime.ts","../src/isoWeek.ts","../src/calendarUtils.ts","../src/arithmetic.ts","../src/temporalProvider.ts","../src/errors.ts","../src/localeVocab.ts","../src/tokens.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\n// relative time stuff. formatDistance() over in formatDistance.ts is\n// still the main one, this just adds formatRelative() / formatRelativeToNow()\n// on top.\n//\n// the difference between these that keeps tripping people up:\n//   formatDistance(date1, date2) -> \"3 days ago\" / \"in 2 hours\"\n//     just describes the gap, hands unit picking + pluralization off to Intl\n//   formatRelative(date, baseDate) -> \"yesterday\" / \"tomorrow\" / \"today\" /\n//     \"in 3 days\" / \"last week\"\n//     this one's calendar-relative, not ms-based. so \"in 1 day\" always means\n//     tomorrow even if it's technically only 23 hours from now\n//   formatRelativeToNow(date) -> literally just formatRelative(date, now)\n\nimport { differenceInDays } from './arithmetic.js';\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { normalizeLocaleTag } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport interface FormatRelativeOptions extends FormatOptions {\n  // 'auto' (default) — Intl picks natural stuff like \"yesterday\"/\"tomorrow\"\n  // when it's ±1 or 0. 'always' forces the stricter \"1 day ago\" style always.\n  numeric?: 'always' | 'auto';\n}\n\nconst rtfCache = new Map<string, Intl.RelativeTimeFormat>();\nconst MAX_RTF_CACHE_SIZE = 100;\n\nfunction getRtf(locale: string, numeric: 'always' | 'auto'): Intl.RelativeTimeFormat {\n  // reusing the same cache shape formatDistance.ts already has\n  const key = `${locale}|${numeric}`;\n  let rtf = rtfCache.get(key);\n  if (rtf) return rtf;\n  if (rtfCache.size >= MAX_RTF_CACHE_SIZE) {\n    const oldestKey = rtfCache.keys().next().value;\n    if (oldestKey !== undefined) rtfCache.delete(oldestKey);\n  }\n  try {\n    rtf = new Intl.RelativeTimeFormat(normalizeLocaleTag(locale), { numeric });\n  } catch (err) {\n    // bad locale tags just throw a plain RangeError from Intl, wrap it in\n    // our own error type instead. every failure here IS a RangeError anyway\n    // so we're not losing anything by converting unconditionally — the\n    // original message still comes through in `reason`\n    throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n  }\n  rtfCache.set(key, rtf);\n  return rtf;\n}\n\n// describes date1 as if you were standing at date2. uses differenceInDays\n// (counts calendar boundaries, not literal 24hr chunks) so something like\n// \"2026-08-04 23:59\" vs \"2026-08-05 00:00\" correctly says \"yesterday\",\n// not \"1 second ago\" which would be technically true but useless\n//\n// differenceInDays(a, b) gives b - a, but we want date1 - date2 here so\n// positive = future relative to date2. hence the argument swap below\nexport function formatRelative(\n  date1: unknown,\n  date2: unknown,\n  options: FormatRelativeOptions = {},\n): string {\n  const locale = options.locale ?? DEFAULT_LOCALE;\n  const numeric = options.numeric ?? 'auto';\n  const rtf = getRtf(locale, numeric);\n  const dayDiff = -differenceInDays(date1, date2);\n  const absDays = Math.abs(dayDiff);\n\n  if (absDays === 0) {\n    return rtf.format(0, 'day'); // shows up as \"now\"\n  }\n  if (absDays < 7) {\n    return rtf.format(dayDiff, 'day');\n  }\n  if (absDays < 30) {\n    return rtf.format(-Math.trunc(-dayDiff / 7), 'week');\n  }\n  if (absDays < 365) {\n    return rtf.format(-Math.trunc(-dayDiff / 30), 'month');\n  }\n  return rtf.format(-Math.trunc(-dayDiff / 365), 'year');\n}\n\nexport function formatRelativeToNow(\n  date: unknown,\n  options: FormatRelativeOptions = {},\n): string {\n  // just uses whatever the system clock says right now as the reference\n  const now = new Date();\n  const nowFields = {\n    year: now.getFullYear(),\n    month: now.getMonth() + 1,\n    day: now.getDate(),\n  };\n  return formatRelative(date, nowFields, options);\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// 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\n// Calendar utility helpers. These are pure functions\n// over the TemporalLike shape — no Temporal namespace needed, same\n// approach as isoWeek.ts and the field-reading helpers in format.ts.\n// Letting callers compute dayOfYear/weekOfYear/etc. without going\n// through format() means they can build their own derived values\n// without committing to a string format.\n//\n// Calendar-sensitivity: the helpers in this module assume the\n// iso8601 (Gregorian) calendar — that's what TemporalLike fields\n// carry for the overwhelming majority of callers. Non-Gregorian\n// calendars (hebrew, islamic, etc.) need their own helpers; this\n// module doesn't try to be calendar-polymorphic the way Temporal\n// itself is. Documented limitation, not a design choice — see\n// VERIFICATION.md for the rationale.\n\nimport { isGregorianLeapYear, dayOfYear, isoWeekYearAndWeek, dayOfWeekFromCivil } from './isoWeek.js';\n\n// A subset of TemporalLike that has the date fields these helpers need,\n// plus optional time fields. PlainTime isn't a DateFieldView (no\n// year/month/day), but PlainDateTime / ZonedDateTime / PlainDate all\n// match. Time fields are optional so callers can pass a PlainDate\n// to startOf(value, 'month') without having to populate hour/minute/etc.\n// Exported so the comparison/arithmetic modules can use the same\n// narrowing.\nexport interface DateFieldView {\n  year?: number;\n  month?: number;\n  day?: number;\n  hour?: number;\n  minute?: number;\n  second?: number;\n  millisecond?: number;\n  dayOfWeek?: number;\n  calendarId?: string;\n}\n\nfunction requireFields(view: DateFieldView, fields: Array<keyof DateFieldView>): void {\n  for (const f of fields) {\n    if (typeof view[f] !== 'number') {\n      throw new Error(\n        `temporal-fmt: calendar helper requires \"${String(f)}\", which this value doesn't have. ` +\n        `Pass a Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n      );\n    }\n  }\n}\n\nexport function daysInMonth(view: DateFieldView): number {\n  requireFields(view, ['year', 'month']);\n  const { year, month } = view;\n  // Standard Gregorian month lengths. February's length depends on\n  // whether `year` is a leap year — the same isGregorianLeapYear check\n  // isoWeek.ts uses for dayOfYear arithmetic.\n  const LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n  if (month === 2 && isGregorianLeapYear(year!)) return 29;\n  return LENGTHS[(month! - 1)!]!;\n}\n\nexport function daysInYear(view: DateFieldView): 365 | 366 {\n  requireFields(view, ['year']);\n  return isGregorianLeapYear(view.year!) ? 366 : 365;\n}\n\nexport function monthsInYear(_view: DateFieldView): 12 {\n  // Gregorian always has 12 months. Other calendars (Hebrew leap years\n  // have 13) need calendar-aware logic this module doesn't carry — see\n  // the file-level comment. The `_view` parameter is kept so the\n  // signature mirrors the other helpers and a future calendar-aware\n  // implementation can use it without changing call sites.\n  return 12;\n}\n\nexport function isLeapYear(view: DateFieldView): boolean {\n  requireFields(view, ['year']);\n  return isGregorianLeapYear(view.year!);\n}\n\n// `isLeapMonth` would require knowing which month of a leap-year-aware\n// calendar is the leap month — Gregorian doesn't have one, so this\n// returns false unconditionally. Kept here so the public surface\n// matches the module's listing; non-Gregorian calendars need\n// a different implementation.\nexport function isLeapMonth(_view: DateFieldView): boolean {\n  return false;\n}\n\nexport function dayOfYearHelper(view: DateFieldView): number {\n  requireFields(view, ['year', 'month', 'day']);\n  return dayOfYear(view.year!, view.month!, view.day!);\n}\n\n// ISO 8601 week and week-year. Delegates to isoWeek.ts's\n// isoWeekYearAndWeek, which does the full Thursday-of-week\n// computation to handle the year-boundary cases (Dec 29-31 belonging\n// to week 1 of next year, Jan 1-3 belonging to week 52/53 of the\n// previous year).\nexport function weekOfYear(view: DateFieldView): number {\n  requireFields(view, ['year', 'month', 'day', 'dayOfWeek']);\n  return isoWeekYearAndWeek(view.year!, view.month!, view.day!, view.dayOfWeek!).week;\n}\n\nexport function weekYear(view: DateFieldView): number {\n  requireFields(view, ['year', 'month', 'day', 'dayOfWeek']);\n  return isoWeekYearAndWeek(view.year!, view.month!, view.day!, view.dayOfWeek!).isoYear;\n}\n\n/**\n * Fiscal-quarter options. `startMonth` is the calendar month (1-12) the\n * fiscal year begins on — e.g. `7` for a fiscal year starting in July.\n * Omitted or `1` gives the calendar-quarter behavior getQuarter() has\n * always had (Jan-Mar = Q1, etc.), so existing callers passing nothing\n * see no change.\n */\nexport interface QuarterOptions {\n  startMonth?: number;\n}\n\nfunction validateStartMonth(startMonth: number): void {\n  if (!Number.isInteger(startMonth) || startMonth < 1 || startMonth > 12) {\n    throw new Error(\n      `temporal-fmt: getQuarter's startMonth must be an integer from 1 to 12 (got ${startMonth}).`\n    );\n  }\n}\n\nexport function getQuarter(view: DateFieldView, options: QuarterOptions = {}): number {\n  requireFields(view, ['month']);\n  const startMonth = options.startMonth ?? 1;\n  validateStartMonth(startMonth);\n  if (startMonth === 1) {\n    // Mirrors the Q token: months 1-3 → Q1, 4-6 → Q2, 7-9 → Q3, 10-12 → Q4.\n    return Math.ceil(view.month! / 3);\n  }\n  // Fiscal case: shift the month so startMonth becomes month 1 of the\n  // fiscal year (mod 12, 1-indexed), then apply the same ceil(/3) rule.\n  // E.g. startMonth=7 (fiscal year starts July): July→1, Aug→2, ...,\n  // Dec→6, Jan→7, ..., June→12. Then Q1 = fiscal months 1-3 (Jul-Sep),\n  // matching the common \"FY starts in July\" convention where Q1 is the\n  // first quarter of the fiscal year, not a quarter numbered by which\n  // calendar quarter it falls in.\n  const shifted = ((view.month! - startMonth + 12) % 12) + 1;\n  return Math.ceil(shifted / 3);\n}\n\n// `getMonth` / `getWeekday` look trivial (just read the field) but\n// they're kept as real functions for API surface consistency with the\n// rest of this module. They also normalize: getWeekday returns 1-7\n// (Mon-Sun, matching Temporal's spec) regardless of what numbering the\n// caller's underlying value uses.\nexport function getMonth(view: DateFieldView): number {\n  requireFields(view, ['month']);\n  return view.month!;\n}\n\nexport function getWeekday(view: DateFieldView): number {\n  requireFields(view, ['dayOfWeek']);\n  return view.dayOfWeek!;\n}\n\n// startOf / endOf return new field bags (not Temporal objects — this\n// module is polyfill-free) with the relevant fields zeroed/extended.\n// Callers can pass the result to a Temporal constructor if they want\n// a typed value.\nexport type StartOfUnit = 'day' | 'month' | 'year' | 'hour' | 'minute' | 'second';\n\n// Returns true if the given unit (when used with startOf/endOf) should\n// also touch the time fields. 'day', 'month', 'year' all imply a\n// resolution coarser than an hour, so startOf zeroes the time fields\n// and endOf maxes them. Sub-hour units (hour/minute/second) only touch\n// the fields finer than themselves.\nfunction touchesTime(unit: StartOfUnit): boolean {\n  return unit === 'day' || unit === 'month' || unit === 'year';\n}\n\n// startOf/endOf reassign year/month/day, which invalidates any\n// dayOfWeek carried over from the input — a plain { ...view } spread\n// leaves the old value sitting there unchanged. Same failure mode\n// businessCalendar.ts's isBusinessDay() works around for add(); we\n// recompute here rather than trust the copied field.\nfunction recomputeDayOfWeek(view: DateFieldView): void {\n  if (typeof view.dayOfWeek !== 'number') return;\n  /* c8 ignore start @preserve -- unreachable: startOf()/endOf() both call\n   * asDateFieldView() before this, which already throws if year/month/day\n   * aren't all numbers — so by the time a value with a numeric dayOfWeek\n   * reaches here, year/month/day are guaranteed present too. */\n  if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n  /* c8 ignore stop @preserve */\n  // dayOfWeekFromCivil: O(1) proleptic-Gregorian weekday. The old\n  // Date.UTC(year, month-1, day) form remapped years 0-99 to 1900-1999\n  // (ECMAScript spec), so startOf/endOf of a first-century date carried\n  // a weekday from the wrong century.\n  view.dayOfWeek = dayOfWeekFromCivil(view.year, view.month, view.day);\n}\n\nexport function startOf(value: unknown, unit: StartOfUnit): DateFieldView {\n  const view = asDateFieldView(value);\n  const result: DateFieldView = { ...view };\n  if (unit === 'year') {\n    result.month = 1;\n    result.day = 1;\n  } else if (unit === 'month') {\n    result.day = 1;\n  }\n  recomputeDayOfWeek(result);\n  if (touchesTime(unit)) {\n    result.hour = 0;\n    result.minute = 0;\n    result.second = 0;\n    result.millisecond = 0;\n  } else if (unit === 'hour') {\n    result.minute = 0;\n    result.second = 0;\n    result.millisecond = 0;\n  } else if (unit === 'minute') {\n    result.second = 0;\n    result.millisecond = 0;\n  } else if (unit === 'second') {\n    result.millisecond = 0;\n  }\n  return result;\n}\n\nexport function endOf(value: unknown, unit: StartOfUnit): DateFieldView {\n  const view = asDateFieldView(value);\n  const result: DateFieldView = { ...view };\n  if (unit === 'year') {\n    result.month = 12;\n    result.day = daysInMonth({ year: result.year!, month: 12 });\n  } else if (unit === 'month') {\n    result.day = daysInMonth({ year: result.year!, month: result.month! });\n  }\n  recomputeDayOfWeek(result);\n  if (touchesTime(unit)) {\n    result.hour = 23;\n    result.minute = 59;\n    result.second = 59;\n    result.millisecond = 999;\n  } else if (unit === 'hour') {\n    result.minute = 59;\n    result.second = 59;\n    result.millisecond = 999;\n  } else if (unit === 'minute') {\n    result.second = 59;\n    result.millisecond = 999;\n  } else if (unit === 'second') {\n    result.millisecond = 999;\n  }\n  return result;\n}\n\n// Type-narrowing helpers used by the comparison/arithmetic modules.\n// Lets them accept any of the four date-carrying Temporal types without\n// importing Temporal itself.\nexport function asDateFieldView(value: unknown): DateFieldView {\n  if (typeof value !== 'object' || value === null) {\n    throw new Error(`temporal-fmt: expected a date-carrying Temporal value, got ${String(value)}.`);\n  }\n  // Temporal instances expose year/month/day/etc. as prototype getters,\n  // not own enumerable properties — so `{ ...value }` would lose them.\n  // Read them explicitly. Only the fields actually present on this\n  // value type end up in the returned view.\n  const v = value as Record<string, unknown>;\n  const out: DateFieldView = {};\n  if (typeof v.year === 'number') out.year = v.year;\n  if (typeof v.month === 'number') out.month = v.month;\n  if (typeof v.day === 'number') out.day = v.day;\n  if (typeof v.hour === 'number') out.hour = v.hour;\n  if (typeof v.minute === 'number') out.minute = v.minute;\n  if (typeof v.second === 'number') out.second = v.second;\n  if (typeof v.millisecond === 'number') out.millisecond = v.millisecond;\n  if (typeof v.dayOfWeek === 'number') out.dayOfWeek = v.dayOfWeek;\n  if (typeof v.calendarId === 'string') out.calendarId = v.calendarId;\n  if (out.year === undefined || out.month === undefined || out.day === undefined) {\n    throw new Error(\n      `temporal-fmt: value is missing year/month/day fields — pass a Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n    );\n  }\n  return out;\n}\n\n// Re-export the TemporalType alias so callers can import everything\n// from one place.\nexport type { TemporalType } from './tokenMetadata.js';\n// Re-export TemporalLike for the same reason.\nexport type { TemporalLike } from './tokens.js';","/*\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// Date arithmetic helpers. Pure functions returning\n// field bags, same convention as calendarUtils.ts/comparison.ts — no\n// Temporal namespace needed. Callers can pass the result back into a\n// Temporal constructor to get a typed value, or chain operations\n// without committing to a Temporal implementation.\n\nimport { asDateFieldView, daysInMonth, type DateFieldView } from './calendarUtils.js';\nimport { dayOfWeekFromCivil, daysFromCivil } from './isoWeek.js';\n\nexport type AddUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\ninterface DateTimeFieldView extends DateFieldView {\n  hour?: number;\n  minute?: number;\n  second?: number;\n  millisecond?: number;\n}\n\nfunction asDateTime(value: unknown): DateTimeFieldView {\n  return asDateFieldView(value) as DateTimeFieldView;\n}\n\n// Recomputes dayOfWeek after year/month/day change. Mirrors\n// calendarUtils.ts's recomputeDayOfWeek — same fix, needed here too\n// since add()/subtract() mutate the date fields via a shallow spread\n// that otherwise leaves the old dayOfWeek sitting there, silently wrong.\nfunction recomputeDayOfWeek(view: DateTimeFieldView): void {\n  if (typeof view.dayOfWeek !== 'number') return;\n  /* c8 ignore start @preserve -- unreachable: every call site (add()'s\n     'years'/'months' cases, shiftDays()) passes a `result` whose\n     year/month/day were just explicitly set, and the value originally\n     came from asDateTime()/asDateFieldView(), which throws unless\n     year/month/day are all present as numbers. So by the time this guard\n     runs, all three are guaranteed valid. Kept in case a future caller\n     constructs a DateTimeFieldView bypassing that validation. */\n  if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n  /* c8 ignore stop @preserve */\n  // dayOfWeekFromCivil: O(1) proleptic-Gregorian weekday. The old\n  // Date.UTC(year, month-1, day) form silently remaps years 0-99 to\n  // 1900-1999 (ECMAScript spec behavior), computing the weekday of the\n  // wrong century — e.g. add(PlainDate('0050-01-01'), 1, 'days') used\n  // to report dayOfWeek 1 (Mon) for a date that's a Sunday.\n  view.dayOfWeek = dayOfWeekFromCivil(view.year, view.month, view.day);\n}\n\n// Adds the requested amount to the value's specified unit. Returns a\n// new field bag; the input is not mutated. Handles month/year overflow\n// by clamping to the last valid day of the target month (Feb 29 + 1\n// year on a non-leap year → Feb 28) — Temporal's `constrain` overflow\n// mode, which is the closest match to \"do the obvious thing\" callers\n// expect from arithmetic.\nexport function add(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n  const v = asDateTime(value);\n  let result: DateTimeFieldView = { ...v };\n  switch (unit) {\n    case 'years': {\n      /* c8 ignore start @preserve -- the `?? 0` fallback is unreachable:\n         asDateTime()/asDateFieldView() (called at the top of add()) throws\n         unless year is present as a number, so result.year is never\n         undefined here. Kept as a guard against a future change that\n         relaxes that validation. */\n      result.year = (result.year ?? 0) + amount;\n      /* c8 ignore stop @preserve */\n      // Clamp day-of-month to the new month's length.\n      const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n      if (result.day! > maxDay) result.day = maxDay;\n      recomputeDayOfWeek(result);\n      break;\n    }\n    case 'months': {\n      // Total months = year*12 + month + amount, then split back with\n      // floor division/modulo. JS's % keeps the sign of the dividend, so\n      // a hand-rolled negative-modulo fixup used to double-borrow the\n      // year for totals < 0: add(PlainDate('0001-01-15'), -13, 'months')\n      // returned year -2 / month 12 instead of Temporal's -0001-12-15\n      // (floorDiv already accounted for the borrow; the fixup took\n      // another year off on top).\n      /* c8 ignore start @preserve -- both `??` fallbacks are unreachable\n         for the same reason as the 'years' case above: asDateFieldView()\n         guarantees year and month are present numbers before add() ever\n         reaches here. */\n      const total = (result.year ?? 0) * 12 + (result.month ?? 1) - 1 + amount;\n      /* c8 ignore stop @preserve */\n      result.year = Math.floor(total / 12);\n      result.month = total - result.year * 12 + 1; // floorMod(total, 12) + 1, always in 1..12\n      const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n      if (result.day! > maxDay) result.day = maxDay;\n      recomputeDayOfWeek(result);\n      break;\n    }\n    case 'weeks':\n      result = shiftDays(result, amount * 7);\n      break;\n    case 'days':\n      result = shiftDays(result, amount);\n      break;\n    case 'hours':\n      result = shiftTime(result, amount * 3_600_000);\n      break;\n    case 'minutes':\n      result = shiftTime(result, amount * 60_000);\n      break;\n    case 'seconds':\n      result = shiftTime(result, amount * 1_000);\n      break;\n    case 'milliseconds':\n      result = shiftTime(result, amount);\n      break;\n  }\n  return result;\n}\n\nfunction shiftDays(v: DateTimeFieldView, days: number): DateTimeFieldView {\n  // Convert Y/M/D to a day count, add, convert back. The forward\n  // direction uses isoWeek.ts's daysFromCivil — the era term previously\n  // inlined here applied Math.floor to Hinnant's -399-offset numerator,\n  // which double-corrects for negative years (the offset form assumes\n  // truncating division; JS's is flooring), shifting every pre-year-0\n  // date by one day. The inverse (civil_from_days) below is floor-safe\n  // as written (its out-of-range `doe` cases self-compensate through the\n  // yoe formula — property-tested over years -1000..3000, all months,\n  // round-trip identity holds).\n  const totalDays = daysFromCivil(v.year!, v.month!, v.day!) + days;\n\n  // Invert: totalDays → Y/M/D. Same source (Howard Hinnant's\n  // civil_from_days). The era term MUST use truncating division — the\n  // -146096 offset exists so that trunc lands the era correctly; with\n  // Math.floor every z < 0 came out one day early (the forward formula's\n  // matching error used to hide this on round-trips; now that the\n  // forward direction is exact, the inverse has to be too).\n  const z = totalDays + 719468;\n  const era2 = Math.trunc((z >= 0 ? z : z - 146096) / 146097);\n  const doe2 = z - era2 * 146097;\n  const yoe2 = Math.floor((doe2 - Math.floor(doe2 / 1460) + Math.floor(doe2 / 36524) - Math.floor(doe2 / 146096)) / 365);\n  const y2out = yoe2 + era2 * 400;\n  const doy2 = doe2 - (365 * yoe2 + Math.floor(yoe2 / 4) - Math.floor(yoe2 / 100));\n  const mp = Math.floor((5 * doy2 + 2) / 153);\n  const d2 = doy2 - Math.floor((153 * mp + 2) / 5) + 1;\n  const m2out = mp < 10 ? mp + 3 : mp - 9;\n  const yOut = m2out <= 2 ? y2out + 1 : y2out;\n\n  const result: DateTimeFieldView = { ...v, year: yOut, month: m2out, day: d2 };\n  recomputeDayOfWeek(result);\n  return result;\n}\n\nfunction shiftTime(v: DateTimeFieldView, msDelta: number): DateTimeFieldView {\n  // Convert all time fields to ms, add, then split back. Day overflow\n  // propagates to shiftDays; sub-second precision is preserved (caller\n  // asking for hours/minutes/seconds/milliseconds gets ms-resolution\n  // arithmetic, which is the limit of these helpers — nanoseconds\n  // aren't supported here, only via Temporal.Duration's own add).\n  const MS_PER_DAY = 86_400_000;\n  const MS_PER_HOUR = 3_600_000;\n  const MS_PER_MINUTE = 60_000;\n  const MS_PER_SECOND = 1_000;\n\n  const totalMs = (v.hour ?? 0) * MS_PER_HOUR\n    + (v.minute ?? 0) * MS_PER_MINUTE\n    + (v.second ?? 0) * MS_PER_SECOND\n    + (v.millisecond ?? 0)\n    + msDelta;\n\n  // Split into day-overflow + within-day ms.\n  const dayOverflow = Math.floor(totalMs / MS_PER_DAY);\n  let withinDay = totalMs % MS_PER_DAY;\n  if (withinDay < 0) { withinDay += MS_PER_DAY; }\n\n  const hour = Math.floor(withinDay / MS_PER_HOUR);\n  const minute = Math.floor((withinDay % MS_PER_HOUR) / MS_PER_MINUTE);\n  const second = Math.floor((withinDay % MS_PER_MINUTE) / MS_PER_SECOND);\n  const millisecond = withinDay % MS_PER_SECOND;\n\n  const result: DateTimeFieldView = { ...v, hour, minute, second, millisecond };\n  if (dayOverflow !== 0) {\n    return shiftDays(result, dayOverflow);\n  }\n  return result;\n}\n\nexport function subtract(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n  return add(value, -amount, unit);\n}\n\n// Per-unit convenience wrappers — matches the grouping of\n// addYears / addMonths / ... / subtractNanoseconds. Nanosecond variants\n// aren't provided here because these helpers operate on field bags, and\n// the millisecond/microsecond/nanosecond split isn't preserved through\n// arithmetic at sub-millisecond precision the way Temporal.Duration's\n// own add preserves it. For ns-precision arithmetic, use Temporal.Duration\n// directly.\nexport const addYears = (v: unknown, n: number) => add(v, n, 'years');\nexport const addMonths = (v: unknown, n: number) => add(v, n, 'months');\nexport const addWeeks = (v: unknown, n: number) => add(v, n, 'weeks');\nexport const addDays = (v: unknown, n: number) => add(v, n, 'days');\nexport const addHours = (v: unknown, n: number) => add(v, n, 'hours');\nexport const addMinutes = (v: unknown, n: number) => add(v, n, 'minutes');\nexport const addSeconds = (v: unknown, n: number) => add(v, n, 'seconds');\nexport const addMilliseconds = (v: unknown, n: number) => add(v, n, 'milliseconds');\n\nexport const subtractYears = (v: unknown, n: number) => subtract(v, n, 'years');\nexport const subtractMonths = (v: unknown, n: number) => subtract(v, n, 'months');\nexport const subtractWeeks = (v: unknown, n: number) => subtract(v, n, 'weeks');\nexport const subtractDays = (v: unknown, n: number) => subtract(v, n, 'days');\nexport const subtractHours = (v: unknown, n: number) => subtract(v, n, 'hours');\nexport const subtractMinutes = (v: unknown, n: number) => subtract(v, n, 'minutes');\nexport const subtractSeconds = (v: unknown, n: number) => subtract(v, n, 'seconds');\nexport const subtractMilliseconds = (v: unknown, n: number) => subtract(v, n, 'milliseconds');\n\n// difference(): returns the count of `unit` boundaries between two values.\n// Pure-integer math, no Temporal.Duration involved. For sub-day\n// precision the result is an integer count of the requested unit (not\n// a Duration field bag) — matches the most common caller expectation\n// (\"how many days between these two dates?\").\nexport type DiffUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\nexport function difference(a: unknown, b: unknown, unit: DiffUnit): number {\n  const av = asDateTime(a);\n  const bv = asDateTime(b);\n\n  // For day-granular units (years/months/weeks/days), compute via\n  // calendar arithmetic on the date fields. For sub-day units, compute\n  // via total-ms and divide.\n  if (unit === 'years') {\n    return bv.year! - av.year!;\n  }\n  if (unit === 'months') {\n    return (bv.year! * 12 + bv.month! - 1) - (av.year! * 12 + av.month! - 1);\n  }\n  if (unit === 'weeks' || unit === 'days') {\n    const aDays = toDayCount(av);\n    const bDays = toDayCount(bv);\n    const days = bDays - aDays;\n    return unit === 'weeks' ? Math.trunc(days / 7) : days;\n  }\n\n  // Sub-day units: total-ms math.\n  const MS_PER_HOUR = 3_600_000;\n  const MS_PER_MINUTE = 60_000;\n  const MS_PER_SECOND = 1_000;\n  const aMs = toTotalMs(av);\n  const bMs = toTotalMs(bv);\n  const diffMs = bMs - aMs;\n  switch (unit) {\n    case 'hours': return Math.trunc(diffMs / MS_PER_HOUR);\n    case 'minutes': return Math.trunc(diffMs / MS_PER_MINUTE);\n    case 'seconds': return Math.trunc(diffMs / MS_PER_SECOND);\n    case 'milliseconds': return diffMs;\n  }\n}\n\nfunction toDayCount(v: DateTimeFieldView): number {\n  // daysFromCivil (isoWeek.ts): the O(1) Hinnant day count, correct for\n  // negative years. The era term previously inlined here double-shifted\n  // pre-year-0 dates by one day (see shiftDays).\n  return daysFromCivil(v.year!, v.month!, v.day!);\n}\n\nfunction toTotalMs(v: DateTimeFieldView): number {\n  const MS_PER_DAY = 86_400_000;\n  const MS_PER_HOUR = 3_600_000;\n  const MS_PER_MINUTE = 60_000;\n  const MS_PER_SECOND = 1_000;\n  return toDayCount(v) * MS_PER_DAY\n    + (v.hour ?? 0) * MS_PER_HOUR\n    + (v.minute ?? 0) * MS_PER_MINUTE\n    + (v.second ?? 0) * MS_PER_SECOND\n    + (v.millisecond ?? 0);\n}\n\n// Per-unit difference wrappers — same convenience pattern as the add*\n// wrappers above.\nexport const differenceInYears = (a: unknown, b: unknown) => difference(a, b, 'years');\nexport const differenceInMonths = (a: unknown, b: unknown) => difference(a, b, 'months');\nexport const differenceInWeeks = (a: unknown, b: unknown) => difference(a, b, 'weeks');\nexport const differenceInDays = (a: unknown, b: unknown) => difference(a, b, 'days');\nexport const differenceInHours = (a: unknown, b: unknown) => difference(a, b, 'hours');\nexport const differenceInMinutes = (a: unknown, b: unknown) => difference(a, b, 'minutes');\nexport const differenceInSeconds = (a: unknown, b: unknown) => difference(a, b, 'seconds');\nexport const differenceInMilliseconds = (a: unknown, b: unknown) => difference(a, b, 'milliseconds');","/*\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\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}"],"mappings":"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","names":[]}