temporal-fmt 0.9.2 → 0.9.4
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 +480 -9
- package/dist/calendarUtils.cjs +259 -4
- package/dist/calendarUtils.cjs.map +1 -1
- package/dist/calendarUtils.js +33 -1
- package/dist/chunk-4N3WCGEY.js +183 -0
- package/dist/{chunk-4JWGUR4O.js.map → chunk-4N3WCGEY.js.map} +1 -1
- package/dist/chunk-5UTV5VP6.js +984 -0
- package/dist/chunk-5UTV5VP6.js.map +1 -0
- package/dist/chunk-ACKKWH5Z.js +578 -0
- package/dist/{chunk-2JHEV7ZF.js.map → chunk-ACKKWH5Z.js.map} +1 -1
- package/dist/chunk-EGUUO6LE.js +439 -0
- package/dist/chunk-EGUUO6LE.js.map +1 -0
- package/dist/chunk-FBPHNRVW.js +2939 -0
- package/dist/chunk-FBPHNRVW.js.map +1 -0
- package/dist/chunk-GPB4JHGI.js +65 -0
- package/dist/chunk-GPB4JHGI.js.map +1 -0
- package/dist/chunk-H7YQX25T.js +170 -0
- package/dist/{chunk-J2AQNXF2.js.map → chunk-H7YQX25T.js.map} +1 -1
- package/dist/chunk-HXY2GUUM.js +182 -0
- package/dist/{chunk-EHLRZULM.js.map → chunk-HXY2GUUM.js.map} +1 -1
- package/dist/chunk-L24NPTAJ.js +328 -0
- package/dist/chunk-L24NPTAJ.js.map +1 -0
- package/dist/chunk-M3GDTHMT.js +169 -0
- package/dist/chunk-M3GDTHMT.js.map +1 -0
- package/dist/chunk-MKPDLQKD.js +51 -0
- package/dist/chunk-MKPDLQKD.js.map +1 -0
- package/dist/chunk-TSLPIURC.js +242 -0
- package/dist/{chunk-VQXUMFB2.js.map → chunk-TSLPIURC.js.map} +1 -1
- package/dist/chunk-UOPAV3WX.js +75 -0
- package/dist/chunk-UOPAV3WX.js.map +1 -0
- package/dist/chunk-VNIUIQA4.js +398 -0
- package/dist/chunk-VNIUIQA4.js.map +1 -0
- package/dist/chunk-YEUASGDK.js +9 -0
- package/dist/chunk-YEUASGDK.js.map +1 -0
- package/dist/chunk-ZQZF5EXW.js +29 -0
- package/dist/chunk-ZQZF5EXW.js.map +1 -0
- package/dist/config.d.cts +4 -2
- package/dist/config.d.ts +4 -2
- package/dist/duration.cjs +638 -26
- package/dist/duration.cjs.map +1 -1
- package/dist/duration.js +28 -1
- package/dist/format.cjs +833 -35
- package/dist/format.cjs.map +1 -1
- package/dist/format.js +20 -1
- package/dist/index.cjs +7698 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -24
- package/dist/index.d.ts +108 -24
- package/dist/index.js +1536 -58
- package/dist/index.js.map +1 -1
- package/dist/interval.cjs +5480 -35
- package/dist/interval.cjs.map +1 -1
- package/dist/interval.js +46 -1
- package/dist/localeRegistry.cjs +375 -32
- package/dist/localeRegistry.cjs.map +1 -1
- package/dist/localeRegistry.js +11 -1
- package/dist/modApi.d.cts +131 -0
- package/dist/modApi.d.ts +131 -0
- package/dist/parse.cjs +1797 -70
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.js +20 -1
- package/dist/pattern.d.cts +1 -0
- package/dist/pattern.d.ts +1 -0
- package/dist/recurrence.cjs +452 -20
- package/dist/recurrence.cjs.map +1 -1
- package/dist/recurrence.js +19 -1
- package/dist/relativeTime.cjs +268 -21
- package/dist/relativeTime.cjs.map +1 -1
- package/dist/relativeTime.js +14 -1
- package/dist/runtime.d.cts +201 -0
- package/dist/runtime.d.ts +201 -0
- package/dist/timezone.cjs +387 -20
- package/dist/timezone.cjs.map +1 -1
- package/dist/timezone.js +26 -1
- package/package.json +4 -6
- package/scripts/cli.mjs +27 -5
- package/dist/chunk-2JHEV7ZF.js +0 -7
- package/dist/chunk-3MZLTVP3.js +0 -2
- package/dist/chunk-3MZLTVP3.js.map +0 -1
- package/dist/chunk-4JWGUR4O.js +0 -11
- package/dist/chunk-5U5WJ465.js +0 -36
- package/dist/chunk-5U5WJ465.js.map +0 -1
- package/dist/chunk-C5YESWFT.js +0 -2
- package/dist/chunk-C5YESWFT.js.map +0 -1
- package/dist/chunk-C6ZZ62ET.js +0 -2
- package/dist/chunk-C6ZZ62ET.js.map +0 -1
- package/dist/chunk-EHLRZULM.js +0 -5
- package/dist/chunk-F4RGUDA3.js +0 -2
- package/dist/chunk-F4RGUDA3.js.map +0 -1
- package/dist/chunk-J2AQNXF2.js +0 -2
- package/dist/chunk-NBXF7V5B.js +0 -2
- package/dist/chunk-NBXF7V5B.js.map +0 -1
- package/dist/chunk-R52YOKI3.js +0 -7
- package/dist/chunk-R52YOKI3.js.map +0 -1
- package/dist/chunk-UJR6DHXY.js +0 -2
- package/dist/chunk-UJR6DHXY.js.map +0 -1
- package/dist/chunk-VQXUMFB2.js +0 -21
- package/dist/chunk-XH46JNPK.js +0 -2
- package/dist/chunk-XH46JNPK.js.map +0 -1
- package/dist/chunk-YB6YAG7E.js +0 -35
- package/dist/chunk-YB6YAG7E.js.map +0 -1
- package/dist/chunk-ZGMTY52O.js +0 -2
- package/dist/chunk-ZGMTY52O.js.map +0 -1
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var TemporalFmtError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
input;
|
|
5
|
+
format;
|
|
6
|
+
token;
|
|
7
|
+
position;
|
|
8
|
+
expected;
|
|
9
|
+
actual;
|
|
10
|
+
reason;
|
|
11
|
+
constructor(message, fields) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "TemporalFmtError";
|
|
14
|
+
this.code = fields.code;
|
|
15
|
+
this.input = fields.input;
|
|
16
|
+
this.format = fields.format;
|
|
17
|
+
this.token = fields.token;
|
|
18
|
+
this.position = fields.position;
|
|
19
|
+
this.expected = fields.expected;
|
|
20
|
+
this.actual = fields.actual;
|
|
21
|
+
this.reason = fields.reason;
|
|
22
|
+
const capture = Error.captureStackTrace;
|
|
23
|
+
if (typeof capture === "function") {
|
|
24
|
+
capture(this, this.constructor);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// lets callers just call err.toJSON() for logging. plain Error doesn't
|
|
28
|
+
// serialize its non-enumerable fields on its own, so this picks them
|
|
29
|
+
// up explicitly
|
|
30
|
+
toJSON() {
|
|
31
|
+
return {
|
|
32
|
+
name: this.name,
|
|
33
|
+
message: this.message,
|
|
34
|
+
code: this.code,
|
|
35
|
+
input: this.input,
|
|
36
|
+
format: this.format,
|
|
37
|
+
token: this.token,
|
|
38
|
+
position: this.position,
|
|
39
|
+
expected: this.expected,
|
|
40
|
+
actual: this.actual,
|
|
41
|
+
reason: this.reason
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var FormatSyntaxError = class extends TemporalFmtError {
|
|
46
|
+
constructor(fields) {
|
|
47
|
+
const { message, ...rest } = fields;
|
|
48
|
+
super(
|
|
49
|
+
message ?? `format string "${fields.format ?? ""}" has a syntax error${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
50
|
+
{ code: "FORMAT_SYNTAX_ERROR", ...rest }
|
|
51
|
+
);
|
|
52
|
+
this.name = "FormatSyntaxError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var UnknownTokenError = class extends TemporalFmtError {
|
|
56
|
+
constructor(fields) {
|
|
57
|
+
const { message, ...rest } = fields;
|
|
58
|
+
super(
|
|
59
|
+
message ?? `token "${fields.token ?? ""}" is not a recognized temporal-fmt token${fields.format ? ` in format string "${fields.format}"` : ""}.`,
|
|
60
|
+
{ code: "UNKNOWN_TOKEN", ...rest }
|
|
61
|
+
);
|
|
62
|
+
this.name = "UnknownTokenError";
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var ParseMismatchError = class extends TemporalFmtError {
|
|
66
|
+
constructor(fields) {
|
|
67
|
+
const { message, ...rest } = fields;
|
|
68
|
+
super(
|
|
69
|
+
message ?? `input "${fields.input ?? ""}" does not match format "${fields.format ?? ""}"${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
70
|
+
{ code: "PARSE_MISMATCH", ...rest }
|
|
71
|
+
);
|
|
72
|
+
this.name = "ParseMismatchError";
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var InvalidDateError = class extends TemporalFmtError {
|
|
76
|
+
constructor(fields) {
|
|
77
|
+
const { message, ...rest } = fields;
|
|
78
|
+
super(
|
|
79
|
+
message ?? `input "${fields.input ?? ""}" does not describe a valid date${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
80
|
+
{ code: "INVALID_DATE", ...rest }
|
|
81
|
+
);
|
|
82
|
+
this.name = "InvalidDateError";
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
/* c8 ignore start @preserve -- InvalidTimeError is part of the public
|
|
86
|
+
error-class surface (exported from index.ts, code 'INVALID_TIME') but
|
|
87
|
+
nothing in this package actually constructs one. went and checked
|
|
88
|
+
whether it could get wired in the same way InvalidTimeZoneError just
|
|
89
|
+
was (see parse.ts's zzz-validation loop) — does hour/minute/second have
|
|
90
|
+
a post-match semantic range check the way zone ids do? nope.
|
|
91
|
+
pattern.ts's regex fragments for HH/H/hh/h/mm/m/ss/s already enforce
|
|
92
|
+
their valid ranges right at the regex level (e.g. HH is
|
|
93
|
+
'(?:[01]\d|2[0-3])', which literally can't match "99"), so an
|
|
94
|
+
out-of-range time just gets rejected as a plain shape mismatch before
|
|
95
|
+
any semantic check would even run. no live gap to hook this into
|
|
96
|
+
without inventing a redundant check purely to give this class a body.
|
|
97
|
+
leaving it unconstructed until the library actually hits a real
|
|
98
|
+
invalid-time case worth reporting */
|
|
99
|
+
var InvalidTimeError = class extends TemporalFmtError {
|
|
100
|
+
constructor(fields) {
|
|
101
|
+
const { message, ...rest } = fields;
|
|
102
|
+
super(
|
|
103
|
+
message ?? `input "${fields.input ?? ""}" does not describe a valid time${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
104
|
+
{ code: "INVALID_TIME", ...rest }
|
|
105
|
+
);
|
|
106
|
+
this.name = "InvalidTimeError";
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
/* c8 ignore stop @preserve */
|
|
110
|
+
var InvalidOffsetError = class extends TemporalFmtError {
|
|
111
|
+
constructor(fields) {
|
|
112
|
+
const { message, ...rest } = fields;
|
|
113
|
+
super(
|
|
114
|
+
message ?? `offset "${fields.actual ?? ""}" is invalid${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
115
|
+
{ code: "INVALID_OFFSET", ...rest }
|
|
116
|
+
);
|
|
117
|
+
this.name = "InvalidOffsetError";
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var InvalidTimeZoneError = class extends TemporalFmtError {
|
|
121
|
+
constructor(fields) {
|
|
122
|
+
const { message, ...rest } = fields;
|
|
123
|
+
super(
|
|
124
|
+
message ?? `time zone "${fields.actual ?? ""}" is not a recognized IANA time zone or fixed offset${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
125
|
+
{ code: "INVALID_TIME_ZONE", ...rest }
|
|
126
|
+
);
|
|
127
|
+
this.name = "InvalidTimeZoneError";
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
/* c8 ignore start @preserve -- InvalidCalendarError is part of the
|
|
131
|
+
public error-class surface (exported from index.ts, code
|
|
132
|
+
'INVALID_CALENDAR') but same story as InvalidTimeError above — nothing
|
|
133
|
+
in this package constructs one. checked for a wiring opportunity the
|
|
134
|
+
same way: there's no user-supplied calendar identifier anywhere in the
|
|
135
|
+
library that we'd validate against a supported list. resolveCalendar()
|
|
136
|
+
in parse.ts derives the calendar entirely from Intl's own resolution
|
|
137
|
+
of the locale string — it's never handed an arbitrary "calendar" value
|
|
138
|
+
a caller could actually get wrong. no live input to reject here.
|
|
139
|
+
leaving it unconstructed until the library accepts an actual calendar
|
|
140
|
+
parameter that could be invalid */
|
|
141
|
+
var InvalidCalendarError = class extends TemporalFmtError {
|
|
142
|
+
constructor(fields) {
|
|
143
|
+
const { message, ...rest } = fields;
|
|
144
|
+
super(
|
|
145
|
+
message ?? `calendar "${fields.actual ?? ""}" is not supported${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
146
|
+
{ code: "INVALID_CALENDAR", ...rest }
|
|
147
|
+
);
|
|
148
|
+
this.name = "InvalidCalendarError";
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
/* c8 ignore stop @preserve */
|
|
152
|
+
var AmbiguousInputError = class extends TemporalFmtError {
|
|
153
|
+
constructor(fields) {
|
|
154
|
+
const { message, ...rest } = fields;
|
|
155
|
+
super(
|
|
156
|
+
message ?? `input "${fields.input ?? ""}" is ambiguous${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
157
|
+
{ code: "AMBIGUOUS_INPUT", ...rest }
|
|
158
|
+
);
|
|
159
|
+
this.name = "AmbiguousInputError";
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
var InvalidLocaleError = class extends TemporalFmtError {
|
|
163
|
+
constructor(fields) {
|
|
164
|
+
const { message, ...rest } = fields;
|
|
165
|
+
super(
|
|
166
|
+
message ?? `locale "${fields.actual ?? ""}" is not a valid BCP-47 tag${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
167
|
+
{ code: "INVALID_LOCALE", ...rest }
|
|
168
|
+
);
|
|
169
|
+
this.name = "InvalidLocaleError";
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
var InvalidDurationError = class extends TemporalFmtError {
|
|
173
|
+
constructor(fields) {
|
|
174
|
+
const { message, ...rest } = fields;
|
|
175
|
+
super(
|
|
176
|
+
message ?? `duration is invalid${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
177
|
+
{ code: "INVALID_DURATION", ...rest }
|
|
178
|
+
);
|
|
179
|
+
this.name = "InvalidDurationError";
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
/* c8 ignore start @preserve -- unreachable from the current test suite,
|
|
183
|
+
see rationale above */
|
|
184
|
+
function wrapUntypedError(err, context) {
|
|
185
|
+
const msg = err.message;
|
|
186
|
+
if (/unknown token|isn't a recognized token/.test(msg)) {
|
|
187
|
+
return new UnknownTokenError({ input: context.input, format: context.format, reason: msg });
|
|
188
|
+
}
|
|
189
|
+
if (/ambiguous/i.test(msg)) {
|
|
190
|
+
return new AmbiguousInputError({ input: context.input, format: context.format, reason: msg });
|
|
191
|
+
}
|
|
192
|
+
if (/offset/.test(msg) && /out of range|exceeds|doesn't match the shape/i.test(msg)) {
|
|
193
|
+
return new InvalidOffsetError({ input: context.input, format: context.format, reason: msg });
|
|
194
|
+
}
|
|
195
|
+
if (/no valid pattern matches/i.test(msg)) {
|
|
196
|
+
return new ParseMismatchError({ input: context.input, format: context.format, reason: msg });
|
|
197
|
+
}
|
|
198
|
+
if (/doesn't describe a valid date\/time|incomplete date|weekday token|quarter token/.test(msg)) {
|
|
199
|
+
return new InvalidDateError({ input: context.input, format: context.format, reason: msg });
|
|
200
|
+
}
|
|
201
|
+
const lowerMsg = msg.toLowerCase();
|
|
202
|
+
const mentionsLocale = lowerMsg.includes("locale");
|
|
203
|
+
if (mentionsLocale && (lowerMsg.includes("produced no") || lowerMsg.includes("not a valid")) || lowerMsg.includes("cutoffs must be")) {
|
|
204
|
+
return new InvalidLocaleError({ input: context.input, format: context.format, reason: msg });
|
|
205
|
+
}
|
|
206
|
+
if (/format string exceeds maximum length|input exceeds maximum length|unterminated quote|isn't a recognized token/i.test(msg)) {
|
|
207
|
+
return new FormatSyntaxError({ input: context.input, format: context.format, reason: msg });
|
|
208
|
+
}
|
|
209
|
+
return new ParseMismatchError({ input: context.input, format: context.format, reason: msg });
|
|
210
|
+
}
|
|
211
|
+
/* c8 ignore stop @preserve */
|
|
212
|
+
|
|
213
|
+
// src/localeVocab.ts
|
|
214
|
+
var customVocabs = /* @__PURE__ */ new Map();
|
|
215
|
+
var MAX_CUSTOM_VOCABS = 500;
|
|
216
|
+
var MAX_LOCALE_TAG_LENGTH = 256;
|
|
217
|
+
var MAX_VOCAB_ENTRY_LENGTH = 256;
|
|
218
|
+
function assertValidVocab(vocab, locale) {
|
|
219
|
+
const required = [
|
|
220
|
+
{ key: "monthLong", length: 12, label: "long month names" },
|
|
221
|
+
{ key: "monthShort", length: 12, label: "short month names" },
|
|
222
|
+
{ key: "weekdayLong", length: 7, label: "long weekday names" },
|
|
223
|
+
{ key: "weekdayShort", length: 7, label: "short weekday names" },
|
|
224
|
+
{ key: "dayPeriod", length: 2, label: "day period markers (AM/PM-equivalent)" }
|
|
225
|
+
];
|
|
226
|
+
for (const { key, length, label } of required) {
|
|
227
|
+
const value = vocab[key];
|
|
228
|
+
if (value === void 0) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`temporal-fmt: registerLocaleVocab for locale "${locale}" is missing required field "${key}" (${label}).`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
if (!Array.isArray(value)) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`temporal-fmt: registerLocaleVocab for locale "${locale}": "${key}" must be an array, got ${typeof value}.`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
if (value.length !== length) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`temporal-fmt: registerLocaleVocab for locale "${locale}": "${key}" must have exactly ${length} entries (got ${value.length}) \u2014 ${label}.`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
value.forEach((entry, i) => {
|
|
244
|
+
if (typeof entry !== "string" || entry.length === 0) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`temporal-fmt: registerLocaleVocab for locale "${locale}": "${key}[${i}]" must be a non-empty string, got ${String(entry)}.`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
if (entry.length > MAX_VOCAB_ENTRY_LENGTH) {
|
|
250
|
+
throw new RangeError(
|
|
251
|
+
`temporal-fmt: registerLocaleVocab for locale "${locale}": "${key}[${i}]" is too long (maximum ${MAX_VOCAB_ENTRY_LENGTH} characters).`
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
assertNoCollision(vocab.monthLong, "MMMM month", locale);
|
|
257
|
+
assertNoCollision(vocab.monthShort, "MMM month", locale);
|
|
258
|
+
assertNoCollision(vocab.weekdayLong, "EEEE weekday", locale);
|
|
259
|
+
assertNoCollision(vocab.weekdayShort, "EEE weekday", locale);
|
|
260
|
+
if (vocab.dayPeriod[0] === vocab.dayPeriod[1]) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`temporal-fmt: registerLocaleVocab for locale "${locale}": dayPeriod entries must differ (both are "${vocab.dayPeriod[0]}"); otherwise parse() can't tell AM from PM.`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
var onVocabChanged = [];
|
|
267
|
+
function subscribeToVocabChanges(listener) {
|
|
268
|
+
onVocabChanged.push(listener);
|
|
269
|
+
}
|
|
270
|
+
function registerLocaleVocab(locale, vocab) {
|
|
271
|
+
if (typeof locale !== "string" || locale.length === 0) {
|
|
272
|
+
throw new Error(`temporal-fmt: registerLocaleVocab requires a non-empty locale string, got ${String(locale)}.`);
|
|
273
|
+
}
|
|
274
|
+
if (locale.length > MAX_LOCALE_TAG_LENGTH) {
|
|
275
|
+
throw new RangeError(`temporal-fmt: registerLocaleVocab locale is too long (maximum ${MAX_LOCALE_TAG_LENGTH} characters).`);
|
|
276
|
+
}
|
|
277
|
+
assertValidVocab(vocab, locale);
|
|
278
|
+
const cacheKey = canonicalCacheKey(locale);
|
|
279
|
+
if (!customVocabs.has(cacheKey) && customVocabs.size >= MAX_CUSTOM_VOCABS) {
|
|
280
|
+
throw new RangeError(`temporal-fmt: registerLocaleVocab reached the ${MAX_CUSTOM_VOCABS}-locale limit.`);
|
|
281
|
+
}
|
|
282
|
+
customVocabs.set(cacheKey, {
|
|
283
|
+
monthLong: [...vocab.monthLong],
|
|
284
|
+
monthShort: [...vocab.monthShort],
|
|
285
|
+
weekdayLong: [...vocab.weekdayLong],
|
|
286
|
+
weekdayShort: [...vocab.weekdayShort],
|
|
287
|
+
dayPeriod: [...vocab.dayPeriod]
|
|
288
|
+
});
|
|
289
|
+
vocabCache.delete(cacheKey);
|
|
290
|
+
for (const listener of onVocabChanged) listener();
|
|
291
|
+
}
|
|
292
|
+
function normalizeLocaleTag(locale) {
|
|
293
|
+
return locale.replace(/_/g, "-");
|
|
294
|
+
}
|
|
295
|
+
var canonicalKeyCache = /* @__PURE__ */ new Map();
|
|
296
|
+
var MAX_CANONICAL_KEY_CACHE = 500;
|
|
297
|
+
function canonicalCacheKey(locale) {
|
|
298
|
+
const hit = canonicalKeyCache.get(locale);
|
|
299
|
+
if (hit !== void 0) return hit;
|
|
300
|
+
let key;
|
|
301
|
+
try {
|
|
302
|
+
key = new Intl.Locale(locale.replace(/_/g, "-")).toString().toLowerCase();
|
|
303
|
+
} catch {
|
|
304
|
+
key = locale;
|
|
305
|
+
}
|
|
306
|
+
if (canonicalKeyCache.size >= MAX_CANONICAL_KEY_CACHE) {
|
|
307
|
+
const oldestKey = canonicalKeyCache.keys().next().value;
|
|
308
|
+
if (oldestKey !== void 0) canonicalKeyCache.delete(oldestKey);
|
|
309
|
+
}
|
|
310
|
+
canonicalKeyCache.set(locale, key);
|
|
311
|
+
return key;
|
|
312
|
+
}
|
|
313
|
+
function assertValidLocaleTag(locale) {
|
|
314
|
+
try {
|
|
315
|
+
new Intl.Locale(normalizeLocaleTag(locale));
|
|
316
|
+
} catch {
|
|
317
|
+
throw new InvalidLocaleError({
|
|
318
|
+
actual: locale,
|
|
319
|
+
reason: "not a valid BCP-47 locale tag"
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
var vocabCache = /* @__PURE__ */ new Map();
|
|
324
|
+
var MAX_VOCAB_CACHE_SIZE = 500;
|
|
325
|
+
function partValue(formatter, date, type) {
|
|
326
|
+
const parts = formatter.formatToParts(date);
|
|
327
|
+
const index = parts.findIndex((p) => p.type === type);
|
|
328
|
+
/* c8 ignore start @preserve -- defensive guard against a real but
|
|
329
|
+
unreproducible failure mode: an Intl implementation that omits the
|
|
330
|
+
requested part type entirely for some locale. Checked every locale
|
|
331
|
+
with unusual dayPeriod/weekday/month rendering available in this
|
|
332
|
+
runtime's ICU data (ja-JP, zh-CN, th-TH, he-IL, ar-SA, ko-KR, fa-IR)
|
|
333
|
+
against the exact formatter options getLocaleVocab uses (notably
|
|
334
|
+
hour12: true for the dayPeriod formatter, which is what makes every
|
|
335
|
+
locale here actually emit a dayPeriod part — omitting it is what
|
|
336
|
+
produced a false "gap" during investigation). None omit their part
|
|
337
|
+
on this runtime. A different ICU version or a non-Node Intl
|
|
338
|
+
implementation could plausibly behave differently, so this stays a
|
|
339
|
+
real check rather than an assertion. */
|
|
340
|
+
/* c8 ignore start @preserve */
|
|
341
|
+
if (index === -1) {
|
|
342
|
+
throw new InvalidLocaleError({
|
|
343
|
+
message: `temporal-fmt: locale produced no "${type}" part while building match vocabulary.`
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
/* c8 ignore stop @preserve */
|
|
347
|
+
let value = parts[index].value;
|
|
348
|
+
const prev = parts[index - 1];
|
|
349
|
+
const next = parts[index + 1];
|
|
350
|
+
if (prev?.type === "literal" && !/\s/.test(prev.value)) value = prev.value + value;
|
|
351
|
+
if (next?.type === "literal" && !/\s/.test(next.value)) value = value + next.value;
|
|
352
|
+
return value;
|
|
353
|
+
}
|
|
354
|
+
function assertNoCollision(names, label, locale) {
|
|
355
|
+
const seen = /* @__PURE__ */ new Map();
|
|
356
|
+
for (let i = 0; i < names.length; i++) {
|
|
357
|
+
const prior = seen.get(names[i]);
|
|
358
|
+
if (prior !== void 0) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
`temporal-fmt: locale "${locale}" renders ${label} index ${prior} and ${i} identically ("${names[i]}"). parse() can't reliably tell these apart for this locale/token, so this combination isn't supported.`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
seen.set(names[i], i);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function getCustomVocab(locale) {
|
|
367
|
+
const cacheKey = canonicalCacheKey(locale);
|
|
368
|
+
return customVocabs.get(cacheKey);
|
|
369
|
+
}
|
|
370
|
+
function getLocaleVocab(locale) {
|
|
371
|
+
assertValidLocaleTag(locale);
|
|
372
|
+
const cacheKey = canonicalCacheKey(locale);
|
|
373
|
+
const custom = customVocabs.get(cacheKey);
|
|
374
|
+
if (custom) {
|
|
375
|
+
return custom;
|
|
376
|
+
}
|
|
377
|
+
const cached = vocabCache.get(cacheKey);
|
|
378
|
+
if (cached) {
|
|
379
|
+
return cached;
|
|
380
|
+
}
|
|
381
|
+
const intlLocale = normalizeLocaleTag(locale);
|
|
382
|
+
const monthLongFmt = new Intl.DateTimeFormat(intlLocale, { month: "long", timeZone: "UTC" });
|
|
383
|
+
const monthShortFmt = new Intl.DateTimeFormat(intlLocale, { month: "short", timeZone: "UTC" });
|
|
384
|
+
const monthLong = [];
|
|
385
|
+
const monthShort = [];
|
|
386
|
+
for (let m = 0; m < 12; m++) {
|
|
387
|
+
const date = new Date(Date.UTC(2020, m, 1));
|
|
388
|
+
monthLong.push(partValue(monthLongFmt, date, "month"));
|
|
389
|
+
monthShort.push(partValue(monthShortFmt, date, "month"));
|
|
390
|
+
}
|
|
391
|
+
assertNoCollision(monthLong, "MMMM month", locale);
|
|
392
|
+
assertNoCollision(monthShort, "MMM month", locale);
|
|
393
|
+
const weekdayLongFmt = new Intl.DateTimeFormat(intlLocale, { weekday: "long", timeZone: "UTC" });
|
|
394
|
+
const weekdayShortFmt = new Intl.DateTimeFormat(intlLocale, { weekday: "short", timeZone: "UTC" });
|
|
395
|
+
const weekdayLong = [];
|
|
396
|
+
const weekdayShort = [];
|
|
397
|
+
for (let d = 0; d < 7; d++) {
|
|
398
|
+
const date = new Date(Date.UTC(2024, 0, 1 + d));
|
|
399
|
+
weekdayLong.push(partValue(weekdayLongFmt, date, "weekday"));
|
|
400
|
+
weekdayShort.push(partValue(weekdayShortFmt, date, "weekday"));
|
|
401
|
+
}
|
|
402
|
+
assertNoCollision(weekdayLong, "EEEE weekday", locale);
|
|
403
|
+
assertNoCollision(weekdayShort, "EEE weekday", locale);
|
|
404
|
+
const dayPeriodFmt = new Intl.DateTimeFormat(intlLocale, { hour: "numeric", hour12: true, timeZone: "UTC" });
|
|
405
|
+
const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), "dayPeriod");
|
|
406
|
+
const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), "dayPeriod");
|
|
407
|
+
const dayPeriod = [.../* @__PURE__ */ new Set([am, pm])];
|
|
408
|
+
const vocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };
|
|
409
|
+
if (vocabCache.size >= MAX_VOCAB_CACHE_SIZE) {
|
|
410
|
+
const oldestKey = vocabCache.keys().next().value;
|
|
411
|
+
if (oldestKey !== void 0) vocabCache.delete(oldestKey);
|
|
412
|
+
}
|
|
413
|
+
vocabCache.set(cacheKey, vocab);
|
|
414
|
+
return vocab;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export {
|
|
418
|
+
TemporalFmtError,
|
|
419
|
+
FormatSyntaxError,
|
|
420
|
+
UnknownTokenError,
|
|
421
|
+
ParseMismatchError,
|
|
422
|
+
InvalidDateError,
|
|
423
|
+
InvalidTimeError,
|
|
424
|
+
InvalidOffsetError,
|
|
425
|
+
InvalidTimeZoneError,
|
|
426
|
+
InvalidCalendarError,
|
|
427
|
+
AmbiguousInputError,
|
|
428
|
+
InvalidLocaleError,
|
|
429
|
+
InvalidDurationError,
|
|
430
|
+
wrapUntypedError,
|
|
431
|
+
subscribeToVocabChanges,
|
|
432
|
+
registerLocaleVocab,
|
|
433
|
+
normalizeLocaleTag,
|
|
434
|
+
canonicalCacheKey,
|
|
435
|
+
assertValidLocaleTag,
|
|
436
|
+
getCustomVocab,
|
|
437
|
+
getLocaleVocab
|
|
438
|
+
};
|
|
439
|
+
//# sourceMappingURL=chunk-EGUUO6LE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/localeVocab.ts"],"sourcesContent":["// structured error classes for parse()/format() failures.\n//\n// as of 0.9.0 every throw site on the parse/format data path —\n// tokenize.ts, pattern.ts, format.ts, parse.ts (parse/safeParse/tryParse/\n// parseToParts/compileParser), plus the two data-path throws in\n// localeVocab.ts (partValue/assertNoCollision on the getLocaleVocab side)\n// — throws one of these typed classes directly instead of a plain\n// `new Error(message)`. every site we migrated kept its exact pre-0.9.0\n// message text, and since subclasses of TemporalFmtError still pass\n// `instanceof Error` and match the same message regexes (e.g.\n// `/token \"HH\" requires/`), this didn't need a semver-major bump for\n// either of those checks. what DOES break in 0.9.0: if anyone was\n// specifically checking `err.constructor === Error` or `err.name ===\n// 'Error'`, that'll now see a different name (e.g. 'FormatSyntaxError').\n// noted in the 0.9.0 changelog.\n//\n// deliberately did NOT migrate:\n// - localeVocab.ts's registration-time throws (assertValidVocab,\n// registerLocaleVocab itself) — these are config-time API-misuse\n// errors on data a developer hands in once at startup, not runtime\n// parse/format failures, so they don't really fit this module's\n// TemporalFmtErrorCode taxonomy as-is. assertNoCollision is shared\n// between the registration path and the data path (getLocaleVocab)\n// though, so its throw stays a plain Error for both until that gets\n// split apart — see the tracking note where it's called.\n//\n// wrapUntypedError() below is still around for localeVocab.ts's\n// registration throws, and for anything a caller passes into\n// safeParse()/tryParse() from outside this package. on the data path\n// itself, safeParse's `if (err instanceof TemporalFmtError) return { ok:\n// false, error: err }` catches everything before it'd even reach the\n// classifier here, so honestly the regex branches below are dead for\n// parse/format/tokenize/pattern at this point — kept as the fallback for\n// whatever hasn't been migrated to a typed throw site yet\n\nexport type TemporalFmtErrorCode =\n | 'FORMAT_SYNTAX_ERROR'\n | 'UNKNOWN_TOKEN'\n | 'PARSE_MISMATCH'\n | 'INVALID_DATE'\n | 'INVALID_TIME'\n | 'INVALID_OFFSET'\n | 'INVALID_TIME_ZONE'\n | 'INVALID_CALENDAR'\n | 'AMBIGUOUS_INPUT'\n | 'INVALID_LOCALE'\n | 'INVALID_DURATION';\n\nexport interface TemporalFmtErrorFields {\n code: TemporalFmtErrorCode;\n input?: string;\n format?: string;\n token?: string;\n position?: number;\n expected?: string;\n actual?: string;\n reason?: string;\n}\n\n// base class. `message` is the human-readable summary, the structured\n// fields are the machine-readable bits a linter or codemod would report\n// on. calling Error.captureStackTrace manually (where it exists) so the\n// stack points at wherever this got thrown from, not at this constructor\n// — same trick Node uses for its own error classes\nexport class TemporalFmtError extends Error {\n readonly code: TemporalFmtErrorCode;\n readonly input?: string;\n readonly format?: string;\n readonly token?: string;\n readonly position?: number;\n readonly expected?: string;\n readonly actual?: string;\n readonly reason?: string;\n\n constructor(message: string, fields: TemporalFmtErrorFields) {\n super(message);\n this.name = 'TemporalFmtError';\n this.code = fields.code;\n this.input = fields.input;\n this.format = fields.format;\n this.token = fields.token;\n this.position = fields.position;\n this.expected = fields.expected;\n this.actual = fields.actual;\n this.reason = fields.reason;\n // don't want the stack pointing at this constructor line — captureStackTrace\n // is V8-only though, so on other engines callers just get the default\n // stack (pointing here) since there's nothing better to do about it\n const capture = (Error as unknown as { captureStackTrace?: (target: Error, ctor?: Function) => void }).captureStackTrace;\n if (typeof capture === 'function') {\n capture(this, this.constructor);\n }\n }\n\n // lets callers just call err.toJSON() for logging. plain Error doesn't\n // serialize its non-enumerable fields on its own, so this picks them\n // up explicitly\n toJSON(): TemporalFmtErrorFields & { name: string; message: string } {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n input: this.input,\n format: this.format,\n token: this.token,\n position: this.position,\n expected: this.expected,\n actual: this.actual,\n reason: this.reason,\n };\n }\n}\n\n// each subclass just fixes `code` so callers can switch on it without\n// having to re-check message text. the constructor only takes the\n// fields that actually vary per call; `code` and the default message\n// template come from the subclass itself\n\nexport class FormatSyntaxError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `format string \"${fields.format ?? ''}\" has a syntax error${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'FORMAT_SYNTAX_ERROR', ...rest },\n );\n this.name = 'FormatSyntaxError';\n }\n}\n\nexport class UnknownTokenError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `token \"${fields.token ?? ''}\" is not a recognized temporal-fmt token${fields.format ? ` in format string \"${fields.format}\"` : ''}.`,\n { code: 'UNKNOWN_TOKEN', ...rest },\n );\n this.name = 'UnknownTokenError';\n }\n}\n\nexport class ParseMismatchError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" does not match format \"${fields.format ?? ''}\"${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'PARSE_MISMATCH', ...rest },\n );\n this.name = 'ParseMismatchError';\n }\n}\n\nexport class InvalidDateError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" does not describe a valid date${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_DATE', ...rest },\n );\n this.name = 'InvalidDateError';\n }\n}\n\n/* c8 ignore start @preserve -- InvalidTimeError is part of the public\n error-class surface (exported from index.ts, code 'INVALID_TIME') but\n nothing in this package actually constructs one. went and checked\n whether it could get wired in the same way InvalidTimeZoneError just\n was (see parse.ts's zzz-validation loop) — does hour/minute/second have\n a post-match semantic range check the way zone ids do? nope.\n pattern.ts's regex fragments for HH/H/hh/h/mm/m/ss/s already enforce\n their valid ranges right at the regex level (e.g. HH is\n '(?:[01]\\d|2[0-3])', which literally can't match \"99\"), so an\n out-of-range time just gets rejected as a plain shape mismatch before\n any semantic check would even run. no live gap to hook this into\n without inventing a redundant check purely to give this class a body.\n leaving it unconstructed until the library actually hits a real\n invalid-time case worth reporting */\nexport class InvalidTimeError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" does not describe a valid time${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_TIME', ...rest },\n );\n this.name = 'InvalidTimeError';\n }\n}\n/* c8 ignore stop @preserve */\n\nexport class InvalidOffsetError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `offset \"${fields.actual ?? ''}\" is invalid${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_OFFSET', ...rest },\n );\n this.name = 'InvalidOffsetError';\n }\n}\n\nexport class InvalidTimeZoneError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `time zone \"${fields.actual ?? ''}\" is not a recognized IANA time zone or fixed offset${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_TIME_ZONE', ...rest },\n );\n this.name = 'InvalidTimeZoneError';\n }\n}\n\n/* c8 ignore start @preserve -- InvalidCalendarError is part of the\n public error-class surface (exported from index.ts, code\n 'INVALID_CALENDAR') but same story as InvalidTimeError above — nothing\n in this package constructs one. checked for a wiring opportunity the\n same way: there's no user-supplied calendar identifier anywhere in the\n library that we'd validate against a supported list. resolveCalendar()\n in parse.ts derives the calendar entirely from Intl's own resolution\n of the locale string — it's never handed an arbitrary \"calendar\" value\n a caller could actually get wrong. no live input to reject here.\n leaving it unconstructed until the library accepts an actual calendar\n parameter that could be invalid */\nexport class InvalidCalendarError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `calendar \"${fields.actual ?? ''}\" is not supported${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_CALENDAR', ...rest },\n );\n this.name = 'InvalidCalendarError';\n }\n}\n/* c8 ignore stop @preserve */\n\nexport class AmbiguousInputError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" is ambiguous${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'AMBIGUOUS_INPUT', ...rest },\n );\n this.name = 'AmbiguousInputError';\n }\n}\n\nexport class InvalidLocaleError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `locale \"${fields.actual ?? ''}\" is not a valid BCP-47 tag${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_LOCALE', ...rest },\n );\n this.name = 'InvalidLocaleError';\n }\n}\n\nexport class InvalidDurationError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `duration is invalid${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_DURATION', ...rest },\n );\n this.name = 'InvalidDurationError';\n }\n}\n\n// wraps a plain Error thrown from a code path that hasn't been migrated\n// to typed errors yet. keeps the original message around in `reason` so\n// callers reading the typed surface still see what actually failed.\n// used by safeParse() in parse.ts.\n//\n// after the 0.9.0 migration, every throw site on the parse/format data\n// path throws a TemporalFmtError directly, so safeParse's `instanceof\n// TemporalFmtError` check always passes before this function would even\n// get called — nothing in the current test suite actually reaches any\n// branch below. kept as the safety net for whenever someone adds an\n// unmigrated throw site down the line (see the c8-ignored call in\n// parse.ts's safeParse) — same reasoning as that call site: removing\n// this would silently break the \"safeParse always returns a\n// TemporalFmtError\" contract the moment anyone adds a bare\n// `throw new Error(...)` without wiring up a typed class for it\n/* c8 ignore start @preserve -- unreachable from the current test suite,\n see rationale above */\nexport function wrapUntypedError(err: Error, context: { input?: string; format?: string }): TemporalFmtError {\n // try to classify by looking at the message — covers the existing\n // parse()/format() throw sites without having to touch them. anything\n // that doesn't match a known pattern falls through to a generic\n // ParseMismatchError, still with the structured fields intact\n const msg = err.message;\n if (/unknown token|isn't a recognized token/.test(msg)) {\n return new UnknownTokenError({ input: context.input, format: context.format, reason: msg });\n }\n if (/ambiguous/i.test(msg)) {\n return new AmbiguousInputError({ input: context.input, format: context.format, reason: msg });\n }\n if (/offset/.test(msg) && /out of range|exceeds|doesn't match the shape/i.test(msg)) {\n return new InvalidOffsetError({ input: context.input, format: context.format, reason: msg });\n }\n if (/no valid pattern matches/i.test(msg)) {\n return new ParseMismatchError({ input: context.input, format: context.format, reason: msg });\n }\n if (/doesn't describe a valid date\\/time|incomplete date|weekday token|quarter token/.test(msg)) {\n return new InvalidDateError({ input: context.input, format: context.format, reason: msg });\n }\n const lowerMsg = msg.toLowerCase();\n const mentionsLocale = lowerMsg.includes('locale');\n if (\n (mentionsLocale && (lowerMsg.includes('produced no') || lowerMsg.includes('not a valid'))) ||\n lowerMsg.includes('cutoffs must be')\n ) {\n return new InvalidLocaleError({ input: context.input, format: context.format, reason: msg });\n }\n if (/format string exceeds maximum length|input exceeds maximum length|unterminated quote|isn't a recognized token/i.test(msg)) {\n return new FormatSyntaxError({ input: context.input, format: context.format, reason: msg });\n }\n return new ParseMismatchError({ input: context.input, format: context.format, reason: msg });\n}\n/* c8 ignore stop @preserve */","// Name lists for the locale-aware tokens (MMMM, MMM, EEEE, EEE, a). Each\n// list is small and fixed (12 months, 7 weekdays, 2 day periods), so we\n// generate the real Intl strings for a locale once and cache them.\n\nimport { InvalidLocaleError } from './errors.js';\n\nexport interface LocaleVocab {\n monthLong: string[]; // index 0 = January\n monthShort: string[];\n weekdayLong: string[]; // index 0 = Monday, per Temporal's dayOfWeek numbering\n weekdayShort: string[];\n dayPeriod: string[]; // typically [AM-ish, PM-ish], deduped\n}\n\n// Custom vocabs registered by callers for locales Intl doesn't cover well\n// (e.g. a 13-month Hebrew leap year, where Intl's 12-month vocabulary\n// silently loses a whole month). Keyed by canonical cache key so the\n// same locale string spelling variants fold together — same convention\n// as the Intl-derived vocab cache above.\nconst customVocabs = new Map<string, LocaleVocab>();\nconst MAX_CUSTOM_VOCABS = 500;\nconst MAX_LOCALE_TAG_LENGTH = 256;\nconst MAX_VOCAB_ENTRY_LENGTH = 256;\n\nfunction assertValidVocab(vocab: Partial<LocaleVocab>, locale: string): void {\n // Strict shape validation at registration time, not lazily on first\n // use — the README's promise is that a malformed registration throws\n // descriptively here, rather than failing later inside format()/parse()\n // with a confusing \"no month part\" or wrong-month error the caller\n // can't trace back to the bad registration.\n const required: Array<{ key: keyof LocaleVocab; length: number; label: string }> = [\n { key: 'monthLong', length: 12, label: 'long month names' },\n { key: 'monthShort', length: 12, label: 'short month names' },\n { key: 'weekdayLong', length: 7, label: 'long weekday names' },\n { key: 'weekdayShort', length: 7, label: 'short weekday names' },\n { key: 'dayPeriod', length: 2, label: 'day period markers (AM/PM-equivalent)' },\n ];\n\n for (const { key, length, label } of required) {\n const value = vocab[key];\n if (value === undefined) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\" is missing required field \"${key}\" (${label}).`\n );\n }\n if (!Array.isArray(value)) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}\" must be an array, got ${typeof value}.`\n );\n }\n if (value.length !== length) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}\" must have exactly ${length} entries (got ${value.length}) — ${label}.`\n );\n }\n value.forEach((entry, i) => {\n if (typeof entry !== 'string' || entry.length === 0) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}[${i}]\" must be a non-empty string, got ${String(entry)}.`\n );\n }\n if (entry.length > MAX_VOCAB_ENTRY_LENGTH) {\n throw new RangeError(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}[${i}]\" is too long (maximum ${MAX_VOCAB_ENTRY_LENGTH} characters).`\n );\n }\n });\n }\n\n // Reuse the same collision check the Intl-derived path uses — a\n // duplicate month name is just as ambiguous when supplied by a caller\n // as when produced by Intl.\n assertNoCollision(vocab.monthLong!, 'MMMM month', locale);\n assertNoCollision(vocab.monthShort!, 'MMM month', locale);\n assertNoCollision(vocab.weekdayLong!, 'EEEE weekday', locale);\n assertNoCollision(vocab.weekdayShort!, 'EEE weekday', locale);\n\n // dayPeriod entries must differ from each other, or parse()'s\n // isPM check (raw === vocab.dayPeriod[1]) can never return true and\n // every 12-hour parse silently resolves to AM. The Intl-derived path\n // dedupes a same-AM/PM collision to length 1, but a caller passing\n // both entries identical is a real bug to surface — not something to\n // dedupe around.\n if (vocab.dayPeriod![0] === vocab.dayPeriod![1]) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": dayPeriod entries must differ ` +\n `(both are \"${vocab.dayPeriod![0]}\"); otherwise parse() can't tell AM from PM.`\n );\n }\n}\n\n// Anything that caches a result derived from *which* vocabulary is\n// active for a locale (right now: parse.ts's compiled pattern cache,\n// whose MMMM/MMM/EEEE/EEE/a fragments embed the vocab's alternations)\n// subscribes here so it gets invalidated when a registration swaps the\n// vocab — mirrors temporalProvider.ts's subscribeToTemporalChanges.\nconst onVocabChanged: Array<() => void> = [];\n\nexport function subscribeToVocabChanges(listener: () => void): void {\n onVocabChanged.push(listener);\n}\n\n/**\n * Supply a custom month/weekday/day-period vocabulary for a locale key,\n * overriding the Intl-derived vocab this library would otherwise build\n * for that key. Useful for locales Intl doesn't cover well — the README's\n * known-limitations section calls out the Hebrew leap-month gap as a\n * specific case this addresses.\n *\n * Throws descriptively on malformed input (wrong array lengths, empty\n * strings, duplicate entries, missing fields) rather than failing later\n * during format/parse.\n *\n * Registered vocab takes precedence over the Intl-derived vocab for that\n * locale key, including for already-cached entries — registering\n * invalidates the prior cache entry for that locale so the next call\n * picks up the new vocab.\n *\n * @example\n * registerLocaleVocab('en-u-ca-hebrew-leap', {\n * monthLong: ['Nisan','Iyar','Sivan','Tammuz','Av','Elul','Tishrei','Marcheshvan','Kislev','Tevet','Shevat','Adar I','Adar II'],\n * monthShort: ['Nis','Iyy','Siv','Tam','Av','Elu','Tish','Chesh','Kis','Tev','Shv','Ad1','Ad2'],\n * weekdayLong: ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'],\n * weekdayShort: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],\n * dayPeriod: ['AM','PM'],\n * });\n */\nexport function registerLocaleVocab(locale: string, vocab: Partial<LocaleVocab>): void {\n if (typeof locale !== 'string' || locale.length === 0) {\n throw new Error(`temporal-fmt: registerLocaleVocab requires a non-empty locale string, got ${String(locale)}.`);\n }\n if (locale.length > MAX_LOCALE_TAG_LENGTH) {\n throw new RangeError(`temporal-fmt: registerLocaleVocab locale is too long (maximum ${MAX_LOCALE_TAG_LENGTH} characters).`);\n }\n assertValidVocab(vocab, locale);\n\n const cacheKey = canonicalCacheKey(locale);\n if (!customVocabs.has(cacheKey) && customVocabs.size >= MAX_CUSTOM_VOCABS) {\n throw new RangeError(`temporal-fmt: registerLocaleVocab reached the ${MAX_CUSTOM_VOCABS}-locale limit.`);\n }\n customVocabs.set(cacheKey, {\n monthLong: [...vocab.monthLong!],\n monthShort: [...vocab.monthShort!],\n weekdayLong: [...vocab.weekdayLong!],\n weekdayShort: [...vocab.weekdayShort!],\n dayPeriod: [...vocab.dayPeriod!],\n });\n // Invalidate the Intl-derived cache entry so any prior format/parse\n // result cached for this locale is rebuilt against the new vocab. Not\n // strictly necessary (getLocaleVocab checks customVocabs first), but\n // cheap and keeps the two caches from drifting out of sync.\n vocabCache.delete(cacheKey);\n // Also invalidate downstream caches that baked the previous vocab in\n // at build time (parse.ts's pattern cache is the one that matters:\n // without this, a cached pattern would keep matching the OLD month /\n // weekday names while format() renders the new ones, so the library's\n // own format() output would fail to parse back).\n for (const listener of onVocabChanged) listener();\n}\n\n// Every locale-keyed cache in this library (this one, formatterCache in\n// tokens.ts, patternCache/calendarCache in parse.ts) used to key on the\n// exact locale string a caller passed in. Intl treats spelling variants of\n// the same locale as equivalent ('en-US' / 'en-us' / 'en_US' all resolve\n// the same way), but a plain string-keyed Map doesn't — so callers mixing\n// spellings for what's really one locale would silently fragment across\n// separate cache entries instead of sharing one, making the bounded\n// eviction limits (MAX_*_CACHE_SIZE) less effective than they look. This\n// doesn't change any cache's *correctness* (each entry is still built from\n// -- and valid for -- whatever locale string produced it), only how many\n// distinct entries equivalent spellings end up costing. Falls back to the\n// original string on a malformed/unrecognized tag rather than throwing —\n// cache-key normalization shouldn't be where a bad locale first surfaces\n// as an error; whatever actually calls `new Intl.DateTimeFormat(locale)`\n// downstream is the right place for that.\n// Intl constructors (DateTimeFormat/NumberFormat/RelativeTimeFormat)\n// reject underscore-separated tags like 'en_US' outright, while\n// canonicalCacheKey and every locale-parsing path in this library\n// tolerates them by normalizing to BCP-47 hyphens. Centralize that\n// normalization so every `new Intl.*(locale)` construction site\n// accepts the same spellings the cache keys do. Genuinely malformed\n// tags still throw downstream, unchanged.\nexport function normalizeLocaleTag(locale: string): string {\n return locale.replace(/_/g, '-');\n}\n\n// Memoized so hot paths (parse()'s resolveCalendar keys off this on\n// every call, and every locale-keyed cache re-derives it) don't build a\n// fresh Intl.Locale per invocation — construction is comparatively\n// expensive. Bounded like every other cache in this library.\nconst canonicalKeyCache = new Map<string, string>();\nconst MAX_CANONICAL_KEY_CACHE = 500;\n\nexport function canonicalCacheKey(locale: string): string {\n const hit = canonicalKeyCache.get(locale);\n if (hit !== undefined) return hit;\n let key: string;\n try {\n // Intl.Locale requires BCP-47 hyphens and rejects underscore-separated\n // tags like 'en_US' outright (RangeError), rather than normalizing\n // them — so without this replace, that spelling would just fall\n // through to the catch below and never fold with 'en-US'.\n key = new Intl.Locale(locale.replace(/_/g, '-')).toString().toLowerCase();\n } catch {\n // Malformed tag: key on the raw string. Callers that must reject\n // malformed tags do it via assertValidLocaleTag() before/instead of\n // relying on this function — cache-key normalization isn't where a\n // bad locale should surface as an error (unchanged behavior).\n key = locale;\n }\n if (canonicalKeyCache.size >= MAX_CANONICAL_KEY_CACHE) {\n const oldestKey = canonicalKeyCache.keys().next().value;\n if (oldestKey !== undefined) canonicalKeyCache.delete(oldestKey);\n }\n canonicalKeyCache.set(locale, key);\n return key;\n}\n\n// Single validation choke point for locale tags at public boundaries.\n// Intl constructors reject malformed tags with a bare engine RangeError\n// (\"Incorrect locale information provided\") that carries none of the\n// library's structured error context; this rethrows as the typed\n// InvalidLocaleError with the offending tag in its fields. Accepts the\n// same spellings as canonicalCacheKey (underscore tags normalized).\nexport function assertValidLocaleTag(locale: string): void {\n try {\n new Intl.Locale(normalizeLocaleTag(locale));\n } catch {\n throw new InvalidLocaleError({\n actual: locale,\n reason: 'not a valid BCP-47 locale tag',\n });\n }\n}\n\nconst vocabCache = new Map<string, LocaleVocab>();\nconst MAX_VOCAB_CACHE_SIZE = 500;\n\n// Some locales (ja-JP) split a field across two parts — month \"8\" plus a\n// counter suffix \"月\" as a separate sibling \"literal\" — while format()'s\n// tokens go through toLocaleString(), which concatenates everything into\n// \"8月\". Reading only the type-tagged part used to drop that suffix, so\n// this locale's vocab never matched what format() actually produced.\n// Only merges *adjacent* literals, not the whole string, since the\n// dayPeriod/weekday formatters below carry an extra hour part that a\n// join-everything approach would wrongly absorb.\nfunction partValue(formatter: Intl.DateTimeFormat, date: Date, type: Intl.DateTimeFormatPartTypes): string {\n const parts = formatter.formatToParts(date);\n const index = parts.findIndex((p) => p.type === type);\n /* c8 ignore start @preserve -- defensive guard against a real but\n unreproducible failure mode: an Intl implementation that omits the\n requested part type entirely for some locale. Checked every locale\n with unusual dayPeriod/weekday/month rendering available in this\n runtime's ICU data (ja-JP, zh-CN, th-TH, he-IL, ar-SA, ko-KR, fa-IR)\n against the exact formatter options getLocaleVocab uses (notably\n hour12: true for the dayPeriod formatter, which is what makes every\n locale here actually emit a dayPeriod part — omitting it is what\n produced a false \"gap\" during investigation). None omit their part\n on this runtime. A different ICU version or a non-Node Intl\n implementation could plausibly behave differently, so this stays a\n real check rather than an assertion. */\n /* c8 ignore start @preserve */\n if (index === -1) {\n throw new InvalidLocaleError({\n message: `temporal-fmt: locale produced no \"${type}\" part while building match vocabulary.`,\n });\n }\n /* c8 ignore stop @preserve */\n let value = parts[index]!.value;\n const prev = parts[index - 1];\n const next = parts[index + 1];\n // skip whitespace literals (the separator before \"AM\") — only a\n // no-space suffix like ja-JP's \"月\" should get folded in\n if (prev?.type === 'literal' && !/\\s/.test(prev.value)) value = prev.value + value;\n if (next?.type === 'literal' && !/\\s/.test(next.value)) value = value + next.value;\n return value;\n}\n\n// Two entries rendering identically means parse()'s reverse lookup\n// (indexOf) can never tell them apart. Weekday collisions already surface\n// via parse()'s dayOfWeek cross-check, but with a confusing same-string\n// error; months have no equivalent cross-check, so a collision there would\n// otherwise resolve silently to the wrong month. Catching both here, once\n// at build time, gives one clear error instead.\nfunction assertNoCollision(names: string[], label: string, locale: string): void {\n const seen = new Map<string, number>();\n for (let i = 0; i < names.length; i++) {\n const prior = seen.get(names[i]!);\n if (prior !== undefined) {\n // Not migrated to a typed error: this function is shared between\n // getLocaleVocab's Intl-derived path (data-path error, would be a\n // good InvalidLocaleError candidate) and assertValidVocab's\n // registration-time check (out of scope for this pass — see the\n // localeVocab.ts registration-error follow-up). Splitting this into\n // two near-duplicate functions just to route error types\n // differently isn't worth it for one throw; revisit together with\n // the registration-error work instead.\n throw new Error(\n `temporal-fmt: locale \"${locale}\" renders ${label} index ${prior} and ${i} identically ` +\n `(\"${names[i]}\"). parse() can't reliably tell these apart for this locale/token, so this ` +\n `combination isn't supported.`\n );\n }\n seen.set(names[i]!, i);\n }\n}\n\n// Exposed for tokens.ts: when a custom vocab is registered for this\n// locale, format()'s locale-aware tokens (MMMM/MMM/EEEE/EEE/a) read\n// straight from the registered array instead of going through\n// Intl.DateTimeFormat. Without this override, format() would silently\n// keep producing Intl's strings while parse() matched against the\n// registered vocab — the two would round-trip-fail.\nexport function getCustomVocab(locale: string): LocaleVocab | undefined {\n const cacheKey = canonicalCacheKey(locale);\n return customVocabs.get(cacheKey);\n}\n\nexport function getLocaleVocab(locale: string): LocaleVocab {\n // Validate before building vocab: a malformed tag used to surface as a\n // raw RangeError from the first new Intl.DateTimeFormat below.\n assertValidLocaleTag(locale);\n const cacheKey = canonicalCacheKey(locale);\n // Registered vocabs take precedence over the Intl-derived one — this\n // is the override mechanism registerLocaleVocab() promises. Checking\n // here, before the Intl cache lookup, means a registration that\n // happens *after* the first Intl-derived vocab was built still takes\n // effect on the next call.\n const custom = customVocabs.get(cacheKey);\n if (custom) {\n return custom;\n }\n const cached = vocabCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const intlLocale = normalizeLocaleTag(locale);\n const monthLongFmt = new Intl.DateTimeFormat(intlLocale, { month: 'long', timeZone: 'UTC' });\n const monthShortFmt = new Intl.DateTimeFormat(intlLocale, { month: 'short', timeZone: 'UTC' });\n const monthLong: string[] = [];\n const monthShort: string[] = [];\n for (let m = 0; m < 12; m++) {\n const date = new Date(Date.UTC(2020, m, 1));\n monthLong.push(partValue(monthLongFmt, date, 'month'));\n monthShort.push(partValue(monthShortFmt, date, 'month'));\n }\n assertNoCollision(monthLong, 'MMMM month', locale);\n assertNoCollision(monthShort, 'MMM month', locale);\n\n const weekdayLongFmt = new Intl.DateTimeFormat(intlLocale, { weekday: 'long', timeZone: 'UTC' });\n const weekdayShortFmt = new Intl.DateTimeFormat(intlLocale, { weekday: 'short', timeZone: 'UTC' });\n const weekdayLong: string[] = [];\n const weekdayShort: string[] = [];\n // 2024-01-01 is a Monday (UTC) — walk 7 days from there for weekday names\n for (let d = 0; d < 7; d++) {\n const date = new Date(Date.UTC(2024, 0, 1 + d));\n weekdayLong.push(partValue(weekdayLongFmt, date, 'weekday'));\n weekdayShort.push(partValue(weekdayShortFmt, date, 'weekday'));\n }\n // redundant with parse()'s dayOfWeek cross-check, but gives a clearer error\n assertNoCollision(weekdayLong, 'EEEE weekday', locale);\n assertNoCollision(weekdayShort, 'EEE weekday', locale);\n\n const dayPeriodFmt = new Intl.DateTimeFormat(intlLocale, { hour: 'numeric', hour12: true, timeZone: 'UTC' });\n const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), 'dayPeriod');\n const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), 'dayPeriod');\n const dayPeriod = [...new Set([am, pm])];\n\n const vocab: LocaleVocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };\n if (vocabCache.size >= MAX_VOCAB_CACHE_SIZE) {\n const oldestKey = vocabCache.keys().next().value;\n if (oldestKey !== undefined) vocabCache.delete(oldestKey);\n }\n vocabCache.set(cacheKey, vocab);\n return vocab;\n}"],"mappings":"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","names":[]}
|