temporal-fmt 0.9.31 → 0.9.41

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 (86) hide show
  1. package/LICENSE +196 -17
  2. package/README.md +319 -9
  3. package/dist/calendarUtils.cjs.map +1 -1
  4. package/dist/calendarUtils.js +2 -2
  5. package/dist/{chunk-GPB4JHGI.js → chunk-5553YBSV.js} +4 -4
  6. package/dist/chunk-5553YBSV.js.map +1 -0
  7. package/dist/{chunk-L24NPTAJ.js → chunk-AX3YNHKK.js} +4 -4
  8. package/dist/chunk-AX3YNHKK.js.map +1 -0
  9. package/dist/{chunk-ACKKWH5Z.js → chunk-C3QMVR5T.js} +4 -4
  10. package/dist/chunk-C3QMVR5T.js.map +1 -0
  11. package/dist/{chunk-UOPAV3WX.js → chunk-CGI4FL6H.js} +2 -2
  12. package/dist/chunk-CGI4FL6H.js.map +1 -0
  13. package/dist/chunk-DDE7YTGV.js +2939 -0
  14. package/dist/chunk-DDE7YTGV.js.map +1 -0
  15. package/dist/{chunk-ZQZF5EXW.js → chunk-DFWTCRS3.js} +1 -1
  16. package/dist/chunk-DFWTCRS3.js.map +1 -0
  17. package/dist/{chunk-DR62Z6GV.js → chunk-HJCPNB7J.js} +178 -4
  18. package/dist/chunk-HJCPNB7J.js.map +1 -0
  19. package/dist/{chunk-4N3WCGEY.js → chunk-JI3B7C5L.js} +5 -5
  20. package/dist/chunk-JI3B7C5L.js.map +1 -0
  21. package/dist/{chunk-MKPDLQKD.js → chunk-LOJO7SGQ.js} +1 -1
  22. package/dist/chunk-LOJO7SGQ.js.map +1 -0
  23. package/dist/{chunk-5UTV5VP6.js → chunk-M6D57BHI.js} +6 -6
  24. package/dist/chunk-M6D57BHI.js.map +1 -0
  25. package/dist/{chunk-EGUUO6LE.js → chunk-OLNU4LON.js} +1 -1
  26. package/dist/chunk-OLNU4LON.js.map +1 -0
  27. package/dist/{chunk-YEUASGDK.js → chunk-SOPTTXUI.js} +1 -1
  28. package/dist/chunk-SOPTTXUI.js.map +1 -0
  29. package/dist/{chunk-M3GDTHMT.js → chunk-UF3RVLVL.js} +3 -3
  30. package/dist/chunk-UF3RVLVL.js.map +1 -0
  31. package/dist/{chunk-H7YQX25T.js → chunk-YXN5RAQ3.js} +3 -3
  32. package/dist/chunk-YXN5RAQ3.js.map +1 -0
  33. package/dist/{chunk-TSLPIURC.js → chunk-Z33O2X7I.js} +2 -2
  34. package/dist/chunk-Z33O2X7I.js.map +1 -0
  35. package/dist/{chunk-HXY2GUUM.js → chunk-ZSHHTAKC.js} +2 -2
  36. package/dist/chunk-ZSHHTAKC.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.map +1 -1
  40. package/dist/duration.js +5 -5
  41. package/dist/format.cjs.map +1 -1
  42. package/dist/format.js +7 -7
  43. package/dist/index.cjs +2744 -1951
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.d.cts +108 -24
  46. package/dist/index.d.ts +108 -24
  47. package/dist/index.js +710 -2265
  48. package/dist/index.js.map +1 -1
  49. package/dist/interval.cjs +4569 -104
  50. package/dist/interval.cjs.map +1 -1
  51. package/dist/interval.js +15 -10
  52. package/dist/localeRegistry.cjs.map +1 -1
  53. package/dist/localeRegistry.js +2 -2
  54. package/dist/modApi.d.cts +131 -0
  55. package/dist/modApi.d.ts +131 -0
  56. package/dist/parse.cjs.map +1 -1
  57. package/dist/parse.js +7 -7
  58. package/dist/recurrence.cjs.map +1 -1
  59. package/dist/recurrence.js +4 -5
  60. package/dist/relativeTime.cjs.map +1 -1
  61. package/dist/relativeTime.js +7 -7
  62. package/dist/runtime.d.cts +201 -0
  63. package/dist/runtime.d.ts +201 -0
  64. package/dist/timezone.cjs.map +1 -1
  65. package/dist/timezone.js +5 -5
  66. package/package.json +4 -4
  67. package/scripts/cli.mjs +10 -0
  68. package/dist/chunk-4N3WCGEY.js.map +0 -1
  69. package/dist/chunk-5UTV5VP6.js.map +0 -1
  70. package/dist/chunk-ACKKWH5Z.js.map +0 -1
  71. package/dist/chunk-DR62Z6GV.js.map +0 -1
  72. package/dist/chunk-EGUUO6LE.js.map +0 -1
  73. package/dist/chunk-GPB4JHGI.js.map +0 -1
  74. package/dist/chunk-H7YQX25T.js.map +0 -1
  75. package/dist/chunk-HXY2GUUM.js.map +0 -1
  76. package/dist/chunk-L24NPTAJ.js.map +0 -1
  77. package/dist/chunk-M3GDTHMT.js.map +0 -1
  78. package/dist/chunk-MKPDLQKD.js.map +0 -1
  79. package/dist/chunk-P3G4JRYC.js +0 -205
  80. package/dist/chunk-P3G4JRYC.js.map +0 -1
  81. package/dist/chunk-RIZ3HN37.js +0 -181
  82. package/dist/chunk-RIZ3HN37.js.map +0 -1
  83. package/dist/chunk-TSLPIURC.js.map +0 -1
  84. package/dist/chunk-UOPAV3WX.js.map +0 -1
  85. package/dist/chunk-YEUASGDK.js.map +0 -1
  86. package/dist/chunk-ZQZF5EXW.js.map +0 -1
package/LICENSE CHANGED
@@ -1,22 +1,201 @@
1
- MIT License
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
2
4
 
3
- Copyright (c) 2026 DirazCoder
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
4
6
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
7
+ 1. Definitions.
11
8
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
14
11
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
22
14
 
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  ![coverage](https://img.shields.io/badge/coverage-100%25%20(c8)-brightgreen?style=flat-square)
4
4
 
5
- Format `Temporal.PlainDate` / `PlainTime` / `PlainDateTime` / `ZonedDateTime` objects
6
- using date-fns-style token strings.
5
+ Format and parse `Temporal` values (`PlainDate`, `PlainTime`, `PlainDateTime`,
6
+ `ZonedDateTime`) using date-fns-style tokens, with real validation — bad input
7
+ throws instead of silently returning garbage. Locale-aware, no deps.
7
8
 
8
9
  Node 26 shipped native `Temporal` and then pointedly left out a custom-string
9
10
  formatter. TC39's take: use `Intl.DateTimeFormat` and leave string-token syntax
@@ -12,7 +13,8 @@ muscle memory from date-fns, moment, or dayjs, that's a rough adjustment. This
12
13
  library exists so you don't have to make it.
13
14
 
14
15
  Zero dependencies. Native on Node 26+, or bring your own via a polyfill or
15
- `setTemporal()`.
16
+ `setTemporal()`. Import from a subpath (`temporal-fmt/format`, `temporal-fmt/parse`,
17
+ etc.) to pull in only what you use — see [Subpath imports](#subpath-imports).
16
18
 
17
19
  Locale-aware tokens need Node 20+ regardless of which path you use — native
18
20
  on 26+, or falling back to the Temporal implementation's own
@@ -26,10 +28,27 @@ npm install temporal-fmt
26
28
 
27
29
  [View on npm](https://www.npmjs.com/package/temporal-fmt)
28
30
 
29
- This library is genuinely large — locales, recurrence, business calendars, timezone disambiguation, an analyzer, config layers, custom token extensibility, a CLI. A substantial amount of configuration and customization is packed in here. But none of that is required reading. The reason this library exists in the first place is formatting and parsing dates with token strings, and that part stays simple: `format(temporal, formatStr)` and `parse(formatStr, input)`, the same shape as date-fns or Day.js. Read [Providing `Temporal`](#providing-temporal) and [Formatting](#formatting)/[Parsing](#parsing), and you're covered for the common case — everything past that is there for when you actually need it, not before.
31
+ ## Get started
32
+
33
+ ```js
34
+ import { format } from 'temporal-fmt/format';
35
+ import { parse } from 'temporal-fmt/parse';
36
+
37
+ const date = Temporal.PlainDate.from('2026-08-04');
38
+ format(date, 'yyyy-MM-dd'); // "2026-08-04"
39
+
40
+ parse('yyyy-MM-dd HH:mm', '2026-08-04 15:45'); // Temporal.PlainDateTime
41
+ ```
42
+
43
+ That's the whole library for most use cases — `format(temporal, formatStr)` in, `parse(formatStr, input)` out, same shape as date-fns or Day.js. Import from the subpaths (`temporal-fmt/format`, `temporal-fmt/parse`) shown above, not the bare `temporal-fmt` package — a bundler only ships what you actually call that way. Measured with esbuild: ~27KB for `format` alone via the subpath, versus ~68KB for the same function pulled from the bare import.
44
+
45
+ Below Node 26, `Temporal` isn't global yet, so you'll need a polyfill first — see [Providing `Temporal`](#providing-temporal). On Node 26+ the snippet above just works.
46
+
47
+ The package looks large on npm — locales, recurrence, business calendars, timezone disambiguation, an analyzer, config layers, a CLI — but none of that is required reading or required bundle weight. It's there behind its own subpaths for when you need it; see [Subpath imports](#subpath-imports) for the full list and [Formatting](#formatting)/[Parsing](#parsing) for the details on the two functions above.
30
48
 
31
49
  ## Contents
32
50
 
51
+ - [Get started](#get-started)
33
52
  - [Providing `Temporal`](#providing-temporal)
34
53
  - [Formatting](#formatting)
35
54
  - [Parsing](#parsing)
@@ -51,6 +70,7 @@ This library is genuinely large — locales, recurrence, business calendars, tim
51
70
  - [Extending with custom tokens](#extending-with-custom-tokens)
52
71
  - [IDE tooling data](#ide-tooling-data)
53
72
  - [CLI](#cli)
73
+ - [Mods](#mods)
54
74
  - [Subpath imports](#subpath-imports)
55
75
  - [Migrating from Day.js or date-fns](#migrating-from-dayjs-or-date-fns)
56
76
  - [Known limitations](#known-limitations)
@@ -71,7 +91,8 @@ Use a polyfill like [`temporal-polyfill`](https://github.com/fullcalendar/tempor
71
91
 
72
92
  ```js
73
93
  import 'temporal-polyfill/global'
74
- import { format, parse } from 'temporal-fmt';
94
+ import { format } from 'temporal-fmt/format';
95
+ import { parse } from 'temporal-fmt/parse';
75
96
 
76
97
  parse(...);
77
98
  ```
@@ -82,7 +103,9 @@ Set a Temporal implementation explicitly, once, before your app's first `format(
82
103
 
83
104
  ```js
84
105
  import { Temporal } from 'temporal-polyfill/full';
85
- import { setTemporal, format, parse } from 'temporal-fmt';
106
+ import { setTemporal } from 'temporal-fmt';
107
+ import { format } from 'temporal-fmt/format';
108
+ import { parse } from 'temporal-fmt/parse';
86
109
 
87
110
  setTemporal(Temporal); // once, before using format or parse
88
111
  ```
@@ -94,7 +117,7 @@ Anything that constructs a `Temporal` value from scratch needs this — `parse()
94
117
  ## Formatting
95
118
 
96
119
  ```js
97
- import { format } from 'temporal-fmt';
120
+ import { format } from 'temporal-fmt/format';
98
121
 
99
122
  const date = Temporal.PlainDate.from('2026-08-04');
100
123
  format(date, 'yyyy-MM-dd'); // "2026-08-04"
@@ -120,7 +143,7 @@ Try a token your input type doesn't support — `HH` on a `PlainDate`, say — a
120
143
  `parse()` builds a real `Temporal.PlainDate` / `PlainTime` / `PlainDateTime` / `ZonedDateTime` out of a string, picking whichever type fits the tokens present:
121
144
 
122
145
  ```js
123
- import { parse } from 'temporal-fmt';
146
+ import { parse } from 'temporal-fmt/parse';
124
147
 
125
148
  parse('yyyy-MM-dd HH:mm', '2026-08-04 15:45'); // Temporal.PlainDateTime
126
149
  parse('yyyy-MM', '2026-08-04T15:45:30'); // throws — shape doesn't match
@@ -872,6 +895,291 @@ temporal-fmt> exit
872
895
 
873
896
  Type a subcommand with all its arguments inline (`validate yyyy-MM-dd`) or just the subcommand name — the REPL prompts for whatever's missing, one field at a time. Errors print and the session keeps going; `exit`, `quit`, or Ctrl+D ends it. This is the same subcommand logic as one-shot mode, just wrapped in a loop that asks instead of exiting on a missing argument — one-shot stays there for scripting, and doesn't touch the REPL machinery.
874
897
 
898
+ ## Mods (advanced, optional)
899
+
900
+ Most people don't need this. It's a feature I added just in case you want to fix or tweak something in the library without forking the whole thing — not something you're expected to reach for day to day. If you never touch `mods/`, nothing about normal usage changes for you.
901
+
902
+ The mod-related scripts (`loadMods.mjs`, `modConfig.mjs`, `semverRange.mjs`) live in this repo's `scripts/` folder, but they're not part of what actually ships in the npm package — the published package only includes `cli.mjs` from that folder. So if you installed this from npm, those files simply aren't on disk in your `node_modules`; they're repo-only. See [Using mods outside the CLI](#using-mods-outside-the-cli) if you want to copy the loader out of this repo and use it yourself.
903
+
904
+ `registerLocale`, `createHolidayCalendar`, and `createFormatter` are already how you extend this library without forking it — [Locales](#locales), [Business calendars and holidays](#business-calendars-and-holidays), and [Extending with custom tokens](#extending-with-custom-tokens) all cover them. Mods are just a delivery mechanism on top of those same functions: drop a file in a `mods/` folder, the CLI picks it up on startup and runs it. No publishing to npm, no build step in this repo, no manifest to register anywhere. If you've used a Minecraft mods folder, it's the same idea — a file the host looks for and loads, not a package the host depends on.
905
+
906
+ This exists so bugfixes and locale corrections don't have to wait on a PR merging and a release going out. If en-GB's holiday list is wrong for your team, a locale you need isn't covered yet, or you want to shave overhead off a hot path, write a mod and drop it in. It's not the right tool for genuinely new capability — if you're building something the override surface can't express, that's a sign to open an issue or PR the feature into the library itself, not to keep stretching a mod to cover it. Whether a given fix ever gets upstreamed into this repo is a separate question from whether it works today as a mod.
907
+
908
+ ### Writing a mod
909
+
910
+ A mod is a `.mjs` file that default-exports an object with a `name` and a `register(ctx, config)` function. `ctx` is the same registration API `index.ts` exports for everyone else — `registerLocale`, `registerLocaleVocab`, `registerRelativeGrammar`, `createFormatter`, `createHolidayCalendar` — nothing beyond that. A mod that needs more than those five functions expose is asking for something this library doesn't support yet, not something to route around by reaching into internals that could shift under it without warning. `config` is `{}` for a loose `.mjs` mod — there's no manifest to declare settings in, so there's nothing to resolve; see [Mod settings and `config/`](#mod-settings-and-config) for mods that need user-adjustable settings, which means packaging as `.tfmod`.
911
+
912
+ ```js
913
+ // mods/en-gb-bank-holidays.mjs
914
+ export default {
915
+ name: 'en-gb-bank-holidays',
916
+ version: '1.0.0',
917
+ register(ctx) {
918
+ ctx.createHolidayCalendar([
919
+ { month: 1, day: 1, name: "New Year's Day" },
920
+ { month: 12, day: 25, name: 'Christmas Day' },
921
+ { month: 12, day: 26, name: 'Boxing Day' },
922
+ ]);
923
+ },
924
+ };
925
+ ```
926
+
927
+ Put that in `mods/` at your project root (the folder the CLI is run from, not inside this package's own checkout) and run any CLI command — the loader reports what it found on stderr:
928
+
929
+ ```
930
+ $ temporal-fmt validate "yyyy-MM-dd"
931
+ temporal-fmt mods:
932
+ loaded en-gb-bank-holidays@1.0.0 (en-gb-bank-holidays.mjs)
933
+ valid
934
+ ```
935
+
936
+ `version` is optional and only shows up in that report — it's for your own tracking, not something the loader checks. That's a different field from `temporalFmtVersion`, which *is* checked against the installed `temporal-fmt` version, but only exists on `.tfmod` manifests (see [Pinning a mod to a `temporal-fmt` version](#pinning-a-mod-to-a-temporal-fmt-version)) — a loose `.mjs` mod has no manifest to declare it in.
937
+
938
+ ### Packaging a mod as `.tfmod`
939
+
940
+ A loose `.mjs` file covers the common case, but it's one file — no bundled data, and the loader has to `import()` it just to find out its `name` before deciding load order. For anything bigger than that, package the mod as a `.tfmod` archive instead: a gzipped tar (same format as `.tgz`, renamed for identity) containing a manifest the loader can read without running any code, plus the mod's actual implementation:
941
+
942
+ ```
943
+ en-gb-bank-holidays.tfmod
944
+ ├── mod.json — name, version, main, requires, priority, temporalFmtVersion, config
945
+ ├── main.mjs — the mod's entry point (same shape as a loose .mjs mod's default export, minus `name`/`version`/`requires`/`priority` — mod.json owns those)
946
+ └── data/ — optional: JSON files, locale tables, anything main.mjs wants to read at register() time
947
+ ```
948
+
949
+ ```json
950
+ // mod.json
951
+ {
952
+ "name": "en-gb-bank-holidays",
953
+ "version": "1.0.0",
954
+ "main": "main.mjs",
955
+ "requires": ["some-other-mod"],
956
+ "priority": 0,
957
+ "temporalFmtVersion": "^0.9.0"
958
+ }
959
+ ```
960
+
961
+ ```js
962
+ // main.mjs
963
+ export default {
964
+ register(ctx) {
965
+ ctx.createHolidayCalendar([
966
+ { month: 1, day: 1, name: "New Year's Day" },
967
+ { month: 12, day: 25, name: 'Christmas Day' },
968
+ ]);
969
+ },
970
+ };
971
+ ```
972
+
973
+ Build the archive with plain `tar` — no special tooling:
974
+
975
+ ```sh
976
+ tar -czf en-gb-bank-holidays.tfmod mod.json main.mjs data/
977
+ ```
978
+
979
+ Drop that in `mods/` alongside any loose `.mjs` mods you have; the loader treats both formats as one pool for load-order and conflict purposes. The report shows `mod.json`'s `name`, not anything from `main.mjs` itself:
980
+
981
+ ```
982
+ $ temporal-fmt validate "yyyy-MM-dd"
983
+ temporal-fmt mods:
984
+ loaded en-gb-bank-holidays@1.0.0 (en-gb-bank-holidays.tfmod)
985
+ valid
986
+ ```
987
+
988
+ Why bother with an archive format at all instead of just supporting multi-file `.mjs` mods directly: `mod.json` is metadata the loader can read with zero code execution, which is what makes cross-mod dependency resolution work honestly — with a loose `.mjs` mod, the loader has no choice but to `import()` the file to learn its `name`/`requires`, before it even knows whether that mod should run. A `.tfmod`'s manifest is checked, and the whole dependency graph is resolved, before `main.mjs` is ever imported. At the current few-mods-loaded-once-at-CLI-startup scale that distinction mostly doesn't matter — but it's the honest reason the format exists rather than "loose files but with a folder," and it's what a "list what's installed without running any of it" feature would build on if that ever comes up.
989
+
990
+ Failure modes are per-archive, same as loose mods — one bad `.tfmod` doesn't block anything else in `mods/`:
991
+
992
+ - `mod.json` missing or malformed (no `name`, no `main`, or `requires`/`priority`/`temporalFmtVersion`/`config` the wrong type) — reported with what was expected, `main.mjs` is never imported.
993
+ - `mod.json` names a `main` file that isn't actually in the archive — reported with the missing filename.
994
+ - The archive isn't a valid gzip/tar (corrupted, wrong format, a `.tfmod` extension slapped on some other file) — reported with the extraction error.
995
+ - `main.mjs`'s default export doesn't have a `register` function — reported, same as a loose mod's malformed export.
996
+ - `temporalFmtVersion` doesn't match the installed `temporal-fmt` version — reported with the range and the actual version, `main.mjs` is never imported. See [Pinning a mod to a `temporal-fmt` version](#pinning-a-mod-to-a-temporal-fmt-version).
997
+
998
+ Extraction happens to a temporary directory that's cleaned up after the load pass — nothing from a `.tfmod` sticks around on disk after the CLI command finishes. Extraction shells out to the system `tar` binary rather than adding a tar/gzip-parsing dependency, consistent with this package staying dependency-free (see [Providing `Temporal`](#providing-temporal) for the same call made about the polyfill) — if `tar` isn't on the system `PATH`, the archive fails to load with that reason rather than crashing the CLI.
999
+
1000
+ ### Pinning a mod to a `temporal-fmt` version
1001
+
1002
+ `mod.json` can declare `temporalFmtVersion`, either an exact version (`"0.9.32"`) or a caret range (`"^0.9.0"`, meaning ">=0.9.0, <0.10.0" — same meaning npm gives `^` in `package.json`). If the installed `temporal-fmt` doesn't satisfy it, the mod fails to load with the range and the actual version, before `main.mjs` is ever imported:
1003
+
1004
+ ```
1005
+ failed holidays.tfmod: "en-gb-bank-holidays" needs temporal-fmt ^2.0.0 (>=2.0.0 <3.0.0), host is 0.9.32
1006
+ ```
1007
+
1008
+ This exists because nothing else catches the alternative: a mod built against one version's override surface (which functions are zero-fanout and therefore overridable — see [Overriding functions](#overriding-functions)) has no way to know if a future release moved a function it depends on, and would otherwise fail with whatever confusing error `register()` happens to throw, or — worse — silently do nothing if the call it expected to matter just no longer has any effect. A declared range turns that into one clear, pre-`register()` failure instead.
1009
+
1010
+ Omitting `temporalFmtVersion` is allowed — the mod loads against whatever version is installed, same as before this field existed. Loose `.mjs` mods have no manifest to put this in at all, so they can't declare a version requirement; that's one real reason to prefer `.tfmod` for anything you plan to distribute rather than just run yourself.
1011
+
1012
+ There's no dependency-resolution logic here, unlike `requires`/`priority` — this is a single boolean check (does the host version satisfy the range), not something that affects load order.
1013
+
1014
+ ### Mod settings and `config/`
1015
+
1016
+ A mod can declare user-adjustable settings in `mod.json`'s `config` array, and `register()` receives the resolved values as its second argument:
1017
+
1018
+ ```json
1019
+ // mod.json
1020
+ {
1021
+ "name": "en-gb-bank-holidays",
1022
+ "main": "main.mjs",
1023
+ "config": [
1024
+ { "key": "includeScottish", "type": "boolean", "default": false },
1025
+ { "key": "observedRule", "type": "enum", "default": "nearest-weekday", "choices": ["nearest-weekday", "strict-date"] },
1026
+ { "key": "yearsAhead", "type": "number", "default": 5, "min": 1, "max": 20 }
1027
+ ]
1028
+ }
1029
+ ```
1030
+
1031
+ ```js
1032
+ // main.mjs
1033
+ export default {
1034
+ register(ctx, config) {
1035
+ const years = config.yearsAhead; // 5, unless overridden below
1036
+ ctx.createHolidayCalendar(buildHolidays({ scottish: config.includeScottish, years }));
1037
+ },
1038
+ };
1039
+ ```
1040
+
1041
+ Four setting types are supported: `string`, `number` (with optional `min`/`max`), `boolean`, and `enum` (a string constrained to `choices`). Every entry needs a `key` and a `default` — the default is what `register()` gets if the user hasn't overridden that setting, which also means a mod with no `config/<name>.json` file on disk at all still runs normally, just entirely on defaults.
1042
+
1043
+ To override a setting, drop a JSON file at `config/<mod-name>.json` — **next to `mods/`, not inside it** (so re-downloading or updating the `.tfmod` never touches a user's settings, the same reason Forge keeps `config/` and `mods/` as siblings rather than bundling settings into the jar):
1044
+
1045
+ ```
1046
+ your-project/
1047
+ ├── mods/
1048
+ │ └── en-gb-bank-holidays.tfmod
1049
+ └── config/
1050
+ └── en-gb-bank-holidays.json — { "includeScottish": true, "yearsAhead": 10 }
1051
+ ```
1052
+
1053
+ Only keys the schema actually declares can be set — anything else is a mistake worth surfacing, not a silent no-op:
1054
+
1055
+ ```
1056
+ temporal-fmt mods:
1057
+ loaded en-gb-bank-holidays@1.0.0 (en-gb-bank-holidays.tfmod)
1058
+ failed config/en-gb-bank-holidays.json: en-gb-bank-holidays: config key "yearsAhead" must be <= 20, got 50 (using default)
1059
+ failed config/en-gb-bank-holidays.json: en-gb-bank-holidays: unknown config key "includeWelsh" (not declared in this mod's schema)
1060
+ ```
1061
+
1062
+ An invalid value for a declared key falls back to that key's default rather than failing the whole mod — one typo'd number in a config file shouldn't take down a working mod, but it's reported so the mistake doesn't go unnoticed either. This is deliberately not JSON Schema: no nesting, no `$ref`, no conditional rules — just the handful of primitive shapes an actual setting realistically is, kept dependency-free the same way `temporalFmtVersion` checking and `.tfmod` extraction are.
1063
+
1064
+ Loose `.mjs` mods have no manifest to declare a schema in, so `register()`'s second argument is always `{}` for them — same as a `.tfmod` mod that didn't declare a `config` field at all.
1065
+
1066
+ ### Load order, dependencies, and conflicts
1067
+
1068
+ By default mods load in filename order — alphabetical, deterministic, but not something you'd want to rely on once two mods actually need to run in a specific order relative to each other. Two fields on the mod object control that directly:
1069
+
1070
+ - `requires: string[]` — other mods' `name` fields that must load (and finish `register()`) before this one. The loader resolves this as a dependency graph, not just "sort requires first" — if A requires B and B requires nothing, B always loads first regardless of filename.
1071
+ - `priority: number` — tiebreak for mods with no dependency relationship to each other. Higher loads later. Defaults to `0`.
1072
+
1073
+ ```js
1074
+ export default {
1075
+ name: 'extended-en-gb-holidays',
1076
+ requires: ['en-gb-bank-holidays'],
1077
+ priority: 10,
1078
+ register(ctx) {
1079
+ // runs after en-gb-bank-holidays, and after anything else at a lower priority
1080
+ },
1081
+ };
1082
+ ```
1083
+
1084
+ Two failure modes come out of this, both reported per-mod without blocking the rest:
1085
+
1086
+ - **Missing dependency** — `requires` names a mod that isn't in `mods/`. That mod fails to load; whatever it would've registered doesn't happen, and other mods that don't depend on it load normally.
1087
+ - **Circular dependency** — A requires B requires A (or a longer cycle). Every mod in the cycle fails, each reported with what it's still waiting on.
1088
+
1089
+ Registration itself is still last-write-wins, same as calling `registerLocale` twice for the same tag outside of mods — that's existing, intentional behavior (see [Locales](#locales)), not something mods change. What mods add is *visibility* into it: if two mods register the same locale tag, the same relative-time-grammar language, or the same custom token name, the load report calls it out as a conflict and says which one won:
1090
+
1091
+ ```
1092
+ temporal-fmt mods:
1093
+ loaded holiday-pack-a (conflict-1.mjs)
1094
+ loaded holiday-pack-b (conflict-2.mjs)
1095
+ conflict on locale "cv-CV": holiday-pack-a, holiday-pack-b — "holiday-pack-b" wins (loaded last)
1096
+ ```
1097
+
1098
+ This is informational, not a failure — both mods still loaded, the last one to register just took the key, and now you know it happened instead of silently getting whichever mod's filename sorted last. If that's not what you want, `priority` is the knob: raise the one that should win, or add a `requires` so the loser explicitly runs first and the winner's intent is unambiguous in the mod itself, not just in a startup log line.
1099
+
1100
+ Mod names have to be unique across `mods/` — two files claiming the same `name` is ambiguous the moment either one shows up in another mod's `requires`, so the second one to load fails with which file already claimed that name.
1101
+
1102
+ ### Overriding functions
1103
+
1104
+ The five registration functions above are additive — they add a locale, a holiday set, a token, alongside whatever's already there. `ctx.overrideFormat` and `ctx.overrideParse` work differently: they let a mod replace the actual `format()`/`parse()` implementation everywhere in the library, which is what makes a real bugfix or performance mod possible rather than just new data being registered alongside an unfixed bug.
1105
+
1106
+ ```js
1107
+ export default {
1108
+ name: 'fast-format',
1109
+ register(ctx) {
1110
+ ctx.overrideFormat((original, value, formatStr, options) => {
1111
+ // Handle the one hot-path format string yourself; fall back to the
1112
+ // real implementation for everything else.
1113
+ if (formatStr === 'yyyy-MM-dd') {
1114
+ return `${value.year}-${String(value.month).padStart(2, '0')}-${String(value.day).padStart(2, '0')}`;
1115
+ }
1116
+ return original(value, formatStr, options);
1117
+ });
1118
+ },
1119
+ };
1120
+ ```
1121
+
1122
+ `impl` always receives the real built-in as its first argument (`original`), regardless of what else is loaded — call it to keep existing behavior for cases you're not trying to change, or ignore it to replace the behavior outright. The override applies consistently everywhere in the library, not just to whoever imports the function from the package root — `formatRange()`'s internal use of `format()`, for instance, sees it too. Remove the mod and restart, and it's back to the unmodified built-in; nothing about this touches the source file on disk.
1123
+
1124
+ **Only one mod may hold each override point.** A second override call for the same function — from any mod, even one that `requires` the first — fails immediately with which mod already owns it:
1125
+
1126
+ ```
1127
+ temporal-fmt mods:
1128
+ loaded override-1 (a-override1.mjs)
1129
+ failed b-override2.mjs: temporal-fmt: "format" is already overridden by mod "override-1" — mod "override-2" can't also override it. [...]
1130
+ ```
1131
+
1132
+ This is a hard failure, not last-write-wins like the registration functions — two mods silently fighting over the same function's behavior is a correctness bug in whatever depends on this library, not a cosmetic surprise. There's no mechanism for two separate mod files to layer through the same override point in sequence; if two mods both need to change a function's behavior, one has to incorporate the other's fix directly rather than composing through the override twice.
1133
+
1134
+ **Which functions are overridable.** `format`, `formatToParts`, and `parse` always were. Beyond those, any function in this library that nothing *else* in the library calls internally is also overridable — if a function has zero internal call sites, there's no risk of some other module holding a stale direct reference that a mod's fix would silently fail to reach, so it gets the same `overrideXxx()` treatment. As of this version, that's:
1135
+
1136
+ `compileFormat`, `compileParser`, `parseRelative`, `explainFormat`, `tokenizeFormat`, `listTokens`, `tokenInfo`, `isValidFormat`, `validateFormat`, `fieldForToken`, `monthsInYear`, `isLeapYear`, `isLeapMonth`, `weekOfYear`, `weekYear`, `getMonth`, `getWeekday`, `isEqual`, `isBefore`, `isAfter`, `clamp`, `isBetween`, `isToday`, `isTomorrow`, `isYesterday`, `isSameDay`, `isSameWeek`, `isSameMonth`, `isSameQuarter`, `isSameYear`, `isWeekday`, `floor`, `ceil`, `truncate`, `parseRFC3339`, `formatRFC3339`, `parseRFC2822`, `parseHTTPDate`, `fromUnixMicroseconds`, `fromUnixNanoseconds`, `toUnixSeconds`, `toUnixMilliseconds`, `toUnixMicroseconds`, `toUnixNanoseconds`, `parseSQL`, `formatSQL`, `formatDurationToParts`, `parseDuration`, `parseISODuration`, `formatISODuration`, `balanceDuration`, `compareDuration`, `subtractDuration`, `getLocale`, `hasLocale`, `createConfig`, `mergeWithConfig`, `listRegisteredGrammars`, `interval`, `overlaps`, `intersection`, `union`, `mergeIntervals`, `formatRangeToParts`, `between`, `parseRRule`, `formatRRule`, `createBusinessCalendar`, `subtractBusinessDays`, `nextHoliday`, `previousHoliday`, `resolveZoned`, `getNextTransition`, `getPreviousTransition`, `possibleInstantsFor`, `getAutocompleteData`, `getHoverDocs`, `getInlineDiagnostics`, `previewFormat`, `getDocUrl`, `translateDateFnsFormatString`.
1137
+
1138
+ Each follows the `ctx.overrideXxx((original, ...args) => ...)` shape shown above for `overrideFormat`. Functions *not* in this list — `round`, `subtract`, `difference`, `formatDistance`, and others that other parts of this library call directly — aren't overridable this way: something else in the codebase holds its own direct reference to them, so a mod's override would silently miss those internal callers, which is worse than not offering the override at all. A function moves onto this list only when an audit confirms nothing internal still calls it directly. If you need to change one of those, that's a real feature request for making it internally indirect first, not something `overrideFormat`-style code can paper over.
1139
+
1140
+ ### If you're writing the mod in TypeScript
1141
+
1142
+ Compile it and rename the output before it goes in `mods/` — the loader only accepts `.mjs`. It won't run a TS file for you, and it won't skip one quietly either: a `.ts` file sitting in `mods/` shows up in the load report as a failure with the exact compile command to run, because a mod that silently never loads is worse than one that fails loudly.
1143
+
1144
+ ```sh
1145
+ tsc en-gb-bank-holidays.ts --module esnext --target esnext --outDir mods
1146
+ mv mods/en-gb-bank-holidays.js mods/en-gb-bank-holidays.mjs
1147
+ ```
1148
+
1149
+ If you're importing `ModContext` or `Mod` for the types while you write it, both are exported from `temporal-fmt` itself:
1150
+
1151
+ ```ts
1152
+ import type { Mod, ModContext } from 'temporal-fmt';
1153
+
1154
+ const mod: Mod = {
1155
+ name: 'en-gb-bank-holidays',
1156
+ register(ctx: ModContext) {
1157
+ ctx.createHolidayCalendar([{ month: 1, day: 1, name: "New Year's Day" }]);
1158
+ },
1159
+ };
1160
+
1161
+ export default mod;
1162
+ ```
1163
+
1164
+ ### What happens when a mod is broken
1165
+
1166
+ Each mod loads independently — one throwing doesn't stop the rest from loading, and it doesn't stop the CLI command you actually ran. Every failure mode ends up as one line in the report:
1167
+
1168
+ - Wrong file extension (`.ts`, `.js`, anything but `.mjs`) — reported with the compile-and-rename instructions above.
1169
+ - Default export isn't shaped right (missing `name`, missing `register`, `register` isn't a function, or `requires`/`priority` are the wrong type) — reported with what was expected.
1170
+ - The file fails to import (a syntax error, a bad import path inside the mod) — reported with the underlying error message.
1171
+ - Two mods claim the same `name` — reported against whichever file loaded second.
1172
+ - A `requires` entry names a mod that isn't present, or is part of a dependency cycle — see [Load order, dependencies, and conflicts](#load-order-dependencies-and-conflicts).
1173
+ - `register()` throws — reported with the thrown message, same as any other registration call in this library (see [Typed errors](#typed-errors) for what `registerLocale`/`createHolidayCalendar` themselves throw on bad input).
1174
+
1175
+ None of these bring down the CLI. A `mods/` folder that doesn't exist is the common case, not a failure — most runs won't have one, and the loader stays silent about it rather than printing "no mods found" noise on every command.
1176
+
1177
+ ### Using mods outside the CLI
1178
+
1179
+ `loadMods()` only exists in `scripts/loadMods.mjs`, not in the published library — it needs `fs`/`path`, and this package stays dependency-free and Node-agnostic on its actual import surface (`import { format } from 'temporal-fmt'` shouldn't drag in filesystem code for someone using this in a browser). If you're embedding `temporal-fmt` in your own app rather than using the CLI, copy that loader (or write your own — it's about 400+ lines) and call it at your own startup with `buildModContext()` and `isMod()` from the library, which *are* published.
1180
+
1181
+ Mod support (loose `.mjs` mods, `.tfmod` archives, and everything under [Mods](#mods)) requires `temporal-fmt` 0.9.4 or later — that's the version it landed in.
1182
+
875
1183
  ## Subpath imports
876
1184
 
877
1185
  Each capability area is also available as a subpath import, for anyone who wants a slice of the package rather than the whole thing:
@@ -890,6 +1198,8 @@ import { registerLocale } from 'temporal-fmt/locale';
890
1198
 
891
1199
  The rest of the API (arithmetic, comparison, rounding, intervals-adjacent helpers not listed above, business calendars, holidays, serialization, config, type guards, typed errors, the analyzer, and IDE tooling data) is only available from the main `temporal-fmt` entry point — there's no dedicated subpath for those yet.
892
1200
 
1201
+ `sideEffects: false` is set in `package.json`, so a bundler with tree-shaking enabled genuinely drops what you don't import. Measured with esbuild: `import { format } from 'temporal-fmt/format'` bundles to ~27KB, versus ~68KB for the same single function pulled from the bare `temporal-fmt` entry — the main entry point re-exports everything, so anything imported from it drags the whole graph along regardless of what you actually call. If bundle size matters for your use case, import from the subpath, not the package root.
1202
+
893
1203
  ## Migrating from Day.js or date-fns
894
1204
 
895
1205
  ### Token mapping
@@ -1185,4 +1495,4 @@ A couple of build-specific notes if you're touching the toolchain:
1185
1495
 
1186
1496
  ## License
1187
1497
 
1188
- MIT — see [LICENSE](./LICENSE).
1498
+ Apache 2.0 — see [LICENSE](./LICENSE).