temporal-fmt 0.9.2 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -1
- package/dist/chunk-4JDAJSEM.js +35 -0
- package/dist/chunk-4JDAJSEM.js.map +1 -0
- package/dist/{chunk-F4RGUDA3.js → chunk-4XRE37WT.js} +2 -2
- package/dist/chunk-GMGZZG6I.js +2 -0
- package/dist/chunk-GMGZZG6I.js.map +1 -0
- package/dist/{chunk-R52YOKI3.js → chunk-NPKJ7QFK.js} +2 -2
- package/dist/{chunk-NBXF7V5B.js → chunk-R4YEFOVE.js} +2 -2
- package/dist/{chunk-4JWGUR4O.js → chunk-SSPABEUR.js} +2 -2
- package/dist/format.cjs +5 -5
- package/dist/format.cjs.map +1 -1
- package/dist/format.js +1 -1
- package/dist/index.cjs +24 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/interval.cjs +2 -2
- package/dist/interval.cjs.map +1 -1
- package/dist/interval.js +1 -1
- package/dist/parse.cjs +9 -9
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.js +1 -1
- package/dist/pattern.d.cts +1 -0
- package/dist/pattern.d.ts +1 -0
- package/dist/relativeTime.cjs +1 -1
- package/dist/relativeTime.cjs.map +1 -1
- package/dist/relativeTime.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-C5YESWFT.js +0 -2
- package/dist/chunk-C5YESWFT.js.map +0 -1
- package/dist/chunk-YB6YAG7E.js +0 -35
- package/dist/chunk-YB6YAG7E.js.map +0 -1
- /package/dist/{chunk-F4RGUDA3.js.map → chunk-4XRE37WT.js.map} +0 -0
- /package/dist/{chunk-R52YOKI3.js.map → chunk-NPKJ7QFK.js.map} +0 -0
- /package/dist/{chunk-NBXF7V5B.js.map → chunk-R4YEFOVE.js.map} +0 -0
- /package/dist/{chunk-4JWGUR4O.js.map → chunk-SSPABEUR.js.map} +0 -0
package/README.md
CHANGED
|
@@ -997,7 +997,172 @@ Neither of these ships as part of this repository — separate packages, install
|
|
|
997
997
|
|
|
998
998
|
## Testing
|
|
999
999
|
|
|
1000
|
-
This library is heavily tested. The `node:test` suite (`test/*.test.js`) runs
|
|
1000
|
+
This library is heavily tested. The `node:test` suite (`test/*.test.js`) runs 1300+ cases covering hand-picked scenarios, fuzzing, and adversarial input, alongside a separate `vitest/` suite unit-testing internals directly. On top of that there's a dedicated conformance suite, smoke tests that check the package actually resolves correctly under CJS/ESM/bundler/nodenext, and type tests. If it's mentioned in this README, it's backed by a test — not just a docstring.
|
|
1001
|
+
|
|
1002
|
+
# Conformance fixtures
|
|
1003
|
+
|
|
1004
|
+
`fixtures.json` is a portable, library-agnostic test-vector set for
|
|
1005
|
+
token-based Temporal formatters. It's written against a different
|
|
1006
|
+
library's token vocabulary, not temporal-fmt's — the fixtures are
|
|
1007
|
+
data, not code. Each case names an `op` (`format` / `parse` /
|
|
1008
|
+
`roundtrip`), an input, a pattern, and an expected result, so any
|
|
1009
|
+
library with a `format`/`parse` pair can be pointed at it.
|
|
1010
|
+
|
|
1011
|
+
`test/conformance.test.js` is the temporal-fmt-specific adapter. It
|
|
1012
|
+
translates fixture patterns into temporal-fmt's actual tokens, runs
|
|
1013
|
+
the cases against `format()`/`parse()`, and checks the result.
|
|
1014
|
+
|
|
1015
|
+
## Why this is separate from `test/adversarial.test.js` and `test/fuzz.test.js`
|
|
1016
|
+
|
|
1017
|
+
Those two check that temporal-fmt is internally consistent under
|
|
1018
|
+
hostile input — clean throw or correct value, never a crash or a
|
|
1019
|
+
silently wrong one. The reference point there is the library's own
|
|
1020
|
+
logic.
|
|
1021
|
+
|
|
1022
|
+
This folder is different: it checks temporal-fmt against an external,
|
|
1023
|
+
shared set of tricky-but-well-defined cases — DST transitions, leap
|
|
1024
|
+
years, offset rendering, calendar limits — where "correct" comes from
|
|
1025
|
+
the fixture, not from temporal-fmt's own code.
|
|
1026
|
+
|
|
1027
|
+
## Pattern translation
|
|
1028
|
+
|
|
1029
|
+
Two fixture tokens don't exist in temporal-fmt:
|
|
1030
|
+
|
|
1031
|
+
| Fixture token | temporal-fmt equivalent | Why |
|
|
1032
|
+
|---|---|---|
|
|
1033
|
+
| `ZZ` (always-signed offset, never `Z`) | `xxx` | Only the **uppercase** `X`/`XX`/`XXX` family collapses `+00:00` to `Z` (see `formatOffset()` in `src/tokens.ts`). Lowercase never does, which is exactly `ZZ`'s semantics. Mapping `ZZ` to `XXX` was tried first and is wrong — it fails `offset-ZZ-format-utc-not-Z` and `zone-utc-roundtrip`, both of which expect `+00:00`, not `Z`. |
|
|
1034
|
+
| `VV` (IANA zone id) | `zzz` | temporal-fmt's only zone-identity token. |
|
|
1035
|
+
|
|
1036
|
+
Translation happens in `translatePattern()` and skips anything inside
|
|
1037
|
+
a quoted literal span. Everything else in the fixture set — `yyyy`,
|
|
1038
|
+
`y`, `MM`, `dd`, `HH`, `mm`, `ss`, `S`..`SSSSSSSSS`, `h`, `a`,
|
|
1039
|
+
`X`/`XX`/`XXX` — already matches temporal-fmt's vocabulary directly.
|
|
1040
|
+
|
|
1041
|
+
## `opinionated` cases
|
|
1042
|
+
|
|
1043
|
+
Some cases are flagged `"opinionated": true` right in the fixture.
|
|
1044
|
+
These encode a design choice of the fixture's source library, not a
|
|
1045
|
+
fact about dates, and temporal-fmt is allowed to disagree with them.
|
|
1046
|
+
The adapter still runs them — if temporal-fmt's behavior differs, it
|
|
1047
|
+
logs a divergence note (printed at the end of the run) instead of
|
|
1048
|
+
failing the suite.
|
|
1049
|
+
|
|
1050
|
+
Two cases currently diverge, both `yy`-pivot ones —
|
|
1051
|
+
`extreme-year-two-digit-pivot-low` and
|
|
1052
|
+
`extreme-year-two-digit-pivot-high`. temporal-fmt refuses `yy` in any
|
|
1053
|
+
format string that isn't a complete date (`yyyy`/`yy` + month + day),
|
|
1054
|
+
so `parse("yy-MM", ...)` throws an incomplete-date error before the
|
|
1055
|
+
question of *which* century a 2-digit year should resolve to ever
|
|
1056
|
+
comes up. The fixture's position — that bare `yy-MM` should resolve
|
|
1057
|
+
via the 00-68/69-99 ECMAScript pivot — is a convention, not a fact
|
|
1058
|
+
about dates; a library is free to pick a different pivot, or, as here,
|
|
1059
|
+
decline to guess a century from `yy` alone at all. Both are documented
|
|
1060
|
+
design choices with their own passing tests
|
|
1061
|
+
(`test/parse.test.js`, `yy pivot: ...`), not something in scope to
|
|
1062
|
+
"fix" by adopting the fixture's convention.
|
|
1063
|
+
|
|
1064
|
+
One other flagged case no longer diverges:
|
|
1065
|
+
**`shape-mixing-H-and-a-rejected`**. `resolveHour()`
|
|
1066
|
+
(`src/parse.ts`) used to cross-check `H` against `a` instead of
|
|
1067
|
+
banning the combination outright, so `13:05 PM` was accepted (13:00 is
|
|
1068
|
+
consistent with PM) and only a genuine contradiction like `01:05 PM`
|
|
1069
|
+
threw. That choice has since been reverted — `H` and `a` are now
|
|
1070
|
+
refused together outright, unconditionally, matching the fixture. The
|
|
1071
|
+
fixture's own `"opinion"` text on that case still describes the old
|
|
1072
|
+
behavior; it's fixture data, not something this adapter edits, so
|
|
1073
|
+
treat the `opinionated` flag there as historical rather than current.
|
|
1074
|
+
|
|
1075
|
+
## History: divergences that have since been fixed
|
|
1076
|
+
|
|
1077
|
+
Everything below was once tracked in `KNOWN_FAILURES` at the top of
|
|
1078
|
+
`test/conformance.test.js`. That set is currently empty — every
|
|
1079
|
+
previously-found divergence has been resolved, either by fixing a
|
|
1080
|
+
real bug or by deliberately adopting the fixture's convention over a
|
|
1081
|
+
prior design choice. Kept here for context on what changed and why,
|
|
1082
|
+
in case any of it needs revisiting.
|
|
1083
|
+
|
|
1084
|
+
**Fixed — real bug: offset seconds were dropped, not rejected.**
|
|
1085
|
+
`formatOffset()` (`src/tokens.ts`) assumed every offset string was
|
|
1086
|
+
exactly 6 characters — sign, `HH`, `:`, `MM` — and never checked for a
|
|
1087
|
+
seconds component. Verified against a real `Temporal.ZonedDateTime`
|
|
1088
|
+
for a pre-1900 `America/New_York` date: the actual offset is
|
|
1089
|
+
`-04:56:02`, 9 characters, because pre-1883 New York ran on local mean
|
|
1090
|
+
time. The old code read that string's middle two digits as minutes,
|
|
1091
|
+
so `X` silently produced `-0402` (wrong) instead of refusing. Now:
|
|
1092
|
+
`X`/`XX`/`XXX`/`x`/`xx` throw when the offset has a seconds component
|
|
1093
|
+
(none of them have anywhere to put it), and `xxx` — the variant that
|
|
1094
|
+
plays `ZZ`'s "always-signed, never-Z" role — passes the full value
|
|
1095
|
+
through unchanged.
|
|
1096
|
+
- `offset-sub-minute-rejected-by-X`
|
|
1097
|
+
- `offset-sub-minute-passes-through-ZZ`
|
|
1098
|
+
|
|
1099
|
+
**Changed — offset-only `ZonedDateTime` construction, previously
|
|
1100
|
+
supported on purpose, is now refused.** `parse()` used to build a
|
|
1101
|
+
`ZonedDateTime` from an offset token alone, no `zzz` zone required.
|
|
1102
|
+
That was deliberate, not an oversight, but the fixture's position — an
|
|
1103
|
+
offset identifies a moment's distance from UTC, not a time zone, so
|
|
1104
|
+
building a `ZonedDateTime` from one alone papers over that distinction
|
|
1105
|
+
— was adopted instead. A pattern with an offset token and no `zzz` now
|
|
1106
|
+
throws; add `zzz` to the pattern (or parse into a
|
|
1107
|
+
`PlainDateTime`/`PlainDate`/`PlainTime` if a zone genuinely isn't
|
|
1108
|
+
needed).
|
|
1109
|
+
- `zone-required-for-zoneddatetime`
|
|
1110
|
+
- `zone-offset-token-rejected-on-plain-type`
|
|
1111
|
+
|
|
1112
|
+
**Added — `y` token (unpadded, variable-width year).** temporal-fmt
|
|
1113
|
+
previously had only `yyyy` (fixed 4 digits) and `yy` (2-digit,
|
|
1114
|
+
truncated). `y` formats and parses a year at any width, sign preserved
|
|
1115
|
+
for years before ISO year 0 — same semantics as `yyyy` minus the
|
|
1116
|
+
fixed width. It has no bounded fallback the way `yyyy` does when
|
|
1117
|
+
something digit-consuming follows (`yyyy` can fall back to an exact
|
|
1118
|
+
4-digit fragment in that case; `y` being unpadded is the entire point
|
|
1119
|
+
of the token, so there's no narrower shape that still means the same
|
|
1120
|
+
thing). `buildCapturingPattern()` (`src/parsePattern.ts`) refuses at
|
|
1121
|
+
build time to place `y` directly next to another digit-reading token
|
|
1122
|
+
or a digit-leading literal, rather than trying to estimate an
|
|
1123
|
+
ambiguity cost for an unbounded-width fragment — there's no finite
|
|
1124
|
+
number of "width choices" to charge for "any number of digits."
|
|
1125
|
+
- `extreme-year-max-supported`
|
|
1126
|
+
- `extreme-year-negative`
|
|
1127
|
+
- `extreme-year-past-max-rejected` — previously passed even without a
|
|
1128
|
+
real `y` token, because "no valid pattern matches" for the
|
|
1129
|
+
then-unrecognized token happened to also throw. Now genuinely tests
|
|
1130
|
+
275761 CE rejection, via the real max-year check on `y`'s parsed
|
|
1131
|
+
value.
|
|
1132
|
+
|
|
1133
|
+
**Fixed — misdiagnosed as a regex gap; the actual cause was `zzz`
|
|
1134
|
+
rejecting valid IANA zone aliases.** `offset-X-parse-accepts-four-digit`
|
|
1135
|
+
expects `X` to parse a 4-digit offset body (`+0530`) alongside a `zzz`
|
|
1136
|
+
zone. This was originally filed as "the capturing regex for `X` in
|
|
1137
|
+
`pattern.ts` doesn't offer the 4-digit shape as an alternative" — that
|
|
1138
|
+
diagnosis was wrong. The `X` regex fragment matches `+0530` correctly
|
|
1139
|
+
in isolation; the actual failure was `isValidTimeZone()`
|
|
1140
|
+
(`src/pattern.ts`) rejecting the fixture's zone name, `Asia/Kolkata`.
|
|
1141
|
+
`isValidTimeZone()` only checked `Intl.supportedValuesOf('timeZone')`,
|
|
1142
|
+
which lists canonical zone ids but not every IANA link/alias name —
|
|
1143
|
+
`Asia/Kolkata` is a legitimate, commonly-used alias for
|
|
1144
|
+
`Asia/Calcutta` that some ICU builds' `supportedValuesOf()` omits.
|
|
1145
|
+
Confirmed against `temporal-polyfill` directly:
|
|
1146
|
+
`Temporal.ZonedDateTime.from()` resolves `Asia/Kolkata` without
|
|
1147
|
+
complaint, so `parse()` was refusing input its own downstream
|
|
1148
|
+
construction step would have accepted. `isValidTimeZone()` now falls
|
|
1149
|
+
back to asking `Temporal` itself (a real `ZonedDateTime.from()` call)
|
|
1150
|
+
when the fast-path `Intl` lookup misses, rather than trusting only the
|
|
1151
|
+
`Intl` list.
|
|
1152
|
+
|
|
1153
|
+
## Adapter mapping notes
|
|
1154
|
+
|
|
1155
|
+
- `parse(formatStr, input, options)` takes `(formatStr, input)` —
|
|
1156
|
+
reversed from the fixture's `pattern`/`input` field order.
|
|
1157
|
+
- temporal-fmt does have a typed error hierarchy (`TemporalFmtError`
|
|
1158
|
+
and its subclasses in `src/errors.ts`), but the fixture's three
|
|
1159
|
+
`expect.throws` values (`"ParseError"`, `"FormatError"`,
|
|
1160
|
+
`"InvalidPatternError"`) don't map cleanly onto temporal-fmt's
|
|
1161
|
+
dozen-plus subclasses, so the adapter doesn't try — it just checks
|
|
1162
|
+
that something extending `Error` was thrown.
|
|
1163
|
+
- `target` in the fixture is informational only. temporal-fmt's
|
|
1164
|
+
`parse()` infers the result shape from which fields the pattern
|
|
1165
|
+
captures, so the adapter never passes `target` as an input.
|
|
1001
1166
|
|
|
1002
1167
|
## Contributing
|
|
1003
1168
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import{a as ue,f as J}from"./chunk-NPKJ7QFK.js";import{a as F}from"./chunk-GMGZZG6I.js";import{c as I}from"./chunk-3MZLTVP3.js";import{a as oe,b as l,c as V,d as x,e as M,g as z,h as j,j as q,m as ae,n as ie,q as B,r as me,t as A}from"./chunk-5U5WJ465.js";function ye(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function X(e,n=!1){let t=e.map(ye);return n?`(?:${t.map(be).join("|")})`:`(?:${t.join("|")})`}function be(e){return e.replace(/[a-zA-Z]/g,n=>`[${n.toLowerCase()}${n.toUpperCase()}]`)}var xe="(?:UTC|[+-]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?|[A-Za-z_]+(?:[+-]\\d{1,2})?(?:\\/[A-Za-z0-9_+-]+)*)",ke={X:"(?:Z|[+-]\\d{2}(?:\\d{2})?)",XX:"(?:Z|[+-]\\d{4})",XXX:"(?:Z|[+-]\\d{2}:\\d{2})",x:"[+-]\\d{2}(?:\\d{2})?",xx:"[+-]\\d{4}",xxx:"[+-]\\d{2}:\\d{2}"};function $e(){return xe}var Z;function Ee(){return Z||(Z=new Set(Intl.supportedValuesOf("timeZone")),Z.add("UTC")),Z}var Ne=/^[+-]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?$/;function W(e){if(Ne.test(e)||Ee().has(e))return!0;try{return I().ZonedDateTime.from({year:2026,month:1,day:1,hour:0,minute:0,second:0,timeZone:e}),!0}catch{return!1}}var Pe={yy:"\\d{2}",MM:"(?:0[1-9]|1[0-2])",M:"(?:1[0-2]|[1-9])",dd:"(?:0[1-9]|[12]\\d|3[01])",d:"(?:[12]\\d|3[01]|[1-9])",HH:"(?:[01]\\d|2[0-3])",H:"(?:1\\d|2[0-3]|[0-9])",hh:"(?:0[1-9]|1[0-2])",h:"(?:1[0-2]|[1-9])",mm:"(?:[0-5]\\d)",m:"(?:[1-5]\\d|[0-9])",ss:"(?:[0-5]\\d)",s:"(?:[1-5]\\d|[0-9])",SSSSSSSSS:"\\d{9}",SSSSSSSS:"\\d{8}",SSSSSSS:"\\d{7}",SSSSSS:"\\d{6}",SSSSS:"\\d{5}",SSSS:"\\d{4}",SSS:"\\d{3}",SS:"\\d{2}",S:"\\d",Q:"[1-4]"},Me="Q[1-4]",Te=new Set(["do","ww","RRRR","D","DD","DDD","LLLL","LLL","cccc","ccc","GGGG","G","zzzz","z"]),ve="-?\\d{4}",De="-?\\d{4,}",Oe="-?\\d+",ee=new Set(["yyyy","yy","y","MM","M","dd","d","HH","H","hh","h","mm","m","ss","s","SSSSSSSSS","SSSSSSSS","SSSSSSS","SSSSSS","SSSSS","SSSS","SSS","SS","S"]),de=new Set(["y"]);function L(e,n,t){if(e==="yyyy")return t!==void 0&&ee.has(t)?ve:De;if(e==="y")return Oe;let r=Pe[e];if(r)return r;if(e==="QQQ")return Me;if(Te.has(e))throw new V({token:e,message:`temporal-fmt: token "${e}" is format-only \u2014 it can't be parsed back into a value. Use a different token in the parse format string (e.g. "d" for "do", "MM" for "ww").`});let s=A(n);switch(e){case"MMMM":return X(s.monthLong);case"MMM":return X(s.monthShort);case"EEEE":return X(s.weekdayLong);case"EEE":return X(s.weekdayShort);case"a":return X(s.dayPeriod,!0);case"zzz":return $e();case"X":case"XX":case"XXX":case"x":case"xx":case"xxx":return ke[e];default:throw new V({token:e,message:`temporal-fmt: unknown token "${e}"`})}}var ce=new Set(["M","d","H","h","m","s"]),ze={M:[{digits:1,min:1,max:9},{digits:2,min:10,max:12}],d:[{digits:1,min:1,max:9},{digits:2,min:10,max:31}],H:[{digits:1,min:0,max:9},{digits:2,min:10,max:23}],h:[{digits:1,min:1,max:9},{digits:2,min:10,max:12}],m:[{digits:1,min:0,max:9},{digits:2,min:10,max:59}],s:[{digits:1,min:0,max:9},{digits:2,min:10,max:59}]};function ne(e,n){let t=new Map;function r(s,o){let i=`${s}:${o}`,u=t.get(i);if(u)return u;if(s===n.length){let a=o===e.length?[[]]:[];return t.set(i,a),a}let S=n[s],h=ze[S];/* c8 ignore start @preserve -- defensive guard, not reachable through
|
|
2
|
+
the public API. enumerateValidSplits's only caller (parse.ts, both
|
|
3
|
+
call sites) passes run.tokens straight from
|
|
4
|
+
pattern.ambiguousRuns, which parsePattern.ts only ever populates
|
|
5
|
+
with tokens already checked against UNPADDED_NUMERIC_TOKENS — the
|
|
6
|
+
exact same key set as UNPADDED_NUMERIC_RANGES. There's no path
|
|
7
|
+
where a token reaches here without having already passed that
|
|
8
|
+
check. */if(!h)throw new Error(`temporal-fmt: internal error \u2014 "${S}" is not an unpadded numeric token`);/* c8 ignore stop @preserve */let m=[];for(let{digits:a,min:g,max:f}of h){if(o+a>e.length)continue;let p=e.slice(o,o+a);if(a===2&&p[0]==="0")continue;let y=Number(p);if(!(y<g||y>f)){for(let D of r(s+1,o+a))if(m.push([y,...D]),m.length===2)break;if(m.length===2)break}}return t.set(i,m),m}return r(0,0)}function Ae(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var ge=12;function Xe(e){return Math.ceil(Math.log2(Math.max(e,1)))}function H(e){return e===void 0?!1:e.kind==="literal"?/^[0-9]/.test(e.value):ee.has(e.value)}function fe(e,n){let t=[],r=[],s="",o=0,i=0,u={names:[],tokens:[]},S=h=>{if(u.tokens.length===1){let m=u.names[0],a=u.tokens[0];s+=`(?<${m}>${L(a,n)})`,H(h)&&(i+=1)}else if(u.tokens.length>=2){let m=`r${o++}`,a=u.tokens.length;s+=`(?<${m}>\\d{${a},${a*2}})`,r.push({groupName:m,groupNames:u.names,tokens:u.tokens}),H(h)&&(i+=Xe(a+1))}u={names:[],tokens:[]}};for(let[h,m]of e.entries()){if(m.kind==="literal"){S(m),s+=Ae(m.value);continue}if(ce.has(m.value)){let p=`g${o++}`;t.push({name:p,token:m.value}),u.names.push(p),u.tokens.push(m.value);continue}S(m);let a=`g${o++}`;t.push({name:a,token:m.value});let g=e[h+1],f=g?.kind==="token"?g.value:void 0;if(de.has(m.value)&&H(g))throw new l({reason:`token "${m.value}" has no fixed width \u2014 it can't be placed directly next to another digit-reading token or a literal that starts with a digit, since there's no way to tell where "${m.value}" ends and the next field begins. Add a non-digit separator (e.g. "-" or " ") after it.`});m.value==="yyyy"&&f===void 0&&H(g)?s+=`(?<${a}>${L(m.value,n,"M")})`:s+=`(?<${a}>${L(m.value,n,f)})`}if(S(void 0),i>ge)throw new l({reason:`format string has too many variable-width numeric tokens glued to digit-consuming neighbors (ambiguity score ${i} > ${ge}). This shape makes the regex engine backtrack exponentially on near-miss input. Add a non-digit separator between these tokens (e.g. "-" or " ") or use their padded forms (MM/dd/HH/mm/ss).`});return{regex:new RegExp(`^(?:${s})$`,"ud"),groups:t,ambiguousRuns:r}}var v=new Map,_e=500;ie(()=>{v.clear()});function re(e,n){let t=JSON.stringify([B(n),e]),r=v.get(t);if(r)return r;if(v.size>=_e){let s=v.keys().next().value;s!==void 0&&v.delete(s)}return r=fe(ue(e),n),v.set(t,r),r}var T=new Map,Re=500;function Ce(e){me(e);let n=B(e);if(T.has(n))return T.get(n);if(T.size>=Re){let i=T.keys().next().value;i!==void 0&&T.delete(i)}let t,r=n.split("-"),s=r.indexOf("u"),o=s===-1?-1:r.indexOf("ca",s+1);if(o!==-1&&o+1<r.length){let i=new Intl.DateTimeFormat(n).resolvedOptions().calendar;t=i==="gregory"?void 0:i}return T.set(n,t),t}function Ie(e,n){if(e==="Z"){/* c8 ignore start @preserve -- unreachable: lowercase tokens' regex
|
|
9
|
+
(OFFSET_SHAPES in pattern.ts) has no "Z" alternative at all, so
|
|
10
|
+
raw === 'Z' can only ever be reached when token is one of the
|
|
11
|
+
uppercase variants (X/XX/XXX). A lowercase token can't even
|
|
12
|
+
capture "Z" as `raw` in the first place. */if(n==="x"||n==="xx"||n==="xxx")throw new Error(`temporal-fmt: offset token "${n}" doesn't accept "Z" \u2014 only the uppercase variants (X/XX/XXX) emit "Z" for UTC. Use "+00:00", "+0000", or "+00" depending on the variant's width.`);/* c8 ignore stop @preserve */return"+00:00"}let t=e[0];/* c8 ignore start @preserve -- unreachable: raw is a regex-captured
|
|
13
|
+
group from an offset token, and every OFFSET_SHAPES pattern
|
|
14
|
+
(pattern.ts) is anchored to either "Z" or a leading [+-]. raw's
|
|
15
|
+
first character can never be anything else by the time it reaches
|
|
16
|
+
this function. */if(t!=="+"&&t!=="-")throw new Error(`temporal-fmt: offset "${e}" for token "${n}" doesn't start with "+", "-", or "Z".`);/* c8 ignore stop @preserve */let r=e.slice(1),s,o;if(r.length===2){/* c8 ignore start @preserve -- unreachable: each offset token's own
|
|
17
|
+
regex shape in OFFSET_SHAPES (pattern.ts) already gates which
|
|
18
|
+
body shapes it can capture. Only X and x ever match a 2-digit
|
|
19
|
+
body — XX/xx/XXX/xxx's regexes can't produce one — so this
|
|
20
|
+
mismatch can never actually fire through parse(). */if(n!=="X"&&n!=="x")throw new Error(`temporal-fmt: offset token "${n}" can't match "${e}" \u2014 it requires minutes, but "${e}" has none.`);/* c8 ignore stop @preserve */s=r,o="00"}else if(r.length===4){/* c8 ignore start @preserve -- unreachable, same reason as the
|
|
21
|
+
2-digit case above: XXX/xxx's regex requires a colon, so it can
|
|
22
|
+
never capture a 4-digit no-colon body in the first place. */if(n==="XXX"||n==="xxx")throw new Error(`temporal-fmt: offset token "${n}" can't match "${e}" \u2014 it requires a colon between hours and minutes (e.g. "${t}${r.slice(0,2)}:${r.slice(2)}").`);/* c8 ignore stop @preserve */s=r.slice(0,2),o=r.slice(2,4)}else if(r.length===5&&r[2]===":"){/* c8 ignore start @preserve -- unreachable, same reason again: only
|
|
23
|
+
XXX/xxx's regex can produce a colon-shaped body; X/x/XX/xx never
|
|
24
|
+
capture one. */if(n!=="XXX"&&n!=="xxx")throw new Error(`temporal-fmt: offset token "${n}" can't match "${e}" \u2014 it doesn't use a colon (use "${t}${r.slice(0,2)}${r.slice(3)}" instead).`);/* c8 ignore stop @preserve */s=r.slice(0,2),o=r.slice(3,5);/* c8 ignore start @preserve -- unreachable: every offset token's
|
|
25
|
+
regex only ever produces a body of length 2, length 4, or length 5
|
|
26
|
+
with a colon at index 2 (see OFFSET_SHAPES in pattern.ts) — no
|
|
27
|
+
shape falls outside those three cases, so this else arm can't be
|
|
28
|
+
taken through parse(). Kept as an exhaustiveness fallback so
|
|
29
|
+
hoursStr/minutesStr are assigned on every path TypeScript can see. */}else throw new Error(`temporal-fmt: offset "${e}" doesn't match the shape token "${n}" accepts.`);/* c8 ignore stop @preserve */let i=Number(s),u=Number(o);if(i>14)throw new z({actual:e,message:`temporal-fmt: offset hours ${i} in "${e}" out of range (max 14 \u2014 Kiritimati, Line Islands is +14:00).`});if(u>59)throw new z({actual:e,message:`temporal-fmt: offset minutes ${u} in "${e}" out of range (max 59).`});if(t==="+"&&i===14&&u!==0)throw new z({actual:e,message:`temporal-fmt: offset "${e}" exceeds the maximum supported UTC offset of +14:00.`});if(t==="-"&&i===12&&u!==0)throw new z({actual:e,message:`temporal-fmt: offset "${e}" exceeds the maximum supported negative UTC offset of -12:00.`});return`${t}${s}:${o}`}function d(e,n,t){e[n]=t}function Fe(e,n,t,r,s){let o=A(r);switch(n){case"yyyy":case"y":d(e,"year",Number(t));break;case"yy":d(e,"twoDigitYear",Number(t));break;case"MM":case"M":d(e,"month",Number(t));break;case"MMMM":d(e,"month",o.monthLong.indexOf(t)+1);break;case"MMM":d(e,"month",o.monthShort.indexOf(t)+1);break;case"dd":case"d":d(e,"day",Number(t));break;case"EEEE":d(e,"weekdayRaw",t),d(e,"weekdayExpected",o.weekdayLong.indexOf(t)+1);break;case"EEE":d(e,"weekdayRaw",t),d(e,"weekdayExpected",o.weekdayShort.indexOf(t)+1);break;case"HH":case"H":d(e,"hour",Number(t));break;case"hh":case"h":d(e,"hour12",Number(t));break;case"mm":case"m":d(e,"minute",Number(t));break;case"ss":case"s":d(e,"second",Number(t));break;case"S":case"SS":case"SSS":case"SSSS":case"SSSSS":case"SSSSSS":case"SSSSSSS":case"SSSSSSSS":case"SSSSSSSSS":{let i=Number(t.padEnd(9,"0"));d(e,"millisecond",Math.floor(i/1e6)),d(e,"microsecond",Math.floor(i/1e3)%1e3),d(e,"nanosecond",i%1e3);break}case"a":{let i=o.dayPeriod.findIndex(u=>u.toLowerCase()===t.toLowerCase());/* c8 ignore start @preserve -- unreachable: the 'a' token's regex
|
|
30
|
+
fragment (pattern.ts's alternation() over vocab.dayPeriod) can
|
|
31
|
+
only ever capture a case-insensitive match of one of
|
|
32
|
+
vocab.dayPeriod's own entries. Both the regex and this lookup
|
|
33
|
+
derive their vocab from the same `locale` via getLocaleVocab(),
|
|
34
|
+
so periodIndex can't come back negative through parse(). */if(i<0)throw new Error(`temporal-fmt: unknown day period "${t}" for locale "${r}".`);/* c8 ignore stop @preserve */d(e,"dayPeriodRaw",t),d(e,"isPM",i===1);break}case"zzz":d(e,"timeZoneId",t);break;case"X":case"XX":case"XXX":case"x":case"xx":case"xxx":d(e,"offsetString",Ie(t,n));break;case"Q":d(e,"quarter",Number(t));break;case"QQQ":d(e,"quarter",Number(t.slice(1)));break}}function le(e,n){let t=n.indexOf("d");if(t!==-1){let r=e.filter(s=>s[t]<=12);if(r.length>0)return r[0]}return e[0]}function Ze(e){if(e.year!==void 0&&e.twoDigitYear!==void 0)throw new l({message:'temporal-fmt: format string mixes a full-year token ("yyyy"/"y") with the two-digit "yy" token.'});if(e.year!==void 0)return e.year;if(e.twoDigitYear!==void 0)return e.twoDigitYear<=68?2e3+e.twoDigitYear:1900+e.twoDigitYear}function Le(e,n,t){if(e.hour!==void 0&&e.hour12!==void 0)throw new l({format:n,message:`temporal-fmt: format string "${n}" mixes a 24-hour token ("HH"/"H") with a 12-hour token ("hh"/"h").`});if(e.hour!==void 0){if(e.dayPeriodRaw!==void 0)throw new l({format:n,message:`temporal-fmt: format string "${n}" mixes a 24-hour token ("HH"/"H") with a day-period token ("a"). Both describe the same field; use one or the other, not both.`});return e.hour}if(e.hour12!==void 0){if(e.isPM===void 0)throw new l({format:n,message:`temporal-fmt: format string "${n}" uses a 12-hour token ("hh"/"h") without an "a" token, so parse() can't tell AM from PM.`});return e.hour12%12+(e.isPM?12:0)}}function se(e,n,t={}){if(e.length>1e3)throw new l({format:e,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`});if(n.length>1e5)throw new l({input:n,message:`temporal-fmt: input exceeds maximum length of ${1e5} characters (got ${n.length}).`});t.parseNumberingSystem&&(n=J(n,t));let r=t.locale??F,s=Ce(r),o=re(e,r),i=o.regex.exec(n);if(!i)throw new x({input:n,format:e,reason:"no valid pattern matches the format string and input shape",message:"temporal-fmt: no valid pattern matches the format string and input shape"});if(o.groups.length===0)throw new x({format:e,reason:`format string "${e}" has no tokens \u2014 nothing to parse into a value.`,message:`temporal-fmt: format string "${e}" has no tokens \u2014 nothing to parse into a value.`});for(let{name:c,token:w}of o.groups)if(w==="zzz"&&!W(i.groups[c]))throw new j({input:n,format:e,actual:i.groups[c],reason:"not a recognized IANA time zone identifier"});let u=new Map;for(let c of o.ambiguousRuns){let w=i.groups[c.groupName],b=ne(w,c.tokens);if(b.length===0)throw new x({input:n,format:e,reason:"no valid pattern matches the format string and input shape",message:"temporal-fmt: no valid pattern matches the format string and input shape"});if(b.length>1){if(!t.lenient)throw new q({input:n,format:e,message:`temporal-fmt: "${w}" in format string "${e}" is ambiguous \u2014 ${b.length} different ways to read tokens "${c.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(b[0])} vs ${JSON.stringify(b[1])}). parse() won't guess; add a separator between these tokens, or use their padded form (e.g. "MM" instead of "M") so each one has a fixed width. Pass { lenient: true } to opt into a documented heuristic that picks one.`});let P=le(b,c.tokens);c.groupNames.forEach((K,Se)=>u.set(K,String(P[Se])))}else c.groupNames.forEach((P,K)=>u.set(P,String(b[0][K])))}let S={};for(let{name:c,token:w}of o.groups){let b=u.get(c)??i.groups[c];Fe(S,w,b,r,e)}let h=Ze(S),m=Le(S,e,r),{month:a,day:g,minute:f,second:p,millisecond:y,microsecond:D,nanosecond:he,timeZoneId:O,offsetString:k,weekdayExpected:U,weekdayRaw:pe,quarter:Q}=S,we=h!==void 0||a!==void 0||g!==void 0,$=h!==void 0&&a!==void 0&&g!==void 0;if(we&&!$)throw new M({format:e,message:`temporal-fmt: format string "${e}" has an incomplete date \u2014 year, month, and day tokens must all be present together.`});let R=m!==void 0||f!==void 0||p!==void 0||y!==void 0;if(O!==void 0&&!($&&R))throw new l({format:e,message:`temporal-fmt: format string "${e}" has a "zzz" token but needs a full date and time to build a ZonedDateTime.`});if(k!==void 0&&!($&&R))throw new l({format:e,message:`temporal-fmt: format string "${e}" has an offset token (X/XX/XXX/x/xx/xxx) but needs a full date and time to build a ZonedDateTime.`});if(U!==void 0&&!$)throw new l({format:e,message:`temporal-fmt: format string "${e}" has a weekday token ("EEEE"/"EEE") but needs a full date to validate it against.`});if(!$&&!R)throw new l({format:e,message:`temporal-fmt: format string "${e}" has no date or time tokens to parse.`});let C=I(),N={hour:m??0,minute:f??0,second:p??0,millisecond:y??0,microsecond:D??0,nanosecond:he??0},Y=s?{calendar:s}:{},G={overflow:"reject"},E;try{if(O!==void 0){let c={overflow:"reject",offset:"prefer"};if(E=C.ZonedDateTime.from({year:h,month:a,day:g,...N,...Y,timeZone:O,...k!==void 0?{offset:k}:{}},c),k!==void 0){let w=E;if(w.hour!==N.hour||w.minute!==N.minute||w.second!==N.second)throw new M({input:n,format:e,message:`"${O}" has no such wall-clock time on this date \u2014 it falls in a DST gap, not an ambiguous or valid instant.`});let P=w.offset;if(P!==k)throw new x({input:n,format:e,message:`has both a "zzz" zone (${O}) and an offset token (${k}), but the zone's actual offset at this date/time is ${P}, not ${k}.`})}}else{if(k!==void 0)throw new x({input:n,format:e,message:`format string "${e}" has an offset token but no "zzz" zone token. An offset does not identify a time zone by itself \u2014 add "zzz" to the pattern, or parse into a PlainDateTime/PlainDate/PlainTime if a zone isn't needed.`});$&&R?E=C.PlainDateTime.from({year:h,month:a,day:g,...N,...Y},G):$?E=C.PlainDate.from({year:h,month:a,day:g,...Y},G):E=C.PlainTime.from(N,G)}}catch(c){throw new M({input:n,format:e,message:`temporal-fmt: "${n}" doesn't describe a valid date/time for format "${e}": ${c.message}`})}if(U!==void 0){let c=E.dayOfWeek;if(c!==U){let w=A(r);throw new M({input:n,format:e,message:`temporal-fmt: "${pe}" doesn't match the actual weekday (${w.weekdayLong[c-1]}) for the parsed date.`})}}if(Q!==void 0&&a!==void 0){let c=Math.ceil(a/3);if(Q!==c)throw new M({format:e,message:`temporal-fmt: format string "${e}" contains a quarter token (Q/QQQ) whose value (Q${Q}) disagrees with the parsed month's actual quarter \u2014 month ${a} is in Q${c}.`})}return E}function He(e,n,t={}){try{return{ok:!0,value:se(e,n,t)}}catch(r){/* c8 ignore start @preserve */return r instanceof oe?{ok:!1,error:r}:{ok:!1,error:ae(r,{input:n,format:e})}}/* c8 ignore stop @preserve */}function Ue(e,n,t={}){try{return se(e,n,t)}catch{return}}function Qe(e,n,t={}){if(e.length>1e3)throw new l({format:e,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`});if(n.length>1e5)throw new l({input:n,message:`temporal-fmt: input exceeds maximum length of ${1e5} characters (got ${n.length}).`});t.parseNumberingSystem&&(n=J(n,t));let r=t.locale??F,s=re(e,r),o=s.regex.exec(n);if(!o)throw new x({input:n,format:e,reason:"no valid pattern matches the format string and input shape",message:"temporal-fmt: no valid pattern matches the format string and input shape"});if(s.groups.length===0)throw new x({format:e,reason:`format string "${e}" has no tokens \u2014 nothing to parse into a value.`,message:`temporal-fmt: format string "${e}" has no tokens \u2014 nothing to parse into a value.`});for(let{name:a,token:g}of s.groups)if(g==="zzz"&&!W(o.groups[a]))throw new j({input:n,format:e,actual:o.groups[a],reason:"not a recognized IANA time zone identifier"});let i=new Map;for(let a of s.ambiguousRuns){let g=o.groups[a.groupName],f=ne(g,a.tokens);if(f.length===0)throw new x({input:n,format:e,reason:"no valid pattern matches the format string and input shape",message:"temporal-fmt: no valid pattern matches the format string and input shape"});if(f.length>1){if(!t.lenient)throw new q({input:n,format:e,message:`temporal-fmt: "${g}" in format string "${e}" is ambiguous \u2014 ${f.length} different ways to read tokens "${a.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(f[0])} vs ${JSON.stringify(f[1])}). parse() won't guess; add a separator between these tokens, or use their padded form (e.g. "MM" instead of "M") so each one has a fixed width. Pass { lenient: true } to opt into a documented heuristic that picks one.`});let p=le(f,a.tokens);a.groupNames.forEach((y,D)=>i.set(y,String(p[D])))}else a.groupNames.forEach((p,y)=>i.set(p,String(f[0][y])))}let u=[],h=o.indices?.groups,m=0;for(let{name:a,token:g}of s.groups){let f=i.get(a)??o.groups[a],p=!i.has(a)&&h?.[a],y=p?p[0]:(o.index??0)+m;u.push({token:g,raw:f,position:y}),m+=f.length}return u}function an(e,n={}){if(e.length>1e3)throw new l({format:e,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`});let t=n.locale??F,r=re(e,t);return{formatStr:e,pattern:r,parse(s,o={}){return se(e,s,{locale:t,...o})},safeParse(s,o={}){return He(e,s,{locale:t,...o})},tryParse(s,o={}){return Ue(e,s,{locale:t,...o})},parseToParts(s,o={}){return Qe(e,s,{locale:t,...o})}}}export{Te as a,ce as b,se as c,He as d,Ue as e,Qe as f,an as g};
|
|
35
|
+
//# sourceMappingURL=chunk-4JDAJSEM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pattern.ts","../src/parsePattern.ts","../src/parse.ts"],"sourcesContent":["import { getLocaleVocab } from './localeVocab.js';\nimport { UnknownTokenError } from './errors.js';\nimport { getTemporal } from './temporalProvider.js';\n\nfunction escapeRegExp(literal: string): string {\n return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction alternation(values: string[], caseInsensitive = false): string {\n const escaped = values.map(escapeRegExp);\n if (!caseInsensitive) return `(?:${escaped.join('|')})`;\n // JS regex has no per-group inline case-insensitive flag, and this\n // fragment gets embedded in one larger pattern built with a single flag\n // set — so case-folding here means listing both cases explicitly rather\n // than relying on a flag.\n return `(?:${escaped.map(foldCase).join('|')})`;\n}\n\n// Expands \"PM\" into a character-class-per-letter pattern matching any\n// casing of it (\"[Pp][Mm]\"), so \"pm\", \"Pm\", \"PM\" all match the same\n// alternative. Only used for the day-period token (see the 'a' case\n// below) — not applied to month/weekday names, where case-folding across\n// scripts is a different and riskier problem this doesn't need to solve.\nfunction foldCase(value: string): string {\n return value.replace(/[a-zA-Z]/g, (ch) => `[${ch.toLowerCase()}${ch.toUpperCase()}]`);\n}\n\n// Every real IANA zone id is letters/digits/'_'/'+'/'-' segments joined by\n// '/' (e.g. \"America/Argentina/Buenos_Aires\", \"Etc/GMT+12\"); UTC and\n// fixed-offset strings are the only other shapes zzz accepts. Matching that\n// *shape* here — instead of alternating all ~400 zone names inline — keeps\n// the compiled regex small regardless of how many zzz tokens appear in a\n// format string. The captured text still gets checked against the real\n// zone set in isValidTimeZone() after the overall pattern matches, so this\n// is strictly a matching-cost change, not a validation-strictness change:\n// a bogus zone id fails \"no valid pattern matches\" exactly like it did when\n// the zone list was inlined (see isValidTimeZone's caller in parse.ts).\nconst TIME_ZONE_SHAPE = '(?:UTC|[+-]\\\\d{2}:\\\\d{2}(?::\\\\d{2}(?:\\\\.\\\\d{1,9})?)?|[A-Za-z_]+(?:[+-]\\\\d{1,2})?(?:\\\\/[A-Za-z0-9_+-]+)*)';\n\n// Per-variant regex shapes for the six offset tokens. Each matches the\n// shape its own format counterpart produces, so a round-trip\n// format->parse succeeds for any input the library itself emitted.\n//\n// Kept loose (no hour/minute range bounds inline) on purpose, mirroring\n// how TIME_ZONE_SHAPE is loose: a permissive shape here lets parse()\n// surface a descriptive out-of-range error post-match (see\n// parseOffsetString in parse.ts) instead of the generic \"no valid pattern\n// matches\" the regex throws when the shape itself fails. \"+99:99\" should\n// tell the user it's out of range, not look like the input never matched\n// the format at all.\n//\n// X / x accept an optional minutes group so whole-hour offsets can be\n// written short (\"+05\") while non-whole-hour offsets still parse\n// (\"+0530\"). The optional group is greedy, so for input \"+0530\" the\n// engine prefers the 4-digit match; only falls back to 2-digit when\n// there's nothing else to consume — same longer-first preference the\n// unpadded numeric tokens use elsewhere in this file (see the comment on\n// NUMERIC_FRAGMENTS).\nconst OFFSET_SHAPES: Record<string, string> = {\n X: '(?:Z|[+-]\\\\d{2}(?:\\\\d{2})?)',\n XX: '(?:Z|[+-]\\\\d{4})',\n XXX: '(?:Z|[+-]\\\\d{2}:\\\\d{2})',\n x: '[+-]\\\\d{2}(?:\\\\d{2})?',\n xx: '[+-]\\\\d{4}',\n xxx: '[+-]\\\\d{2}:\\\\d{2}',\n};\n\nfunction getTimeZoneFragment(): string {\n return TIME_ZONE_SHAPE;\n}\n\nlet validZoneSet: Set<string> | undefined;\n\n// Real zone ids plus the couple of aliases zzz has always accepted even\n// though Intl.supportedValuesOf('timeZone') doesn't list them (UTC isn't\n// itself an IANA zone name, it's the identity offset).\nfunction getValidZoneSet(): Set<string> {\n if (!validZoneSet) {\n validZoneSet = new Set(Intl.supportedValuesOf('timeZone'));\n validZoneSet.add('UTC');\n }\n return validZoneSet;\n}\n\nconst FIXED_OFFSET_RE = /^[+-]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?$/;\n\n// Called post-match on whatever the bounded TIME_ZONE_SHAPE captured, since\n// that shape is deliberately looser than \"a real zone id\" (it has to be, to\n// stay a fixed-size regex fragment — see the comment above). A fixed offset\n// is valid by construction; anything else has to be a real IANA name.\n//\n// Intl.supportedValuesOf('timeZone') is checked first as a fast path — it's\n// a plain Set lookup and covers the overwhelming majority of real input.\n// But it lists only canonical zone ids, not every IANA link/alias name:\n// \"Asia/Kolkata\" is a legitimate, commonly-used alias for \"Asia/Calcutta\"\n// that Temporal.ZonedDateTime.from() itself resolves correctly, yet some\n// ICU builds' supportedValuesOf() omits it. Rejecting it here — even\n// though the exact same string would construct a real ZonedDateTime one\n// call later — was a real bug: parse() refused input its own downstream\n// construction step would have accepted. Anything Intl doesn't recognize\n// gets a second check against Temporal itself before being refused.\nexport function isValidTimeZone(raw: string): boolean {\n if (FIXED_OFFSET_RE.test(raw) || getValidZoneSet().has(raw)) return true;\n try {\n getTemporal().ZonedDateTime.from({\n year: 2026, month: 1, day: 1, hour: 0, minute: 0, second: 0,\n timeZone: raw,\n });\n return true;\n } catch {\n return false;\n }\n}\n\n// mirrors the ranges pad() in tokens.ts actually produces — keep in sync\n// if those ever change\n//\n// Unpadded alternatives (M, H, h, m, s) list the longer branch first\n// (e.g. '1[0-2]|[1-9]', not '[1-9]|1[0-2]'). This matters only when two\n// unpadded tokens are glued with no separator: with short-first ordering,\n// a regex engine takes the first successful overall match, and won't\n// backtrack into a token's second alternative unless its first choice\n// makes the *rest* of the pattern fail outright. If the short reading also\n// happens to leave a valid match for the next token, the engine stops\n// there — silently, deterministically, and with no relation to which\n// reading a human intended. E.g. \"Md\" against \"121\": short-first order\n// resolves it as month=1/day=21 (M grabs '1', d gets '21', which is a\n// valid day) instead of month=12/day=1. Longer-first ordering fixes this\n// by making the greedy match try to consume as many digits as possible\n// before ever handing digits to the next token, which is the reading\n// that matches how format() itself produces glued output in the first\n// place (format() always emits the token's natural width, so decoding\n// should prefer the same). Found via the token×token combinatorial glue\n// matrix in combinatorial.test.js — see that file for the full case list.\nconst NUMERIC_FRAGMENTS: Record<string, string> = {\n yy: '\\\\d{2}',\n MM: '(?:0[1-9]|1[0-2])',\n M: '(?:1[0-2]|[1-9])',\n dd: '(?:0[1-9]|[12]\\\\d|3[01])',\n d: '(?:[12]\\\\d|3[01]|[1-9])',\n HH: '(?:[01]\\\\d|2[0-3])',\n H: '(?:1\\\\d|2[0-3]|[0-9])',\n hh: '(?:0[1-9]|1[0-2])',\n h: '(?:1[0-2]|[1-9])',\n mm: '(?:[0-5]\\\\d)',\n m: '(?:[1-5]\\\\d|[0-9])',\n ss: '(?:[0-5]\\\\d)',\n s: '(?:[1-5]\\\\d|[0-9])',\n SSSSSSSSS: '\\\\d{9}',\n SSSSSSSS: '\\\\d{8}',\n SSSSSSS: '\\\\d{7}',\n SSSSSS: '\\\\d{6}',\n SSSSS: '\\\\d{5}',\n SSSS: '\\\\d{4}',\n SSS: '\\\\d{3}',\n SS: '\\\\d{2}',\n S: '\\\\d',\n // Q is always a single digit 1-4 (no padding variant, so no width ambiguity\n // with adjacent digit tokens the way M/d/H/m/s have).\n Q: '[1-4]',\n};\n\n// QQQ matches its own formatted output: the literal \"Q\" prefix plus a single\n// digit 1-4. Kept as a static fragment rather than routed through vocab,\n// since the \"Q\" prefix is part of the token's own contract, not locale-vocab\n// data that could ever differ.\nconst QQQ_FRAGMENT = 'Q[1-4]';\n\n// Format-only tokens — they have no parse counterpart. The tokenizer still\n// recognizes them (so format() can use them), but parse()'s regex builder\n// rejects them with a clear error rather than silently dropping the token\n// or falling through to the generic \"unknown token\" message.\n// Exported so analyze.ts can include them in the public analyzer surface.\nexport const FORMAT_ONLY_TOKENS = new Set(['do', 'ww', 'RRRR', 'D', 'DD', 'DDD', 'LLLL', 'LLL', 'cccc', 'ccc', 'GGGG', 'G', 'zzzz', 'z']);\n\n// pad()'s year formatter (tokens.ts) never truncates: it preserves the sign\n// for BCE years and doesn't cap width past 9999, so a formatted \"yyyy\" can\n// be longer than 4 digits or start with '-'. YYYY_EXTENDED accepts that;\n// YYYY_EXACT is the plain 4-unsigned-digit case. Which one a given \"yyyy\"\n// occurrence gets depends on what follows it — see buildCapturingPattern in\n// parsePattern.ts. Two separate fragments instead of one `-?\\d{4,}` because\n// an open-ended-width year directly followed by another digit token (e.g.\n// \"yyyyMM\") lets the year's own greediness silently eat digits meant for\n// the next token — same class of bug as UNPADDED_NUMERIC_TOKENS below, but\n// unbounded-width, so it can't reuse enumerateValidSplits' fixed-range\n// splitting. Restricting to exactly 4 digits whenever something could\n// follow closes that off entirely, at the cost of \"yyyyMM\" not being able\n// to represent a 5-digit year — an already-rare case doubly rare in\n// combination with a glued adjacent token.\nconst YYYY_EXACT = '-?\\\\d{4}';\nconst YYYY_EXTENDED = '-?\\\\d{4,}';\n\n// \"y\" is yyyy's unpadded sibling: any width, 1 digit up through Temporal's\n// max supported year (275760), with the same optional leading \"-\" for\n// years before ISO year 0. Unlike yyyy, there's no bounded fallback to\n// reach for when something digit-consuming follows — yyyy can fall back\n// to an exact 4 digits because it's always exactly 4 digits in that case,\n// but \"y\" being unpadded is the entire point of the token, so there's no\n// narrower shape that still means the same thing. buildCapturingPattern\n// (parsePattern.ts) refuses to build a pattern where \"y\" is immediately\n// followed by another digit-consuming element, rather than trying to\n// bound this fragment the way YYYY_EXACT does.\nconst Y_FRAGMENT = '-?\\\\d+';\n\n// True for any token whose matched text can start with a digit — i.e.\n// every token here except the locale-named ones (MMMM/MMM/EEEE/EEE/a) and\n// zzz (which can start with a digit only via a fixed offset like \"+09:00\",\n// already handled by requiring a leading sign there). Used to decide\n// whether a \"yyyy\" immediately before this token needs the exact-4-digit\n// fragment instead of the open-ended one.\n// Exported for parsePattern.ts's ReDoS guard: a token whose regex\n// fragment can begin with a bare digit (see the guard comments there).\nexport const DIGIT_LEADING_TOKENS = new Set([\n 'yyyy', 'yy', 'y', 'MM', 'M', 'dd', 'd', 'HH', 'H', 'hh', 'h', 'mm', 'm', 'ss', 's',\n 'SSSSSSSSS', 'SSSSSSSS', 'SSSSSSS', 'SSSSSS', 'SSSSS', 'SSSS', 'SSS', 'SS', 'S',\n]);\n\n// Tokens whose regex fragment has no upper bound on width — nothing\n// caps how many digits they might consume. yyyy's YYYY_EXTENDED form is\n// also unbounded, but it's only ever selected when the *next* piece\n// already isn't digit-consuming (see tokenFragment below), so by\n// construction it never reaches a position where the ambiguity guard in\n// parsePattern.ts would need to weigh in. \"y\" has no such self-limiting\n// selection rule — it's unbounded unconditionally — so it needs an\n// explicit guard there instead of a bit-cost estimate: there's no\n// meaningful number of \"width choices\" to assign to \"any number of\n// digits,\" so parsePattern.ts refuses outright rather than pretending a\n// finite ambiguity score covers it.\nexport const UNBOUNDED_WIDTH_TOKENS = new Set(['y']);\n\nexport function tokenFragment(token: string, locale: string, nextToken?: string): string {\n if (token === 'yyyy') {\n return nextToken !== undefined && DIGIT_LEADING_TOKENS.has(nextToken) ? YYYY_EXACT : YYYY_EXTENDED;\n }\n if (token === 'y') {\n return Y_FRAGMENT;\n }\n\n const numeric = NUMERIC_FRAGMENTS[token];\n if (numeric) {\n return numeric;\n }\n\n if (token === 'QQQ') {\n return QQQ_FRAGMENT;\n }\n\n if (FORMAT_ONLY_TOKENS.has(token)) {\n throw new UnknownTokenError({\n token,\n message:\n `temporal-fmt: token \"${token}\" is format-only — it can't be parsed back into a value. ` +\n `Use a different token in the parse format string (e.g. \"d\" for \"do\", \"MM\" for \"ww\").`,\n });\n }\n\n const vocab = getLocaleVocab(locale);\n switch (token) {\n case 'MMMM': return alternation(vocab.monthLong);\n case 'MMM': return alternation(vocab.monthShort);\n case 'EEEE': return alternation(vocab.weekdayLong);\n case 'EEE': return alternation(vocab.weekdayShort);\n // Case-insensitive on purpose: \"pm\"/\"Pm\"/\"PM\" all mean the same thing,\n // and unlike the Md-glue ambiguity elsewhere in this file, there's no\n // second valid reading to guess wrong — so rejecting on case buys no\n // correctness, only friction against real-world data (mixed-case CSV\n // exports, lowercase log timestamps).\n case 'a': return alternation(vocab.dayPeriod, true);\n case 'zzz': return getTimeZoneFragment();\n case 'X': case 'XX': case 'XXX':\n case 'x': case 'xx': case 'xxx':\n return OFFSET_SHAPES[token]!;\n /* c8 ignore start @preserve -- defensive guard, not reachable through\n the public API. tokenFragment's only caller (buildCapturingPattern\n in parsePattern.ts) always passes piece.value straight from\n tokenize.ts, which only ever emits strings from tokens.ts's TOKENS\n table. Every token in that table is handled above: the numeric\n ones via NUMERIC_FRAGMENTS, QQQ via QQQ_FRAGMENT, the format-only\n ones via the FORMAT_ONLY_TOKENS check earlier in this function, and\n everything else via one of the switch cases. There's no token\n string that can reach this default without either tokens.ts\n registering something new or tokenize.ts being bypassed, neither of\n which happens on the parse() path. */\n default:\n throw new UnknownTokenError({ token, message: `temporal-fmt: unknown token \"${token}\"` });\n /* c8 ignore stop @preserve */\n }\n}\n\n// Tokens whose fragment is variable-width (1-2 digits, no leading zero).\n// Two or more of these glued with no literal separator between them can\n// have more than one digit-split that's independently valid against every\n// fragment in the run — see the big comment on NUMERIC_FRAGMENTS above.\n// Reordering alternation branches picks a winner for *some* of these\n// cases, but can't make both directions of a pair (e.g. \"Md\" and \"dM\")\n// agree, because the ambiguity is in the input string itself, not in how\n// any one fragment is written. exported so parsePattern.ts can find runs\n// of these that need split-counting at match time instead of a single\n// fixed regex.\nexport const UNPADDED_NUMERIC_TOKENS = new Set(['M', 'd', 'H', 'h', 'm', 's']);\n\n// Every accept width for a given unpadded numeric token, as plain min/max\n// value + digit-length pairs — used to enumerate candidate splits of a\n// digit run at match time. Mirrors NUMERIC_FRAGMENTS's semantics exactly\n// (same accepted values), just as data instead of regex source, since\n// enumerating splits against a compiled regex per-candidate would be\n// slower and harder to reason about than checking numeric ranges directly.\nexport const UNPADDED_NUMERIC_RANGES: Record<string, Array<{ digits: 1 | 2; min: number; max: number }>> = {\n M: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 12 }],\n d: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 31 }],\n H: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 23 }],\n h: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 12 }],\n m: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 59 }],\n s: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 59 }],\n};\n\n/**\n * Given the literal digit string a run of N adjacent unpadded-numeric\n * tokens matched as a whole (e.g. \"112\" for a 2-token run), enumerates\n * every way to split it into N pieces (one per token, each piece 1-2\n * digits per that token's own width rule) and returns every split where\n * every piece is independently valid for its token. Length 0 means the\n * run's regex match shouldn't have been possible in the first place\n * (shouldn't happen — the caller only invokes this after the whole\n * pattern already matched, meaning at least one split exists: the one the\n * regex actually took). Length 1 means the reading is unambiguous.\n * Length 2+ means true ambiguity — the caller should throw rather than\n * pick one.\n *\n * Recursive over token count rather than hardcoded to 2, so a 3+ token\n * unseparated run (e.g. \"Hms\") is covered by the same logic without a\n * special case — those are rarer in practice but not impossible, and a\n * partial fix that only covered pairs would leave the identical bug for\n * anyone writing a 3-token glued run.\n */\nexport function enumerateValidSplits(digits: string, tokens: string[]): number[][] {\n const memo = new Map<string, number[][]>();\n\n function solve(tokenIndex: number, offset: number): number[][] {\n const key = `${tokenIndex}:${offset}`;\n const cached = memo.get(key);\n if (cached) {\n return cached;\n }\n\n if (tokenIndex === tokens.length) {\n const result = offset === digits.length ? [[]] : [];\n memo.set(key, result);\n return result;\n }\n\n const token = tokens[tokenIndex];\n const ranges = UNPADDED_NUMERIC_RANGES[token!];\n /* c8 ignore start @preserve -- defensive guard, not reachable through\n the public API. enumerateValidSplits's only caller (parse.ts, both\n call sites) passes run.tokens straight from\n pattern.ambiguousRuns, which parsePattern.ts only ever populates\n with tokens already checked against UNPADDED_NUMERIC_TOKENS — the\n exact same key set as UNPADDED_NUMERIC_RANGES. There's no path\n where a token reaches here without having already passed that\n check. */\n if (!ranges) {\n throw new Error(`temporal-fmt: internal error — \"${token}\" is not an unpadded numeric token`);\n }\n /* c8 ignore stop @preserve */\n\n const results: number[][] = [];\n for (const { digits: width, min, max } of ranges) {\n if (offset + width > digits.length) continue;\n const piece = digits.slice(offset, offset + width);\n if (width === 2 && piece[0] === '0') continue;\n const value = Number(piece);\n if (value < min || value > max) continue;\n\n for (const restSplit of solve(tokenIndex + 1, offset + width)) {\n results.push([value, ...restSplit]);\n if (results.length === 2) break;\n }\n if (results.length === 2) break;\n }\n\n memo.set(key, results);\n return results;\n }\n\n return solve(0, 0);\n}","import type { Piece } from './tokenize.js';\nimport { tokenFragment, UNPADDED_NUMERIC_TOKENS, DIGIT_LEADING_TOKENS, UNBOUNDED_WIDTH_TOKENS } from './pattern.js';\nimport { FormatSyntaxError } from './errors.js';\n\nfunction escapeRegExp(literal: string): string {\n return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n// Guards against catastrophic-backtracking (ReDoS) patterns.\n//\n// Every confirmed ReDoS in this library shares one shape: two or more\n// variable-width digit-consuming regex fragments placed so the engine\n// can't tell where one ends and the next begins — either glued with no\n// separator (\"MdMdMd…\", \"HmsHms…\") or separated only by a literal that\n// itself starts with a digit (\"M1M1M1…\", \"yyyy1yyyy1…\"). Each fragment\n// then has multiple ways to divide the digit run, and a failing match\n// makes the engine explore every combination — exponential in the number\n// of fragments. Measured against the pre-fix code: \"Md\"×13 (26-char\n// format, 40-char input) ≈ 2.7 s; \"yyyy1\"×8 (48-char format) ≈ 26 s;\n// both grow roughly ×3–14 per additional fragment.\n//\n// Three structural defenses, applied at pattern-build time:\n//\n// 1. A run of 2+ glued unpadded-numeric tokens is emitted as ONE\n// bounded digit group `(?<rN>\\d{R,2R})` instead of R separate\n// variable-width fragments. The per-token split is resolved after\n// the match by enumerateValidSplits() (pattern.ts) — the exact\n// machinery parse() already used to detect ambiguous glued runs —\n// so the documented behavior (unique split resolves; 2+ valid\n// splits throws in strict mode / heuristic-picks in lenient; 0\n// splits is a mismatch) is preserved. A lone `\\d{R,2R}` group\n// backtracks at most R+1 times, which is linear.\n//\n// 2. yyyy uses the exact `-?\\d{4}` fragment not only when the next\n// *token* is digit-leading (existing rule) but also when the next\n// *literal* starts with a digit. An open-ended `-?\\d{4,}` year\n// glued to a digit literal is the cheapest exponential engine\n// there is (unbounded width choices per year), and the exact form\n// matches everything the open-ended form matched in that position\n// except years with 5+ digits glued directly to an unquoted digit\n// literal — a pathological corner deliberately traded away.\n//\n// 3. An ambiguity budget for what's left: every adjacency between a\n// variable-width digit consumer (a lone unpadded token, or a glued\n// run group) and a digit-consuming successor (a digit-leading token\n// or a literal starting with a digit) costs log2 of the consumer's\n// width choices. A pattern whose total exceeds MAX_AMBIGUITY_BITS\n// (12 — a hard ceiling of 4096 backtrack paths) is rejected at\n// build time with a FormatSyntaxError. Realistic format strings\n// score 0–3; the \"M1M1M1…\" attack scores one bit per glued pair.\nconst MAX_AMBIGUITY_BITS = 12;\n\nfunction widthChoicesBits(choices: number): number {\n return Math.ceil(Math.log2(Math.max(choices, 1)));\n}\n\n// Does this piece's regex fragment START by consuming a bare digit?\n// (Tokens whose match can begin with 0-9, and literals whose first\n// character is a digit.) Used to find the boundaries where a preceding\n// variable-width digit consumer could trade digits with a successor.\nfunction isDigitConsumingStart(piece: Piece | undefined): boolean {\n if (piece === undefined) return false;\n if (piece.kind === 'literal') return /^[0-9]/.test(piece.value);\n return DIGIT_LEADING_TOKENS.has(piece.value);\n}\n\nexport interface CapturingPattern {\n regex: RegExp;\n groups: Array<{ name: string; token: string }>; // token pieces, in order\n // Runs of 2+ adjacent unpadded-numeric tokens with no literal separator\n // between them (e.g. \"Md\", \"Hms\"). Each run is captured by ONE regex\n // group named `groupName` spanning the run's whole digit run\n // (R..2·R digits); per-token values come from enumerateValidSplits()\n // at match time. `groupNames` lists the per-token group names — they\n // appear in `groups` for structure/positions but have no counterpart\n // in the regex itself, so consumers must read their values from the\n // split enumeration, not from match.groups.\n ambiguousRuns: Array<{ groupName: string; groupNames: string[]; tokens: string[] }>;\n}\n\n/**\n * Same walk as buildPatternSource() in pattern.ts, but each token piece\n * gets its own named capture group (positionally named so the same token,\n * e.g. \"yyyy\", could in theory appear twice) so a caller can pull the\n * matched substring for each token back out after a successful match.\n */\nexport function buildCapturingPattern(pieces: Piece[], locale: string): CapturingPattern {\n const groups: Array<{ name: string; token: string }> = [];\n const ambiguousRuns: Array<{ groupName: string; groupNames: string[]; tokens: string[] }> = [];\n let source = '';\n let i = 0;\n let ambiguityBits = 0;\n\n // Tracks the current run of adjacent unpadded-numeric token pieces (no\n // literal or non-unpadded token has broken it yet). Names are recorded\n // in declaration order so a run of 2+ can emit one group while its\n // member tokens still get individual entries in `groups`.\n let currentRun: { names: string[]; tokens: string[] } = { names: [], tokens: [] };\n\n const flushRun = (nextPiece: Piece | undefined) => {\n if (currentRun.tokens.length === 1) {\n // A lone unpadded token: emit its normal (two-way) fragment. Two\n // width choices are harmless on their own; if the next element\n // can consume a digit, charge one ambiguity bit for the boundary.\n const name = currentRun.names[0]!;\n const token = currentRun.tokens[0]!;\n source += `(?<${name}>${tokenFragment(token, locale)})`;\n if (isDigitConsumingStart(nextPiece)) ambiguityBits += 1;\n } else if (currentRun.tokens.length >= 2) {\n // Emit the accumulated run as ONE bounded digit group. R unpadded\n // tokens accept between R and 2R digits in total; anything outside\n // that span can't match regardless of how the digits split, so the\n // single group's acceptance region is exactly the union of the old\n // per-token fragments' regions. Backtracking into this group is\n // bounded at R+1 width choices — linear, not exponential.\n const runName = `r${i++}`;\n const tokenCount = currentRun.tokens.length;\n source += `(?<${runName}>\\\\d{${tokenCount},${tokenCount * 2}})`;\n // Note: no `groups` entry for the run group itself — `groups` lists\n // per-token pieces only (its members were already pushed when\n // visited), so consumers like parseToParts see exactly one entry\n // per token, same as before this run-group change. The regex group\n // is reached through ambiguousRuns[].groupName.\n ambiguousRuns.push({\n groupName: runName,\n groupNames: currentRun.names,\n tokens: currentRun.tokens,\n });\n // A run group adjacent to a digit-consuming successor keeps its\n // (R+1) width choices at that boundary — charge the budget.\n if (isDigitConsumingStart(nextPiece)) {\n ambiguityBits += widthChoicesBits(tokenCount + 1);\n }\n }\n currentRun = { names: [], tokens: [] };\n };\n\n for (const [idx, piece] of pieces.entries()) {\n if (piece.kind === 'literal') {\n flushRun(piece);\n source += escapeRegExp(piece.value);\n continue;\n }\n\n if (UNPADDED_NUMERIC_TOKENS.has(piece.value)) {\n // Part of a (potential) glued run — defer fragment emission to\n // flushRun so a run of 2+ collapses into one bounded group.\n const name = `g${i++}`;\n groups.push({ name, token: piece.value });\n currentRun.names.push(name);\n currentRun.tokens.push(piece.value);\n continue;\n }\n\n flushRun(piece);\n const name = `g${i++}`;\n groups.push({ name, token: piece.value });\n const nextPiece = pieces[idx + 1];\n const nextToken = nextPiece?.kind === 'token' ? nextPiece.value : undefined;\n\n // \"y\" has no bounded fallback the way yyyy does (see tokenFragment in\n // pattern.ts) — it's unpadded by definition, so there's no narrower\n // fixed-width shape to fall back to. A run of \"any number of digits\"\n // immediately next to another digit consumer has no finite ambiguity\n // score to charge against MAX_AMBIGUITY_BITS below, so this is\n // refused outright at build time instead of estimated.\n if (UNBOUNDED_WIDTH_TOKENS.has(piece.value) && isDigitConsumingStart(nextPiece)) {\n throw new FormatSyntaxError({\n reason:\n `token \"${piece.value}\" has no fixed width — it can't be placed directly next to another ` +\n `digit-reading token or a literal that starts with a digit, since there's no way to tell ` +\n `where \"${piece.value}\" ends and the next field begins. Add a non-digit separator (e.g. \"-\" or \" \") after it.`,\n });\n }\n\n // yyyy picks its fragment based on what follows: the exact 4-digit\n // form whenever a digit-consuming element comes next (digit-leading\n // token — the pre-existing rule — OR a literal starting with a\n // digit, added by the ReDoS fix; see the guard block above). The\n // open-ended form is only safe when nothing digit-consuming can\n // follow it.\n if (piece.value === 'yyyy' && nextToken === undefined && isDigitConsumingStart(nextPiece)) {\n source += `(?<${name}>${tokenFragment(piece.value, locale, 'M')})`;\n } else {\n source += `(?<${name}>${tokenFragment(piece.value, locale, nextToken)})`;\n }\n }\n flushRun(undefined);\n\n if (ambiguityBits > MAX_AMBIGUITY_BITS) {\n throw new FormatSyntaxError({\n reason:\n `format string has too many variable-width numeric tokens glued to digit-consuming neighbors ` +\n `(ambiguity score ${ambiguityBits} > ${MAX_AMBIGUITY_BITS}). ` +\n `This shape makes the regex engine backtrack exponentially on near-miss input. ` +\n `Add a non-digit separator between these tokens (e.g. \"-\" or \" \") or use their padded forms (MM/dd/HH/mm/ss).`,\n });\n }\n\n // 'd' flag enables match.indices.groups — used by parseToParts to\n // report each token's actual position in the input. Without it,\n // computing per-group positions would require a separate walk of the\n // piece list against the input, duplicating logic the regex already\n // has. Backward-compatible: 'd' only adds an `indices` property to\n // the match result, no behavioral change to the match itself.\n return { regex: new RegExp(`^(?:${source})$`, 'ud'), groups, ambiguousRuns };\n}","import { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { buildCapturingPattern, type CapturingPattern } from './parsePattern.js';\nimport { enumerateValidSplits, isValidTimeZone } from './pattern.js';\nimport { getLocaleVocab, canonicalCacheKey, assertValidLocaleTag, subscribeToVocabChanges } from './localeVocab.js';\nimport { getTemporal } from './temporalProvider.js';\nimport { MAX_FORMAT_LENGTH, MAX_INPUT_LENGTH } from './constants.js';\nimport { TemporalFmtError, InvalidTimeZoneError, InvalidOffsetError, FormatSyntaxError, ParseMismatchError, AmbiguousInputError, InvalidDateError, wrapUntypedError } from './errors.js';\nimport { applyParseNumbering, type NumberingParseOptions } from './numbering.js';\n\n// format strings are short hand-written literals reused across many calls —\n// cache the compiled capturing pattern per (formatStr, locale) pair instead\n// of rebuilding it every call.\nconst patternCache = new Map<string, CapturingPattern>();\nconst MAX_CACHE_SIZE = 500;\n\n// A compiled pattern embeds locale-vocabulary alternations (MMMM/MMM/\n// EEEE/EEE/a fragments) at build time. When registerLocaleVocab()\n// swaps a locale's vocabulary, every cached pattern for any locale\n// becomes potentially stale — the cached regex would keep matching the\n// OLD vocabulary while format() renders the new one, breaking the\n// format→parse round-trip for the library's own output. Clear the whole\n// cache on any vocab change: rebuilds are cheap and self-limiting via\n// the cache size cap.\nsubscribeToVocabChanges(() => { patternCache.clear(); });\n\nfunction getPattern(formatStr: string, locale: string): CapturingPattern {\n const key = JSON.stringify([canonicalCacheKey(locale), formatStr]);\n let pattern = patternCache.get(key);\n if (pattern) {\n return pattern;\n }\n if (patternCache.size >= MAX_CACHE_SIZE) {\n const oldestKey = patternCache.keys().next().value;\n if (oldestKey !== undefined) patternCache.delete(oldestKey);\n }\n pattern = buildCapturingPattern(tokenize(formatStr), locale);\n patternCache.set(key, pattern);\n return pattern;\n}\n\n// Requires an explicit `-u-ca-` extension (e.g. 'en-u-ca-hebrew') to apply\n// a non-Gregorian calendar, per parse()'s own docstring. 'gregory' counts\n// as \"no calendar\" so the default locale keeps constructing plain ISO 8601.\n//\n// Used to key off resolvedOptions().calendar instead — a locale's\n// *default* calendar, whether the caller asked for one or not. That broke\n// th-TH silently: its default is 'buddhist', so plain Gregorian-looking\n// digits parsed 543 years off, while format() has no matching calendar\n// step and just prints the object's own ISO fields either way.\nconst calendarCache = new Map<string, string | undefined>();\nconst MAX_CALENDAR_CACHE_SIZE = 500;\n\nfunction resolveCalendar(locale: string): string | undefined {\n // Typed validation first: a genuinely malformed tag (bare private-use\n // singleton, control characters, garbage) surfaces as the library's\n // InvalidLocaleError instead of a raw engine RangeError — same code as\n // before, different (documented, structured) error class.\n assertValidLocaleTag(locale);\n // canonicalCacheKey is memoized (localeVocab.ts), so the common path —\n // repeated parse() calls with the same locale — no longer constructs a\n // fresh Intl.Locale per call just to compute the calendar-cache key.\n const canonicalLocale = canonicalCacheKey(locale);\n if (calendarCache.has(canonicalLocale)) {\n return calendarCache.get(canonicalLocale);\n }\n if (calendarCache.size >= MAX_CALENDAR_CACHE_SIZE) {\n const oldestKey = calendarCache.keys().next().value;\n if (oldestKey !== undefined) calendarCache.delete(oldestKey);\n }\n let calendar: string | undefined;\n const parts = canonicalLocale.split('-');\n const extensionIndex = parts.indexOf('u');\n const calendarKeyIndex = extensionIndex === -1 ? -1 : parts.indexOf('ca', extensionIndex + 1);\n if (calendarKeyIndex !== -1 && calendarKeyIndex + 1 < parts.length) {\n const resolved = new Intl.DateTimeFormat(canonicalLocale).resolvedOptions().calendar;\n calendar = resolved === 'gregory' ? undefined : resolved;\n }\n calendarCache.set(canonicalLocale, calendar);\n return calendar;\n}\n\ninterface Fields {\n year?: number;\n twoDigitYear?: number;\n month?: number;\n day?: number;\n hour?: number;\n hour12?: number;\n dayPeriodRaw?: string;\n isPM?: boolean;\n minute?: number;\n second?: number;\n millisecond?: number;\n microsecond?: number;\n nanosecond?: number;\n timeZoneId?: string;\n // Canonical `+HH:MM` form of any offset token (X/XX/XXX/x/xx/xxx)\n // captured in this pattern. Distinct from timeZoneId because the two\n // can coexist in the same pattern (e.g. \"yyyy-MM-dd HH:mm zzz XXX\") —\n // see the cross-check after construction for how a mismatch between\n // them is resolved.\n offsetString?: string;\n weekdayExpected?: number;\n weekdayRaw?: string;\n quarter?: number;\n}\n\n// Normalizes a captured offset-token string into the canonical `+HH:MM`\n// shape Temporal.ZonedDateTime.from accepts as a `timeZone` value. Throws\n// descriptive errors for out-of-range hours/minutes, since the regex\n// shape (OFFSET_SHAPES in pattern.ts) is deliberately permissive — a\n// post-match range check here gives the user a specific error (\"offset\n// hours 99 out of range, max 14\") instead of \"no valid pattern matches\".\n//\n// Range bounds: -12:00 to +14:00, the standard IANA offset range\n// (Baker/Howland at -12, Kiritimati at +14). +14:01 / -12:01 etc. are\n// rejected explicitly even though the per-piece bounds (hours ≤ 14,\n// minutes ≤ 59) alone wouldn't catch them.\nfunction parseOffsetString(raw: string, token: string): string {\n if (raw === 'Z') {\n /* c8 ignore start @preserve -- unreachable: lowercase tokens' regex\n (OFFSET_SHAPES in pattern.ts) has no \"Z\" alternative at all, so\n raw === 'Z' can only ever be reached when token is one of the\n uppercase variants (X/XX/XXX). A lowercase token can't even\n capture \"Z\" as `raw` in the first place. */\n if (token === 'x' || token === 'xx' || token === 'xxx') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" doesn't accept \"Z\" — only the uppercase variants (X/XX/XXX) emit \"Z\" for UTC. ` +\n `Use \"+00:00\", \"+0000\", or \"+00\" depending on the variant's width.`\n );\n }\n /* c8 ignore stop @preserve */\n return '+00:00';\n }\n\n const sign = raw[0];\n /* c8 ignore start @preserve -- unreachable: raw is a regex-captured\n group from an offset token, and every OFFSET_SHAPES pattern\n (pattern.ts) is anchored to either \"Z\" or a leading [+-]. raw's\n first character can never be anything else by the time it reaches\n this function. */\n if (sign !== '+' && sign !== '-') {\n throw new Error(`temporal-fmt: offset \"${raw}\" for token \"${token}\" doesn't start with \"+\", \"-\", or \"Z\".`);\n }\n /* c8 ignore stop @preserve */\n const body = raw.slice(1);\n let hoursStr: string;\n let minutesStr: string;\n if (body.length === 2) {\n /* c8 ignore start @preserve -- unreachable: each offset token's own\n regex shape in OFFSET_SHAPES (pattern.ts) already gates which\n body shapes it can capture. Only X and x ever match a 2-digit\n body — XX/xx/XXX/xxx's regexes can't produce one — so this\n mismatch can never actually fire through parse(). */\n // +HH — only X/x emit this shape; XX/xx/XXX/xxx always carry minutes.\n if (token !== 'X' && token !== 'x') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it requires minutes, but \"${raw}\" has none.`\n );\n }\n /* c8 ignore stop @preserve */\n hoursStr = body;\n minutesStr = '00';\n } else if (body.length === 4) {\n /* c8 ignore start @preserve -- unreachable, same reason as the\n 2-digit case above: XXX/xxx's regex requires a colon, so it can\n never capture a 4-digit no-colon body in the first place. */\n // +HHMM — X/x (when minutes are non-zero) or XX/xx.\n if (token === 'XXX' || token === 'xxx') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it requires a colon between hours and minutes (e.g. \"${sign}${body.slice(0, 2)}:${body.slice(2)}\").`\n );\n }\n /* c8 ignore stop @preserve */\n hoursStr = body.slice(0, 2);\n minutesStr = body.slice(2, 4);\n } else if (body.length === 5 && body[2] === ':') {\n /* c8 ignore start @preserve -- unreachable, same reason again: only\n XXX/xxx's regex can produce a colon-shaped body; X/x/XX/xx never\n capture one. */\n // +HH:MM — XXX/xxx only.\n if (token !== 'XXX' && token !== 'xxx') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it doesn't use a colon (use \"${sign}${body.slice(0, 2)}${body.slice(3)}\" instead).`\n );\n }\n /* c8 ignore stop @preserve */\n hoursStr = body.slice(0, 2);\n minutesStr = body.slice(3, 5);\n /* c8 ignore start @preserve -- unreachable: every offset token's\n regex only ever produces a body of length 2, length 4, or length 5\n with a colon at index 2 (see OFFSET_SHAPES in pattern.ts) — no\n shape falls outside those three cases, so this else arm can't be\n taken through parse(). Kept as an exhaustiveness fallback so\n hoursStr/minutesStr are assigned on every path TypeScript can see. */\n } else {\n throw new Error(`temporal-fmt: offset \"${raw}\" doesn't match the shape token \"${token}\" accepts.`);\n }\n /* c8 ignore stop @preserve */\n\n const hours = Number(hoursStr);\n const minutes = Number(minutesStr);\n // Per-piece range checks catch most malformed input.\n if (hours > 14) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset hours ${hours} in \"${raw}\" out of range (max 14 — Kiritimati, Line Islands is +14:00).`,\n });\n }\n if (minutes > 59) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset minutes ${minutes} in \"${raw}\" out of range (max 59).`,\n });\n }\n // Boundary: +14:01..+14:59 and -12:01..-12:59 are out of range even\n // though each piece alone is in bounds — the overall offset exceeds\n // the IANA-supported range.\n if (sign === '+' && hours === 14 && minutes !== 0) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset \"${raw}\" exceeds the maximum supported UTC offset of +14:00.`,\n });\n }\n if (sign === '-' && hours === 12 && minutes !== 0) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset \"${raw}\" exceeds the maximum supported negative UTC offset of -12:00.`,\n });\n }\n return `${sign}${hoursStr}:${minutesStr}`;\n}\n\nfunction assignField<T>(fields: Fields, key: keyof Fields, value: T): void {\n (fields as Record<string, T | undefined>)[key] = value;\n}\n\nfunction applyGroup(fields: Fields, token: string, raw: string, locale: string, formatStr: string): void {\n const vocab = getLocaleVocab(locale);\n switch (token) {\n case 'yyyy': case 'y':\n assignField(fields, 'year', Number(raw));\n break;\n case 'yy':\n assignField(fields, 'twoDigitYear', Number(raw));\n break;\n case 'MM': case 'M':\n assignField(fields, 'month', Number(raw));\n break;\n case 'MMMM':\n assignField(fields, 'month', vocab.monthLong.indexOf(raw) + 1);\n break;\n case 'MMM':\n assignField(fields, 'month', vocab.monthShort.indexOf(raw) + 1);\n break;\n case 'dd': case 'd':\n assignField(fields, 'day', Number(raw));\n break;\n case 'EEEE':\n assignField(fields, 'weekdayRaw', raw);\n assignField(fields, 'weekdayExpected', vocab.weekdayLong.indexOf(raw) + 1);\n break;\n case 'EEE':\n assignField(fields, 'weekdayRaw', raw);\n assignField(fields, 'weekdayExpected', vocab.weekdayShort.indexOf(raw) + 1);\n break;\n case 'HH': case 'H':\n assignField(fields, 'hour', Number(raw));\n break;\n case 'hh': case 'h':\n assignField(fields, 'hour12', Number(raw));\n break;\n case 'mm': case 'm':\n assignField(fields, 'minute', Number(raw));\n break;\n case 'ss': case 's':\n assignField(fields, 'second', Number(raw));\n break;\n case 'S': case 'SS': case 'SSS': case 'SSSS': case 'SSSSS':\n case 'SSSSSS': case 'SSSSSSS': case 'SSSSSSSS': case 'SSSSSSSSS': {\n // The captured digits are the leading N digits of a nanosecond-of-second\n // value, not the whole thing — \"5\" under SSSSSSSSS means 500000000ns\n // (half a second), not 5ns. Right-padding to 9 digits before splitting\n // is what makes that work; left-padding (or just Number(raw)) would\n // read \"5\" as 5ns instead.\n const nanoOfSecond = Number(raw.padEnd(9, '0'));\n assignField(fields, 'millisecond', Math.floor(nanoOfSecond / 1_000_000));\n assignField(fields, 'microsecond', Math.floor(nanoOfSecond / 1_000) % 1_000);\n assignField(fields, 'nanosecond', nanoOfSecond % 1_000);\n break;\n }\n case 'a': {\n // Matches case-insensitively (see pattern.ts's foldCase), so the\n // lookup here has to fold too, or \"pm\" would pass the regex and\n // then fail this indexOf against the exact-case vocab.\n const periodIndex = vocab.dayPeriod.findIndex((p) => p.toLowerCase() === raw.toLowerCase());\n /* c8 ignore start @preserve -- unreachable: the 'a' token's regex\n fragment (pattern.ts's alternation() over vocab.dayPeriod) can\n only ever capture a case-insensitive match of one of\n vocab.dayPeriod's own entries. Both the regex and this lookup\n derive their vocab from the same `locale` via getLocaleVocab(),\n so periodIndex can't come back negative through parse(). */\n if (periodIndex < 0) throw new Error(`temporal-fmt: unknown day period \"${raw}\" for locale \"${locale}\".`);\n /* c8 ignore stop @preserve */\n assignField(fields, 'dayPeriodRaw', raw);\n assignField(fields, 'isPM', periodIndex === 1);\n break;\n }\n case 'zzz':\n assignField(fields, 'timeZoneId', raw);\n break;\n case 'X': case 'XX': case 'XXX':\n case 'x': case 'xx': case 'xxx':\n assignField(fields, 'offsetString', parseOffsetString(raw, token));\n break;\n case 'Q':\n assignField(fields, 'quarter', Number(raw));\n break;\n case 'QQQ':\n // strips the literal \"Q\" prefix the token itself formats; the suffix\n // digit is the quarter value 1-4\n assignField(fields, 'quarter', Number(raw.slice(1)));\n break;\n }\n}\n\n// The lenient split-selection heuristic for ambiguous glued numeric runs.\n// See README \"Lenient parse mode\" — the strict default throws on these,\n// lenient mode opts into picking one split instead.\nfunction pickLenientSplit(splits: number[][], tokens: string[]): number[] {\n // Prefer the split where a \"d\" (day) token, if any, has a value of 12 or\n // less. Rationale: when a person writes a glued run like \"121\" for an\n // Md format string, the reading \"Dec 1\" (M=12, d=1) is what they\n // typically meant — if they meant \"Jan 21\" they would more often have\n // written it as \"1/21\" or \"01/21\" with a separator or padding, since the\n // 2-digit day is the more naturally-cohesive unit to keep glued. This\n // isn't a guarantee, which is exactly why lenient mode is opt-in — but\n // it's a reasonable default when the caller has asked us to guess.\n const dayIndex = tokens.indexOf('d');\n if (dayIndex !== -1) {\n const smallDaySplits = splits.filter((s) => s[dayIndex]! <= 12);\n if (smallDaySplits.length > 0) {\n return smallDaySplits[0]!;\n }\n }\n // Fallback to the first valid split when the day heuristic doesn't\n // narrow it down — deterministic, and \"first\" here means \"whichever\n // enumerateValidSplits returned first\", which is a depth-first\n // leftmost-shortest walk over the candidate splits.\n return splits[0]!;\n}\n\n// emulates strptime (POSIX) for 2-digit years so the result doesn't depend\n// on the current clock: 00-68 -> 2000-2068, 69-99 -> 1900-1999\n// https://www.man7.org/linux//man-pages/man3/strptime.3p.html\nfunction resolveYear(fields: Fields): number | undefined {\n if (fields.year !== undefined && fields.twoDigitYear !== undefined) {\n // FormatSyntaxError, not ParseMismatchError: this is a contradiction\n // in the format string itself (both a full-year token and \"yy\" are\n // present), not a mismatch between the format and a specific input.\n // \"y\" and \"yyyy\" both land in fields.year, so this also covers a\n // \"y\"+\"yy\" mix, not just \"yyyy\"+\"yy\" — kept generic rather than\n // naming a specific pair.\n throw new FormatSyntaxError({\n message: 'temporal-fmt: format string mixes a full-year token (\"yyyy\"/\"y\") with the two-digit \"yy\" token.',\n });\n }\n if (fields.year !== undefined) return fields.year;\n if (fields.twoDigitYear !== undefined) {\n return fields.twoDigitYear <= 68 ? 2000 + fields.twoDigitYear : 1900 + fields.twoDigitYear;\n }\n return undefined;\n}\n\nfunction resolveHour(fields: Fields, formatStr: string, locale: string): number | undefined {\n if (fields.hour !== undefined && fields.hour12 !== undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" mixes a 24-hour token (\"HH\"/\"H\") with a ` +\n `12-hour token (\"hh\"/\"h\").`,\n });\n }\n if (fields.hour !== undefined) {\n if (fields.dayPeriodRaw !== undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" mixes a 24-hour token (\"HH\"/\"H\") with a ` +\n `day-period token (\"a\"). Both describe the same field; use one or the other, not both.`,\n });\n }\n return fields.hour;\n }\n if (fields.hour12 !== undefined) {\n if (fields.isPM === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" uses a 12-hour token (\"hh\"/\"h\") without an \"a\" token, ` +\n `so parse() can't tell AM from PM.`,\n });\n }\n return (fields.hour12 % 12) + (fields.isPM ? 12 : 0);\n }\n return undefined;\n}\n\n/**\n * Parses `input` against `formatStr` and builds the real Temporal value it\n * describes: a `Temporal.PlainDate`, `PlainTime`, `PlainDateTime`, or\n * `ZonedDateTime` depending on which tokens are present.\n *\n * Returns `unknown` — this package has no ambient `Temporal` types to return\n * a real one against.\n *\n * `options.locale` picks the calendar the result is built in. Pass a locale\n * tag with a `-u-ca-` extension (e.g. `'en-u-ca-hebrew'`) to parse into a\n * non-Gregorian calendar.\n *\n * @throws if `input` doesn't match `formatStr`'s shape at all\n * @throws if it matches the shape but describes an impossible date (e.g. Feb\n * 30) or self-contradictory data (e.g. a weekday name that doesn't match the\n * actual date)\n *\n * @example\n * parse('yyyy-MM-dd HH:mm', '2026-08-04 15:45') // Temporal.PlainDateTime\n * parse('yyyy-MM', '2026-08-04T15:45:30') // throws — shape doesn't match\n * parse('yyyy-MM-dd', '2026-02-30') // throws — not a real date\n */\nexport function parse(formatStr: string, input: string, options: NumberingParseOptions = {}): unknown | undefined {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n\n if (input.length > MAX_INPUT_LENGTH) {\n throw new FormatSyntaxError({\n input,\n message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`,\n });\n }\n\n // Transliterate non-ASCII numerals to ASCII before any matching happens,\n // when the caller opts in via parseNumberingSystem. Every regex this\n // module builds expects 0-9; this is the one place that assumption\n // could otherwise be violated by locale-native input digits.\n if (options.parseNumberingSystem) {\n input = applyParseNumbering(input, options);\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n const calendar = resolveCalendar(locale);\n const pattern = getPattern(formatStr, locale);\n const match = pattern.regex.exec(input);\n if (!match) {\n throw new ParseMismatchError({\n input, format: formatStr,\n // reason set explicitly (not just message): parse.test.js's\n // \"safeParse: failure returns { ok: false, error } with a\n // TemporalFmtError\" asserts result.error.reason carries the\n // underlying detail text, matching what wrapUntypedError used to\n // populate when this message reached it as a caught plain Error.\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n\n if (pattern.groups.length === 0) {\n // ParseMismatchError, not FormatSyntaxError: pinned by\n // errors.test.js's \"format string with no tokens at all falls\n // through to ParseMismatchError\" — wrapUntypedError's classifier\n // has no branch for this message, so it lands on the\n // PARSE_MISMATCH fallback, and a direct throw here needs to\n // agree with that.\n throw new ParseMismatchError({\n format: formatStr,\n // reason set explicitly: errors.test.js's \"format string with no\n // tokens at all falls through to ParseMismatchError\" asserts\n // error.reason matches /has no tokens/.\n reason: `format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n message: `temporal-fmt: format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n });\n }\n\n // The regex's zzz fragment only matches a bounded zone-id *shape* (see\n // TIME_ZONE_SHAPE in pattern.ts) rather than alternating every real IANA\n // name inline, so a shape match isn't proof of a real zone yet — check\n // Zone ids can't be enumerated in the regex itself (there are ~400 of\n // them and they change over time as IANA updates the tz database), so\n // the regex only captures the zzz group's shape and this loop checks\n // each captured zzz group against the actual zone list here. Unlike a\n // regex-shape mismatch, this failure has a specific cause worth\n // naming: the shape matched but the zone id itself isn't recognized.\n for (const { name, token } of pattern.groups) {\n if (token === 'zzz' && !isValidTimeZone(match.groups![name]!)) {\n throw new InvalidTimeZoneError({\n input, format: formatStr, actual: match.groups![name],\n reason: 'not a recognized IANA time zone identifier',\n });\n }\n }\n\n // A run of 2+ adjacent unpadded-numeric tokens with no literal separator\n // (e.g. \"Md\", \"dM\", \"Hms\") is captured by a single bounded digit group in\n // the regex (see buildCapturingPattern — per-token variable-width\n // fragments made near-miss matching exponential in the number of glued\n // tokens). Resolve each run's per-token split here instead: unique valid\n // split resolves (identical to what the old regex's own greedy match\n // produced, since the old match was always one of the valid splits);\n // 2+ valid splits is genuinely ambiguous input — strict mode throws,\n // lenient opts into the documented heuristic. A span with 0 valid\n // splits matched the run's width window but names no valid per-token\n // assignment, which the old per-token fragments rejected at match\n // time — surface the same \"no valid pattern matches\" error.\n const runValues = new Map<string, string>();\n for (const run of pattern.ambiguousRuns) {\n const runDigits = match.groups![run.groupName]!;\n const splits = enumerateValidSplits(runDigits, run.tokens);\n if (splits.length === 0) {\n throw new ParseMismatchError({\n input, format: formatStr,\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n if (splits.length > 1) {\n // Strict default — throw on ambiguity. The whole point of the\n // library's parse() is to refuse to guess when the same input has\n // more than one valid reading. Lenient mode (opt-in via\n // options.lenient) instead picks one split via a documented\n // heuristic — see pickLenientSplit() above and the README section\n // \"Lenient parse mode\" for why this is strictly additive and never\n // the default.\n if (!options.lenient) {\n throw new AmbiguousInputError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${runDigits}\" in format string \"${formatStr}\" is ambiguous — ` +\n `${splits.length} different ways to read tokens \"${run.tokens.join('')}\" (with no separator ` +\n `between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[1])}). ` +\n `parse() won't guess; add a separator between these tokens, or use their padded form ` +\n `(e.g. \"MM\" instead of \"M\") so each one has a fixed width. ` +\n `Pass { lenient: true } to opt into a documented heuristic that picks one.`,\n });\n }\n const picked = pickLenientSplit(splits, run.tokens);\n run.groupNames.forEach((name, idx) => runValues.set(name, String(picked[idx])));\n } else {\n run.groupNames.forEach((name, idx) => runValues.set(name, String(splits[0]![idx])));\n }\n }\n const fields: Fields = {};\n // Per-token values for glued-run members come from the split\n // enumeration above (runValues); every other token reads its own\n // regex group directly.\n for (const { name, token } of pattern.groups) {\n const raw = runValues.get(name) ?? match.groups![name]!;\n applyGroup(fields, token, raw, locale, formatStr);\n }\n\n const year = resolveYear(fields);\n const hour = resolveHour(fields, formatStr, locale);\n const { month, day, minute, second, millisecond, microsecond, nanosecond, timeZoneId, offsetString, weekdayExpected, weekdayRaw, quarter } = fields;\n\n const hasAnyDatePart = year !== undefined || month !== undefined || day !== undefined;\n const hasFullDate = year !== undefined && month !== undefined && day !== undefined;\n if (hasAnyDatePart && !hasFullDate) {\n throw new InvalidDateError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has an incomplete date — ` +\n `year, month, and day tokens must all be present together.`,\n });\n }\n\n const hasTime = hour !== undefined || minute !== undefined || second !== undefined || millisecond !== undefined;\n\n if (timeZoneId !== undefined && !(hasFullDate && hasTime)) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has a \"zzz\" token but needs a full date and time ` +\n `to build a ZonedDateTime.`,\n });\n }\n\n // Mirror zzz's full-date-and-time requirement: an offset alone is\n // meaningless without a wall-clock instant to anchor it to. Throws the\n // same kind of \"needs full date and time\" error zzz throws — separate\n // message so a caller reading it can tell which token type they\n // forgot to pair with a full date+time.\n if (offsetString !== undefined && !(hasFullDate && hasTime)) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has an offset token (X/XX/XXX/x/xx/xxx) but needs a full date and time ` +\n `to build a ZonedDateTime.`,\n });\n }\n\n if (weekdayExpected !== undefined && !hasFullDate) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has a weekday token (\"EEEE\"/\"EEE\") but needs ` +\n `a full date to validate it against.`,\n });\n }\n\n if (!hasFullDate && !hasTime) {\n // shouldn't happen — every token maps to a date, time, zone, or\n // weekday field, and weekday-without-date already threw above\n throw new FormatSyntaxError({\n format: formatStr,\n message: `temporal-fmt: format string \"${formatStr}\" has no date or time tokens to parse.`,\n });\n }\n\n const temporal = getTemporal();\n const timeFields = {\n hour: hour ?? 0,\n minute: minute ?? 0,\n second: second ?? 0,\n millisecond: millisecond ?? 0,\n microsecond: microsecond ?? 0,\n nanosecond: nanosecond ?? 0,\n };\n // omitted entirely for the default calendar (see resolveCalendar) so\n // construction stays plain ISO 8601 unless a caller's locale asks for\n // something else — Temporal calendars don't apply to time-only values.\n const calendarField = calendar ? { calendar } : {};\n\n // overflow: 'reject' — without it Temporal *clamps* out-of-range fields\n // (Feb 30 silently becomes Feb 28) instead of throwing, which would\n // contradict the \"throws on genuinely invalid data\" behavior parse() promises.\n const reject = { overflow: 'reject' as const };\n\n let result: unknown;\n try {\n if (timeZoneId !== undefined) {\n // offset: 'prefer' never throws on a mismatch — it just falls back to\n // the zone's real offset at this instant, silently overriding\n // whatever the offset token said. That's also what resolves a\n // repeated wall-clock time (DST fall-back): without an explicit\n // offset, Temporal defaults to the first occurrence, so passing the\n // token's offset here is what lets a second-occurrence input resolve\n // to the second occurrence instead of always falling back to the\n // first. Either way, \"prefer\" can't be used to detect disagreement —\n // that's checked explicitly below, once we have a real ZonedDateTime\n // to compare against, instead of relying on the wording of whatever\n // error Temporal's active implementation happens to throw (that\n // wording isn't part of the spec and differs between the native\n // Temporal global and userland polyfills).\n const zoneOptions: Temporal.ZonedDateTimeFromOptions = { overflow: 'reject', offset: 'prefer' };\n result = temporal.ZonedDateTime.from(\n {\n year: year!, month: month!, day: day!, ...timeFields, ...calendarField,\n timeZone: timeZoneId,\n ...(offsetString !== undefined ? { offset: offsetString } : {}),\n },\n zoneOptions\n );\n if (offsetString !== undefined) {\n // 'prefer' silently rewrites the wall-clock time itself when the\n // input falls in a DST gap (the time never occurred, so there's\n // no instant to prefer toward) — it doesn't just pick a\n // different offset for the same clock time, the way it does for\n // an overlap. Checking offsetString alone can't tell \"gap,\n // silently moved\" apart from \"overlap, correctly resolved,\"\n // since both can produce an actualOffset that differs from what\n // was parsed. Comparing the wall-clock fields catches the gap\n // case: they can only drift from the parsed input if Temporal\n // moved the clock time to escape the gap.\n //\n // Only checked when an offset token was given: with no offset\n // token to disagree with, a gap shifting forward is the\n // documented, wanted behavior (there's nothing to reject against).\n const zdt = result as Temporal.ZonedDateTime;\n const wallClockShifted =\n zdt.hour !== timeFields.hour ||\n zdt.minute !== timeFields.minute ||\n zdt.second !== timeFields.second;\n if (wallClockShifted) {\n // InvalidDateError, not AmbiguousInputError: the message\n // *mentions* \"not an ambiguous... instant\" as a negation, which\n // is a false-positive match against wrapUntypedError's\n // /ambiguous/i classifier regex — the actual failure here is a\n // wall-clock time that doesn't exist (DST gap), which is an\n // invalid-value problem, not an ambiguous-reading-of-input\n // problem the way the numeric-token-glue ambiguity above is.\n throw new InvalidDateError({\n input, format: formatStr,\n message:\n `\"${timeZoneId}\" has no such wall-clock time on this date — it falls in a DST gap, ` +\n `not an ambiguous or valid instant.`,\n });\n }\n const actualOffset = zdt.offset;\n if (actualOffset !== offsetString) {\n throw new ParseMismatchError({\n input, format: formatStr,\n message:\n `has both a \"zzz\" zone (${timeZoneId}) and an offset token (${offsetString}), ` +\n `but the zone's actual offset at this date/time is ${actualOffset}, not ${offsetString}.`,\n });\n }\n }\n } else if (offsetString !== undefined) {\n // An offset token with no \"zzz\" zone token present. Previously this\n // built a fixed-offset ZonedDateTime directly from the offset\n // string; now it's refused outright — an offset identifies a\n // moment's distance from UTC, not a zone, and building a\n // ZonedDateTime without a real zone identity papers over that\n // difference instead of surfacing it.\n throw new ParseMismatchError({\n input, format: formatStr,\n message:\n `format string \"${formatStr}\" has an offset token but no \"zzz\" zone token. ` +\n `An offset does not identify a time zone by itself — add \"zzz\" to the pattern, ` +\n `or parse into a PlainDateTime/PlainDate/PlainTime if a zone isn't needed.`,\n });\n } else if (hasFullDate && hasTime) {\n result = temporal.PlainDateTime.from({ year: year!, month: month!, day: day!, ...timeFields, ...calendarField }, reject);\n } else if (hasFullDate) {\n result = temporal.PlainDate.from({ year: year!, month: month!, day: day!, ...calendarField }, reject);\n } else {\n result = temporal.PlainTime.from(timeFields, reject);\n }\n } catch (err) {\n throw new InvalidDateError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${input}\" doesn't describe a valid date/time for format \"${formatStr}\": ` +\n `${(err as Error).message}`,\n });\n }\n\n if (weekdayExpected !== undefined) {\n const actual = (result as { dayOfWeek: number }).dayOfWeek;\n if (actual !== weekdayExpected) {\n const vocab = getLocaleVocab(locale);\n throw new InvalidDateError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${weekdayRaw}\" doesn't match the actual weekday (${vocab.weekdayLong[actual - 1]}) ` +\n `for the parsed date.`,\n });\n }\n }\n\n // Q/QQQ is a derived field of the month: 1-3 -> Q1, 4-6 -> Q2, 7-9 -> Q3,\n // 10-12 -> Q4. If a format string carries a quarter token alongside\n // month/date tokens, parse() cross-checks the parsed quarter against the\n // month the same way EEEE cross-checks weekday against date — silently\n // accepting a mismatch would defeat the point of having a quarter token\n // at all, since you'd be telling parse() one thing and the date another.\n if (quarter !== undefined && month !== undefined) {\n const expectedQuarter = Math.ceil(month / 3);\n if (quarter !== expectedQuarter) {\n throw new InvalidDateError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" contains a quarter token (Q/QQQ) whose value ` +\n `(Q${quarter}) disagrees with the parsed month's actual quarter — month ${month} is in ` +\n `Q${expectedQuarter}.`,\n });\n }\n }\n\n return result;\n}\n\n// safeParse: returns a discriminated union instead of throwing. The\n// happy path returns `{ ok: true, value }` with the Temporal instance\n// (typed as `unknown` since this package has no ambient Temporal types).\n// The error path returns `{ ok: false, error }` where `error` is a\n// `TemporalFmtError` subclass when the failure is one the typed-error\n// surface in errors.ts knows how to classify (most of them), or a\n// wrapped plain `Error` (still inside a TemporalFmtError shell) when\n// the throw site hasn't been migrated yet. Callers needing the original\n// thrown object for backward compatibility should use parse() directly.\nexport type SafeParseResult =\n | { ok: true; value: unknown }\n | { ok: false; error: TemporalFmtError };\n\nexport function safeParse(formatStr: string, input: string, options: NumberingParseOptions = {}): SafeParseResult {\n try {\n return { ok: true, value: parse(formatStr, input, options) };\n } catch (err) {\n // Pass through typed errors unchanged — preserves the structured\n // fields (code/token/position/etc.) the existing typed-error\n // surface already populated.\n //\n // Every reachable throw site in parse()'s call graph now throws a\n // TemporalFmtError directly, so this condition is always true in the\n // current test suite; the false path (falling through to\n // wrapUntypedError below) is kept as a safety net for any throw site\n // added later without being migrated to a typed class immediately —\n // removing it would silently break the \"safeParse always returns a\n // TemporalFmtError\" contract documented above. Ignoring the whole\n // if/else as one unit is harmless for the true branch (real tests\n // still execute and count it — c8 just excludes it from the\n // denominator), and it's the only shape that actually suppresses the\n // false-branch marker, since c8 has no standalone \"ignore else\".\n /* c8 ignore start @preserve */\n if (err instanceof TemporalFmtError) {\n return { ok: false, error: err };\n }\n return { ok: false, error: wrapUntypedError(err as Error, { input, format: formatStr }) };\n }\n /* c8 ignore stop @preserve */\n}\n\n// tryParse: best-effort variant. Returns the parsed value or undefined.\n// Suppresses diagnostics entirely — when callers need the reason for\n// a failure, they should use safeParse(). Intentionally loose on the\n// return type (unknown) since this package has no ambient Temporal\n// types to return a real one against.\nexport function tryParse(formatStr: string, input: string, options: NumberingParseOptions = {}): unknown | undefined {\n try {\n return parse(formatStr, input, options);\n } catch {\n return undefined;\n }\n}\n\n// parseToParts: returns the matched groups with token labels, before\n// any Temporal construction. Useful for callers that want to inspect\n// what each token captured (e.g. to build a non-Temporal result, or to\n// cross-check fields themselves) without committing to the inferred\n// Temporal type parse() would build.\n//\n// Throws the same errors parse() throws for early validation (unknown\n// token, unterminated quote, no-match, ambiguity in strict mode) since\n// those failures happen before any group assignment. Construction-time\n// errors (Feb 30, weekday mismatch, etc.) do not happen here —\n// parseToParts doesn't construct anything, so it can't fail at that step.\nexport interface ParsedPart {\n token: string;\n raw: string;\n // Field name (year/month/day/...) this token would assign if handed\n // to parse()'s applyGroup loop. undefined for tokens that don't map\n // to a single field (none today, but kept here so future additions\n // don't have to widen the type).\n field?: string;\n // Position of `raw` in `input`, 0-indexed. Lets a caller highlight\n // the matched span in an editor/CLI.\n position: number;\n}\n\nexport function parseToParts(formatStr: string, input: string, options: NumberingParseOptions = {}): ParsedPart[] {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n if (input.length > MAX_INPUT_LENGTH) {\n throw new FormatSyntaxError({\n input,\n message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`,\n });\n }\n\n // Same numeral transliteration parse() does — see the comment there.\n if (options.parseNumberingSystem) {\n input = applyParseNumbering(input, options);\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pattern = getPattern(formatStr, locale);\n const match = pattern.regex.exec(input);\n if (!match) {\n throw new ParseMismatchError({\n input, format: formatStr,\n // reason set explicitly (not just message): parse.test.js's\n // \"safeParse: failure returns { ok: false, error } with a\n // TemporalFmtError\" asserts result.error.reason carries the\n // underlying detail text, matching what wrapUntypedError used to\n // populate when this message reached it as a caught plain Error.\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n if (pattern.groups.length === 0) {\n // ParseMismatchError, not FormatSyntaxError: pinned by\n // errors.test.js's \"format string with no tokens at all falls\n // through to ParseMismatchError\" — wrapUntypedError's classifier\n // has no branch for this message, so it lands on the\n // PARSE_MISMATCH fallback, and a direct throw here needs to\n // agree with that.\n throw new ParseMismatchError({\n format: formatStr,\n // reason set explicitly: errors.test.js's \"format string with no\n // tokens at all falls through to ParseMismatchError\" asserts\n // error.reason matches /has no tokens/.\n reason: `format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n message: `temporal-fmt: format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n });\n }\n // Same zzz shape-validation parse() does — kept here for parity, so\n // a caller using parseToParts sees the same InvalidTimeZoneError for\n // a bogus zone id, not a silently-accepted bogus zone.\n for (const { name, token } of pattern.groups) {\n if (token === 'zzz' && !isValidTimeZone(match.groups![name]!)) {\n throw new InvalidTimeZoneError({\n input, format: formatStr, actual: match.groups![name],\n reason: 'not a recognized IANA time zone identifier',\n });\n }\n }\n\n // Glued-run split handling: same as parse() — each run's single regex\n // group is split into per-token values by enumerateValidSplits();\n // unique split resolves, 2+ splits throws in strict mode / heuristic-\n // picks in lenient, 0 splits is a shape mismatch. parseToParts mirrors\n // parse() so callers switching between the two on the same input get\n // consistent results.\n const runValues = new Map<string, string>();\n for (const run of pattern.ambiguousRuns) {\n const runDigits = match.groups![run.groupName]!;\n const splits = enumerateValidSplits(runDigits, run.tokens);\n if (splits.length === 0) {\n throw new ParseMismatchError({\n input, format: formatStr,\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n if (splits.length > 1) {\n if (!options.lenient) {\n throw new AmbiguousInputError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${runDigits}\" in format string \"${formatStr}\" is ambiguous — ` +\n `${splits.length} different ways to read tokens \"${run.tokens.join('')}\" (with no separator ` +\n `between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[1])}). ` +\n `parse() won't guess; add a separator between these tokens, or use their padded form ` +\n `(e.g. \"MM\" instead of \"M\") so each one has a fixed width. ` +\n `Pass { lenient: true } to opt into a documented heuristic that picks one.`,\n });\n }\n const picked = pickLenientSplit(splits, run.tokens);\n run.groupNames.forEach((name, idx) => runValues.set(name, String(picked[idx])));\n } else {\n run.groupNames.forEach((name, idx) => runValues.set(name, String(splits[0]![idx])));\n }\n }\n\n const parts: ParsedPart[] = [];\n // match.indices.groups (provided by the regex 'd' flag) gives the\n // [start, end] of each named group in the input. Used here so positions\n // are accurate even when literals separate tokens — summing raw\n // lengths alone wouldn't account for the literal characters between\n // groups. Falls back to the cumulative-raw-length heuristic on engines\n // without 'd' support (none we target, but the fallback keeps the\n // code robust if the flag is ever removed).\n const indices = (match as RegExpMatchArray & { indices?: { groups?: Record<string, [number, number]> } }).indices;\n const groupIndices = indices?.groups;\n let consumed = 0;\n for (const { name, token } of pattern.groups) {\n const raw = runValues.get(name) ?? match.groups![name]!;\n // Glued-run members have no regex group of their own — the regex's\n // recorded indices point at the run's overall span, not the\n // individual token's slice within it — so fall back to cumulative-\n // raw-length for them. Positions stay monotonic but may not be\n // exact for tokens inside a resolved run. Documented as a known\n // limitation; the alternative (re-running the regex with the chosen\n // split baked in) would mean a second match pass for a corner case\n // the caller opted into by gluing unpadded tokens.\n const fromIndices = !runValues.has(name) && groupIndices?.[name];\n /* c8 ignore next */\n const position = fromIndices ? fromIndices[0] : (match.index ?? 0) + consumed;\n parts.push({ token, raw, position });\n consumed += raw.length;\n }\n return parts;\n}\n\n// compileParser: pre-compiles a format string into an object whose\n// parse()/safeParse()/parseToParts() methods skip the per-call\n// pattern-cache lookup. The patternCache in this module means a plain\n// parse(fmt, input) call already pays only a Map lookup after the first\n// call, so compileParser is mostly an ergonomics affordance — useful\n// for callers who want to hold the compiled parser explicitly (e.g. to\n// inspect the pattern via the .pattern property).\nexport interface CompiledParser {\n parse(input: string, options?: NumberingParseOptions): unknown;\n safeParse(input: string, options?: NumberingParseOptions): SafeParseResult;\n tryParse(input: string, options?: NumberingParseOptions): unknown | undefined;\n parseToParts(input: string, options?: NumberingParseOptions): ParsedPart[];\n readonly formatStr: string;\n readonly pattern: CapturingPattern;\n}\n\nexport function compileParser(formatStr: string, options: NumberingParseOptions = {}): CompiledParser {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n // Pre-compile against the default locale; per-call locales will\n // re-resolve via getPattern() if they differ. Most callers use one\n // locale consistently, so pre-compiling against the default keeps\n // the fast path fast.\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pattern = getPattern(formatStr, locale);\n return {\n formatStr,\n pattern,\n parse(input: string, opts: NumberingParseOptions = {}) {\n return parse(formatStr, input, { locale, ...opts });\n },\n safeParse(input: string, opts: NumberingParseOptions = {}) {\n return safeParse(formatStr, input, { locale, ...opts });\n },\n tryParse(input: string, opts: NumberingParseOptions = {}) {\n return tryParse(formatStr, input, { locale, ...opts });\n },\n parseToParts(input: string, opts: NumberingParseOptions = {}) {\n return parseToParts(formatStr, input, { locale, ...opts });\n },\n };\n}"],"mappings":"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","names":["escapeRegExp","literal","alternation","values","caseInsensitive","escaped","foldCase","value","ch","TIME_ZONE_SHAPE","OFFSET_SHAPES","getTimeZoneFragment","validZoneSet","getValidZoneSet","FIXED_OFFSET_RE","isValidTimeZone","raw","getTemporal","NUMERIC_FRAGMENTS","QQQ_FRAGMENT","FORMAT_ONLY_TOKENS","YYYY_EXACT","YYYY_EXTENDED","Y_FRAGMENT","DIGIT_LEADING_TOKENS","UNBOUNDED_WIDTH_TOKENS","tokenFragment","token","locale","nextToken","numeric","UnknownTokenError","vocab","getLocaleVocab","UNPADDED_NUMERIC_TOKENS","UNPADDED_NUMERIC_RANGES","enumerateValidSplits","digits","tokens","memo","solve","tokenIndex","offset","key","cached","result","ranges","results","width","min","max","piece","restSplit","escapeRegExp","literal","MAX_AMBIGUITY_BITS","widthChoicesBits","choices","isDigitConsumingStart","piece","DIGIT_LEADING_TOKENS","buildCapturingPattern","pieces","locale","groups","ambiguousRuns","source","i","ambiguityBits","currentRun","flushRun","nextPiece","name","token","tokenFragment","runName","tokenCount","idx","UNPADDED_NUMERIC_TOKENS","nextToken","UNBOUNDED_WIDTH_TOKENS","FormatSyntaxError","patternCache","MAX_CACHE_SIZE","subscribeToVocabChanges","getPattern","formatStr","locale","key","canonicalCacheKey","pattern","oldestKey","buildCapturingPattern","tokenize","calendarCache","MAX_CALENDAR_CACHE_SIZE","resolveCalendar","assertValidLocaleTag","canonicalLocale","calendar","parts","extensionIndex","calendarKeyIndex","resolved","parseOffsetString","raw","token","sign","body","hoursStr","minutesStr","hours","minutes","InvalidOffsetError","assignField","fields","value","applyGroup","vocab","getLocaleVocab","nanoOfSecond","periodIndex","p","pickLenientSplit","splits","tokens","dayIndex","smallDaySplits","resolveYear","FormatSyntaxError","resolveHour","parse","input","options","applyParseNumbering","DEFAULT_LOCALE","match","ParseMismatchError","name","isValidTimeZone","InvalidTimeZoneError","runValues","run","runDigits","enumerateValidSplits","AmbiguousInputError","picked","idx","year","hour","month","day","minute","second","millisecond","microsecond","nanosecond","timeZoneId","offsetString","weekdayExpected","weekdayRaw","quarter","hasAnyDatePart","hasFullDate","InvalidDateError","hasTime","temporal","getTemporal","timeFields","calendarField","reject","result","zoneOptions","zdt","actualOffset","err","actual","expectedQuarter","safeParse","TemporalFmtError","wrapUntypedError","tryParse","parseToParts","groupIndices","consumed","fromIndices","position","compileParser","opts"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as p,b as h}from"./chunk-
|
|
2
|
-
//# sourceMappingURL=chunk-
|
|
1
|
+
import{a as p,b as h}from"./chunk-SSPABEUR.js";import{p as b}from"./chunk-5U5WJ465.js";import{a}from"./chunk-UJR6DHXY.js";import{n as f}from"./chunk-EHLRZULM.js";function x(t,n,o="closed"){if(a(t,n)>0)throw new Error("temporal-fmt: interval start must be \u2264 end (got start > end). Pass them in chronological order.");return{start:t,end:n,bounds:o}}function F(t,n){let o=a(n,t.start),r=a(n,t.end),e=t.bounds==="open"||t.bounds==="half-open-end"?o>0:o>=0,s=t.bounds==="open"||t.bounds==="half-open-start"?r<0:r<=0;return e&&s}function E(t,n){return m(t,n)}function m(t,n){return!(a(t.end,n.start)<0||a(t.start,n.end)>0)}function S(t,n){return a(t.end,n.start)<0}function T(t,n){return a(t.start,n.end)>0}function B(t,n){if(!m(t,n))return null;let o=a(t.start,n.start)>=0?t.start:n.start,r=a(t.end,n.end)<=0?t.end:n.end,e=t.bounds==="open"||t.bounds==="half-open-end"||n.bounds==="open"||n.bounds==="half-open-end",s=t.bounds==="open"||t.bounds==="half-open-start"||n.bounds==="open"||n.bounds==="half-open-start";return{start:o,end:r,bounds:e&&s?"open":e?"half-open-end":s?"half-open-start":"closed"}}function R(t,n){if(!m(t,n))return null;let o=a(t.start,n.start)<=0?t.start:n.start,r=a(t.end,n.end)>=0?t.end:n.end,e=(t.bounds==="open"||t.bounds==="half-open-end")&&(n.bounds==="open"||n.bounds==="half-open-end"),s=(t.bounds==="open"||t.bounds==="half-open-start")&&(n.bounds==="open"||n.bounds==="half-open-start");return{start:o,end:r,bounds:e&&s?"open":e?"half-open-end":s?"half-open-start":"closed"}}function v(t,n){if(!m(t,n))return[t];let o=[],r=t.bounds==="closed"||t.bounds==="half-open-end",e=t.bounds==="closed"||t.bounds==="half-open-start";return a(t.start,n.start)<0&&o.push({start:t.start,end:n.start,bounds:r?"half-open-end":"open"}),a(t.end,n.end)>0&&o.push({start:n.end,end:t.end,bounds:e?"half-open-start":"open"}),o}var A=v;function O(t){if(t.length===0)return[];let n=[...t].sort((r,e)=>a(r.start,e.start)),o=[{...n[0]}];for(let r=1;r<n.length;r++){let e=n[r],s=o[o.length-1];m(s,e)||a(s.end,e.start)===0?a(e.end,s.end)>0&&(s.end=e.end):o.push(e)}return o}var I=864e13;function k(t,n){if(n<=0)throw new Error(`temporal-fmt: splitInterval requires n > 0 (got ${n}).`);if(n===1)return[t];let o=f(t.start),r=f(t.end),e=y(o),s=y(r);if(Math.abs(e)>I||Math.abs(s)>I)throw new RangeError("temporal-fmt: splitInterval() endpoints are outside the representable Date range (approximately \xB1275,760 years). Split the interval into smaller ranges first.");let l=(s-e)/n,d=[];for(let u=0;u<n;u++){let c=e+u*l,i=u===n-1?s:c+l;d.push({start:w(c,o),end:w(i,r),bounds:u===0?t.bounds:"half-open-start"})}return d}function y(t){let n=t.year,o=t.month,r=t.day,e=o<=2?n-1:n,s=Math.floor((e>=0?e:e-399)/400),l=e-s*400,d=o>2?o-3:o+9,u=Math.floor((153*d+2)/5)+r-1,c=l*365+Math.floor(l/4)-Math.floor(l/100)+u;return(s*146097+c-719468)*864e5+(t.hour??0)*36e5+(t.minute??0)*6e4+(t.second??0)*1e3+(t.millisecond??0)}function w(t,n){let r=Math.floor(t/864e5),e=t%864e5;e<0&&(e+=864e5);let s=Math.floor(e/36e5),l=Math.floor(e%36e5/6e4),d=Math.floor(e%6e4/1e3),u=e%1e3,c=r*864e5,i=new Date(c);return{...n,year:i.getUTCFullYear(),month:i.getUTCMonth()+1,day:i.getUTCDate(),hour:s,minute:l,second:d,millisecond:u}}function V(t,n,o={}){try{let r=p(t.start,n,o),e=p(t.end,n,o);return`${r} \u2013 ${e}`}catch(r){try{let e=new Intl.DateTimeFormat(b(o.locale??"en-US"),{}),s=_(t.start),l=_(t.end);return e.formatRange(s,l)}catch{throw r}}}function C(t,n,o={}){let r=h(t.start,n,o),e=h(t.end,n,o),s=[...r];return s.push({type:"literal",value:" \u2013 "}),s.push(...e),s}function _(t){let n=t;if(typeof n?.toInstant=="function")return new Date(n.toInstant().epochMilliseconds);if(typeof n?.year=="number"&&typeof n?.month=="number"&&typeof n?.day=="number")return new Date(Date.UTC(n.year,n.month-1,n.day,n.hour??0,n.minute??0,n.second??0,n.millisecond??0));throw new Error(`temporal-fmt: formatRange() expected Temporal values with year/month/day, got ${typeof t}.`)}export{x as a,F as b,E as c,m as d,S as e,T as f,B as g,R as h,v as i,A as j,O as k,k as l,V as m,C as n};
|
|
2
|
+
//# sourceMappingURL=chunk-4XRE37WT.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as I,c as L}from"./chunk-3MZLTVP3.js";import{k as x,p as X,q as b,s}from"./chunk-5U5WJ465.js";import{b as p,c as w}from"./chunk-C6ZZ62ET.js";function r(e,t){let n=e<0,o=String(Math.abs(e)).padStart(t,"0");return n?"-"+o:o}r.fraction=function(t,n){let o=t.millisecond*1e6+(t.microsecond??0)*1e3+(t.nanosecond??0);return r(o,9).slice(0,n)};var G="en-US",l=new Map,P=500;function E(e,t){let n=JSON.stringify([b(e),t]),o=l.get(n);if(o)return o;if(l.size>=P){let a=l.keys().next().value;a!==void 0&&l.delete(a)}try{o=new Intl.DateTimeFormat(X(e),t)}catch(a){throw new x({actual:e,reason:a.message})}return l.set(n,o),o}var y;I(()=>{y=void 0});function M(){if(y===void 0){y=!1;try{let e=L();new Intl.DateTimeFormat("en-US",{day:"numeric"}).formatToParts(e.PlainDate.from({year:1970,month:1,day:1})),y=!0}catch{}}return y}function h(e,t,n,o){let a=e?.calendarId,i={...n,calendar:a&&a!=="iso8601"?a:"gregory"},d=e.toLocaleString;if(typeof d!="function"||d===Object.prototype.toLocaleString)throw new Error(`temporal-fmt: locale-aware part "${o}" needs a value that implements toLocaleString (a real Temporal object). A plain field bag cannot render locale-aware names \u2014 pass a Temporal.PlainDate/PlainDateTime/ZonedDateTime.`);if(!M())try{return d.call(e,X(t),i)}catch(u){throw u instanceof RangeError?new x({actual:t,reason:u.message}):u}let{toInstant:F,timeZoneId:D}=e,O=typeof F=="function"&&typeof D=="string",Z=O?e.toInstant():e,$={...i,...O?{timeZone:D}:{}},g=E(t,$).formatToParts(Z),S=g.findIndex(u=>u.type===o);if(S===-1)throw new Error(`temporal-fmt: locale "${t}" produced no "${o}" part for this token. This usually means the Temporal object is missing the field the token needs.`);let f=g[S].value,k=g[S-1],T=g[S+1];return k?.type==="literal"&&!/\s/.test(k.value)&&(f=k.value+f),T?.type==="literal"&&!/\s/.test(T.value)&&(f=f+T.value),f}function W(e,t){let n=s(t);if(n)return e<12?n.dayPeriod[0]:n.dayPeriod[1];let o=new Date(Date.UTC(1970,0,1,e)),i=E(t,{hour:"numeric",hour12:!0,timeZone:"UTC"}).formatToParts(o).find(d=>d.type==="dayPeriod");if(!i)throw new Error(`temporal-fmt: locale "${t}" produced no "dayPeriod" part for token "a".`);return i.value}function m(e,t,n,o,a,i){return a&&i!==void 0&&i>=0&&i<a.length?a[i]:h(e,t,n,o)}function c(e,t){if(e==="+00:00"&&(t==="X"||t==="XX"||t==="XXX"))return"Z";if(e.length>6){if(t==="xxx")return e;throw new Error(`temporal-fmt: token "${t}" cannot represent the offset "${e}", which has a seconds component. None of the X/XX/XXX/x/xx tokens support offset seconds; use "xxx" instead, which formats the full offset unchanged.`)}let n=e[0],o=e.slice(1,3),a=e.slice(4,6);switch(t){case"X":case"x":return a==="00"?`${n}${o}`:`${n}${o}${a}`;case"XX":case"xx":return`${n}${o}${a}`;case"XXX":case"xxx":return`${n}${o}:${a}`}}var H=[["yyyy",e=>r(e.year,4),"year"],["yy",e=>{if(e.year<0)throw new Error(`temporal-fmt: token "yy" doesn't support negative years (got ${e.year}), since truncating to 2 digits would make it indistinguishable from a positive year. Use "yyyy" instead.`);return r(e.year%100,2)},"year"],["y",e=>r(e.year,0),"year"],["MMMM",(e,t)=>{let n=s(t);return m(e,t,{month:"long"},"month",n?.monthLong,e.month-1)},"month"],["MMM",(e,t)=>{let n=s(t);return m(e,t,{month:"short"},"month",n?.monthShort,e.month-1)},"month"],["MM",e=>r(e.month,2),"month"],["M",e=>String(e.month),"month"],["dd",e=>r(e.day,2),"day"],["d",e=>String(e.day),"day"],["EEEE",(e,t)=>{let n=s(t);return m(e,t,{weekday:"long"},"weekday",n?.weekdayLong,e.dayOfWeek-1)},"dayOfWeek"],["EEE",(e,t)=>{let n=s(t);return m(e,t,{weekday:"short"},"weekday",n?.weekdayShort,e.dayOfWeek-1)},"dayOfWeek"],["HH",e=>r(e.hour,2),"hour"],["H",e=>String(e.hour),"hour"],["hh",e=>r(e.hour%12||12,2),"hour"],["h",e=>String(e.hour%12||12),"hour"],["mm",e=>r(e.minute,2),"minute"],["m",e=>String(e.minute),"minute"],["ss",e=>r(e.second,2),"second"],["s",e=>String(e.second),"second"],["SSSSSSSSS",e=>r.fraction(e,9),"millisecond"],["SSSSSSSS",e=>r.fraction(e,8),"millisecond"],["SSSSSSS",e=>r.fraction(e,7),"millisecond"],["SSSSSS",e=>r.fraction(e,6),"millisecond"],["SSSSS",e=>r.fraction(e,5),"millisecond"],["SSSS",e=>r.fraction(e,4),"millisecond"],["SSS",e=>r.fraction(e,3),"millisecond"],["SS",e=>r.fraction(e,2),"millisecond"],["S",e=>r.fraction(e,1),"millisecond"],["a",(e,t)=>W(e.hour,t),"hour"],["zzz",e=>e.timeZoneId,"timeZoneId"],["xxx",e=>c(e.offset,"xxx"),"offset"],["xx",e=>c(e.offset,"xx"),"offset"],["X",e=>c(e.offset,"X"),"offset"],["XX",e=>c(e.offset,"XX"),"offset"],["XXX",e=>c(e.offset,"XXX"),"offset"],["x",e=>c(e.offset,"x"),"offset"],["do",e=>{let t=e.day,n=t%10,o=t%100;return o>=11&&o<=13?t+"th":n===1?t+"st":n===2?t+"nd":n===3?t+"rd":t+"th"},"day"],["Q",e=>String(Math.ceil(e.month/3)),"month"],["QQQ",e=>"Q"+Math.ceil(e.month/3),"month"],["ww",e=>{let{week:t}=w(e.year,e.month,e.day,e.dayOfWeek);return r(t,2)},"dayOfWeek"],["RRRR",e=>{let{isoYear:t}=w(e.year,e.month,e.day,e.dayOfWeek);return r(t,4)},"dayOfWeek"],["D",e=>String(p(e.year,e.month,e.day)),"day"],["DD",e=>r(p(e.year,e.month,e.day),2),"day"],["DDD",e=>r(p(e.year,e.month,e.day),3),"day"],["LLLL",(e,t)=>{let n=s(t);return m(e,t,{month:"long"},"month",n?.monthLong,e.month-1)},"month"],["LLL",(e,t)=>{let n=s(t);return m(e,t,{month:"short"},"month",n?.monthShort,e.month-1)},"month"],["cccc",(e,t)=>{let n=s(t);return m(e,t,{weekday:"long"},"weekday",n?.weekdayLong,e.dayOfWeek-1)},"dayOfWeek"],["ccc",(e,t)=>{let n=s(t);return m(e,t,{weekday:"short"},"weekday",n?.weekdayShort,e.dayOfWeek-1)},"dayOfWeek"],["GGGG",(e,t)=>h(e,t,{era:"long"},"era"),"year"],["G",(e,t)=>h(e,t,{era:"short"},"era"),"year"],["zzzz",(e,t)=>h(e,t,{timeZoneName:"longGeneric"},"timeZoneName"),"timeZoneId"],["z",(e,t)=>h(e,t,{timeZoneName:"short"},"timeZoneName"),"timeZoneId"]];export{G as a,H as b};
|
|
2
|
+
//# sourceMappingURL=chunk-GMGZZG6I.js.map
|