temporal-fmt 0.8.982 → 0.9.0

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.
Files changed (45) hide show
  1. package/dist/{chunk-FRS42YLM.js → chunk-AXQLUIDY.js} +43 -28
  2. package/dist/chunk-AXQLUIDY.js.map +1 -0
  3. package/dist/{chunk-KCSGCK7L.js → chunk-CKVHXFX7.js} +162 -87
  4. package/dist/chunk-CKVHXFX7.js.map +1 -0
  5. package/dist/{chunk-B3BFPB3T.js → chunk-FQA4XJME.js} +2 -2
  6. package/dist/{chunk-BNIIELG5.js → chunk-FTMLZ4EN.js} +2 -2
  7. package/dist/{chunk-YNXTPXB5.js → chunk-JCIMP43G.js} +3 -5
  8. package/dist/{chunk-YNXTPXB5.js.map → chunk-JCIMP43G.js.map} +1 -1
  9. package/dist/{chunk-RTPA5A5T.js → chunk-KDYK72Z2.js} +3 -5
  10. package/dist/{chunk-RTPA5A5T.js.map → chunk-KDYK72Z2.js.map} +1 -1
  11. package/dist/{chunk-6MIVOWWX.js → chunk-M33NSC4E.js} +15 -8
  12. package/dist/chunk-M33NSC4E.js.map +1 -0
  13. package/dist/{chunk-USONQ7MV.js → chunk-O6GVMBLW.js} +182 -12
  14. package/dist/chunk-O6GVMBLW.js.map +1 -0
  15. package/dist/{chunk-XIKLQMF7.js → chunk-VSUCBUTC.js} +2 -2
  16. package/dist/duration.cjs +93 -90
  17. package/dist/duration.cjs.map +1 -1
  18. package/dist/duration.js +2 -3
  19. package/dist/format.cjs +148 -109
  20. package/dist/format.cjs.map +1 -1
  21. package/dist/format.js +4 -5
  22. package/dist/index.cjs +548 -466
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.js +10 -12
  25. package/dist/index.js.map +1 -1
  26. package/dist/interval.cjs +132 -98
  27. package/dist/interval.cjs.map +1 -1
  28. package/dist/interval.js +5 -6
  29. package/dist/localeRegistry.cjs +86 -81
  30. package/dist/localeRegistry.cjs.map +1 -1
  31. package/dist/localeRegistry.js +2 -3
  32. package/dist/parse.cjs +342 -271
  33. package/dist/parse.cjs.map +1 -1
  34. package/dist/parse.js +4 -5
  35. package/dist/relativeTime.js +3 -3
  36. package/package.json +1 -1
  37. package/dist/chunk-3RLYULUQ.js +0 -178
  38. package/dist/chunk-3RLYULUQ.js.map +0 -1
  39. package/dist/chunk-6MIVOWWX.js.map +0 -1
  40. package/dist/chunk-FRS42YLM.js.map +0 -1
  41. package/dist/chunk-KCSGCK7L.js.map +0 -1
  42. package/dist/chunk-USONQ7MV.js.map +0 -1
  43. /package/dist/{chunk-B3BFPB3T.js.map → chunk-FQA4XJME.js.map} +0 -0
  44. /package/dist/{chunk-BNIIELG5.js.map → chunk-FTMLZ4EN.js.map} +0 -0
  45. /package/dist/{chunk-XIKLQMF7.js.map → chunk-VSUCBUTC.js.map} +0 -0
@@ -1,14 +1,18 @@
1
1
  import {
2
2
  applyNumbering,
3
3
  tokenize
4
- } from "./chunk-6MIVOWWX.js";
4
+ } from "./chunk-M33NSC4E.js";
5
5
  import {
6
6
  DEFAULT_LOCALE,
7
7
  TOKENS
8
- } from "./chunk-XIKLQMF7.js";
8
+ } from "./chunk-VSUCBUTC.js";
9
9
  import {
10
10
  MAX_FORMAT_LENGTH
11
11
  } from "./chunk-EDUFSRWJ.js";
12
+ import {
13
+ FormatSyntaxError,
14
+ UnknownTokenError
15
+ } from "./chunk-O6GVMBLW.js";
12
16
 
13
17
  // src/format.ts
14
18
  var HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));
@@ -27,9 +31,10 @@ function getPieces(formatStr) {
27
31
  }
28
32
  function format(temporal, formatStr, options = {}) {
29
33
  if (formatStr.length > MAX_FORMAT_LENGTH) {
30
- throw new Error(
31
- `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
32
- );
34
+ throw new FormatSyntaxError({
35
+ format: formatStr,
36
+ message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
37
+ });
33
38
  }
34
39
  const locale = options.locale ?? DEFAULT_LOCALE;
35
40
  const pieces = getPieces(formatStr);
@@ -46,13 +51,15 @@ function format(temporal, formatStr, options = {}) {
46
51
  here. Same defensive-guard category as the twin checks in
47
52
  analyze.ts/formatDuration.ts. */
48
53
  if (!handler) {
49
- throw new Error(`temporal-fmt: unknown token "${piece.value}"`);
54
+ throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token "${piece.value}"` });
50
55
  }
51
56
  /* c8 ignore stop @preserve */
52
57
  if (temporal[handler.field] === void 0) {
53
- throw new Error(
54
- `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
55
- );
58
+ throw new FormatSyntaxError({
59
+ format: formatStr,
60
+ token: piece.value,
61
+ message: `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
62
+ });
56
63
  }
57
64
  result += handler.fn(temporal, locale);
58
65
  }
@@ -60,9 +67,10 @@ function format(temporal, formatStr, options = {}) {
60
67
  }
61
68
  function formatToParts(temporal, formatStr, options = {}) {
62
69
  if (formatStr.length > MAX_FORMAT_LENGTH) {
63
- throw new Error(
64
- `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
65
- );
70
+ throw new FormatSyntaxError({
71
+ format: formatStr,
72
+ message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
73
+ });
66
74
  }
67
75
  const locale = options.locale ?? DEFAULT_LOCALE;
68
76
  const pieces = getPieces(formatStr);
@@ -87,13 +95,15 @@ function formatToParts(temporal, formatStr, options = {}) {
87
95
  const handler = HANDLER_BY_TOKEN.get(piece.value);
88
96
  /* c8 ignore start @preserve -- unreachable, see the note in format() above */
89
97
  if (!handler) {
90
- throw new Error(`temporal-fmt: unknown token "${piece.value}"`);
98
+ throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token "${piece.value}"` });
91
99
  }
92
100
  /* c8 ignore stop @preserve */
93
101
  if (temporal[handler.field] === void 0) {
94
- throw new Error(
95
- `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
96
- );
102
+ throw new FormatSyntaxError({
103
+ format: formatStr,
104
+ token: piece.value,
105
+ message: `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
106
+ });
97
107
  }
98
108
  result.push({ type: "token", value: applyNumbering(handler.fn(temporal, locale), options), token: piece.value });
99
109
  }
@@ -101,9 +111,10 @@ function formatToParts(temporal, formatStr, options = {}) {
101
111
  }
102
112
  function compileFormat(formatStr) {
103
113
  if (formatStr.length > MAX_FORMAT_LENGTH) {
104
- throw new Error(
105
- `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
106
- );
114
+ throw new FormatSyntaxError({
115
+ format: formatStr,
116
+ message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
117
+ });
107
118
  }
108
119
  const pieces = getPieces(formatStr);
109
120
  return {
@@ -118,11 +129,13 @@ function compileFormat(formatStr) {
118
129
  continue;
119
130
  }
120
131
  const handler = HANDLER_BY_TOKEN.get(piece.value);
121
- if (!handler) throw new Error(`temporal-fmt: unknown token "${piece.value}"`);
132
+ if (!handler) throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token "${piece.value}"` });
122
133
  if (temporal[handler.field] === void 0) {
123
- throw new Error(
124
- `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
125
- );
134
+ throw new FormatSyntaxError({
135
+ format: formatStr,
136
+ token: piece.value,
137
+ message: `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
138
+ });
126
139
  }
127
140
  result += handler.fn(temporal, locale);
128
141
  }
@@ -139,11 +152,13 @@ function compileFormat(formatStr) {
139
152
  continue;
140
153
  }
141
154
  const handler = HANDLER_BY_TOKEN.get(piece.value);
142
- if (!handler) throw new Error(`temporal-fmt: unknown token "${piece.value}"`);
155
+ if (!handler) throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token "${piece.value}"` });
143
156
  if (temporal[handler.field] === void 0) {
144
- throw new Error(
145
- `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
146
- );
157
+ throw new FormatSyntaxError({
158
+ format: formatStr,
159
+ token: piece.value,
160
+ message: `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
161
+ });
147
162
  }
148
163
  out.push({ type: "token", value: applyNumbering(handler.fn(temporal, locale), options), token: piece.value });
149
164
  }
@@ -165,4 +180,4 @@ export {
165
180
  _getPieces,
166
181
  _handlerFor
167
182
  };
168
- //# sourceMappingURL=chunk-FRS42YLM.js.map
183
+ //# sourceMappingURL=chunk-AXQLUIDY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/format.ts"],"sourcesContent":["import { TOKENS, DEFAULT_LOCALE, type TemporalLike, type FormatOptions } from './tokens.js';\nimport { tokenize, type Piece } from './tokenize.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\nimport { applyNumbering, type NumberingFormatOptions } from './numbering.js';\nimport { FormatSyntaxError, UnknownTokenError } from './errors.js';\n\nconst HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));\n\n// Pre-tokenized format strings, keyed by (formatStr) — locale doesn't\n// change the tokenization step, only the per-token rendering, so the\n// piece list is shared across locales. Same eviction shape as the\n// other caches in this library.\nconst tokenizeCache = new Map<string, Piece[]>();\nconst MAX_TOKENIZE_CACHE_SIZE = 500;\n\nfunction getPieces(formatStr: string): Piece[] {\n let pieces = tokenizeCache.get(formatStr);\n if (pieces) return pieces;\n if (tokenizeCache.size >= MAX_TOKENIZE_CACHE_SIZE) {\n const oldestKey = tokenizeCache.keys().next().value;\n if (oldestKey !== undefined) tokenizeCache.delete(oldestKey);\n }\n pieces = tokenize(formatStr);\n tokenizeCache.set(formatStr, pieces);\n return pieces;\n}\n\n/**\n * Format a Temporal.PlainDate, PlainTime, PlainDateTime, or ZonedDateTime\n * using a date-fns-style token string.\n *\n * @example\n * format(Temporal.Now.plainDateISO(), 'yyyy-MM-dd') // \"2026-08-04\"\n * format(zdt, \"MMM d, yyyy 'at' h:mm a\") // \"Aug 4, 2026 at 3:45 PM\"\n * format(zdt, 'MMMM d, yyyy', { locale: 'fr-FR' }) // \"août 4, 2026\"\n *\n * Throws on a token the input type doesn't support (e.g. 'HH' on a PlainDate).\n */\nexport function format(temporal: TemporalLike, formatStr: string, options: NumberingFormatOptions = {}): string {\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 const locale = options.locale ?? DEFAULT_LOCALE;\n const pieces = getPieces(formatStr);\n let result = '';\n\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n /* c8 ignore start @preserve -- unreachable: tokenize() only ever emits\n tokens present in TOKENS, and HANDLER_BY_TOKEN is built from that\n same list, so every token piece.value can hold already has an entry\n here. Same defensive-guard category as the twin checks in\n analyze.ts/formatDuration.ts. */\n if (!handler) {\n throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n }\n /* c8 ignore stop @preserve */\n\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\n `temporal-fmt: token \"${piece.value}\" requires \"${handler.field}\", ` +\n `which this Temporal object doesn't have. ` +\n `(e.g. PlainDate has no time fields, PlainTime has no date fields)`,\n });\n }\n\n result += handler.fn(temporal, locale);\n }\n\n // Numeral transliteration happens last and only on request — every\n // upstream token handler still emits plain ASCII digits, so this is\n // the single place output digits can diverge from that default.\n return applyNumbering(result, options);\n}\n\n// Shape mirrors Intl.DateTimeFormat.formatToParts: each entry is either\n// a literal (carrying no token info) or a token piece (carrying the\n// token string and the formatted value). Letting callers iterate parts\n// means they can build custom output — strip a token, swap a separator,\n// render each token to its own DOM node — without re-implementing the\n// tokenizer or the field-check logic.\nexport interface FormattedPart {\n type: 'literal' | 'token';\n value: string;\n // Present when `type === 'token'`. Carries the token string (e.g. \"yyyy\")\n // so a caller can look up its metadata via tokenInfo() from analyze.ts.\n token?: string;\n}\n\nexport function formatToParts(temporal: TemporalLike, formatStr: string, options: NumberingFormatOptions = {}): FormattedPart[] {\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 const locale = options.locale ?? DEFAULT_LOCALE;\n const pieces = getPieces(formatStr);\n const result: FormattedPart[] = [];\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n // Collapse adjacent literals so formatToParts stays consistent with\n // how format() walks pieces — a multi-char literal \"at \" is one\n // entry, not one per character. Same merge logic as appendLiteral\n // in tokenize.ts.\n const last = result[result.length - 1];\n /* c8 ignore start @preserve -- unreachable through tokenize()'s own\n output: appendLiteral() in tokenize.ts already merges every\n adjacent literal character before pieces ever reaches here, so\n two 'literal' pieces never sit back-to-back in the array this\n loop walks. Kept as defense-in-depth in case that invariant ever\n changes upstream. */\n if (last && last.type === 'literal') {\n last.value += piece.value;\n } else {\n /* c8 ignore stop @preserve */\n result.push({ type: 'literal', value: piece.value });\n }\n continue;\n }\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n /* c8 ignore start @preserve -- unreachable, see the note in format() above */\n if (!handler) {\n throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n }\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\n `temporal-fmt: token \"${piece.value}\" requires \"${handler.field}\", ` +\n `which this Temporal object doesn't have. ` +\n `(e.g. PlainDate has no time fields, PlainTime has no date fields)`,\n });\n }\n // Numeral transliteration applies per-token-part here, rather than\n // once at the end like format() does, so a caller styling individual\n // parts (e.g. one <span> per token) still gets correctly-transliterated\n // digits in each part instead of plain ASCII.\n result.push({ type: 'token', value: applyNumbering(handler.fn(temporal, locale), options), token: piece.value });\n }\n return result;\n}\n\n// Pre-compiles a format string into an object whose format()/formatToParts()\n// methods skip the tokenization step on every call. The tokenizeCache in\n// this module means a plain format(temporal, fmt) call already pays only\n// a Map lookup for tokenization after the first call, so compileFormat()\n// is mostly a typing/ergonomics affordance — useful for callers who want\n// to hold onto a compiled form explicitly (e.g. to inspect the pieces\n// via the .pieces property, or to pass the compiled object around\n// instead of the string).\nexport interface CompiledFormat {\n format(temporal: TemporalLike, options?: NumberingFormatOptions): string;\n formatToParts(temporal: TemporalLike, options?: NumberingFormatOptions): FormattedPart[];\n readonly pieces: ReadonlyArray<Piece>;\n readonly formatStr: string;\n}\n\nexport function compileFormat(formatStr: string): CompiledFormat {\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-tokenize once. Validation (unknown tokens, unterminated quotes)\n // happens here, not lazily on first format() call — surfaces a bad\n // format string at compile time rather than at first use, which is\n // the point of compiling up front.\n const pieces = getPieces(formatStr);\n return {\n formatStr,\n pieces,\n format(temporal: TemporalLike, options: NumberingFormatOptions = {}) {\n const locale = options.locale ?? DEFAULT_LOCALE;\n let result = '';\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n /* c8 ignore next -- unreachable, see the note in format() above */\n if (!handler) throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\n `temporal-fmt: token \"${piece.value}\" requires \"${handler.field}\", ` +\n `which this Temporal object doesn't have. ` +\n `(e.g. PlainDate has no time fields, PlainTime has no date fields)`,\n });\n }\n result += handler.fn(temporal, locale);\n }\n return applyNumbering(result, options);\n },\n formatToParts(temporal: TemporalLike, options: NumberingFormatOptions = {}) {\n const locale = options.locale ?? DEFAULT_LOCALE;\n const out: FormattedPart[] = [];\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n const last = out[out.length - 1];\n /* c8 ignore next -- unreachable, see the note in formatToParts() above */\n if (last && last.type === 'literal') last.value += piece.value;\n else out.push({ type: 'literal', value: piece.value });\n continue;\n }\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n /* c8 ignore next -- unreachable, see the note in format() above */\n if (!handler) throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\n `temporal-fmt: token \"${piece.value}\" requires \"${handler.field}\", ` +\n `which this Temporal object doesn't have. ` +\n `(e.g. PlainDate has no time fields, PlainTime has no date fields)`,\n });\n }\n out.push({ type: 'token', value: applyNumbering(handler.fn(temporal, locale), options), token: piece.value });\n }\n return out;\n },\n };\n}\n\n// Exported so analyze.ts can reuse the same tokenization cache rather\n// than re-tokenizing when a caller asks for both a format() and an\n// analyzeFormat() on the same string.\nexport function _getPieces(formatStr: string): Piece[] {\n return getPieces(formatStr);\n}\n\n// Exported for analyze.ts — same reason as above. The handler map is\n// the source of truth for \"what field does this token need\" — analyze\n// consumes it to compute requiredFields and compatibleTypes.\nexport function _handlerFor(token: string): { fn: (t: TemporalLike, locale: string) => string; field: keyof TemporalLike } | undefined {\n return HANDLER_BY_TOKEN.get(token);\n}"],"mappings":"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","names":[]}
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  applyParseNumbering,
3
3
  tokenize
4
- } from "./chunk-6MIVOWWX.js";
4
+ } from "./chunk-M33NSC4E.js";
5
5
  import {
6
6
  DEFAULT_LOCALE
7
- } from "./chunk-XIKLQMF7.js";
7
+ } from "./chunk-VSUCBUTC.js";
8
8
  import {
9
9
  MAX_FORMAT_LENGTH,
10
10
  MAX_INPUT_LENGTH
@@ -13,14 +13,18 @@ import {
13
13
  getTemporal
14
14
  } from "./chunk-MXXYIMFJ.js";
15
15
  import {
16
+ AmbiguousInputError,
17
+ FormatSyntaxError,
18
+ InvalidDateError,
19
+ InvalidOffsetError,
16
20
  InvalidTimeZoneError,
21
+ ParseMismatchError,
17
22
  TemporalFmtError,
18
- wrapUntypedError
19
- } from "./chunk-USONQ7MV.js";
20
- import {
23
+ UnknownTokenError,
21
24
  canonicalCacheKey,
22
- getLocaleVocab
23
- } from "./chunk-3RLYULUQ.js";
25
+ getLocaleVocab,
26
+ wrapUntypedError
27
+ } from "./chunk-O6GVMBLW.js";
24
28
 
25
29
  // src/pattern.ts
26
30
  function escapeRegExp(literal) {
@@ -126,9 +130,10 @@ function tokenFragment(token, locale, nextToken) {
126
130
  return QQQ_FRAGMENT;
127
131
  }
128
132
  if (FORMAT_ONLY_TOKENS.has(token)) {
129
- throw new Error(
130
- `temporal-fmt: token "${token}" 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").`
131
- );
133
+ throw new UnknownTokenError({
134
+ token,
135
+ message: `temporal-fmt: token "${token}" 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").`
136
+ });
132
137
  }
133
138
  const vocab = getLocaleVocab(locale);
134
139
  switch (token) {
@@ -168,7 +173,7 @@ function tokenFragment(token, locale, nextToken) {
168
173
  registering something new or tokenize.ts being bypassed, neither of
169
174
  which happens on the parse() path. */
170
175
  default:
171
- throw new Error(`temporal-fmt: unknown token "${token}"`);
176
+ throw new UnknownTokenError({ token, message: `temporal-fmt: unknown token "${token}"` });
172
177
  }
173
178
  }
174
179
  var UNPADDED_NUMERIC_TOKENS = /* @__PURE__ */ new Set(["M", "d", "H", "h", "m", "s"]);
@@ -382,22 +387,28 @@ function parseOffsetString(raw, token) {
382
387
  const hours = Number(hoursStr);
383
388
  const minutes = Number(minutesStr);
384
389
  if (hours > 14) {
385
- throw new Error(
386
- `temporal-fmt: offset hours ${hours} in "${raw}" out of range (max 14 \u2014 Kiritimati, Line Islands is +14:00).`
387
- );
390
+ throw new InvalidOffsetError({
391
+ actual: raw,
392
+ message: `temporal-fmt: offset hours ${hours} in "${raw}" out of range (max 14 \u2014 Kiritimati, Line Islands is +14:00).`
393
+ });
388
394
  }
389
395
  if (minutes > 59) {
390
- throw new Error(`temporal-fmt: offset minutes ${minutes} in "${raw}" out of range (max 59).`);
396
+ throw new InvalidOffsetError({
397
+ actual: raw,
398
+ message: `temporal-fmt: offset minutes ${minutes} in "${raw}" out of range (max 59).`
399
+ });
391
400
  }
392
401
  if (sign === "+" && hours === 14 && minutes !== 0) {
393
- throw new Error(
394
- `temporal-fmt: offset "${raw}" exceeds the maximum supported UTC offset of +14:00.`
395
- );
402
+ throw new InvalidOffsetError({
403
+ actual: raw,
404
+ message: `temporal-fmt: offset "${raw}" exceeds the maximum supported UTC offset of +14:00.`
405
+ });
396
406
  }
397
407
  if (sign === "-" && hours === 12 && minutes !== 0) {
398
- throw new Error(
399
- `temporal-fmt: offset "${raw}" exceeds the maximum supported negative UTC offset of -12:00.`
400
- );
408
+ throw new InvalidOffsetError({
409
+ actual: raw,
410
+ message: `temporal-fmt: offset "${raw}" exceeds the maximum supported negative UTC offset of -12:00.`
411
+ });
401
412
  }
402
413
  return `${sign}${hoursStr}:${minutesStr}`;
403
414
  }
@@ -511,9 +522,9 @@ function pickLenientSplit(splits, tokens) {
511
522
  }
512
523
  function resolveYear(fields) {
513
524
  if (fields.year !== void 0 && fields.twoDigitYear !== void 0) {
514
- throw new Error(
515
- 'temporal-fmt: format string mixes "yyyy" and "yy" year representations.'
516
- );
525
+ throw new FormatSyntaxError({
526
+ message: 'temporal-fmt: format string mixes "yyyy" and "yy" year representations.'
527
+ });
517
528
  }
518
529
  if (fields.year !== void 0) return fields.year;
519
530
  if (fields.twoDigitYear !== void 0) {
@@ -523,27 +534,30 @@ function resolveYear(fields) {
523
534
  }
524
535
  function resolveHour(fields, formatStr, locale) {
525
536
  if (fields.hour !== void 0 && fields.hour12 !== void 0) {
526
- throw new Error(
527
- `temporal-fmt: format string "${formatStr}" mixes a 24-hour token ("HH"/"H") with a 12-hour token ("hh"/"h").`
528
- );
537
+ throw new FormatSyntaxError({
538
+ format: formatStr,
539
+ message: `temporal-fmt: format string "${formatStr}" mixes a 24-hour token ("HH"/"H") with a 12-hour token ("hh"/"h").`
540
+ });
529
541
  }
530
542
  if (fields.hour !== void 0) {
531
543
  if (fields.dayPeriodRaw !== void 0) {
532
544
  const vocab = getLocaleVocab(locale);
533
545
  const expected = fields.hour < 12 ? vocab.dayPeriod[0] : vocab.dayPeriod[1];
534
546
  if (fields.dayPeriodRaw.toLowerCase() !== expected?.toLowerCase()) {
535
- throw new Error(
536
- `temporal-fmt: format string "${formatStr}" contains a day period that contradicts the 24-hour value.`
537
- );
547
+ throw new FormatSyntaxError({
548
+ format: formatStr,
549
+ message: `temporal-fmt: format string "${formatStr}" contains a day period that contradicts the 24-hour value.`
550
+ });
538
551
  }
539
552
  }
540
553
  return fields.hour;
541
554
  }
542
555
  if (fields.hour12 !== void 0) {
543
556
  if (fields.isPM === void 0) {
544
- throw new Error(
545
- `temporal-fmt: format string "${formatStr}" uses a 12-hour token ("hh"/"h") without an "a" token, so parse() can't tell AM from PM.`
546
- );
557
+ throw new FormatSyntaxError({
558
+ format: formatStr,
559
+ message: `temporal-fmt: format string "${formatStr}" uses a 12-hour token ("hh"/"h") without an "a" token, so parse() can't tell AM from PM.`
560
+ });
547
561
  }
548
562
  return fields.hour12 % 12 + (fields.isPM ? 12 : 0);
549
563
  }
@@ -551,14 +565,16 @@ function resolveHour(fields, formatStr, locale) {
551
565
  }
552
566
  function parse(formatStr, input, options = {}) {
553
567
  if (formatStr.length > MAX_FORMAT_LENGTH) {
554
- throw new Error(
555
- `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
556
- );
568
+ throw new FormatSyntaxError({
569
+ format: formatStr,
570
+ message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
571
+ });
557
572
  }
558
573
  if (input.length > MAX_INPUT_LENGTH) {
559
- throw new Error(
560
- `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`
561
- );
574
+ throw new FormatSyntaxError({
575
+ input,
576
+ message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`
577
+ });
562
578
  }
563
579
  if (options.parseNumberingSystem) {
564
580
  input = applyParseNumbering(input, options);
@@ -568,10 +584,27 @@ function parse(formatStr, input, options = {}) {
568
584
  const pattern = getPattern(formatStr, locale);
569
585
  const match = pattern.regex.exec(input);
570
586
  if (!match) {
571
- throw new Error(`temporal-fmt: no valid pattern matches the format string and input shape`);
587
+ throw new ParseMismatchError({
588
+ input,
589
+ format: formatStr,
590
+ // reason set explicitly (not just message): parse.test.js's
591
+ // "safeParse: failure returns { ok: false, error } with a
592
+ // TemporalFmtError" asserts result.error.reason carries the
593
+ // underlying detail text, matching what wrapUntypedError used to
594
+ // populate when this message reached it as a caught plain Error.
595
+ reason: "no valid pattern matches the format string and input shape",
596
+ message: `temporal-fmt: no valid pattern matches the format string and input shape`
597
+ });
572
598
  }
573
599
  if (pattern.groups.length === 0) {
574
- throw new Error(`temporal-fmt: format string "${formatStr}" has no tokens \u2014 nothing to parse into a value.`);
600
+ throw new ParseMismatchError({
601
+ format: formatStr,
602
+ // reason set explicitly: errors.test.js's "format string with no
603
+ // tokens at all falls through to ParseMismatchError" asserts
604
+ // error.reason matches /has no tokens/.
605
+ reason: `format string "${formatStr}" has no tokens \u2014 nothing to parse into a value.`,
606
+ message: `temporal-fmt: format string "${formatStr}" has no tokens \u2014 nothing to parse into a value.`
607
+ });
575
608
  }
576
609
  for (const { name, token } of pattern.groups) {
577
610
  if (token === "zzz" && !isValidTimeZone(match.groups[name])) {
@@ -589,9 +622,11 @@ function parse(formatStr, input, options = {}) {
589
622
  const splits = enumerateValidSplits(runDigits, run.tokens);
590
623
  if (splits.length > 1) {
591
624
  if (!options.lenient) {
592
- throw new Error(
593
- `temporal-fmt: "${runDigits}" in format string "${formatStr}" is ambiguous \u2014 ${splits.length} different ways to read tokens "${run.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[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.`
594
- );
625
+ throw new AmbiguousInputError({
626
+ input,
627
+ format: formatStr,
628
+ message: `temporal-fmt: "${runDigits}" in format string "${formatStr}" is ambiguous \u2014 ${splits.length} different ways to read tokens "${run.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[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.`
629
+ });
595
630
  }
596
631
  runPicks.push({ groupNames: run.groupNames, values: pickLenientSplit(splits, run.tokens) });
597
632
  }
@@ -611,28 +646,35 @@ function parse(formatStr, input, options = {}) {
611
646
  const hasAnyDatePart = year !== void 0 || month !== void 0 || day !== void 0;
612
647
  const hasFullDate = year !== void 0 && month !== void 0 && day !== void 0;
613
648
  if (hasAnyDatePart && !hasFullDate) {
614
- throw new Error(
615
- `temporal-fmt: format string "${formatStr}" has an incomplete date \u2014 year, month, and day tokens must all be present together.`
616
- );
649
+ throw new InvalidDateError({
650
+ format: formatStr,
651
+ message: `temporal-fmt: format string "${formatStr}" has an incomplete date \u2014 year, month, and day tokens must all be present together.`
652
+ });
617
653
  }
618
654
  const hasTime = hour !== void 0 || minute !== void 0 || second !== void 0 || millisecond !== void 0;
619
655
  if (timeZoneId !== void 0 && !(hasFullDate && hasTime)) {
620
- throw new Error(
621
- `temporal-fmt: format string "${formatStr}" has a "zzz" token but needs a full date and time to build a ZonedDateTime.`
622
- );
656
+ throw new FormatSyntaxError({
657
+ format: formatStr,
658
+ message: `temporal-fmt: format string "${formatStr}" has a "zzz" token but needs a full date and time to build a ZonedDateTime.`
659
+ });
623
660
  }
624
661
  if (offsetString !== void 0 && !(hasFullDate && hasTime)) {
625
- throw new Error(
626
- `temporal-fmt: format string "${formatStr}" has an offset token (X/XX/XXX/x/xx/xxx) but needs a full date and time to build a ZonedDateTime.`
627
- );
662
+ throw new FormatSyntaxError({
663
+ format: formatStr,
664
+ message: `temporal-fmt: format string "${formatStr}" has an offset token (X/XX/XXX/x/xx/xxx) but needs a full date and time to build a ZonedDateTime.`
665
+ });
628
666
  }
629
667
  if (weekdayExpected !== void 0 && !hasFullDate) {
630
- throw new Error(
631
- `temporal-fmt: format string "${formatStr}" has a weekday token ("EEEE"/"EEE") but needs a full date to validate it against.`
632
- );
668
+ throw new FormatSyntaxError({
669
+ format: formatStr,
670
+ message: `temporal-fmt: format string "${formatStr}" has a weekday token ("EEEE"/"EEE") but needs a full date to validate it against.`
671
+ });
633
672
  }
634
673
  if (!hasFullDate && !hasTime) {
635
- throw new Error(`temporal-fmt: format string "${formatStr}" has no date or time tokens to parse.`);
674
+ throw new FormatSyntaxError({
675
+ format: formatStr,
676
+ message: `temporal-fmt: format string "${formatStr}" has no date or time tokens to parse.`
677
+ });
636
678
  }
637
679
  const temporal = getTemporal();
638
680
  const timeFields = {
@@ -665,15 +707,19 @@ function parse(formatStr, input, options = {}) {
665
707
  const zdt = result;
666
708
  const wallClockShifted = zdt.hour !== timeFields.hour || zdt.minute !== timeFields.minute || zdt.second !== timeFields.second;
667
709
  if (wallClockShifted) {
668
- throw new Error(
669
- `"${timeZoneId}" has no such wall-clock time on this date \u2014 it falls in a DST gap, not an ambiguous or valid instant.`
670
- );
710
+ throw new InvalidDateError({
711
+ input,
712
+ format: formatStr,
713
+ message: `"${timeZoneId}" has no such wall-clock time on this date \u2014 it falls in a DST gap, not an ambiguous or valid instant.`
714
+ });
671
715
  }
672
716
  const actualOffset = zdt.offset;
673
717
  if (actualOffset !== offsetString) {
674
- throw new Error(
675
- `has both a "zzz" zone (${timeZoneId}) and an offset token (${offsetString}), but the zone's actual offset at this date/time is ${actualOffset}, not ${offsetString}.`
676
- );
718
+ throw new ParseMismatchError({
719
+ input,
720
+ format: formatStr,
721
+ message: `has both a "zzz" zone (${timeZoneId}) and an offset token (${offsetString}), but the zone's actual offset at this date/time is ${actualOffset}, not ${offsetString}.`
722
+ });
677
723
  }
678
724
  }
679
725
  } else if (offsetString !== void 0) {
@@ -686,25 +732,30 @@ function parse(formatStr, input, options = {}) {
686
732
  result = temporal.PlainTime.from(timeFields, reject);
687
733
  }
688
734
  } catch (err) {
689
- throw new Error(
690
- `temporal-fmt: "${input}" doesn't describe a valid date/time for format "${formatStr}": ${err.message}`
691
- );
735
+ throw new InvalidDateError({
736
+ input,
737
+ format: formatStr,
738
+ message: `temporal-fmt: "${input}" doesn't describe a valid date/time for format "${formatStr}": ${err.message}`
739
+ });
692
740
  }
693
741
  if (weekdayExpected !== void 0) {
694
742
  const actual = result.dayOfWeek;
695
743
  if (actual !== weekdayExpected) {
696
744
  const vocab = getLocaleVocab(locale);
697
- throw new Error(
698
- `temporal-fmt: "${weekdayRaw}" doesn't match the actual weekday (${vocab.weekdayLong[actual - 1]}) for the parsed date.`
699
- );
745
+ throw new InvalidDateError({
746
+ input,
747
+ format: formatStr,
748
+ message: `temporal-fmt: "${weekdayRaw}" doesn't match the actual weekday (${vocab.weekdayLong[actual - 1]}) for the parsed date.`
749
+ });
700
750
  }
701
751
  }
702
752
  if (quarter !== void 0 && month !== void 0) {
703
753
  const expectedQuarter = Math.ceil(month / 3);
704
754
  if (quarter !== expectedQuarter) {
705
- throw new Error(
706
- `temporal-fmt: format string "${formatStr}" contains a quarter token (Q/QQQ) whose value (Q${quarter}) disagrees with the parsed month's actual quarter \u2014 month ${month} is in Q${expectedQuarter}.`
707
- );
755
+ throw new InvalidDateError({
756
+ format: formatStr,
757
+ message: `temporal-fmt: format string "${formatStr}" contains a quarter token (Q/QQQ) whose value (Q${quarter}) disagrees with the parsed month's actual quarter \u2014 month ${month} is in Q${expectedQuarter}.`
758
+ });
708
759
  }
709
760
  }
710
761
  return result;
@@ -713,11 +764,13 @@ function safeParse(formatStr, input, options = {}) {
713
764
  try {
714
765
  return { ok: true, value: parse(formatStr, input, options) };
715
766
  } catch (err) {
767
+ /* c8 ignore start @preserve */
716
768
  if (err instanceof TemporalFmtError) {
717
769
  return { ok: false, error: err };
718
770
  }
719
771
  return { ok: false, error: wrapUntypedError(err, { input, format: formatStr }) };
720
772
  }
773
+ /* c8 ignore stop @preserve */
721
774
  }
722
775
  function tryParse(formatStr, input, options = {}) {
723
776
  try {
@@ -728,14 +781,16 @@ function tryParse(formatStr, input, options = {}) {
728
781
  }
729
782
  function parseToParts(formatStr, input, options = {}) {
730
783
  if (formatStr.length > MAX_FORMAT_LENGTH) {
731
- throw new Error(
732
- `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
733
- );
784
+ throw new FormatSyntaxError({
785
+ format: formatStr,
786
+ message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
787
+ });
734
788
  }
735
789
  if (input.length > MAX_INPUT_LENGTH) {
736
- throw new Error(
737
- `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`
738
- );
790
+ throw new FormatSyntaxError({
791
+ input,
792
+ message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`
793
+ });
739
794
  }
740
795
  if (options.parseNumberingSystem) {
741
796
  input = applyParseNumbering(input, options);
@@ -744,10 +799,27 @@ function parseToParts(formatStr, input, options = {}) {
744
799
  const pattern = getPattern(formatStr, locale);
745
800
  const match = pattern.regex.exec(input);
746
801
  if (!match) {
747
- throw new Error(`temporal-fmt: no valid pattern matches the format string and input shape`);
802
+ throw new ParseMismatchError({
803
+ input,
804
+ format: formatStr,
805
+ // reason set explicitly (not just message): parse.test.js's
806
+ // "safeParse: failure returns { ok: false, error } with a
807
+ // TemporalFmtError" asserts result.error.reason carries the
808
+ // underlying detail text, matching what wrapUntypedError used to
809
+ // populate when this message reached it as a caught plain Error.
810
+ reason: "no valid pattern matches the format string and input shape",
811
+ message: `temporal-fmt: no valid pattern matches the format string and input shape`
812
+ });
748
813
  }
749
814
  if (pattern.groups.length === 0) {
750
- throw new Error(`temporal-fmt: format string "${formatStr}" has no tokens \u2014 nothing to parse into a value.`);
815
+ throw new ParseMismatchError({
816
+ format: formatStr,
817
+ // reason set explicitly: errors.test.js's "format string with no
818
+ // tokens at all falls through to ParseMismatchError" asserts
819
+ // error.reason matches /has no tokens/.
820
+ reason: `format string "${formatStr}" has no tokens \u2014 nothing to parse into a value.`,
821
+ message: `temporal-fmt: format string "${formatStr}" has no tokens \u2014 nothing to parse into a value.`
822
+ });
751
823
  }
752
824
  for (const { name, token } of pattern.groups) {
753
825
  if (token === "zzz" && !isValidTimeZone(match.groups[name])) {
@@ -765,9 +837,11 @@ function parseToParts(formatStr, input, options = {}) {
765
837
  const splits = enumerateValidSplits(runDigits, run.tokens);
766
838
  if (splits.length > 1) {
767
839
  if (!options.lenient) {
768
- throw new Error(
769
- `temporal-fmt: "${runDigits}" in format string "${formatStr}" is ambiguous \u2014 ${splits.length} different ways to read tokens "${run.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[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.`
770
- );
840
+ throw new AmbiguousInputError({
841
+ input,
842
+ format: formatStr,
843
+ message: `temporal-fmt: "${runDigits}" in format string "${formatStr}" is ambiguous \u2014 ${splits.length} different ways to read tokens "${run.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[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.`
844
+ });
771
845
  }
772
846
  runPicks.push({ groupNames: run.groupNames, values: pickLenientSplit(splits, run.tokens) });
773
847
  }
@@ -791,9 +865,10 @@ function parseToParts(formatStr, input, options = {}) {
791
865
  }
792
866
  function compileParser(formatStr, options = {}) {
793
867
  if (formatStr.length > MAX_FORMAT_LENGTH) {
794
- throw new Error(
795
- `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
796
- );
868
+ throw new FormatSyntaxError({
869
+ format: formatStr,
870
+ message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
871
+ });
797
872
  }
798
873
  const locale = options.locale ?? DEFAULT_LOCALE;
799
874
  const pattern = getPattern(formatStr, locale);
@@ -824,4 +899,4 @@ export {
824
899
  parseToParts,
825
900
  compileParser
826
901
  };
827
- //# sourceMappingURL=chunk-KCSGCK7L.js.map
902
+ //# sourceMappingURL=chunk-CKVHXFX7.js.map