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.
Files changed (103) hide show
  1. package/README.md +480 -9
  2. package/dist/calendarUtils.cjs +259 -4
  3. package/dist/calendarUtils.cjs.map +1 -1
  4. package/dist/calendarUtils.js +33 -1
  5. package/dist/chunk-4N3WCGEY.js +183 -0
  6. package/dist/{chunk-4JWGUR4O.js.map → chunk-4N3WCGEY.js.map} +1 -1
  7. package/dist/chunk-5UTV5VP6.js +984 -0
  8. package/dist/chunk-5UTV5VP6.js.map +1 -0
  9. package/dist/chunk-ACKKWH5Z.js +578 -0
  10. package/dist/{chunk-2JHEV7ZF.js.map → chunk-ACKKWH5Z.js.map} +1 -1
  11. package/dist/chunk-EGUUO6LE.js +439 -0
  12. package/dist/chunk-EGUUO6LE.js.map +1 -0
  13. package/dist/chunk-FBPHNRVW.js +2939 -0
  14. package/dist/chunk-FBPHNRVW.js.map +1 -0
  15. package/dist/chunk-GPB4JHGI.js +65 -0
  16. package/dist/chunk-GPB4JHGI.js.map +1 -0
  17. package/dist/chunk-H7YQX25T.js +170 -0
  18. package/dist/{chunk-J2AQNXF2.js.map → chunk-H7YQX25T.js.map} +1 -1
  19. package/dist/chunk-HXY2GUUM.js +182 -0
  20. package/dist/{chunk-EHLRZULM.js.map → chunk-HXY2GUUM.js.map} +1 -1
  21. package/dist/chunk-L24NPTAJ.js +328 -0
  22. package/dist/chunk-L24NPTAJ.js.map +1 -0
  23. package/dist/chunk-M3GDTHMT.js +169 -0
  24. package/dist/chunk-M3GDTHMT.js.map +1 -0
  25. package/dist/chunk-MKPDLQKD.js +51 -0
  26. package/dist/chunk-MKPDLQKD.js.map +1 -0
  27. package/dist/chunk-TSLPIURC.js +242 -0
  28. package/dist/{chunk-VQXUMFB2.js.map → chunk-TSLPIURC.js.map} +1 -1
  29. package/dist/chunk-UOPAV3WX.js +75 -0
  30. package/dist/chunk-UOPAV3WX.js.map +1 -0
  31. package/dist/chunk-VNIUIQA4.js +398 -0
  32. package/dist/chunk-VNIUIQA4.js.map +1 -0
  33. package/dist/chunk-YEUASGDK.js +9 -0
  34. package/dist/chunk-YEUASGDK.js.map +1 -0
  35. package/dist/chunk-ZQZF5EXW.js +29 -0
  36. package/dist/chunk-ZQZF5EXW.js.map +1 -0
  37. package/dist/config.d.cts +4 -2
  38. package/dist/config.d.ts +4 -2
  39. package/dist/duration.cjs +638 -26
  40. package/dist/duration.cjs.map +1 -1
  41. package/dist/duration.js +28 -1
  42. package/dist/format.cjs +833 -35
  43. package/dist/format.cjs.map +1 -1
  44. package/dist/format.js +20 -1
  45. package/dist/index.cjs +7698 -163
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.cts +108 -24
  48. package/dist/index.d.ts +108 -24
  49. package/dist/index.js +1536 -58
  50. package/dist/index.js.map +1 -1
  51. package/dist/interval.cjs +5480 -35
  52. package/dist/interval.cjs.map +1 -1
  53. package/dist/interval.js +46 -1
  54. package/dist/localeRegistry.cjs +375 -32
  55. package/dist/localeRegistry.cjs.map +1 -1
  56. package/dist/localeRegistry.js +11 -1
  57. package/dist/modApi.d.cts +131 -0
  58. package/dist/modApi.d.ts +131 -0
  59. package/dist/parse.cjs +1797 -70
  60. package/dist/parse.cjs.map +1 -1
  61. package/dist/parse.js +20 -1
  62. package/dist/pattern.d.cts +1 -0
  63. package/dist/pattern.d.ts +1 -0
  64. package/dist/recurrence.cjs +452 -20
  65. package/dist/recurrence.cjs.map +1 -1
  66. package/dist/recurrence.js +19 -1
  67. package/dist/relativeTime.cjs +268 -21
  68. package/dist/relativeTime.cjs.map +1 -1
  69. package/dist/relativeTime.js +14 -1
  70. package/dist/runtime.d.cts +201 -0
  71. package/dist/runtime.d.ts +201 -0
  72. package/dist/timezone.cjs +387 -20
  73. package/dist/timezone.cjs.map +1 -1
  74. package/dist/timezone.js +26 -1
  75. package/package.json +4 -6
  76. package/scripts/cli.mjs +27 -5
  77. package/dist/chunk-2JHEV7ZF.js +0 -7
  78. package/dist/chunk-3MZLTVP3.js +0 -2
  79. package/dist/chunk-3MZLTVP3.js.map +0 -1
  80. package/dist/chunk-4JWGUR4O.js +0 -11
  81. package/dist/chunk-5U5WJ465.js +0 -36
  82. package/dist/chunk-5U5WJ465.js.map +0 -1
  83. package/dist/chunk-C5YESWFT.js +0 -2
  84. package/dist/chunk-C5YESWFT.js.map +0 -1
  85. package/dist/chunk-C6ZZ62ET.js +0 -2
  86. package/dist/chunk-C6ZZ62ET.js.map +0 -1
  87. package/dist/chunk-EHLRZULM.js +0 -5
  88. package/dist/chunk-F4RGUDA3.js +0 -2
  89. package/dist/chunk-F4RGUDA3.js.map +0 -1
  90. package/dist/chunk-J2AQNXF2.js +0 -2
  91. package/dist/chunk-NBXF7V5B.js +0 -2
  92. package/dist/chunk-NBXF7V5B.js.map +0 -1
  93. package/dist/chunk-R52YOKI3.js +0 -7
  94. package/dist/chunk-R52YOKI3.js.map +0 -1
  95. package/dist/chunk-UJR6DHXY.js +0 -2
  96. package/dist/chunk-UJR6DHXY.js.map +0 -1
  97. package/dist/chunk-VQXUMFB2.js +0 -21
  98. package/dist/chunk-XH46JNPK.js +0 -2
  99. package/dist/chunk-XH46JNPK.js.map +0 -1
  100. package/dist/chunk-YB6YAG7E.js +0 -35
  101. package/dist/chunk-YB6YAG7E.js.map +0 -1
  102. package/dist/chunk-ZGMTY52O.js +0 -2
  103. package/dist/chunk-ZGMTY52O.js.map +0 -1
package/dist/interval.js CHANGED
@@ -1,2 +1,47 @@
1
- import{a,b,c,d,e,f,g,h,i,j,k,l,m,n}from"./chunk-F4RGUDA3.js";import"./chunk-4JWGUR4O.js";import"./chunk-R52YOKI3.js";import"./chunk-C5YESWFT.js";import"./chunk-3MZLTVP3.js";import"./chunk-5U5WJ465.js";import"./chunk-UJR6DHXY.js";import"./chunk-EHLRZULM.js";import"./chunk-C6ZZ62ET.js";export{b as contains,i as difference,m as formatRange,n as formatRangeToParts,g as intersection,d as intersects,a as interval,f as isAfter,e as isBefore,k as mergeIntervals,c as overlaps,l as splitInterval,j as subtract,h as union};
1
+ import {
2
+ contains,
3
+ difference,
4
+ formatRange,
5
+ formatRangeToParts,
6
+ intersection,
7
+ intersects,
8
+ interval,
9
+ isAfter,
10
+ isBefore,
11
+ mergeIntervals,
12
+ overlaps,
13
+ splitInterval,
14
+ subtract,
15
+ union
16
+ } from "./chunk-FBPHNRVW.js";
17
+ import "./chunk-H7YQX25T.js";
18
+ import "./chunk-ACKKWH5Z.js";
19
+ import "./chunk-4N3WCGEY.js";
20
+ import "./chunk-UOPAV3WX.js";
21
+ import "./chunk-5UTV5VP6.js";
22
+ import "./chunk-M3GDTHMT.js";
23
+ import "./chunk-L24NPTAJ.js";
24
+ import "./chunk-ZQZF5EXW.js";
25
+ import "./chunk-YEUASGDK.js";
26
+ import "./chunk-EGUUO6LE.js";
27
+ import "./chunk-VNIUIQA4.js";
28
+ import "./chunk-TSLPIURC.js";
29
+ import "./chunk-HXY2GUUM.js";
30
+ import "./chunk-MKPDLQKD.js";
31
+ export {
32
+ contains,
33
+ difference,
34
+ formatRange,
35
+ formatRangeToParts,
36
+ intersection,
37
+ intersects,
38
+ interval,
39
+ isAfter,
40
+ isBefore,
41
+ mergeIntervals,
42
+ overlaps,
43
+ splitInterval,
44
+ subtract,
45
+ union
46
+ };
2
47
  //# sourceMappingURL=interval.js.map
@@ -1,36 +1,379 @@
1
- "use strict";var L=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var v=(t,e)=>{for(var r in e)L(t,r,{get:e[r],enumerable:!0})},z=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of K(e))!q.call(t,n)&&n!==r&&L(t,n,{get:()=>e[n],enumerable:!(o=R(e,n))||o.enumerable});return t};var B=t=>z(L({},"__esModule",{value:!0}),t);var tt={};v(tt,{getLocale:()=>J,hasLocale:()=>Q,registerLocale:()=>W});module.exports=B(tt);var E=class extends Error{code;input;format;token;position;expected;actual;reason;constructor(e,r){super(e),this.name="TemporalFmtError",this.code=r.code,this.input=r.input,this.format=r.format,this.token=r.token,this.position=r.position,this.expected=r.expected,this.actual=r.actual,this.reason=r.reason;let o=Error.captureStackTrace;typeof o=="function"&&o(this,this.constructor)}toJSON(){return{name:this.name,message:this.message,code:this.code,input:this.input,format:this.format,token:this.token,position:this.position,expected:this.expected,actual:this.actual,reason:this.reason}}};/* c8 ignore start @preserve -- InvalidTimeError is part of the public
2
- error-class surface (exported from index.ts, code 'INVALID_TIME')
3
- but nothing in this package constructs one. Investigated wiring it
4
- in the same way InvalidTimeZoneError was just wired (see parse.ts's
5
- zzz-validation loop): checked whether hour/minute/second have a
6
- post-match semantic range check the way zone ids do. They don't —
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/localeRegistry.ts
21
+ var localeRegistry_exports = {};
22
+ __export(localeRegistry_exports, {
23
+ getLocale: () => getLocale,
24
+ hasLocale: () => hasLocale,
25
+ registerLocale: () => registerLocale
26
+ });
27
+ module.exports = __toCommonJS(localeRegistry_exports);
28
+
29
+ // src/errors.ts
30
+ var TemporalFmtError = class extends Error {
31
+ code;
32
+ input;
33
+ format;
34
+ token;
35
+ position;
36
+ expected;
37
+ actual;
38
+ reason;
39
+ constructor(message, fields) {
40
+ super(message);
41
+ this.name = "TemporalFmtError";
42
+ this.code = fields.code;
43
+ this.input = fields.input;
44
+ this.format = fields.format;
45
+ this.token = fields.token;
46
+ this.position = fields.position;
47
+ this.expected = fields.expected;
48
+ this.actual = fields.actual;
49
+ this.reason = fields.reason;
50
+ const capture = Error.captureStackTrace;
51
+ if (typeof capture === "function") {
52
+ capture(this, this.constructor);
53
+ }
54
+ }
55
+ // lets callers just call err.toJSON() for logging. plain Error doesn't
56
+ // serialize its non-enumerable fields on its own, so this picks them
57
+ // up explicitly
58
+ toJSON() {
59
+ return {
60
+ name: this.name,
61
+ message: this.message,
62
+ code: this.code,
63
+ input: this.input,
64
+ format: this.format,
65
+ token: this.token,
66
+ position: this.position,
67
+ expected: this.expected,
68
+ actual: this.actual,
69
+ reason: this.reason
70
+ };
71
+ }
72
+ };
73
+ /* c8 ignore start @preserve -- InvalidTimeError is part of the public
74
+ error-class surface (exported from index.ts, code 'INVALID_TIME') but
75
+ nothing in this package actually constructs one. went and checked
76
+ whether it could get wired in the same way InvalidTimeZoneError just
77
+ was (see parse.ts's zzz-validation loop) — does hour/minute/second have
78
+ a post-match semantic range check the way zone ids do? nope.
7
79
  pattern.ts's regex fragments for HH/H/hh/h/mm/m/ss/s already enforce
8
- their valid ranges at the regex level (e.g. HH is '(?:[01]\d|2[0-3])',
9
- which can't match "99" in the first place), so an out-of-range time
10
- is rejected as a plain shape mismatch before any semantic check
11
- could run. There's no live gap to hook this into without inventing a
12
- redundant check purely to give this class a body. Left unconstructed
13
- until the library actually has a real invalid-time case to report. *//* c8 ignore stop @preserve *//* c8 ignore start @preserve -- InvalidCalendarError is part of the
80
+ their valid ranges right at the regex level (e.g. HH is
81
+ '(?:[01]\d|2[0-3])', which literally can't match "99"), so an
82
+ out-of-range time just gets rejected as a plain shape mismatch before
83
+ any semantic check would even run. no live gap to hook this into
84
+ without inventing a redundant check purely to give this class a body.
85
+ leaving it unconstructed until the library actually hits a real
86
+ invalid-time case worth reporting */
87
+ /* c8 ignore stop @preserve */
88
+ /* c8 ignore start @preserve -- InvalidCalendarError is part of the
14
89
  public error-class surface (exported from index.ts, code
15
- 'INVALID_CALENDAR') but nothing in this package constructs one.
16
- Checked for a wiring opportunity the same way InvalidTimeZoneError
17
- got one: there's no user-supplied calendar identifier anywhere in
18
- the library to validate against a supported list. resolveCalendar()
90
+ 'INVALID_CALENDAR') but same story as InvalidTimeError above — nothing
91
+ in this package constructs one. checked for a wiring opportunity the
92
+ same way: there's no user-supplied calendar identifier anywhere in the
93
+ library that we'd validate against a supported list. resolveCalendar()
19
94
  in parse.ts derives the calendar entirely from Intl's own resolution
20
- of the locale string — it's never handed an arbitrary "calendar"
21
- value a caller could get wrong. There's no live input to reject.
22
- Left unconstructed until the library actually accepts a calendar
23
- parameter that could be invalid. *//* c8 ignore stop @preserve */var c=class extends E{constructor(e){let{message:r,...o}=e;super(r??`locale "${e.actual??""}" is not a valid BCP-47 tag${e.reason?`: ${e.reason}`:""}.`,{code:"INVALID_LOCALE",...o}),this.name="InvalidLocaleError"}};/* c8 ignore start @preserve -- unreachable from the current test suite,
24
- see rationale above *//* c8 ignore stop @preserve */var f=new Map,V=500,b=256,I=256;function G(t,e){let r=[{key:"monthLong",length:12,label:"long month names"},{key:"monthShort",length:12,label:"short month names"},{key:"weekdayLong",length:7,label:"long weekday names"},{key:"weekdayShort",length:7,label:"short weekday names"},{key:"dayPeriod",length:2,label:"day period markers (AM/PM-equivalent)"}];for(let{key:o,length:n,label:a}of r){let s=t[o];if(s===void 0)throw new Error(`temporal-fmt: registerLocaleVocab for locale "${e}" is missing required field "${o}" (${a}).`);if(!Array.isArray(s))throw new Error(`temporal-fmt: registerLocaleVocab for locale "${e}": "${o}" must be an array, got ${typeof s}.`);if(s.length!==n)throw new Error(`temporal-fmt: registerLocaleVocab for locale "${e}": "${o}" must have exactly ${n} entries (got ${s.length}) \u2014 ${a}.`);s.forEach((i,g)=>{if(typeof i!="string"||i.length===0)throw new Error(`temporal-fmt: registerLocaleVocab for locale "${e}": "${o}[${g}]" must be a non-empty string, got ${String(i)}.`);if(i.length>I)throw new RangeError(`temporal-fmt: registerLocaleVocab for locale "${e}": "${o}[${g}]" is too long (maximum ${I} characters).`)})}if(u(t.monthLong,"MMMM month",e),u(t.monthShort,"MMM month",e),u(t.weekdayLong,"EEEE weekday",e),u(t.weekdayShort,"EEE weekday",e),t.dayPeriod[0]===t.dayPeriod[1])throw new Error(`temporal-fmt: registerLocaleVocab for locale "${e}": dayPeriod entries must differ (both are "${t.dayPeriod[0]}"); otherwise parse() can't tell AM from PM.`)}var X=[];function x(t,e){if(typeof t!="string"||t.length===0)throw new Error(`temporal-fmt: registerLocaleVocab requires a non-empty locale string, got ${String(t)}.`);if(t.length>b)throw new RangeError(`temporal-fmt: registerLocaleVocab locale is too long (maximum ${b} characters).`);G(e,t);let r=_(t);if(!f.has(r)&&f.size>=V)throw new RangeError(`temporal-fmt: registerLocaleVocab reached the ${V}-locale limit.`);f.set(r,{monthLong:[...e.monthLong],monthShort:[...e.monthShort],weekdayLong:[...e.weekdayLong],weekdayShort:[...e.weekdayShort],dayPeriod:[...e.dayPeriod]}),l.delete(r);for(let o of X)o()}function F(t){return t.replace(/_/g,"-")}var p=new Map,H=500;function _(t){let e=p.get(t);if(e!==void 0)return e;let r;try{r=new Intl.Locale(t.replace(/_/g,"-")).toString().toLowerCase()}catch{r=t}if(p.size>=H){let o=p.keys().next().value;o!==void 0&&p.delete(o)}return p.set(t,r),r}function Z(t){try{new Intl.Locale(F(t))}catch{throw new c({actual:t,reason:"not a valid BCP-47 locale tag"})}}var l=new Map,Y=500;function d(t,e,r){let o=t.formatToParts(e),n=o.findIndex(g=>g.type===r);/* c8 ignore start @preserve -- defensive guard against a real but
25
- unreproducible failure mode: an Intl implementation that omits the
26
- requested part type entirely for some locale. Checked every locale
27
- with unusual dayPeriod/weekday/month rendering available in this
28
- runtime's ICU data (ja-JP, zh-CN, th-TH, he-IL, ar-SA, ko-KR, fa-IR)
29
- against the exact formatter options getLocaleVocab uses (notably
30
- hour12: true for the dayPeriod formatter, which is what makes every
31
- locale here actually emit a dayPeriod part — omitting it is what
32
- produced a false "gap" during investigation). None omit their part
33
- on this runtime. A different ICU version or a non-Node Intl
34
- implementation could plausibly behave differently, so this stays a
35
- real check rather than an assertion. *//* c8 ignore start @preserve */if(n===-1)throw new c({message:`temporal-fmt: locale produced no "${r}" part while building match vocabulary.`});/* c8 ignore stop @preserve */let a=o[n].value,s=o[n-1],i=o[n+1];return s?.type==="literal"&&!/\s/.test(s.value)&&(a=s.value+a),i?.type==="literal"&&!/\s/.test(i.value)&&(a=a+i.value),a}function u(t,e,r){let o=new Map;for(let n=0;n<t.length;n++){let a=o.get(t[n]);if(a!==void 0)throw new Error(`temporal-fmt: locale "${r}" renders ${e} index ${a} and ${n} identically ("${t[n]}"). parse() can't reliably tell these apart for this locale/token, so this combination isn't supported.`);o.set(t[n],n)}}function S(t){Z(t);let e=_(t),r=f.get(e);if(r)return r;let o=l.get(e);if(o)return o;let n=F(t),a=new Intl.DateTimeFormat(n,{month:"long",timeZone:"UTC"}),s=new Intl.DateTimeFormat(n,{month:"short",timeZone:"UTC"}),i=[],g=[];for(let m=0;m<12;m++){let h=new Date(Date.UTC(2020,m,1));i.push(d(a,h,"month")),g.push(d(s,h,"month"))}u(i,"MMMM month",t),u(g,"MMM month",t);let N=new Intl.DateTimeFormat(n,{weekday:"long",timeZone:"UTC"}),O=new Intl.DateTimeFormat(n,{weekday:"short",timeZone:"UTC"}),w=[],y=[];for(let m=0;m<7;m++){let h=new Date(Date.UTC(2024,0,1+m));w.push(d(N,h,"weekday")),y.push(d(O,h,"weekday"))}u(w,"EEEE weekday",t),u(y,"EEE weekday",t);let A=new Intl.DateTimeFormat(n,{hour:"numeric",hour12:!0,timeZone:"UTC"}),D=d(A,new Date(Date.UTC(2020,0,1,1)),"dayPeriod"),P=d(A,new Date(Date.UTC(2020,0,1,13)),"dayPeriod"),U=[...new Set([D,P])],k={monthLong:i,monthShort:g,weekdayLong:w,weekdayShort:y,dayPeriod:U};if(l.size>=Y){let m=l.keys().next().value;m!==void 0&&l.delete(m)}return l.set(e,k),k}var $=new Map,M=256,C=256;function T(t){try{return new Intl.Locale(t.replace(/_/g,"-")).toString().toLowerCase()}catch{return t}}function W(t,e){if(typeof t!="string"||t.length===0)throw new c({actual:String(t),reason:"locale string must be non-empty"});if(t.length>M)throw new c({actual:String(t.length),reason:`locale tag must be at most ${M} characters`});x(t,e),j(e,t);let r=T(t);$.set(r,{...e})}function j(t,e){let r=(o,n,a)=>{if(o!==void 0){if(!Array.isArray(o))throw new c({actual:String(o),reason:`locale "${e}": "${n}" must be an array`});if(o.length!==a)throw new c({actual:String(o.length),reason:`locale "${e}": "${n}" must have ${a} entries (got ${o.length})`});for(let s=0;s<o.length;s++){if(typeof o[s]!="string"||o[s].length===0)throw new c({actual:String(o[s]),reason:`locale "${e}": "${n}[${s}]" must be a non-empty string`});if(o[s].length>C)throw new c({actual:String(o[s].length),reason:`locale "${e}": "${n}[${s}]" must be at most ${C} characters`})}}};r(t.quartersLong,"quartersLong",4),r(t.quartersShort,"quartersShort",4),r(t.erasLong,"erasLong",2),r(t.erasShort,"erasShort",2),r(t.ordinals,"ordinals",4)}function J(t){let e=T(t),r=$.get(e);if(r)return r;try{return{...S(t)}}catch{return}}function Q(t){let e=T(t);return $.has(e)}0&&(module.exports={getLocale,hasLocale,registerLocale});
95
+ of the locale string — it's never handed an arbitrary "calendar" value
96
+ a caller could actually get wrong. no live input to reject here.
97
+ leaving it unconstructed until the library accepts an actual calendar
98
+ parameter that could be invalid */
99
+ /* c8 ignore stop @preserve */
100
+ var InvalidLocaleError = class extends TemporalFmtError {
101
+ constructor(fields) {
102
+ const { message, ...rest } = fields;
103
+ super(
104
+ message ?? `locale "${fields.actual ?? ""}" is not a valid BCP-47 tag${fields.reason ? `: ${fields.reason}` : ""}.`,
105
+ { code: "INVALID_LOCALE", ...rest }
106
+ );
107
+ this.name = "InvalidLocaleError";
108
+ }
109
+ };
110
+ /* c8 ignore start @preserve -- unreachable from the current test suite,
111
+ see rationale above */
112
+ /* c8 ignore stop @preserve */
113
+
114
+ // src/localeVocab.ts
115
+ var customVocabs = /* @__PURE__ */ new Map();
116
+ var MAX_CUSTOM_VOCABS = 500;
117
+ var MAX_LOCALE_TAG_LENGTH = 256;
118
+ var MAX_VOCAB_ENTRY_LENGTH = 256;
119
+ function assertValidVocab(vocab, locale) {
120
+ const required = [
121
+ { key: "monthLong", length: 12, label: "long month names" },
122
+ { key: "monthShort", length: 12, label: "short month names" },
123
+ { key: "weekdayLong", length: 7, label: "long weekday names" },
124
+ { key: "weekdayShort", length: 7, label: "short weekday names" },
125
+ { key: "dayPeriod", length: 2, label: "day period markers (AM/PM-equivalent)" }
126
+ ];
127
+ for (const { key, length, label } of required) {
128
+ const value = vocab[key];
129
+ if (value === void 0) {
130
+ throw new Error(
131
+ `temporal-fmt: registerLocaleVocab for locale "${locale}" is missing required field "${key}" (${label}).`
132
+ );
133
+ }
134
+ if (!Array.isArray(value)) {
135
+ throw new Error(
136
+ `temporal-fmt: registerLocaleVocab for locale "${locale}": "${key}" must be an array, got ${typeof value}.`
137
+ );
138
+ }
139
+ if (value.length !== length) {
140
+ throw new Error(
141
+ `temporal-fmt: registerLocaleVocab for locale "${locale}": "${key}" must have exactly ${length} entries (got ${value.length}) \u2014 ${label}.`
142
+ );
143
+ }
144
+ value.forEach((entry, i) => {
145
+ if (typeof entry !== "string" || entry.length === 0) {
146
+ throw new Error(
147
+ `temporal-fmt: registerLocaleVocab for locale "${locale}": "${key}[${i}]" must be a non-empty string, got ${String(entry)}.`
148
+ );
149
+ }
150
+ if (entry.length > MAX_VOCAB_ENTRY_LENGTH) {
151
+ throw new RangeError(
152
+ `temporal-fmt: registerLocaleVocab for locale "${locale}": "${key}[${i}]" is too long (maximum ${MAX_VOCAB_ENTRY_LENGTH} characters).`
153
+ );
154
+ }
155
+ });
156
+ }
157
+ assertNoCollision(vocab.monthLong, "MMMM month", locale);
158
+ assertNoCollision(vocab.monthShort, "MMM month", locale);
159
+ assertNoCollision(vocab.weekdayLong, "EEEE weekday", locale);
160
+ assertNoCollision(vocab.weekdayShort, "EEE weekday", locale);
161
+ if (vocab.dayPeriod[0] === vocab.dayPeriod[1]) {
162
+ throw new Error(
163
+ `temporal-fmt: registerLocaleVocab for locale "${locale}": dayPeriod entries must differ (both are "${vocab.dayPeriod[0]}"); otherwise parse() can't tell AM from PM.`
164
+ );
165
+ }
166
+ }
167
+ var onVocabChanged = [];
168
+ function registerLocaleVocab(locale, vocab) {
169
+ if (typeof locale !== "string" || locale.length === 0) {
170
+ throw new Error(`temporal-fmt: registerLocaleVocab requires a non-empty locale string, got ${String(locale)}.`);
171
+ }
172
+ if (locale.length > MAX_LOCALE_TAG_LENGTH) {
173
+ throw new RangeError(`temporal-fmt: registerLocaleVocab locale is too long (maximum ${MAX_LOCALE_TAG_LENGTH} characters).`);
174
+ }
175
+ assertValidVocab(vocab, locale);
176
+ const cacheKey = canonicalCacheKey(locale);
177
+ if (!customVocabs.has(cacheKey) && customVocabs.size >= MAX_CUSTOM_VOCABS) {
178
+ throw new RangeError(`temporal-fmt: registerLocaleVocab reached the ${MAX_CUSTOM_VOCABS}-locale limit.`);
179
+ }
180
+ customVocabs.set(cacheKey, {
181
+ monthLong: [...vocab.monthLong],
182
+ monthShort: [...vocab.monthShort],
183
+ weekdayLong: [...vocab.weekdayLong],
184
+ weekdayShort: [...vocab.weekdayShort],
185
+ dayPeriod: [...vocab.dayPeriod]
186
+ });
187
+ vocabCache.delete(cacheKey);
188
+ for (const listener of onVocabChanged) listener();
189
+ }
190
+ function normalizeLocaleTag(locale) {
191
+ return locale.replace(/_/g, "-");
192
+ }
193
+ var canonicalKeyCache = /* @__PURE__ */ new Map();
194
+ var MAX_CANONICAL_KEY_CACHE = 500;
195
+ function canonicalCacheKey(locale) {
196
+ const hit = canonicalKeyCache.get(locale);
197
+ if (hit !== void 0) return hit;
198
+ let key;
199
+ try {
200
+ key = new Intl.Locale(locale.replace(/_/g, "-")).toString().toLowerCase();
201
+ } catch {
202
+ key = locale;
203
+ }
204
+ if (canonicalKeyCache.size >= MAX_CANONICAL_KEY_CACHE) {
205
+ const oldestKey = canonicalKeyCache.keys().next().value;
206
+ if (oldestKey !== void 0) canonicalKeyCache.delete(oldestKey);
207
+ }
208
+ canonicalKeyCache.set(locale, key);
209
+ return key;
210
+ }
211
+ function assertValidLocaleTag(locale) {
212
+ try {
213
+ new Intl.Locale(normalizeLocaleTag(locale));
214
+ } catch {
215
+ throw new InvalidLocaleError({
216
+ actual: locale,
217
+ reason: "not a valid BCP-47 locale tag"
218
+ });
219
+ }
220
+ }
221
+ var vocabCache = /* @__PURE__ */ new Map();
222
+ var MAX_VOCAB_CACHE_SIZE = 500;
223
+ function partValue(formatter, date, type) {
224
+ const parts = formatter.formatToParts(date);
225
+ const index = parts.findIndex((p) => p.type === type);
226
+ /* c8 ignore start @preserve -- defensive guard against a real but
227
+ unreproducible failure mode: an Intl implementation that omits the
228
+ requested part type entirely for some locale. Checked every locale
229
+ with unusual dayPeriod/weekday/month rendering available in this
230
+ runtime's ICU data (ja-JP, zh-CN, th-TH, he-IL, ar-SA, ko-KR, fa-IR)
231
+ against the exact formatter options getLocaleVocab uses (notably
232
+ hour12: true for the dayPeriod formatter, which is what makes every
233
+ locale here actually emit a dayPeriod part — omitting it is what
234
+ produced a false "gap" during investigation). None omit their part
235
+ on this runtime. A different ICU version or a non-Node Intl
236
+ implementation could plausibly behave differently, so this stays a
237
+ real check rather than an assertion. */
238
+ /* c8 ignore start @preserve */
239
+ if (index === -1) {
240
+ throw new InvalidLocaleError({
241
+ message: `temporal-fmt: locale produced no "${type}" part while building match vocabulary.`
242
+ });
243
+ }
244
+ /* c8 ignore stop @preserve */
245
+ let value = parts[index].value;
246
+ const prev = parts[index - 1];
247
+ const next = parts[index + 1];
248
+ if (prev?.type === "literal" && !/\s/.test(prev.value)) value = prev.value + value;
249
+ if (next?.type === "literal" && !/\s/.test(next.value)) value = value + next.value;
250
+ return value;
251
+ }
252
+ function assertNoCollision(names, label, locale) {
253
+ const seen = /* @__PURE__ */ new Map();
254
+ for (let i = 0; i < names.length; i++) {
255
+ const prior = seen.get(names[i]);
256
+ if (prior !== void 0) {
257
+ throw new Error(
258
+ `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.`
259
+ );
260
+ }
261
+ seen.set(names[i], i);
262
+ }
263
+ }
264
+ function getLocaleVocab(locale) {
265
+ assertValidLocaleTag(locale);
266
+ const cacheKey = canonicalCacheKey(locale);
267
+ const custom = customVocabs.get(cacheKey);
268
+ if (custom) {
269
+ return custom;
270
+ }
271
+ const cached = vocabCache.get(cacheKey);
272
+ if (cached) {
273
+ return cached;
274
+ }
275
+ const intlLocale = normalizeLocaleTag(locale);
276
+ const monthLongFmt = new Intl.DateTimeFormat(intlLocale, { month: "long", timeZone: "UTC" });
277
+ const monthShortFmt = new Intl.DateTimeFormat(intlLocale, { month: "short", timeZone: "UTC" });
278
+ const monthLong = [];
279
+ const monthShort = [];
280
+ for (let m = 0; m < 12; m++) {
281
+ const date = new Date(Date.UTC(2020, m, 1));
282
+ monthLong.push(partValue(monthLongFmt, date, "month"));
283
+ monthShort.push(partValue(monthShortFmt, date, "month"));
284
+ }
285
+ assertNoCollision(monthLong, "MMMM month", locale);
286
+ assertNoCollision(monthShort, "MMM month", locale);
287
+ const weekdayLongFmt = new Intl.DateTimeFormat(intlLocale, { weekday: "long", timeZone: "UTC" });
288
+ const weekdayShortFmt = new Intl.DateTimeFormat(intlLocale, { weekday: "short", timeZone: "UTC" });
289
+ const weekdayLong = [];
290
+ const weekdayShort = [];
291
+ for (let d = 0; d < 7; d++) {
292
+ const date = new Date(Date.UTC(2024, 0, 1 + d));
293
+ weekdayLong.push(partValue(weekdayLongFmt, date, "weekday"));
294
+ weekdayShort.push(partValue(weekdayShortFmt, date, "weekday"));
295
+ }
296
+ assertNoCollision(weekdayLong, "EEEE weekday", locale);
297
+ assertNoCollision(weekdayShort, "EEE weekday", locale);
298
+ const dayPeriodFmt = new Intl.DateTimeFormat(intlLocale, { hour: "numeric", hour12: true, timeZone: "UTC" });
299
+ const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), "dayPeriod");
300
+ const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), "dayPeriod");
301
+ const dayPeriod = [.../* @__PURE__ */ new Set([am, pm])];
302
+ const vocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };
303
+ if (vocabCache.size >= MAX_VOCAB_CACHE_SIZE) {
304
+ const oldestKey = vocabCache.keys().next().value;
305
+ if (oldestKey !== void 0) vocabCache.delete(oldestKey);
306
+ }
307
+ vocabCache.set(cacheKey, vocab);
308
+ return vocab;
309
+ }
310
+
311
+ // src/localeRegistry.ts
312
+ var extendedVocabs = /* @__PURE__ */ new Map();
313
+ var MAX_LOCALE_TAG_LENGTH2 = 256;
314
+ var MAX_VOCAB_STRING_LENGTH = 256;
315
+ function canonicalKey(locale) {
316
+ try {
317
+ return new Intl.Locale(locale.replace(/_/g, "-")).toString().toLowerCase();
318
+ } catch {
319
+ return locale;
320
+ }
321
+ }
322
+ function registerLocale(locale, vocab) {
323
+ if (typeof locale !== "string" || locale.length === 0) {
324
+ throw new InvalidLocaleError({ actual: String(locale), reason: "locale string must be non-empty" });
325
+ }
326
+ if (locale.length > MAX_LOCALE_TAG_LENGTH2) {
327
+ throw new InvalidLocaleError({ actual: String(locale.length), reason: `locale tag must be at most ${MAX_LOCALE_TAG_LENGTH2} characters` });
328
+ }
329
+ registerLocaleVocab(locale, vocab);
330
+ validateExtended(vocab, locale);
331
+ const key = canonicalKey(locale);
332
+ extendedVocabs.set(key, { ...vocab });
333
+ }
334
+ function validateExtended(vocab, locale) {
335
+ const checkArray = (val, key, length) => {
336
+ if (val === void 0) return;
337
+ if (!Array.isArray(val)) {
338
+ throw new InvalidLocaleError({ actual: String(val), reason: `locale "${locale}": "${key}" must be an array` });
339
+ }
340
+ if (val.length !== length) {
341
+ throw new InvalidLocaleError({ actual: String(val.length), reason: `locale "${locale}": "${key}" must have ${length} entries (got ${val.length})` });
342
+ }
343
+ for (let i = 0; i < val.length; i++) {
344
+ if (typeof val[i] !== "string" || val[i].length === 0) {
345
+ throw new InvalidLocaleError({ actual: String(val[i]), reason: `locale "${locale}": "${key}[${i}]" must be a non-empty string` });
346
+ }
347
+ if (val[i].length > MAX_VOCAB_STRING_LENGTH) {
348
+ throw new InvalidLocaleError({ actual: String(val[i].length), reason: `locale "${locale}": "${key}[${i}]" must be at most ${MAX_VOCAB_STRING_LENGTH} characters` });
349
+ }
350
+ }
351
+ };
352
+ checkArray(vocab.quartersLong, "quartersLong", 4);
353
+ checkArray(vocab.quartersShort, "quartersShort", 4);
354
+ checkArray(vocab.erasLong, "erasLong", 2);
355
+ checkArray(vocab.erasShort, "erasShort", 2);
356
+ checkArray(vocab.ordinals, "ordinals", 4);
357
+ }
358
+ function getLocale(locale) {
359
+ const key = canonicalKey(locale);
360
+ const ext = extendedVocabs.get(key);
361
+ if (ext) return ext;
362
+ try {
363
+ const base = getLocaleVocab(locale);
364
+ return { ...base };
365
+ } catch {
366
+ return void 0;
367
+ }
368
+ }
369
+ function hasLocale(locale) {
370
+ const key = canonicalKey(locale);
371
+ return extendedVocabs.has(key);
372
+ }
373
+ // Annotate the CommonJS export names for ESM import in node:
374
+ 0 && (module.exports = {
375
+ getLocale,
376
+ hasLocale,
377
+ registerLocale
378
+ });
36
379
  //# sourceMappingURL=localeRegistry.cjs.map