@hyperfrontend/versioning 0.1.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 (353) hide show
  1. package/ARCHITECTURE.md +593 -0
  2. package/CHANGELOG.md +35 -0
  3. package/FUNDING.md +141 -0
  4. package/LICENSE.md +21 -0
  5. package/README.md +195 -0
  6. package/SECURITY.md +82 -0
  7. package/changelog/compare/diff.d.ts +128 -0
  8. package/changelog/compare/diff.d.ts.map +1 -0
  9. package/changelog/compare/index.cjs.js +628 -0
  10. package/changelog/compare/index.cjs.js.map +1 -0
  11. package/changelog/compare/index.d.ts +4 -0
  12. package/changelog/compare/index.d.ts.map +1 -0
  13. package/changelog/compare/index.esm.js +612 -0
  14. package/changelog/compare/index.esm.js.map +1 -0
  15. package/changelog/compare/is-equal.d.ts +114 -0
  16. package/changelog/compare/is-equal.d.ts.map +1 -0
  17. package/changelog/index.cjs.js +6448 -0
  18. package/changelog/index.cjs.js.map +1 -0
  19. package/changelog/index.d.ts +6 -0
  20. package/changelog/index.d.ts.map +1 -0
  21. package/changelog/index.esm.js +6358 -0
  22. package/changelog/index.esm.js.map +1 -0
  23. package/changelog/models/changelog.d.ts +86 -0
  24. package/changelog/models/changelog.d.ts.map +1 -0
  25. package/changelog/models/commit-ref.d.ts +51 -0
  26. package/changelog/models/commit-ref.d.ts.map +1 -0
  27. package/changelog/models/entry.d.ts +84 -0
  28. package/changelog/models/entry.d.ts.map +1 -0
  29. package/changelog/models/index.cjs.js +2043 -0
  30. package/changelog/models/index.cjs.js.map +1 -0
  31. package/changelog/models/index.d.ts +11 -0
  32. package/changelog/models/index.d.ts.map +1 -0
  33. package/changelog/models/index.esm.js +2026 -0
  34. package/changelog/models/index.esm.js.map +1 -0
  35. package/changelog/models/schema.d.ts +68 -0
  36. package/changelog/models/schema.d.ts.map +1 -0
  37. package/changelog/models/section.d.ts +25 -0
  38. package/changelog/models/section.d.ts.map +1 -0
  39. package/changelog/operations/add-entry.d.ts +56 -0
  40. package/changelog/operations/add-entry.d.ts.map +1 -0
  41. package/changelog/operations/add-item.d.ts +18 -0
  42. package/changelog/operations/add-item.d.ts.map +1 -0
  43. package/changelog/operations/filter-by-predicate.d.ts +81 -0
  44. package/changelog/operations/filter-by-predicate.d.ts.map +1 -0
  45. package/changelog/operations/filter-by-range.d.ts +63 -0
  46. package/changelog/operations/filter-by-range.d.ts.map +1 -0
  47. package/changelog/operations/filter-entries.d.ts +9 -0
  48. package/changelog/operations/filter-entries.d.ts.map +1 -0
  49. package/changelog/operations/index.cjs.js +2455 -0
  50. package/changelog/operations/index.cjs.js.map +1 -0
  51. package/changelog/operations/index.d.ts +15 -0
  52. package/changelog/operations/index.d.ts.map +1 -0
  53. package/changelog/operations/index.esm.js +2411 -0
  54. package/changelog/operations/index.esm.js.map +1 -0
  55. package/changelog/operations/merge.d.ts +88 -0
  56. package/changelog/operations/merge.d.ts.map +1 -0
  57. package/changelog/operations/remove-entry.d.ts +45 -0
  58. package/changelog/operations/remove-entry.d.ts.map +1 -0
  59. package/changelog/operations/remove-section.d.ts +50 -0
  60. package/changelog/operations/remove-section.d.ts.map +1 -0
  61. package/changelog/operations/transform.d.ts +143 -0
  62. package/changelog/operations/transform.d.ts.map +1 -0
  63. package/changelog/parse/index.cjs.js +1282 -0
  64. package/changelog/parse/index.cjs.js.map +1 -0
  65. package/changelog/parse/index.d.ts +5 -0
  66. package/changelog/parse/index.d.ts.map +1 -0
  67. package/changelog/parse/index.esm.js +1275 -0
  68. package/changelog/parse/index.esm.js.map +1 -0
  69. package/changelog/parse/line.d.ts +48 -0
  70. package/changelog/parse/line.d.ts.map +1 -0
  71. package/changelog/parse/parser.d.ts +16 -0
  72. package/changelog/parse/parser.d.ts.map +1 -0
  73. package/changelog/parse/tokenizer.d.ts +49 -0
  74. package/changelog/parse/tokenizer.d.ts.map +1 -0
  75. package/changelog/serialize/index.cjs.js +574 -0
  76. package/changelog/serialize/index.cjs.js.map +1 -0
  77. package/changelog/serialize/index.d.ts +6 -0
  78. package/changelog/serialize/index.d.ts.map +1 -0
  79. package/changelog/serialize/index.esm.js +564 -0
  80. package/changelog/serialize/index.esm.js.map +1 -0
  81. package/changelog/serialize/templates.d.ts +81 -0
  82. package/changelog/serialize/templates.d.ts.map +1 -0
  83. package/changelog/serialize/to-json.d.ts +57 -0
  84. package/changelog/serialize/to-json.d.ts.map +1 -0
  85. package/changelog/serialize/to-string.d.ts +30 -0
  86. package/changelog/serialize/to-string.d.ts.map +1 -0
  87. package/commits/index.cjs.js +648 -0
  88. package/commits/index.cjs.js.map +1 -0
  89. package/commits/index.d.ts +3 -0
  90. package/commits/index.d.ts.map +1 -0
  91. package/commits/index.esm.js +629 -0
  92. package/commits/index.esm.js.map +1 -0
  93. package/commits/models/breaking.d.ts +39 -0
  94. package/commits/models/breaking.d.ts.map +1 -0
  95. package/commits/models/commit-type.d.ts +32 -0
  96. package/commits/models/commit-type.d.ts.map +1 -0
  97. package/commits/models/conventional.d.ts +49 -0
  98. package/commits/models/conventional.d.ts.map +1 -0
  99. package/commits/models/index.cjs.js +207 -0
  100. package/commits/models/index.cjs.js.map +1 -0
  101. package/commits/models/index.d.ts +7 -0
  102. package/commits/models/index.d.ts.map +1 -0
  103. package/commits/models/index.esm.js +193 -0
  104. package/commits/models/index.esm.js.map +1 -0
  105. package/commits/parse/body.d.ts +18 -0
  106. package/commits/parse/body.d.ts.map +1 -0
  107. package/commits/parse/footer.d.ts +16 -0
  108. package/commits/parse/footer.d.ts.map +1 -0
  109. package/commits/parse/header.d.ts +15 -0
  110. package/commits/parse/header.d.ts.map +1 -0
  111. package/commits/parse/index.cjs.js +505 -0
  112. package/commits/parse/index.cjs.js.map +1 -0
  113. package/commits/parse/index.d.ts +5 -0
  114. package/commits/parse/index.d.ts.map +1 -0
  115. package/commits/parse/index.esm.js +499 -0
  116. package/commits/parse/index.esm.js.map +1 -0
  117. package/commits/parse/message.d.ts +17 -0
  118. package/commits/parse/message.d.ts.map +1 -0
  119. package/commits/utils/replace-char.d.ts +19 -0
  120. package/commits/utils/replace-char.d.ts.map +1 -0
  121. package/flow/executor/execute.d.ts +72 -0
  122. package/flow/executor/execute.d.ts.map +1 -0
  123. package/flow/executor/index.cjs.js +4402 -0
  124. package/flow/executor/index.cjs.js.map +1 -0
  125. package/flow/executor/index.d.ts +3 -0
  126. package/flow/executor/index.d.ts.map +1 -0
  127. package/flow/executor/index.esm.js +4398 -0
  128. package/flow/executor/index.esm.js.map +1 -0
  129. package/flow/factory.d.ts +58 -0
  130. package/flow/factory.d.ts.map +1 -0
  131. package/flow/index.cjs.js +8506 -0
  132. package/flow/index.cjs.js.map +1 -0
  133. package/flow/index.d.ts +7 -0
  134. package/flow/index.d.ts.map +1 -0
  135. package/flow/index.esm.js +8451 -0
  136. package/flow/index.esm.js.map +1 -0
  137. package/flow/models/flow.d.ts +130 -0
  138. package/flow/models/flow.d.ts.map +1 -0
  139. package/flow/models/index.cjs.js +285 -0
  140. package/flow/models/index.cjs.js.map +1 -0
  141. package/flow/models/index.d.ts +7 -0
  142. package/flow/models/index.d.ts.map +1 -0
  143. package/flow/models/index.esm.js +268 -0
  144. package/flow/models/index.esm.js.map +1 -0
  145. package/flow/models/step.d.ts +108 -0
  146. package/flow/models/step.d.ts.map +1 -0
  147. package/flow/models/types.d.ts +150 -0
  148. package/flow/models/types.d.ts.map +1 -0
  149. package/flow/presets/conventional.d.ts +59 -0
  150. package/flow/presets/conventional.d.ts.map +1 -0
  151. package/flow/presets/independent.d.ts +61 -0
  152. package/flow/presets/independent.d.ts.map +1 -0
  153. package/flow/presets/index.cjs.js +3903 -0
  154. package/flow/presets/index.cjs.js.map +1 -0
  155. package/flow/presets/index.d.ts +4 -0
  156. package/flow/presets/index.d.ts.map +1 -0
  157. package/flow/presets/index.esm.js +3889 -0
  158. package/flow/presets/index.esm.js.map +1 -0
  159. package/flow/presets/synced.d.ts +65 -0
  160. package/flow/presets/synced.d.ts.map +1 -0
  161. package/flow/steps/analyze-commits.d.ts +19 -0
  162. package/flow/steps/analyze-commits.d.ts.map +1 -0
  163. package/flow/steps/calculate-bump.d.ts +27 -0
  164. package/flow/steps/calculate-bump.d.ts.map +1 -0
  165. package/flow/steps/create-commit.d.ts +16 -0
  166. package/flow/steps/create-commit.d.ts.map +1 -0
  167. package/flow/steps/create-tag.d.ts +22 -0
  168. package/flow/steps/create-tag.d.ts.map +1 -0
  169. package/flow/steps/fetch-registry.d.ts +19 -0
  170. package/flow/steps/fetch-registry.d.ts.map +1 -0
  171. package/flow/steps/generate-changelog.d.ts +25 -0
  172. package/flow/steps/generate-changelog.d.ts.map +1 -0
  173. package/flow/steps/index.cjs.js +3523 -0
  174. package/flow/steps/index.cjs.js.map +1 -0
  175. package/flow/steps/index.d.ts +8 -0
  176. package/flow/steps/index.d.ts.map +1 -0
  177. package/flow/steps/index.esm.js +3504 -0
  178. package/flow/steps/index.esm.js.map +1 -0
  179. package/flow/steps/update-packages.d.ts +25 -0
  180. package/flow/steps/update-packages.d.ts.map +1 -0
  181. package/flow/utils/interpolate.d.ts +11 -0
  182. package/flow/utils/interpolate.d.ts.map +1 -0
  183. package/git/factory.d.ts +233 -0
  184. package/git/factory.d.ts.map +1 -0
  185. package/git/index.cjs.js +2863 -0
  186. package/git/index.cjs.js.map +1 -0
  187. package/git/index.d.ts +5 -0
  188. package/git/index.d.ts.map +1 -0
  189. package/git/index.esm.js +2785 -0
  190. package/git/index.esm.js.map +1 -0
  191. package/git/models/commit.d.ts +129 -0
  192. package/git/models/commit.d.ts.map +1 -0
  193. package/git/models/index.cjs.js +755 -0
  194. package/git/models/index.cjs.js.map +1 -0
  195. package/git/models/index.d.ts +7 -0
  196. package/git/models/index.d.ts.map +1 -0
  197. package/git/models/index.esm.js +729 -0
  198. package/git/models/index.esm.js.map +1 -0
  199. package/git/models/ref.d.ts +120 -0
  200. package/git/models/ref.d.ts.map +1 -0
  201. package/git/models/tag.d.ts +141 -0
  202. package/git/models/tag.d.ts.map +1 -0
  203. package/git/operations/commit.d.ts +97 -0
  204. package/git/operations/commit.d.ts.map +1 -0
  205. package/git/operations/head-info.d.ts +29 -0
  206. package/git/operations/head-info.d.ts.map +1 -0
  207. package/git/operations/index.cjs.js +1954 -0
  208. package/git/operations/index.cjs.js.map +1 -0
  209. package/git/operations/index.d.ts +14 -0
  210. package/git/operations/index.d.ts.map +1 -0
  211. package/git/operations/index.esm.js +1903 -0
  212. package/git/operations/index.esm.js.map +1 -0
  213. package/git/operations/log.d.ts +104 -0
  214. package/git/operations/log.d.ts.map +1 -0
  215. package/git/operations/manage-tags.d.ts +60 -0
  216. package/git/operations/manage-tags.d.ts.map +1 -0
  217. package/git/operations/query-tags.d.ts +88 -0
  218. package/git/operations/query-tags.d.ts.map +1 -0
  219. package/git/operations/stage.d.ts +66 -0
  220. package/git/operations/stage.d.ts.map +1 -0
  221. package/git/operations/status.d.ts +173 -0
  222. package/git/operations/status.d.ts.map +1 -0
  223. package/index.cjs.js +16761 -0
  224. package/index.cjs.js.map +1 -0
  225. package/index.d.ts +102 -0
  226. package/index.d.ts.map +1 -0
  227. package/index.esm.js +16427 -0
  228. package/index.esm.js.map +1 -0
  229. package/package.json +200 -0
  230. package/registry/factory.d.ts +18 -0
  231. package/registry/factory.d.ts.map +1 -0
  232. package/registry/index.cjs.js +543 -0
  233. package/registry/index.cjs.js.map +1 -0
  234. package/registry/index.d.ts +5 -0
  235. package/registry/index.d.ts.map +1 -0
  236. package/registry/index.esm.js +535 -0
  237. package/registry/index.esm.js.map +1 -0
  238. package/registry/models/index.cjs.js +69 -0
  239. package/registry/models/index.cjs.js.map +1 -0
  240. package/registry/models/index.d.ts +6 -0
  241. package/registry/models/index.d.ts.map +1 -0
  242. package/registry/models/index.esm.js +66 -0
  243. package/registry/models/index.esm.js.map +1 -0
  244. package/registry/models/package-info.d.ts +55 -0
  245. package/registry/models/package-info.d.ts.map +1 -0
  246. package/registry/models/registry.d.ts +62 -0
  247. package/registry/models/registry.d.ts.map +1 -0
  248. package/registry/models/version-info.d.ts +67 -0
  249. package/registry/models/version-info.d.ts.map +1 -0
  250. package/registry/npm/cache.d.ts +50 -0
  251. package/registry/npm/cache.d.ts.map +1 -0
  252. package/registry/npm/client.d.ts +30 -0
  253. package/registry/npm/client.d.ts.map +1 -0
  254. package/registry/npm/index.cjs.js +456 -0
  255. package/registry/npm/index.cjs.js.map +1 -0
  256. package/registry/npm/index.d.ts +4 -0
  257. package/registry/npm/index.d.ts.map +1 -0
  258. package/registry/npm/index.esm.js +451 -0
  259. package/registry/npm/index.esm.js.map +1 -0
  260. package/semver/compare/compare.d.ts +100 -0
  261. package/semver/compare/compare.d.ts.map +1 -0
  262. package/semver/compare/index.cjs.js +386 -0
  263. package/semver/compare/index.cjs.js.map +1 -0
  264. package/semver/compare/index.d.ts +3 -0
  265. package/semver/compare/index.d.ts.map +1 -0
  266. package/semver/compare/index.esm.js +370 -0
  267. package/semver/compare/index.esm.js.map +1 -0
  268. package/semver/compare/sort.d.ts +36 -0
  269. package/semver/compare/sort.d.ts.map +1 -0
  270. package/semver/format/index.cjs.js +58 -0
  271. package/semver/format/index.cjs.js.map +1 -0
  272. package/semver/format/index.d.ts +2 -0
  273. package/semver/format/index.d.ts.map +1 -0
  274. package/semver/format/index.esm.js +53 -0
  275. package/semver/format/index.esm.js.map +1 -0
  276. package/semver/format/to-string.d.ts +31 -0
  277. package/semver/format/to-string.d.ts.map +1 -0
  278. package/semver/increment/bump.d.ts +37 -0
  279. package/semver/increment/bump.d.ts.map +1 -0
  280. package/semver/increment/index.cjs.js +223 -0
  281. package/semver/increment/index.cjs.js.map +1 -0
  282. package/semver/increment/index.d.ts +2 -0
  283. package/semver/increment/index.d.ts.map +1 -0
  284. package/semver/increment/index.esm.js +219 -0
  285. package/semver/increment/index.esm.js.map +1 -0
  286. package/semver/index.cjs.js +1499 -0
  287. package/semver/index.cjs.js.map +1 -0
  288. package/semver/index.d.ts +6 -0
  289. package/semver/index.d.ts.map +1 -0
  290. package/semver/index.esm.js +1458 -0
  291. package/semver/index.esm.js.map +1 -0
  292. package/semver/models/index.cjs.js +153 -0
  293. package/semver/models/index.cjs.js.map +1 -0
  294. package/semver/models/index.d.ts +5 -0
  295. package/semver/models/index.d.ts.map +1 -0
  296. package/semver/models/index.esm.js +139 -0
  297. package/semver/models/index.esm.js.map +1 -0
  298. package/semver/models/range.d.ts +83 -0
  299. package/semver/models/range.d.ts.map +1 -0
  300. package/semver/models/version.d.ts +78 -0
  301. package/semver/models/version.d.ts.map +1 -0
  302. package/semver/parse/index.cjs.js +799 -0
  303. package/semver/parse/index.cjs.js.map +1 -0
  304. package/semver/parse/index.d.ts +5 -0
  305. package/semver/parse/index.d.ts.map +1 -0
  306. package/semver/parse/index.esm.js +793 -0
  307. package/semver/parse/index.esm.js.map +1 -0
  308. package/semver/parse/range.d.ts +38 -0
  309. package/semver/parse/range.d.ts.map +1 -0
  310. package/semver/parse/version.d.ts +49 -0
  311. package/semver/parse/version.d.ts.map +1 -0
  312. package/workspace/discovery/changelog-path.d.ts +21 -0
  313. package/workspace/discovery/changelog-path.d.ts.map +1 -0
  314. package/workspace/discovery/dependencies.d.ts +145 -0
  315. package/workspace/discovery/dependencies.d.ts.map +1 -0
  316. package/workspace/discovery/discover-changelogs.d.ts +76 -0
  317. package/workspace/discovery/discover-changelogs.d.ts.map +1 -0
  318. package/workspace/discovery/index.cjs.js +2300 -0
  319. package/workspace/discovery/index.cjs.js.map +1 -0
  320. package/workspace/discovery/index.d.ts +13 -0
  321. package/workspace/discovery/index.d.ts.map +1 -0
  322. package/workspace/discovery/index.esm.js +2283 -0
  323. package/workspace/discovery/index.esm.js.map +1 -0
  324. package/workspace/discovery/packages.d.ts +83 -0
  325. package/workspace/discovery/packages.d.ts.map +1 -0
  326. package/workspace/index.cjs.js +4445 -0
  327. package/workspace/index.cjs.js.map +1 -0
  328. package/workspace/index.d.ts +52 -0
  329. package/workspace/index.d.ts.map +1 -0
  330. package/workspace/index.esm.js +4394 -0
  331. package/workspace/index.esm.js.map +1 -0
  332. package/workspace/models/index.cjs.js +284 -0
  333. package/workspace/models/index.cjs.js.map +1 -0
  334. package/workspace/models/index.d.ts +10 -0
  335. package/workspace/models/index.d.ts.map +1 -0
  336. package/workspace/models/index.esm.js +261 -0
  337. package/workspace/models/index.esm.js.map +1 -0
  338. package/workspace/models/project.d.ts +118 -0
  339. package/workspace/models/project.d.ts.map +1 -0
  340. package/workspace/models/workspace.d.ts +139 -0
  341. package/workspace/models/workspace.d.ts.map +1 -0
  342. package/workspace/operations/batch-update.d.ts +99 -0
  343. package/workspace/operations/batch-update.d.ts.map +1 -0
  344. package/workspace/operations/cascade-bump.d.ts +125 -0
  345. package/workspace/operations/cascade-bump.d.ts.map +1 -0
  346. package/workspace/operations/index.cjs.js +2675 -0
  347. package/workspace/operations/index.cjs.js.map +1 -0
  348. package/workspace/operations/index.d.ts +12 -0
  349. package/workspace/operations/index.d.ts.map +1 -0
  350. package/workspace/operations/index.esm.js +2663 -0
  351. package/workspace/operations/index.esm.js.map +1 -0
  352. package/workspace/operations/validate.d.ts +85 -0
  353. package/workspace/operations/validate.d.ts.map +1 -0
@@ -0,0 +1,2411 @@
1
+ /**
2
+ * Safe copies of Date built-in via factory function and static methods.
3
+ *
4
+ * Since constructors cannot be safely captured via Object.assign, this module
5
+ * provides a factory function that uses Reflect.construct internally.
6
+ *
7
+ * These references are captured at module initialization time to protect against
8
+ * prototype pollution attacks. Import only what you need for tree-shaking.
9
+ *
10
+ * @module @hyperfrontend/immutable-api-utils/built-in-copy/date
11
+ */
12
+ // Capture references at module initialization time
13
+ const _Date = globalThis.Date;
14
+ const _Reflect$3 = globalThis.Reflect;
15
+ function createDate(...args) {
16
+ return _Reflect$3.construct(_Date, args);
17
+ }
18
+
19
+ /**
20
+ * Safe copies of Error built-ins via factory functions.
21
+ *
22
+ * Since constructors cannot be safely captured via Object.assign, this module
23
+ * provides factory functions that use Reflect.construct internally.
24
+ *
25
+ * These references are captured at module initialization time to protect against
26
+ * prototype pollution attacks. Import only what you need for tree-shaking.
27
+ *
28
+ * @module @hyperfrontend/immutable-api-utils/built-in-copy/error
29
+ */
30
+ // Capture references at module initialization time
31
+ const _Error = globalThis.Error;
32
+ const _Reflect$2 = globalThis.Reflect;
33
+ /**
34
+ * (Safe copy) Creates a new Error using the captured Error constructor.
35
+ * Use this instead of `new Error()`.
36
+ *
37
+ * @param message - Optional error message.
38
+ * @param options - Optional error options.
39
+ * @returns A new Error instance.
40
+ */
41
+ const createError = (message, options) => _Reflect$2.construct(_Error, [message, options]);
42
+
43
+ /**
44
+ * Creates a new changelog item.
45
+ *
46
+ * @param description - The description text of the change
47
+ * @param options - Optional configuration for scope, commits, references, and breaking flag
48
+ * @returns A new ChangelogItem object
49
+ */
50
+ /**
51
+ * Creates a new changelog entry.
52
+ *
53
+ * @param version - The version string (e.g., '1.0.0')
54
+ * @param options - Optional configuration for date, sections, and other properties
55
+ * @returns A new ChangelogEntry object
56
+ */
57
+ function createChangelogEntry(version, options) {
58
+ return {
59
+ version,
60
+ date: options?.date ?? null,
61
+ unreleased: options?.unreleased,
62
+ compareUrl: options?.compareUrl,
63
+ sections: options?.sections ?? [],
64
+ rawContent: options?.rawContent,
65
+ };
66
+ }
67
+ /**
68
+ * Creates an unreleased changelog entry.
69
+ *
70
+ * @param sections - Optional array of changelog sections
71
+ * @returns A new ChangelogEntry object marked as unreleased
72
+ */
73
+ function createUnreleasedEntry(sections = []) {
74
+ return {
75
+ version: 'Unreleased',
76
+ date: null,
77
+ unreleased: true,
78
+ sections,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Changelog Entry Addition
84
+ *
85
+ * Functions for adding new entries to a changelog.
86
+ */
87
+ /**
88
+ * Adds a new entry to a changelog.
89
+ *
90
+ * @param changelog - The changelog to add to
91
+ * @param entry - The entry to add
92
+ * @param options - Optional add options
93
+ * @returns A new changelog with the entry added
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * const newChangelog = addEntry(changelog, {
98
+ * version: '1.2.0',
99
+ * date: '2024-01-15',
100
+ * unreleased: false,
101
+ * sections: [...]
102
+ * })
103
+ * ```
104
+ */
105
+ function addEntry(changelog, entry, options) {
106
+ const position = options?.position ?? 'start';
107
+ const replaceExisting = options?.replaceExisting ?? false;
108
+ const updateMetadata = options?.updateMetadata ?? false;
109
+ // Check for existing entry
110
+ const existingIndex = changelog.entries.findIndex((e) => e.version === entry.version);
111
+ if (existingIndex !== -1 && !replaceExisting) {
112
+ throw createError(`Entry with version "${entry.version}" already exists. Use replaceExisting: true to replace.`);
113
+ }
114
+ let newEntries;
115
+ if (existingIndex !== -1 && replaceExisting) {
116
+ // Replace existing entry
117
+ newEntries = [...changelog.entries];
118
+ newEntries[existingIndex] = entry;
119
+ }
120
+ else {
121
+ // Add new entry
122
+ const insertIndex = position === 'start' ? 0 : position === 'end' ? changelog.entries.length : position;
123
+ newEntries = [...changelog.entries];
124
+ newEntries.splice(insertIndex, 0, entry);
125
+ }
126
+ // Build new metadata if requested
127
+ const metadata = updateMetadata ? { ...changelog.metadata, warnings: [] } : changelog.metadata;
128
+ return {
129
+ ...changelog,
130
+ entries: newEntries,
131
+ metadata,
132
+ };
133
+ }
134
+ /**
135
+ * Adds or updates an unreleased entry.
136
+ *
137
+ * @param changelog - The changelog to add the unreleased entry to
138
+ * @param sections - Sections to include in the unreleased entry
139
+ * @returns A new changelog with unreleased entry added/updated
140
+ */
141
+ function addUnreleasedEntry(changelog, sections) {
142
+ const unreleasedEntry = createUnreleasedEntry(sections);
143
+ return addEntry(changelog, unreleasedEntry, { position: 'start', replaceExisting: true });
144
+ }
145
+ /**
146
+ * Creates a new release entry from the current unreleased entry.
147
+ *
148
+ * @param changelog - The changelog containing the unreleased entry
149
+ * @param version - The version number for the release
150
+ * @param date - The release date (defaults to today)
151
+ * @param compareUrl - Optional comparison URL
152
+ * @returns A new changelog with the unreleased entry converted to a release
153
+ */
154
+ function releaseUnreleased(changelog, version, date, compareUrl) {
155
+ const unreleasedIndex = changelog.entries.findIndex((e) => e.unreleased);
156
+ if (unreleasedIndex === -1) {
157
+ throw createError('No unreleased entry found');
158
+ }
159
+ const unreleased = changelog.entries[unreleasedIndex];
160
+ const releaseDate = date ?? createDate().toISOString().split('T')[0];
161
+ const releaseEntry = createChangelogEntry(version, {
162
+ date: releaseDate,
163
+ unreleased: false,
164
+ compareUrl,
165
+ sections: unreleased.sections,
166
+ });
167
+ const newEntries = [...changelog.entries];
168
+ newEntries[unreleasedIndex] = releaseEntry;
169
+ return {
170
+ ...changelog,
171
+ entries: newEntries,
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Changelog Item Addition
177
+ *
178
+ * Functions for adding items to changelog entries.
179
+ */
180
+ /**
181
+ * Adds an item to a specific section within an entry.
182
+ *
183
+ * @param changelog - The changelog containing the entry to modify
184
+ * @param version - The version identifier of the entry to update
185
+ * @param sectionType - Category identifier for grouping changes (e.g., 'features', 'fixes')
186
+ * @param item - Description of the change with optional scope and metadata
187
+ * @returns A new changelog with the item added
188
+ */
189
+ function addItemToEntry(changelog, version, sectionType, item) {
190
+ const entryIndex = changelog.entries.findIndex((e) => e.version === version);
191
+ if (entryIndex === -1) {
192
+ throw createError(`Entry with version "${version}" not found`);
193
+ }
194
+ const entry = changelog.entries[entryIndex];
195
+ const sectionIndex = entry.sections.findIndex((s) => s.type === sectionType);
196
+ let newSections;
197
+ if (sectionIndex === -1) {
198
+ // Create new section
199
+ newSections = [
200
+ ...entry.sections,
201
+ {
202
+ type: sectionType,
203
+ heading: sectionType.charAt(0).toUpperCase() + sectionType.slice(1),
204
+ items: [item],
205
+ },
206
+ ];
207
+ }
208
+ else {
209
+ // Add to existing section
210
+ newSections = [...entry.sections];
211
+ newSections[sectionIndex] = {
212
+ ...newSections[sectionIndex],
213
+ items: [...newSections[sectionIndex].items, item],
214
+ };
215
+ }
216
+ const newEntry = {
217
+ ...entry,
218
+ sections: newSections,
219
+ };
220
+ const newEntries = [...changelog.entries];
221
+ newEntries[entryIndex] = newEntry;
222
+ return {
223
+ ...changelog,
224
+ entries: newEntries,
225
+ };
226
+ }
227
+
228
+ /**
229
+ * Safe copies of Set built-in via factory function.
230
+ *
231
+ * Since constructors cannot be safely captured via Object.assign, this module
232
+ * provides a factory function that uses Reflect.construct internally.
233
+ *
234
+ * These references are captured at module initialization time to protect against
235
+ * prototype pollution attacks. Import only what you need for tree-shaking.
236
+ *
237
+ * @module @hyperfrontend/immutable-api-utils/built-in-copy/set
238
+ */
239
+ // Capture references at module initialization time
240
+ const _Set = globalThis.Set;
241
+ const _Reflect$1 = globalThis.Reflect;
242
+ /**
243
+ * (Safe copy) Creates a new Set using the captured Set constructor.
244
+ * Use this instead of `new Set()`.
245
+ *
246
+ * @param iterable - Optional iterable of values.
247
+ * @returns A new Set instance.
248
+ */
249
+ const createSet = (iterable) => _Reflect$1.construct(_Set, iterable ? [iterable] : []);
250
+
251
+ /**
252
+ * Changelog Entry Removal
253
+ *
254
+ * Functions for removing entries from a changelog.
255
+ */
256
+ /**
257
+ * Removes an entry from a changelog by version.
258
+ *
259
+ * @param changelog - The changelog to remove from
260
+ * @param version - The version to remove
261
+ * @param options - Optional removal options
262
+ * @returns A new changelog without the specified entry
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * const newChangelog = removeEntry(changelog, '1.0.0')
267
+ * ```
268
+ */
269
+ function removeEntry(changelog, version, options) {
270
+ const throwIfNotFound = options?.throwIfNotFound ?? true;
271
+ const entryIndex = changelog.entries.findIndex((e) => e.version === version);
272
+ if (entryIndex === -1) {
273
+ if (throwIfNotFound) {
274
+ throw createError(`Entry with version "${version}" not found`);
275
+ }
276
+ return changelog;
277
+ }
278
+ const newEntries = [...changelog.entries];
279
+ newEntries.splice(entryIndex, 1);
280
+ return {
281
+ ...changelog,
282
+ entries: newEntries,
283
+ };
284
+ }
285
+ /**
286
+ * Removes multiple entries from a changelog.
287
+ *
288
+ * @param changelog - The changelog to remove from
289
+ * @param versions - The versions to remove
290
+ * @param options - Optional removal options
291
+ * @returns A new changelog without the specified entries
292
+ */
293
+ function removeEntries(changelog, versions, options) {
294
+ const versionsSet = createSet(versions);
295
+ const newEntries = changelog.entries.filter((e) => !versionsSet.has(e.version));
296
+ if (options?.throwIfNotFound !== false) {
297
+ const removedVersions = changelog.entries.filter((e) => versionsSet.has(e.version)).map((e) => e.version);
298
+ const notFoundVersions = versions.filter((v) => !removedVersions.includes(v));
299
+ if (notFoundVersions.length > 0) {
300
+ throw createError(`Entries not found for versions: ${notFoundVersions.join(', ')}`);
301
+ }
302
+ }
303
+ return {
304
+ ...changelog,
305
+ entries: newEntries,
306
+ };
307
+ }
308
+ /**
309
+ * Removes the unreleased entry if it exists.
310
+ *
311
+ * @param changelog - The changelog to remove the unreleased entry from
312
+ * @param options - Optional removal options
313
+ * @returns A new changelog without the unreleased entry
314
+ */
315
+ function removeUnreleased(changelog, options) {
316
+ const unreleasedIndex = changelog.entries.findIndex((e) => e.unreleased);
317
+ if (unreleasedIndex === -1) {
318
+ if (options?.throwIfNotFound ?? true) {
319
+ throw createError('No unreleased entry found');
320
+ }
321
+ return changelog;
322
+ }
323
+ const newEntries = [...changelog.entries];
324
+ newEntries.splice(unreleasedIndex, 1);
325
+ return {
326
+ ...changelog,
327
+ entries: newEntries,
328
+ };
329
+ }
330
+
331
+ /**
332
+ * Changelog Section/Item Removal
333
+ *
334
+ * Functions for removing sections and items from changelog entries.
335
+ */
336
+ /**
337
+ * Removes a section from an entry.
338
+ *
339
+ * @param changelog - The changelog containing the entry to modify
340
+ * @param version - The version of the entry to modify
341
+ * @param sectionType - The section type to remove
342
+ * @param options - Optional removal options
343
+ * @returns A new changelog without the specified section
344
+ */
345
+ function removeSection(changelog, version, sectionType, options) {
346
+ const throwIfNotFound = options?.throwIfNotFound ?? true;
347
+ const entryIndex = changelog.entries.findIndex((e) => e.version === version);
348
+ if (entryIndex === -1) {
349
+ if (throwIfNotFound) {
350
+ throw createError(`Entry with version "${version}" not found`);
351
+ }
352
+ return changelog;
353
+ }
354
+ const entry = changelog.entries[entryIndex];
355
+ const sectionIndex = entry.sections.findIndex((s) => s.type === sectionType);
356
+ if (sectionIndex === -1) {
357
+ if (throwIfNotFound) {
358
+ throw createError(`Section with type "${sectionType}" not found in version "${version}"`);
359
+ }
360
+ return changelog;
361
+ }
362
+ const newSections = [...entry.sections];
363
+ newSections.splice(sectionIndex, 1);
364
+ const newEntry = {
365
+ ...entry,
366
+ sections: newSections,
367
+ };
368
+ const newEntries = [...changelog.entries];
369
+ newEntries[entryIndex] = newEntry;
370
+ return {
371
+ ...changelog,
372
+ entries: newEntries,
373
+ };
374
+ }
375
+ /**
376
+ * Removes an item from an entry by description.
377
+ *
378
+ * @param changelog - The changelog containing the entry to modify
379
+ * @param version - The version of the entry to modify
380
+ * @param sectionType - The section type containing the item
381
+ * @param itemDescription - The description of the item to remove
382
+ * @param options - Optional removal options
383
+ * @returns A new changelog without the specified item
384
+ */
385
+ function removeItem(changelog, version, sectionType, itemDescription, options) {
386
+ const throwIfNotFound = options?.throwIfNotFound ?? true;
387
+ const entryIndex = changelog.entries.findIndex((e) => e.version === version);
388
+ if (entryIndex === -1) {
389
+ if (throwIfNotFound) {
390
+ throw createError(`Entry with version "${version}" not found`);
391
+ }
392
+ return changelog;
393
+ }
394
+ const entry = changelog.entries[entryIndex];
395
+ const sectionIndex = entry.sections.findIndex((s) => s.type === sectionType);
396
+ if (sectionIndex === -1) {
397
+ if (throwIfNotFound) {
398
+ throw createError(`Section with type "${sectionType}" not found in version "${version}"`);
399
+ }
400
+ return changelog;
401
+ }
402
+ const section = entry.sections[sectionIndex];
403
+ const itemIndex = section.items.findIndex((i) => i.description === itemDescription);
404
+ if (itemIndex === -1) {
405
+ if (throwIfNotFound) {
406
+ throw createError(`Item with description "${itemDescription}" not found in section "${sectionType}"`);
407
+ }
408
+ return changelog;
409
+ }
410
+ const newItems = [...section.items];
411
+ newItems.splice(itemIndex, 1);
412
+ const newSection = {
413
+ ...section,
414
+ items: newItems,
415
+ };
416
+ const newSections = [...entry.sections];
417
+ newSections[sectionIndex] = newSection;
418
+ const newEntry = {
419
+ ...entry,
420
+ sections: newSections,
421
+ };
422
+ const newEntries = [...changelog.entries];
423
+ newEntries[entryIndex] = newEntry;
424
+ return {
425
+ ...changelog,
426
+ entries: newEntries,
427
+ };
428
+ }
429
+ /**
430
+ * Removes empty sections from all entries.
431
+ *
432
+ * @param changelog - The changelog to remove empty sections from
433
+ * @returns A new changelog with empty sections removed
434
+ */
435
+ function removeEmptySections(changelog) {
436
+ const newEntries = changelog.entries.map((entry) => {
437
+ const nonEmptySections = entry.sections.filter((s) => s.items.length > 0);
438
+ if (nonEmptySections.length === entry.sections.length) {
439
+ return entry; // No change
440
+ }
441
+ return {
442
+ ...entry,
443
+ sections: nonEmptySections,
444
+ };
445
+ });
446
+ return {
447
+ ...changelog,
448
+ entries: newEntries,
449
+ };
450
+ }
451
+ /**
452
+ * Removes empty entries (entries with no sections or only empty sections).
453
+ *
454
+ * @param changelog - The changelog to remove empty entries from
455
+ * @param keepUnreleased - Whether to keep an empty unreleased entry (default: true)
456
+ * @returns A new changelog with empty entries removed
457
+ */
458
+ function removeEmptyEntries(changelog, keepUnreleased = true) {
459
+ const newEntries = changelog.entries.filter((entry) => {
460
+ // Check if entry has any items
461
+ const hasItems = entry.sections.some((s) => s.items.length > 0);
462
+ if (hasItems)
463
+ return true;
464
+ if (keepUnreleased && entry.unreleased)
465
+ return true;
466
+ if (entry.rawContent)
467
+ return true;
468
+ return false;
469
+ });
470
+ return {
471
+ ...changelog,
472
+ entries: newEntries,
473
+ };
474
+ }
475
+
476
+ /**
477
+ * Changelog Predicate-Based Filtering
478
+ *
479
+ * Functions for filtering changelog entries, sections, and items using predicates.
480
+ */
481
+ /**
482
+ * Filters entries using a predicate function.
483
+ *
484
+ * @param changelog - The changelog to filter
485
+ * @param predicate - Function that returns true for entries to keep
486
+ * @returns A new changelog with filtered entries
487
+ *
488
+ * @example
489
+ * ```ts
490
+ * const filtered = filterEntries(changelog, (entry) => !entry.unreleased)
491
+ * ```
492
+ */
493
+ function filterEntries(changelog, predicate) {
494
+ const newEntries = changelog.entries.filter(predicate);
495
+ return {
496
+ ...changelog,
497
+ entries: newEntries,
498
+ };
499
+ }
500
+ /**
501
+ * Filters entries that have breaking changes.
502
+ *
503
+ * @param changelog - The changelog to filter
504
+ * @returns A new changelog with only entries containing breaking changes
505
+ */
506
+ function filterBreakingChanges(changelog) {
507
+ return filterEntries(changelog, (entry) => {
508
+ // Check for breaking section
509
+ const hasBreakingSection = entry.sections.some((s) => s.type === 'breaking');
510
+ if (hasBreakingSection)
511
+ return true;
512
+ // Check for breaking items in other sections
513
+ return entry.sections.some((section) => section.items.some((item) => item.breaking));
514
+ });
515
+ }
516
+ /**
517
+ * Filters sections within each entry.
518
+ *
519
+ * @param changelog - The changelog to filter
520
+ * @param predicate - Function that returns true for sections to keep
521
+ * @returns A new changelog with filtered sections
522
+ */
523
+ function filterSections(changelog, predicate) {
524
+ const newEntries = changelog.entries.map((entry) => ({
525
+ ...entry,
526
+ sections: entry.sections.filter((section) => predicate(section, entry)),
527
+ }));
528
+ return { ...changelog, entries: newEntries };
529
+ }
530
+ /**
531
+ * Keeps only specified section types.
532
+ *
533
+ * @param changelog - The changelog to filter
534
+ * @param types - Section types to keep
535
+ * @returns A new changelog with only specified section types
536
+ */
537
+ function filterSectionTypes(changelog, types) {
538
+ const typeSet = createSet(types);
539
+ return filterSections(changelog, (section) => typeSet.has(section.type));
540
+ }
541
+ /**
542
+ * Filters items within sections.
543
+ *
544
+ * @param changelog - The changelog to filter
545
+ * @param predicate - Function that returns true for items to keep
546
+ * @returns A new changelog with filtered items
547
+ */
548
+ function filterItems(changelog, predicate) {
549
+ const newEntries = changelog.entries.map((entry) => ({
550
+ ...entry,
551
+ sections: entry.sections.map((section) => ({
552
+ ...section,
553
+ items: section.items.filter((item) => predicate(item, section, entry)),
554
+ })),
555
+ }));
556
+ return { ...changelog, entries: newEntries };
557
+ }
558
+ /**
559
+ * Filters items by scope.
560
+ *
561
+ * @param changelog - The changelog to filter
562
+ * @param scopes - Scopes to include
563
+ * @returns A new changelog with only items matching the scopes
564
+ */
565
+ function filterByScope(changelog, scopes) {
566
+ const scopeSet = createSet(scopes);
567
+ return filterItems(changelog, (item) => {
568
+ if (!item.scope)
569
+ return false;
570
+ return scopeSet.has(item.scope);
571
+ });
572
+ }
573
+ /**
574
+ * Excludes items by scope.
575
+ *
576
+ * @param changelog - The changelog to filter
577
+ * @param scopes - Scopes to exclude
578
+ * @returns A new changelog without items matching the scopes
579
+ */
580
+ function excludeByScope(changelog, scopes) {
581
+ const scopeSet = createSet(scopes);
582
+ return filterItems(changelog, (item) => {
583
+ if (!item.scope)
584
+ return true;
585
+ return !scopeSet.has(item.scope);
586
+ });
587
+ }
588
+
589
+ /**
590
+ * Safe copies of Math built-in methods.
591
+ *
592
+ * These references are captured at module initialization time to protect against
593
+ * prototype pollution attacks. Import only what you need for tree-shaking.
594
+ *
595
+ * @module @hyperfrontend/immutable-api-utils/built-in-copy/math
596
+ */
597
+ // Capture references at module initialization time
598
+ const _Math = globalThis.Math;
599
+ // ============================================================================
600
+ // Min/Max
601
+ // ============================================================================
602
+ /**
603
+ * (Safe copy) Returns the larger of zero or more numbers.
604
+ */
605
+ const max = _Math.max;
606
+
607
+ /**
608
+ * Safe copies of Number built-in methods and constants.
609
+ *
610
+ * These references are captured at module initialization time to protect against
611
+ * prototype pollution attacks. Import only what you need for tree-shaking.
612
+ *
613
+ * @module @hyperfrontend/immutable-api-utils/built-in-copy/number
614
+ */
615
+ // Capture references at module initialization time
616
+ const _parseInt = globalThis.parseInt;
617
+ const _isNaN = globalThis.isNaN;
618
+ // ============================================================================
619
+ // Parsing
620
+ // ============================================================================
621
+ /**
622
+ * (Safe copy) Parses a string and returns an integer.
623
+ */
624
+ const parseInt = _parseInt;
625
+ // ============================================================================
626
+ // Global Type Checking (legacy, less strict)
627
+ // ============================================================================
628
+ /**
629
+ * (Safe copy) Global isNaN function (coerces to number first, less strict than Number.isNaN).
630
+ */
631
+ const globalIsNaN = _isNaN;
632
+
633
+ /**
634
+ * Compares two semantic versions.
635
+ *
636
+ * @param a - First version
637
+ * @param b - Second version
638
+ * @returns -1 if a < b, 0 if a == b, 1 if a > b
639
+ *
640
+ * @example
641
+ * compare(parseVersion('1.0.0'), parseVersion('2.0.0')) // -1
642
+ * compare(parseVersion('1.0.0'), parseVersion('1.0.0')) // 0
643
+ * compare(parseVersion('2.0.0'), parseVersion('1.0.0')) // 1
644
+ */
645
+ function compare(a, b) {
646
+ // Compare major, minor, patch
647
+ if (a.major !== b.major) {
648
+ return a.major < b.major ? -1 : 1;
649
+ }
650
+ if (a.minor !== b.minor) {
651
+ return a.minor < b.minor ? -1 : 1;
652
+ }
653
+ if (a.patch !== b.patch) {
654
+ return a.patch < b.patch ? -1 : 1;
655
+ }
656
+ // Compare prerelease
657
+ // Version with prerelease has lower precedence than release
658
+ if (a.prerelease.length === 0 && b.prerelease.length > 0) {
659
+ return 1; // a is release, b is prerelease -> a > b
660
+ }
661
+ if (a.prerelease.length > 0 && b.prerelease.length === 0) {
662
+ return -1; // a is prerelease, b is release -> a < b
663
+ }
664
+ // Both have prerelease - compare identifiers
665
+ const maxLen = max(a.prerelease.length, b.prerelease.length);
666
+ for (let i = 0; i < maxLen; i++) {
667
+ const aId = a.prerelease[i];
668
+ const bId = b.prerelease[i];
669
+ // Shorter prerelease array has lower precedence
670
+ if (aId === undefined && bId !== undefined) {
671
+ return -1;
672
+ }
673
+ if (aId !== undefined && bId === undefined) {
674
+ return 1;
675
+ }
676
+ if (aId === undefined || bId === undefined) {
677
+ continue;
678
+ }
679
+ // Compare identifiers
680
+ const cmp = compareIdentifiers(aId, bId);
681
+ if (cmp !== 0) {
682
+ return cmp;
683
+ }
684
+ }
685
+ return 0;
686
+ }
687
+ /**
688
+ * Checks if a version satisfies a comparator.
689
+ *
690
+ * @param version - Version to check
691
+ * @param comparator - Comparator to test against
692
+ * @returns True if version satisfies the comparator
693
+ */
694
+ function satisfiesComparator(version, comparator) {
695
+ const cmp = compare(version, comparator.version);
696
+ switch (comparator.operator) {
697
+ case '=':
698
+ return cmp === 0;
699
+ case '>':
700
+ return cmp === 1;
701
+ case '>=':
702
+ return cmp >= 0;
703
+ case '<':
704
+ return cmp === -1;
705
+ case '<=':
706
+ return cmp <= 0;
707
+ case '^':
708
+ case '~':
709
+ // These should have been expanded during parsing
710
+ // If we encounter them here, treat as >=
711
+ return cmp >= 0;
712
+ default:
713
+ return false;
714
+ }
715
+ }
716
+ /**
717
+ * Checks if a version satisfies a range.
718
+ *
719
+ * @param version - Version to check
720
+ * @param range - Range to test against
721
+ * @returns True if version satisfies the range
722
+ *
723
+ * @example
724
+ * satisfies(parseVersion('1.2.3'), parseRange('^1.0.0')) // true
725
+ * satisfies(parseVersion('2.0.0'), parseRange('^1.0.0')) // false
726
+ */
727
+ function satisfies(version, range) {
728
+ // Empty range matches any
729
+ if (range.sets.length === 0) {
730
+ return true;
731
+ }
732
+ // OR logic: at least one set must be satisfied
733
+ for (const set of range.sets) {
734
+ // AND logic: all comparators in set must be satisfied
735
+ let allSatisfied = true;
736
+ // Empty comparator set matches any
737
+ if (set.comparators.length === 0) {
738
+ return true;
739
+ }
740
+ for (const comp of set.comparators) {
741
+ if (!satisfiesComparator(version, comp)) {
742
+ allSatisfied = false;
743
+ break;
744
+ }
745
+ }
746
+ if (allSatisfied) {
747
+ return true;
748
+ }
749
+ }
750
+ return false;
751
+ }
752
+ // ============================================================================
753
+ // Internal helpers
754
+ // ============================================================================
755
+ /**
756
+ * Compares two prerelease identifiers.
757
+ * Numeric identifiers have lower precedence than alphanumeric.
758
+ * Numeric identifiers are compared numerically.
759
+ * Alphanumeric identifiers are compared lexically.
760
+ *
761
+ * @param a - First prerelease identifier
762
+ * @param b - Second prerelease identifier
763
+ * @returns -1 if a < b, 0 if equal, 1 if a > b
764
+ */
765
+ function compareIdentifiers(a, b) {
766
+ const aIsNumeric = isNumeric(a);
767
+ const bIsNumeric = isNumeric(b);
768
+ // Numeric identifiers have lower precedence
769
+ if (aIsNumeric && !bIsNumeric) {
770
+ return -1;
771
+ }
772
+ if (!aIsNumeric && bIsNumeric) {
773
+ return 1;
774
+ }
775
+ // Both numeric - compare as numbers
776
+ if (aIsNumeric && bIsNumeric) {
777
+ const aNum = parseInt(a, 10);
778
+ const bNum = parseInt(b, 10);
779
+ if (aNum < bNum)
780
+ return -1;
781
+ if (aNum > bNum)
782
+ return 1;
783
+ return 0;
784
+ }
785
+ // Both alphanumeric - compare lexically
786
+ if (a < b)
787
+ return -1;
788
+ if (a > b)
789
+ return 1;
790
+ return 0;
791
+ }
792
+ /**
793
+ * Checks if a string consists only of digits.
794
+ *
795
+ * @param str - String to check for numeric content
796
+ * @returns True if string contains only digits
797
+ */
798
+ function isNumeric(str) {
799
+ if (str.length === 0)
800
+ return false;
801
+ for (let i = 0; i < str.length; i++) {
802
+ const code = str.charCodeAt(i);
803
+ if (code < 48 || code > 57) {
804
+ return false;
805
+ }
806
+ }
807
+ return true;
808
+ }
809
+
810
+ /**
811
+ * Creates a new Comparator.
812
+ *
813
+ * @param operator - The comparison operator
814
+ * @param version - The version to compare against
815
+ * @returns A new Comparator
816
+ */
817
+ function createComparator(operator, version) {
818
+ return { operator, version };
819
+ }
820
+ /**
821
+ * Creates a new ComparatorSet.
822
+ *
823
+ * @param comparators - Array of comparators (AND logic)
824
+ * @returns A new ComparatorSet
825
+ */
826
+ function createComparatorSet(comparators) {
827
+ return { comparators };
828
+ }
829
+ /**
830
+ * Creates a new Range.
831
+ *
832
+ * @param sets - Array of comparator sets (OR logic)
833
+ * @param raw - Original raw string
834
+ * @returns A new Range
835
+ */
836
+ function createRange(sets, raw) {
837
+ return { sets, raw };
838
+ }
839
+
840
+ /**
841
+ * Creates a new SemVer object.
842
+ *
843
+ * @param options - Version components
844
+ * @returns A new SemVer object
845
+ */
846
+ function createSemVer(options) {
847
+ return {
848
+ major: options.major,
849
+ minor: options.minor,
850
+ patch: options.patch,
851
+ prerelease: options.prerelease ?? [],
852
+ build: options.build ?? [],
853
+ raw: options.raw,
854
+ };
855
+ }
856
+
857
+ /**
858
+ * Maximum range string length.
859
+ */
860
+ const MAX_RANGE_LENGTH = 1024;
861
+ /**
862
+ * Parses a semver range string.
863
+ *
864
+ * Supports:
865
+ * - Exact: 1.2.3, =1.2.3
866
+ * - Comparators: >1.0.0, >=1.0.0, <2.0.0, <=2.0.0
867
+ * - Caret: ^1.2.3 (compatible with version)
868
+ * - Tilde: ~1.2.3 (approximately equivalent)
869
+ * - X-ranges: 1.x, 1.2.x, *
870
+ * - Hyphen ranges: 1.0.0 - 2.0.0
871
+ * - OR: 1.0.0 || 2.0.0
872
+ * - AND: >=1.0.0 <2.0.0
873
+ *
874
+ * @param input - The range string to parse
875
+ * @returns A ParseRangeResult with the parsed range or error
876
+ */
877
+ function parseRange(input) {
878
+ if (!input || typeof input !== 'string') {
879
+ return { success: false, error: 'Range string is required' };
880
+ }
881
+ if (input.length > MAX_RANGE_LENGTH) {
882
+ return { success: false, error: `Range string exceeds maximum length of ${MAX_RANGE_LENGTH}` };
883
+ }
884
+ // Trim whitespace
885
+ const trimmed = input.trim();
886
+ // Handle wildcard/any
887
+ if (trimmed === '' || trimmed === '*' || trimmed.toLowerCase() === 'x') {
888
+ return { success: true, range: createRange([], input) };
889
+ }
890
+ // Split by || for OR logic
891
+ const orParts = splitByOr(trimmed);
892
+ const sets = [];
893
+ for (const part of orParts) {
894
+ const setResult = parseComparatorSet(part.trim());
895
+ if (!setResult.success) {
896
+ return { success: false, error: setResult.error };
897
+ }
898
+ if (setResult.set) {
899
+ sets.push(setResult.set);
900
+ }
901
+ }
902
+ return { success: true, range: createRange(sets, input) };
903
+ }
904
+ /**
905
+ * Splits a string by || delimiter, respecting nesting.
906
+ *
907
+ * @param input - Range string containing OR groups
908
+ * @returns Array of OR-separated parts
909
+ */
910
+ function splitByOr(input) {
911
+ const parts = [];
912
+ let current = '';
913
+ let pos = 0;
914
+ while (pos < input.length) {
915
+ if (input[pos] === '|' && pos + 1 < input.length && input[pos + 1] === '|') {
916
+ parts.push(current);
917
+ current = '';
918
+ pos += 2;
919
+ }
920
+ else {
921
+ current += input[pos];
922
+ pos++;
923
+ }
924
+ }
925
+ parts.push(current);
926
+ return parts;
927
+ }
928
+ /**
929
+ * Parses a single comparator set (space-separated comparators = AND logic).
930
+ *
931
+ * @param input - Comparator set string
932
+ * @returns Parsed set result
933
+ */
934
+ function parseComparatorSet(input) {
935
+ if (!input || input.trim() === '') {
936
+ return { success: true }; // Empty set matches any
937
+ }
938
+ const trimmed = input.trim();
939
+ // Check for hyphen range: "1.0.0 - 2.0.0"
940
+ const hyphenMatch = parseHyphenRange(trimmed);
941
+ if (hyphenMatch.isHyphenRange) {
942
+ if (!hyphenMatch.success) {
943
+ return { success: false, error: hyphenMatch.error };
944
+ }
945
+ return { success: true, set: hyphenMatch.set };
946
+ }
947
+ // Split by whitespace for AND logic
948
+ const tokens = splitByWhitespace(trimmed);
949
+ const comparators = [];
950
+ for (const token of tokens) {
951
+ const compResult = parseSingleComparator(token);
952
+ if (!compResult.success) {
953
+ return { success: false, error: compResult.error };
954
+ }
955
+ if (compResult.comparators) {
956
+ comparators.push(...compResult.comparators);
957
+ }
958
+ }
959
+ if (comparators.length === 0) {
960
+ return { success: true }; // Empty matches any
961
+ }
962
+ return { success: true, set: createComparatorSet(comparators) };
963
+ }
964
+ /**
965
+ * Checks for and parses hyphen ranges like "1.0.0 - 2.0.0".
966
+ *
967
+ * @param input - Potential hyphen range string
968
+ * @returns Hyphen range parsing result
969
+ */
970
+ function parseHyphenRange(input) {
971
+ // Look for " - " (space-hyphen-space)
972
+ let hyphenPos = -1;
973
+ for (let i = 0; i < input.length - 2; i++) {
974
+ if (input[i] === ' ' && input[i + 1] === '-' && input[i + 2] === ' ') {
975
+ hyphenPos = i;
976
+ break;
977
+ }
978
+ }
979
+ if (hyphenPos === -1) {
980
+ return { isHyphenRange: false, success: true };
981
+ }
982
+ const leftPart = input.slice(0, hyphenPos).trim();
983
+ const rightPart = input.slice(hyphenPos + 3).trim();
984
+ const leftVersion = parseSimpleVersion(leftPart);
985
+ if (!leftVersion) {
986
+ return { isHyphenRange: true, success: false, error: `Invalid left side of hyphen range: "${leftPart}"` };
987
+ }
988
+ const rightVersion = parseSimpleVersion(rightPart);
989
+ if (!rightVersion) {
990
+ return { isHyphenRange: true, success: false, error: `Invalid right side of hyphen range: "${rightPart}"` };
991
+ }
992
+ // Hyphen range: >=left <=right
993
+ const comparators = [createComparator('>=', leftVersion), createComparator('<=', rightVersion)];
994
+ return {
995
+ isHyphenRange: true,
996
+ success: true,
997
+ set: createComparatorSet(comparators),
998
+ };
999
+ }
1000
+ /**
1001
+ * Splits by whitespace.
1002
+ *
1003
+ * @param input - String to split
1004
+ * @returns Array of whitespace-separated tokens
1005
+ */
1006
+ function splitByWhitespace(input) {
1007
+ const tokens = [];
1008
+ let current = '';
1009
+ for (const char of input) {
1010
+ if (char === ' ' || char === '\t') {
1011
+ if (current) {
1012
+ tokens.push(current);
1013
+ current = '';
1014
+ }
1015
+ }
1016
+ else {
1017
+ current += char;
1018
+ }
1019
+ }
1020
+ if (current) {
1021
+ tokens.push(current);
1022
+ }
1023
+ return tokens;
1024
+ }
1025
+ /**
1026
+ * Parses a single comparator token (e.g., ">=1.0.0", "^1.2.3", "~1.0").
1027
+ *
1028
+ * @param token - Comparator token to parse
1029
+ * @returns Parsed comparator result
1030
+ */
1031
+ function parseSingleComparator(token) {
1032
+ let pos = 0;
1033
+ let operator = '=';
1034
+ // Parse operator
1035
+ if (token[pos] === '^') {
1036
+ operator = '^';
1037
+ pos++;
1038
+ }
1039
+ else if (token[pos] === '~') {
1040
+ operator = '~';
1041
+ pos++;
1042
+ }
1043
+ else if (token[pos] === '>') {
1044
+ if (token[pos + 1] === '=') {
1045
+ operator = '>=';
1046
+ pos += 2;
1047
+ }
1048
+ else {
1049
+ operator = '>';
1050
+ pos++;
1051
+ }
1052
+ }
1053
+ else if (token[pos] === '<') {
1054
+ if (token[pos + 1] === '=') {
1055
+ operator = '<=';
1056
+ pos += 2;
1057
+ }
1058
+ else {
1059
+ operator = '<';
1060
+ pos++;
1061
+ }
1062
+ }
1063
+ else if (token[pos] === '=') {
1064
+ operator = '=';
1065
+ pos++;
1066
+ }
1067
+ const versionPart = token.slice(pos);
1068
+ // Handle wildcards: *, x, X
1069
+ if (versionPart === '*' || versionPart.toLowerCase() === 'x') {
1070
+ // Wildcard matches any - return empty (will be handled as match-all)
1071
+ return { success: true, comparators: [] };
1072
+ }
1073
+ // Handle x-ranges: 1.x, 1.2.x
1074
+ if (versionPart.includes('x') || versionPart.includes('X') || versionPart.includes('*')) {
1075
+ return parseXRange(versionPart);
1076
+ }
1077
+ // Parse version
1078
+ const version = parseSimpleVersion(versionPart);
1079
+ if (!version) {
1080
+ return { success: false, error: `Invalid version in comparator: "${versionPart}"` };
1081
+ }
1082
+ // For caret and tilde, expand to range
1083
+ if (operator === '^') {
1084
+ return expandCaretRange(version);
1085
+ }
1086
+ if (operator === '~') {
1087
+ return expandTildeRange(version);
1088
+ }
1089
+ return { success: true, comparators: [createComparator(operator, version)] };
1090
+ }
1091
+ /**
1092
+ * Parses x-ranges like 1.x, 1.2.x, etc.
1093
+ *
1094
+ * @param input - X-range string to parse
1095
+ * @param _operator - Range operator (unused but kept for interface consistency)
1096
+ * @returns Comparator result
1097
+ */
1098
+ function parseXRange(input, _operator) {
1099
+ const parts = input.split('.');
1100
+ const nums = [];
1101
+ for (const part of parts) {
1102
+ const lower = part.toLowerCase();
1103
+ if (lower === 'x' || lower === '*' || lower === '') {
1104
+ break;
1105
+ }
1106
+ const num = parseInt(part, 10);
1107
+ if (globalIsNaN(num) || num < 0) {
1108
+ return { success: false, error: `Invalid x-range: "${input}"` };
1109
+ }
1110
+ nums.push(num);
1111
+ }
1112
+ if (nums.length === 0) {
1113
+ // * or X alone - match any
1114
+ return { success: true, comparators: [] };
1115
+ }
1116
+ if (nums.length === 1) {
1117
+ // 1.x or 1.* -> >=1.0.0 <2.0.0
1118
+ const lower = createSemVer({ major: nums[0], minor: 0, patch: 0 });
1119
+ const upper = createSemVer({ major: nums[0] + 1, minor: 0, patch: 0 });
1120
+ return { success: true, comparators: [createComparator('>=', lower), createComparator('<', upper)] };
1121
+ }
1122
+ // 1.2.x -> >=1.2.0 <1.3.0
1123
+ const lower = createSemVer({ major: nums[0], minor: nums[1], patch: 0 });
1124
+ const upper = createSemVer({ major: nums[0], minor: nums[1] + 1, patch: 0 });
1125
+ return { success: true, comparators: [createComparator('>=', lower), createComparator('<', upper)] };
1126
+ }
1127
+ /**
1128
+ * Expands caret range: ^1.2.3 -> >=1.2.3 <2.0.0
1129
+ *
1130
+ * @param version - Base version for caret range
1131
+ * @returns Expanded comparator result
1132
+ */
1133
+ function expandCaretRange(version) {
1134
+ let upperMajor = version.major;
1135
+ let upperMinor = 0;
1136
+ let upperPatch = 0;
1137
+ if (version.major === 0) {
1138
+ if (version.minor === 0) {
1139
+ // ^0.0.x -> >=0.0.x <0.0.(x+1)
1140
+ upperPatch = version.patch + 1;
1141
+ upperMinor = version.minor;
1142
+ }
1143
+ else {
1144
+ // ^0.x.y -> >=0.x.y <0.(x+1).0
1145
+ upperMinor = version.minor + 1;
1146
+ }
1147
+ }
1148
+ else {
1149
+ // ^x.y.z -> >=x.y.z <(x+1).0.0
1150
+ upperMajor = version.major + 1;
1151
+ }
1152
+ const upper = createSemVer({ major: upperMajor, minor: upperMinor, patch: upperPatch });
1153
+ return { success: true, comparators: [createComparator('>=', version), createComparator('<', upper)] };
1154
+ }
1155
+ /**
1156
+ * Expands tilde range: ~1.2.3 -> >=1.2.3 <1.3.0
1157
+ *
1158
+ * @param version - Base version for tilde range
1159
+ * @returns Expanded comparator result
1160
+ */
1161
+ function expandTildeRange(version) {
1162
+ const upper = createSemVer({
1163
+ major: version.major,
1164
+ minor: version.minor + 1,
1165
+ patch: 0,
1166
+ });
1167
+ return { success: true, comparators: [createComparator('>=', version), createComparator('<', upper)] };
1168
+ }
1169
+ /**
1170
+ * Parses a simple version string (no range operators).
1171
+ * More lenient - accepts partial versions.
1172
+ *
1173
+ * @param input - Version string to parse
1174
+ * @returns Parsed SemVer or null if invalid
1175
+ */
1176
+ function parseSimpleVersion(input) {
1177
+ if (!input)
1178
+ return null;
1179
+ let pos = 0;
1180
+ // Skip leading v
1181
+ if (input[pos] === 'v' || input[pos] === 'V') {
1182
+ pos++;
1183
+ }
1184
+ const parts = input.slice(pos).split('.');
1185
+ if (parts.length === 0)
1186
+ return null;
1187
+ const nums = [];
1188
+ for (const part of parts) {
1189
+ // Stop at prerelease or build
1190
+ const dashIdx = part.indexOf('-');
1191
+ const plusIdx = part.indexOf('+');
1192
+ let numPart = part;
1193
+ if (dashIdx !== -1) {
1194
+ numPart = part.slice(0, dashIdx);
1195
+ }
1196
+ else if (plusIdx !== -1) {
1197
+ numPart = part.slice(0, plusIdx);
1198
+ }
1199
+ if (numPart === '' || numPart.toLowerCase() === 'x' || numPart === '*') {
1200
+ break;
1201
+ }
1202
+ const num = parseInt(numPart, 10);
1203
+ if (globalIsNaN(num) || num < 0)
1204
+ return null;
1205
+ nums.push(num);
1206
+ }
1207
+ if (nums.length === 0)
1208
+ return null;
1209
+ return createSemVer({
1210
+ major: nums[0],
1211
+ minor: nums[1] ?? 0,
1212
+ patch: nums[2] ?? 0,
1213
+ prerelease: [],
1214
+ build: [],
1215
+ raw: input,
1216
+ });
1217
+ }
1218
+
1219
+ /**
1220
+ * Maximum version string length to prevent memory exhaustion.
1221
+ */
1222
+ const MAX_VERSION_LENGTH = 256;
1223
+ /**
1224
+ * Parses a semantic version string.
1225
+ *
1226
+ * Accepts versions in the format: MAJOR.MINOR.PATCH[-prerelease][+build]
1227
+ * Optional leading 'v' or '=' prefixes are stripped.
1228
+ *
1229
+ * @param input - The version string to parse
1230
+ * @returns A ParseVersionResult with the parsed version or error
1231
+ *
1232
+ * @example
1233
+ * parseVersion('1.2.3') // { success: true, version: { major: 1, minor: 2, patch: 3, ... } }
1234
+ * parseVersion('v1.0.0-alpha.1+build.123') // { success: true, ... }
1235
+ * parseVersion('invalid') // { success: false, error: '...' }
1236
+ */
1237
+ function parseVersion(input) {
1238
+ // Input validation
1239
+ if (!input || typeof input !== 'string') {
1240
+ return { success: false, error: 'Version string is required' };
1241
+ }
1242
+ if (input.length > MAX_VERSION_LENGTH) {
1243
+ return { success: false, error: `Version string exceeds maximum length of ${MAX_VERSION_LENGTH}` };
1244
+ }
1245
+ // Strip leading whitespace
1246
+ let pos = 0;
1247
+ while (pos < input.length && isWhitespace(input.charCodeAt(pos))) {
1248
+ pos++;
1249
+ }
1250
+ // Strip trailing whitespace
1251
+ let end = input.length;
1252
+ while (end > pos && isWhitespace(input.charCodeAt(end - 1))) {
1253
+ end--;
1254
+ }
1255
+ // Strip optional leading 'v' or '='
1256
+ if (pos < end) {
1257
+ const code = input.charCodeAt(pos);
1258
+ if (code === 118 || code === 86) {
1259
+ // 'v' or 'V'
1260
+ pos++;
1261
+ }
1262
+ else if (code === 61) {
1263
+ // '='
1264
+ pos++;
1265
+ }
1266
+ }
1267
+ // Parse major version
1268
+ const majorResult = parseNumericIdentifier(input, pos, end);
1269
+ if (!majorResult.success) {
1270
+ return { success: false, error: majorResult.error ?? 'Invalid major version' };
1271
+ }
1272
+ pos = majorResult.endPos;
1273
+ // Expect dot
1274
+ if (pos >= end || input.charCodeAt(pos) !== 46) {
1275
+ // '.'
1276
+ return { success: false, error: 'Expected "." after major version' };
1277
+ }
1278
+ pos++;
1279
+ // Parse minor version
1280
+ const minorResult = parseNumericIdentifier(input, pos, end);
1281
+ if (!minorResult.success) {
1282
+ return { success: false, error: minorResult.error ?? 'Invalid minor version' };
1283
+ }
1284
+ pos = minorResult.endPos;
1285
+ // Expect dot
1286
+ if (pos >= end || input.charCodeAt(pos) !== 46) {
1287
+ // '.'
1288
+ return { success: false, error: 'Expected "." after minor version' };
1289
+ }
1290
+ pos++;
1291
+ // Parse patch version
1292
+ const patchResult = parseNumericIdentifier(input, pos, end);
1293
+ if (!patchResult.success) {
1294
+ return { success: false, error: patchResult.error ?? 'Invalid patch version' };
1295
+ }
1296
+ pos = patchResult.endPos;
1297
+ // Parse optional prerelease
1298
+ const prerelease = [];
1299
+ if (pos < end && input.charCodeAt(pos) === 45) {
1300
+ // '-'
1301
+ pos++;
1302
+ const prereleaseResult = parseIdentifiers(input, pos, end, [43]); // Stop at '+'
1303
+ if (!prereleaseResult.success) {
1304
+ return { success: false, error: prereleaseResult.error ?? 'Invalid prerelease' };
1305
+ }
1306
+ prerelease.push(...prereleaseResult.identifiers);
1307
+ pos = prereleaseResult.endPos;
1308
+ }
1309
+ // Parse optional build metadata
1310
+ const build = [];
1311
+ if (pos < end && input.charCodeAt(pos) === 43) {
1312
+ // '+'
1313
+ pos++;
1314
+ const buildResult = parseIdentifiers(input, pos, end, []);
1315
+ if (!buildResult.success) {
1316
+ return { success: false, error: buildResult.error ?? 'Invalid build metadata' };
1317
+ }
1318
+ build.push(...buildResult.identifiers);
1319
+ pos = buildResult.endPos;
1320
+ }
1321
+ // Check for trailing characters
1322
+ if (pos < end) {
1323
+ return { success: false, error: `Unexpected character at position ${pos}: "${input[pos]}"` };
1324
+ }
1325
+ return {
1326
+ success: true,
1327
+ version: createSemVer({
1328
+ major: majorResult.value,
1329
+ minor: minorResult.value,
1330
+ patch: patchResult.value,
1331
+ prerelease,
1332
+ build,
1333
+ raw: input,
1334
+ }),
1335
+ };
1336
+ }
1337
+ /**
1338
+ * Parses a numeric identifier (non-negative integer, no leading zeros except for "0").
1339
+ *
1340
+ * @param input - Input string to parse
1341
+ * @param start - Start position in the input
1342
+ * @param end - End position in the input
1343
+ * @returns Numeric parsing result
1344
+ */
1345
+ function parseNumericIdentifier(input, start, end) {
1346
+ if (start >= end) {
1347
+ return { success: false, value: 0, endPos: start, error: 'Expected numeric identifier' };
1348
+ }
1349
+ let pos = start;
1350
+ const firstCode = input.charCodeAt(pos);
1351
+ // Must start with a digit
1352
+ if (!isDigit(firstCode)) {
1353
+ return { success: false, value: 0, endPos: pos, error: 'Expected digit' };
1354
+ }
1355
+ // Check for leading zero (only "0" is valid, not "01", "007", etc.)
1356
+ if (firstCode === 48 && pos + 1 < end && isDigit(input.charCodeAt(pos + 1))) {
1357
+ return { success: false, value: 0, endPos: pos, error: 'Numeric identifier cannot have leading zeros' };
1358
+ }
1359
+ // Consume digits
1360
+ let value = 0;
1361
+ while (pos < end && isDigit(input.charCodeAt(pos))) {
1362
+ value = value * 10 + (input.charCodeAt(pos) - 48);
1363
+ pos++;
1364
+ // Prevent overflow
1365
+ if (value > Number.MAX_SAFE_INTEGER) {
1366
+ return { success: false, value: 0, endPos: pos, error: 'Numeric identifier is too large' };
1367
+ }
1368
+ }
1369
+ return { success: true, value, endPos: pos };
1370
+ }
1371
+ /**
1372
+ * Parses dot-separated identifiers (for prerelease/build).
1373
+ *
1374
+ * @param input - Input string to parse
1375
+ * @param start - Start position in the input
1376
+ * @param end - End position in the input
1377
+ * @param stopCodes - Character codes that signal end of identifiers
1378
+ * @returns Identifiers parsing result
1379
+ */
1380
+ function parseIdentifiers(input, start, end, stopCodes) {
1381
+ const identifiers = [];
1382
+ let pos = start;
1383
+ while (pos < end) {
1384
+ // Check for stop characters
1385
+ if (stopCodes.includes(input.charCodeAt(pos))) {
1386
+ break;
1387
+ }
1388
+ // Parse one identifier
1389
+ const identStart = pos;
1390
+ while (pos < end) {
1391
+ const code = input.charCodeAt(pos);
1392
+ // Stop at dot or stop characters
1393
+ if (code === 46 || stopCodes.includes(code)) {
1394
+ break;
1395
+ }
1396
+ // Must be alphanumeric or hyphen
1397
+ if (!isAlphanumeric(code) && code !== 45) {
1398
+ return { success: false, identifiers: [], endPos: pos, error: `Invalid character in identifier: "${input[pos]}"` };
1399
+ }
1400
+ pos++;
1401
+ }
1402
+ // Empty identifier is not allowed
1403
+ if (pos === identStart) {
1404
+ return { success: false, identifiers: [], endPos: pos, error: 'Empty identifier' };
1405
+ }
1406
+ identifiers.push(input.slice(identStart, pos));
1407
+ // Consume dot separator
1408
+ if (pos < end && input.charCodeAt(pos) === 46) {
1409
+ pos++;
1410
+ // Dot at end is invalid
1411
+ if (pos >= end || stopCodes.includes(input.charCodeAt(pos))) {
1412
+ return { success: false, identifiers: [], endPos: pos, error: 'Identifier expected after dot' };
1413
+ }
1414
+ }
1415
+ }
1416
+ return { success: true, identifiers, endPos: pos };
1417
+ }
1418
+ /**
1419
+ * Checks if a character code is a digit (0-9).
1420
+ *
1421
+ * @param code - Character code to check
1422
+ * @returns True if the code represents a digit
1423
+ */
1424
+ function isDigit(code) {
1425
+ return code >= 48 && code <= 57;
1426
+ }
1427
+ /**
1428
+ * Checks if a character code is alphanumeric or hyphen.
1429
+ *
1430
+ * @param code - Character code to check
1431
+ * @returns True if the code represents an alphanumeric character
1432
+ */
1433
+ function isAlphanumeric(code) {
1434
+ return ((code >= 48 && code <= 57) || // 0-9
1435
+ (code >= 65 && code <= 90) || // A-Z
1436
+ (code >= 97 && code <= 122) // a-z
1437
+ );
1438
+ }
1439
+ /**
1440
+ * Checks if a character code is whitespace.
1441
+ *
1442
+ * @param code - Character code to check
1443
+ * @returns True if the code represents whitespace
1444
+ */
1445
+ function isWhitespace(code) {
1446
+ return code === 32 || code === 9 || code === 10 || code === 13;
1447
+ }
1448
+
1449
+ /**
1450
+ * Changelog Range-Based Filtering
1451
+ *
1452
+ * Functions for filtering changelog entries by version range, date range, and count.
1453
+ */
1454
+ /**
1455
+ * Filters entries by version range using semver.
1456
+ *
1457
+ * @param changelog - The changelog to filter
1458
+ * @param range - Semver range string (e.g., '>=1.0.0 <2.0.0')
1459
+ * @returns A new changelog with entries matching the range
1460
+ *
1461
+ * @example
1462
+ * ```ts
1463
+ * const majors = filterByVersionRange(changelog, '>=1.0.0 <2.0.0')
1464
+ * ```
1465
+ */
1466
+ function filterByVersionRange(changelog, range) {
1467
+ const rangeResult = parseRange(range);
1468
+ if (!rangeResult.success) {
1469
+ throw createError(`Invalid version range: ${range}`);
1470
+ }
1471
+ return filterEntries(changelog, (entry) => {
1472
+ // Skip unreleased
1473
+ if (entry.unreleased)
1474
+ return false;
1475
+ const versionResult = parseVersion(entry.version);
1476
+ if (!versionResult.success)
1477
+ return false;
1478
+ return satisfies(versionResult.version, rangeResult.range);
1479
+ });
1480
+ }
1481
+ /**
1482
+ * Filters entries from a start version.
1483
+ *
1484
+ * @param changelog - The changelog to filter
1485
+ * @param startVersion - The minimum version (inclusive)
1486
+ * @returns A new changelog with entries >= startVersion
1487
+ */
1488
+ function filterFromVersion(changelog, startVersion) {
1489
+ const startResult = parseVersion(startVersion);
1490
+ if (!startResult.success) {
1491
+ throw createError(`Invalid start version: ${startVersion}`);
1492
+ }
1493
+ return filterEntries(changelog, (entry) => {
1494
+ if (entry.unreleased)
1495
+ return true;
1496
+ const versionResult = parseVersion(entry.version);
1497
+ if (!versionResult.success)
1498
+ return false;
1499
+ return compare(versionResult.version, startResult.version) >= 0;
1500
+ });
1501
+ }
1502
+ /**
1503
+ * Filters entries up to an end version.
1504
+ *
1505
+ * @param changelog - The changelog to apply the version filter to
1506
+ * @param endVersion - The maximum version (inclusive)
1507
+ * @returns A new changelog with entries <= endVersion
1508
+ */
1509
+ function filterToVersion(changelog, endVersion) {
1510
+ const endResult = parseVersion(endVersion);
1511
+ if (!endResult.success) {
1512
+ throw createError(`Invalid end version: ${endVersion}`);
1513
+ }
1514
+ return filterEntries(changelog, (entry) => {
1515
+ if (entry.unreleased)
1516
+ return false;
1517
+ const versionResult = parseVersion(entry.version);
1518
+ if (!versionResult.success)
1519
+ return false;
1520
+ return compare(versionResult.version, endResult.version) <= 0;
1521
+ });
1522
+ }
1523
+ /**
1524
+ * Gets entries within a version range (inclusive).
1525
+ *
1526
+ * @param changelog - The changelog to filter
1527
+ * @param startVersion - The minimum version (inclusive)
1528
+ * @param endVersion - The maximum version (inclusive)
1529
+ * @returns A new changelog with entries in the range
1530
+ */
1531
+ function filterVersionRange(changelog, startVersion, endVersion) {
1532
+ const startResult = parseVersion(startVersion);
1533
+ const endResult = parseVersion(endVersion);
1534
+ if (!startResult.success) {
1535
+ throw createError(`Invalid start version: ${startVersion}`);
1536
+ }
1537
+ if (!endResult.success) {
1538
+ throw createError(`Invalid end version: ${endVersion}`);
1539
+ }
1540
+ return filterEntries(changelog, (entry) => {
1541
+ if (entry.unreleased)
1542
+ return false;
1543
+ const versionResult = parseVersion(entry.version);
1544
+ if (!versionResult.success)
1545
+ return false;
1546
+ const cmpStart = compare(versionResult.version, startResult.version);
1547
+ const cmpEnd = compare(versionResult.version, endResult.version);
1548
+ return cmpStart >= 0 && cmpEnd <= 0;
1549
+ });
1550
+ }
1551
+ /**
1552
+ * Gets the N most recent entries.
1553
+ *
1554
+ * @param changelog - The changelog to filter
1555
+ * @param count - Number of entries to keep
1556
+ * @param includeUnreleased - Whether to include unreleased in count (default: false)
1557
+ * @returns A new changelog with only the most recent entries
1558
+ */
1559
+ function filterRecentEntries(changelog, count, includeUnreleased = false) {
1560
+ if (count <= 0) {
1561
+ return { ...changelog, entries: [] };
1562
+ }
1563
+ const entries = includeUnreleased
1564
+ ? changelog.entries.slice(0, count)
1565
+ : (() => {
1566
+ const result = [];
1567
+ let remaining = count;
1568
+ for (const entry of changelog.entries) {
1569
+ if (entry.unreleased) {
1570
+ result.push(entry);
1571
+ }
1572
+ else if (remaining > 0) {
1573
+ result.push(entry);
1574
+ remaining--;
1575
+ }
1576
+ if (remaining <= 0 && !entry.unreleased)
1577
+ break;
1578
+ }
1579
+ return result;
1580
+ })();
1581
+ return { ...changelog, entries };
1582
+ }
1583
+ /**
1584
+ * Filters entries by release date.
1585
+ *
1586
+ * @param changelog - The changelog to filter
1587
+ * @param startDate - Start date (inclusive, ISO format)
1588
+ * @param endDate - End date (inclusive, ISO format)
1589
+ * @returns A new changelog with entries in the date range
1590
+ */
1591
+ function filterByDateRange(changelog, startDate, endDate) {
1592
+ return filterEntries(changelog, (entry) => {
1593
+ if (!entry.date)
1594
+ return false;
1595
+ if (startDate && entry.date < startDate)
1596
+ return false;
1597
+ if (endDate && entry.date > endDate)
1598
+ return false;
1599
+ return true;
1600
+ });
1601
+ }
1602
+
1603
+ /**
1604
+ * Safe copies of Map built-in via factory function.
1605
+ *
1606
+ * Since constructors cannot be safely captured via Object.assign, this module
1607
+ * provides a factory function that uses Reflect.construct internally.
1608
+ *
1609
+ * These references are captured at module initialization time to protect against
1610
+ * prototype pollution attacks. Import only what you need for tree-shaking.
1611
+ *
1612
+ * @module @hyperfrontend/immutable-api-utils/built-in-copy/map
1613
+ */
1614
+ // Capture references at module initialization time
1615
+ const _Map = globalThis.Map;
1616
+ const _Reflect = globalThis.Reflect;
1617
+ /**
1618
+ * (Safe copy) Creates a new Map using the captured Map constructor.
1619
+ * Use this instead of `new Map()`.
1620
+ *
1621
+ * @param iterable - Optional iterable of key-value pairs.
1622
+ * @returns A new Map instance.
1623
+ */
1624
+ const createMap = (iterable) => _Reflect.construct(_Map, iterable ? [iterable] : []);
1625
+
1626
+ /**
1627
+ * Changelog Equality Comparison
1628
+ *
1629
+ * Functions for comparing changelogs for structural equality.
1630
+ * Uses deep comparison without relying on JSON serialization.
1631
+ */
1632
+ /**
1633
+ * Checks if two changelog entries are equal.
1634
+ *
1635
+ * @param a - First entry
1636
+ * @param b - Second entry
1637
+ * @returns True if entries are equal
1638
+ */
1639
+ function isEntryEqual(a, b) {
1640
+ if (a.version !== b.version)
1641
+ return false;
1642
+ if (a.date !== b.date)
1643
+ return false;
1644
+ if (a.unreleased !== b.unreleased)
1645
+ return false;
1646
+ if (a.compareUrl !== b.compareUrl)
1647
+ return false;
1648
+ if (a.rawContent !== b.rawContent)
1649
+ return false;
1650
+ // Check sections
1651
+ if (a.sections.length !== b.sections.length)
1652
+ return false;
1653
+ for (let i = 0; i < a.sections.length; i++) {
1654
+ if (!isSectionEqual(a.sections[i], b.sections[i]))
1655
+ return false;
1656
+ }
1657
+ return true;
1658
+ }
1659
+ /**
1660
+ * Checks if two changelog sections are equal.
1661
+ *
1662
+ * @param a - First section
1663
+ * @param b - Second section
1664
+ * @returns True if sections are equal
1665
+ */
1666
+ function isSectionEqual(a, b) {
1667
+ if (a.type !== b.type)
1668
+ return false;
1669
+ if (a.heading !== b.heading)
1670
+ return false;
1671
+ // Check items
1672
+ if (a.items.length !== b.items.length)
1673
+ return false;
1674
+ for (let i = 0; i < a.items.length; i++) {
1675
+ if (!isItemEqual(a.items[i], b.items[i]))
1676
+ return false;
1677
+ }
1678
+ return true;
1679
+ }
1680
+ /**
1681
+ * Checks if two changelog items are equal.
1682
+ *
1683
+ * @param a - First item
1684
+ * @param b - Second item
1685
+ * @returns True if items are equal
1686
+ */
1687
+ function isItemEqual(a, b) {
1688
+ if (a.scope !== b.scope)
1689
+ return false;
1690
+ if (a.description !== b.description)
1691
+ return false;
1692
+ if (a.breaking !== b.breaking)
1693
+ return false;
1694
+ // Check commits
1695
+ if (a.commits.length !== b.commits.length)
1696
+ return false;
1697
+ for (let i = 0; i < a.commits.length; i++) {
1698
+ if (!isCommitRefEqual(a.commits[i], b.commits[i]))
1699
+ return false;
1700
+ }
1701
+ // Check references
1702
+ if (a.references.length !== b.references.length)
1703
+ return false;
1704
+ for (let i = 0; i < a.references.length; i++) {
1705
+ if (!isIssueRefEqual(a.references[i], b.references[i]))
1706
+ return false;
1707
+ }
1708
+ return true;
1709
+ }
1710
+ /**
1711
+ * Checks if two commit references are equal.
1712
+ *
1713
+ * @param a - First commit ref
1714
+ * @param b - Second commit ref
1715
+ * @returns True if commit refs are equal
1716
+ */
1717
+ function isCommitRefEqual(a, b) {
1718
+ return a.hash === b.hash && a.shortHash === b.shortHash && a.url === b.url;
1719
+ }
1720
+ /**
1721
+ * Checks if two issue references are equal.
1722
+ *
1723
+ * @param a - First issue ref
1724
+ * @param b - Second issue ref
1725
+ * @returns True if issue refs are equal
1726
+ */
1727
+ function isIssueRefEqual(a, b) {
1728
+ return a.number === b.number && a.type === b.type && a.url === b.url;
1729
+ }
1730
+
1731
+ /**
1732
+ * Changelog Merging
1733
+ *
1734
+ * Functions for merging multiple changelogs or changelog entries.
1735
+ */
1736
+ /**
1737
+ * Default merge options.
1738
+ */
1739
+ const DEFAULT_MERGE_OPTIONS = {
1740
+ entryStrategy: 'union',
1741
+ sectionStrategy: 'union',
1742
+ itemStrategy: 'union',
1743
+ useSourceHeader: true,
1744
+ sortByVersion: true,
1745
+ removeDuplicates: true,
1746
+ };
1747
+ /**
1748
+ * Merges two changelogs together.
1749
+ *
1750
+ * @param source - The source changelog
1751
+ * @param target - The target changelog
1752
+ * @param options - Optional merge options
1753
+ * @returns The merge result with merged changelog and stats
1754
+ *
1755
+ * @example
1756
+ * ```ts
1757
+ * const result = mergeChangelogs(mainChangelog, branchChangelog)
1758
+ * console.log(`Merged ${result.stats.merged} entries`)
1759
+ * ```
1760
+ */
1761
+ function mergeChangelogs(source, target, options) {
1762
+ const opts = resolveOptions(options);
1763
+ // Build version maps
1764
+ const sourceByVersion = createMap();
1765
+ const targetByVersion = createMap();
1766
+ for (const entry of source.entries) {
1767
+ sourceByVersion.set(entry.version, entry);
1768
+ }
1769
+ for (const entry of target.entries) {
1770
+ targetByVersion.set(entry.version, entry);
1771
+ }
1772
+ const mergedEntries = [];
1773
+ const allVersions = createSet([...sourceByVersion.keys(), ...targetByVersion.keys()]);
1774
+ let sourceOnly = 0;
1775
+ let targetOnly = 0;
1776
+ let merged = 0;
1777
+ let conflictsResolved = 0;
1778
+ for (const version of allVersions) {
1779
+ const sourceEntry = sourceByVersion.get(version);
1780
+ const targetEntry = targetByVersion.get(version);
1781
+ if (sourceEntry && !targetEntry) {
1782
+ // Only in source
1783
+ mergedEntries.push(sourceEntry);
1784
+ sourceOnly++;
1785
+ }
1786
+ else if (!sourceEntry && targetEntry) {
1787
+ // Only in target
1788
+ mergedEntries.push(targetEntry);
1789
+ targetOnly++;
1790
+ }
1791
+ else if (sourceEntry && targetEntry) {
1792
+ // In both - merge
1793
+ if (isEntryEqual(sourceEntry, targetEntry)) {
1794
+ mergedEntries.push(sourceEntry);
1795
+ }
1796
+ else {
1797
+ const mergedEntry = mergeEntries(sourceEntry, targetEntry, opts);
1798
+ mergedEntries.push(mergedEntry);
1799
+ conflictsResolved++;
1800
+ }
1801
+ merged++;
1802
+ }
1803
+ }
1804
+ // Sort by version if requested
1805
+ let finalEntries = mergedEntries;
1806
+ if (opts.sortByVersion) {
1807
+ finalEntries = sortEntriesByVersion(mergedEntries);
1808
+ }
1809
+ // Build header
1810
+ const header = opts.useSourceHeader ? source.header : target.header;
1811
+ // Build metadata
1812
+ const metadata = {
1813
+ format: source.metadata.format,
1814
+ isConventional: source.metadata.isConventional || target.metadata.isConventional,
1815
+ repositoryUrl: source.metadata.repositoryUrl ?? target.metadata.repositoryUrl,
1816
+ packageName: source.metadata.packageName ?? target.metadata.packageName,
1817
+ warnings: [...source.metadata.warnings, ...target.metadata.warnings],
1818
+ };
1819
+ const changelog = {
1820
+ header,
1821
+ entries: finalEntries,
1822
+ metadata,
1823
+ };
1824
+ return {
1825
+ changelog,
1826
+ stats: {
1827
+ totalEntries: finalEntries.length,
1828
+ sourceOnly,
1829
+ targetOnly,
1830
+ merged,
1831
+ conflictsResolved,
1832
+ },
1833
+ };
1834
+ }
1835
+ /**
1836
+ * Merges two changelog entries.
1837
+ *
1838
+ * @param source - Source entry
1839
+ * @param target - Target entry
1840
+ * @param opts - Resolved merge options
1841
+ * @returns Merged entry
1842
+ */
1843
+ function mergeEntries(source, target, opts) {
1844
+ // Use strategy for scalar values
1845
+ const date = opts.entryStrategy === 'target' ? target.date : source.date;
1846
+ const compareUrl = opts.entryStrategy === 'target' ? target.compareUrl : source.compareUrl;
1847
+ const unreleased = source.unreleased || target.unreleased;
1848
+ // Merge sections
1849
+ const sections = mergeSections(source.sections, target.sections, opts);
1850
+ return {
1851
+ version: source.version,
1852
+ date,
1853
+ unreleased,
1854
+ compareUrl,
1855
+ sections,
1856
+ };
1857
+ }
1858
+ /**
1859
+ * Merges section arrays.
1860
+ *
1861
+ * @param sourceSections - Array of sections from the source changelog
1862
+ * @param targetSections - Array of sections from the target changelog
1863
+ * @param opts - Resolved merge options
1864
+ * @returns Merged sections
1865
+ */
1866
+ function mergeSections(sourceSections, targetSections, opts) {
1867
+ const sourceByType = createMap();
1868
+ const targetByType = createMap();
1869
+ for (const section of sourceSections) {
1870
+ sourceByType.set(section.type, section);
1871
+ }
1872
+ for (const section of targetSections) {
1873
+ targetByType.set(section.type, section);
1874
+ }
1875
+ const result = [];
1876
+ const allTypes = createSet([...sourceByType.keys(), ...targetByType.keys()]);
1877
+ for (const type of allTypes) {
1878
+ const sourceSection = sourceByType.get(type);
1879
+ const targetSection = targetByType.get(type);
1880
+ if (sourceSection && !targetSection) {
1881
+ result.push(sourceSection);
1882
+ }
1883
+ else if (!sourceSection && targetSection) {
1884
+ result.push(targetSection);
1885
+ }
1886
+ else if (sourceSection && targetSection) {
1887
+ if (isSectionEqual(sourceSection, targetSection)) {
1888
+ result.push(sourceSection);
1889
+ }
1890
+ else {
1891
+ const mergedSection = mergeSection(sourceSection, targetSection, opts);
1892
+ result.push(mergedSection);
1893
+ }
1894
+ }
1895
+ }
1896
+ return result;
1897
+ }
1898
+ /**
1899
+ * Merges two sections.
1900
+ *
1901
+ * @param source - Section from the source changelog
1902
+ * @param target - Section from the target changelog
1903
+ * @param opts - Resolved merge options
1904
+ * @returns Merged section
1905
+ */
1906
+ function mergeSection(source, target, opts) {
1907
+ const heading = opts.sectionStrategy === 'target' ? target.heading : source.heading;
1908
+ const items = mergeItems(source.items, target.items, opts);
1909
+ return {
1910
+ type: source.type,
1911
+ heading,
1912
+ items,
1913
+ };
1914
+ }
1915
+ /**
1916
+ * Merges item arrays.
1917
+ *
1918
+ * @param sourceItems - Array of items from the source section
1919
+ * @param targetItems - Array of items from the target section
1920
+ * @param opts - Resolved merge options
1921
+ * @returns Merged items
1922
+ */
1923
+ function mergeItems(sourceItems, targetItems, opts) {
1924
+ if (opts.itemStrategy === 'source') {
1925
+ return [...sourceItems];
1926
+ }
1927
+ if (opts.itemStrategy === 'target') {
1928
+ return [...targetItems];
1929
+ }
1930
+ // Union strategy - combine all items
1931
+ const result = [...sourceItems];
1932
+ for (const targetItem of targetItems) {
1933
+ // Check if item already exists in source
1934
+ const exists = sourceItems.some((sourceItem) => isItemEqual(sourceItem, targetItem));
1935
+ if (!exists) {
1936
+ // Check by description only (for 'latest' strategy)
1937
+ if (opts.itemStrategy === 'latest') {
1938
+ const existingIndex = result.findIndex((item) => item.description === targetItem.description);
1939
+ if (existingIndex !== -1) {
1940
+ // Replace with target (newer)
1941
+ result[existingIndex] = targetItem;
1942
+ continue;
1943
+ }
1944
+ }
1945
+ result.push(targetItem);
1946
+ }
1947
+ }
1948
+ return result;
1949
+ }
1950
+ /**
1951
+ * Resolves merge options with defaults.
1952
+ *
1953
+ * @param options - Optional merge options to resolve with defaults
1954
+ * @returns Fully resolved merge options with all required fields
1955
+ */
1956
+ function resolveOptions(options) {
1957
+ if (!options) {
1958
+ return DEFAULT_MERGE_OPTIONS;
1959
+ }
1960
+ return {
1961
+ entryStrategy: options.entryStrategy ?? DEFAULT_MERGE_OPTIONS.entryStrategy,
1962
+ sectionStrategy: options.sectionStrategy ?? DEFAULT_MERGE_OPTIONS.sectionStrategy,
1963
+ itemStrategy: options.itemStrategy ?? DEFAULT_MERGE_OPTIONS.itemStrategy,
1964
+ useSourceHeader: options.useSourceHeader ?? DEFAULT_MERGE_OPTIONS.useSourceHeader,
1965
+ sortByVersion: options.sortByVersion ?? DEFAULT_MERGE_OPTIONS.sortByVersion,
1966
+ removeDuplicates: options.removeDuplicates ?? DEFAULT_MERGE_OPTIONS.removeDuplicates,
1967
+ };
1968
+ }
1969
+ /**
1970
+ * Sorts entries by version (descending - newest first).
1971
+ *
1972
+ * @param entries - Entries to sort
1973
+ * @returns Sorted entries
1974
+ */
1975
+ function sortEntriesByVersion(entries) {
1976
+ return [...entries].sort((a, b) => {
1977
+ // Unreleased always comes first
1978
+ if (a.unreleased && !b.unreleased)
1979
+ return -1;
1980
+ if (!a.unreleased && b.unreleased)
1981
+ return 1;
1982
+ if (a.unreleased && b.unreleased)
1983
+ return 0;
1984
+ // Parse versions for comparison
1985
+ const aResult = parseVersion(a.version);
1986
+ const bResult = parseVersion(b.version);
1987
+ if (!aResult.success || !bResult.success) {
1988
+ // Fall back to string comparison
1989
+ return b.version.localeCompare(a.version);
1990
+ }
1991
+ // Sort descending (newest first)
1992
+ return compare(bResult.version, aResult.version);
1993
+ });
1994
+ }
1995
+ /**
1996
+ * Appends entries from target to source (no conflict resolution).
1997
+ *
1998
+ * @param source - The base changelog to append to
1999
+ * @param target - The changelog whose entries will be appended
2000
+ * @param position - Where to insert ('start' or 'end')
2001
+ * @returns A new changelog with combined entries
2002
+ */
2003
+ function appendChangelog(source, target, position = 'end') {
2004
+ const entries = position === 'start' ? [...target.entries, ...source.entries] : [...source.entries, ...target.entries];
2005
+ return {
2006
+ ...source,
2007
+ entries,
2008
+ };
2009
+ }
2010
+ /**
2011
+ * Combines multiple changelogs into one.
2012
+ *
2013
+ * @param changelogs - Array of changelogs to combine
2014
+ * @param options - Optional merge options
2015
+ * @returns The combined changelog
2016
+ */
2017
+ function combineChangelogs(changelogs, options) {
2018
+ if (changelogs.length === 0) {
2019
+ throw createError('At least one changelog is required');
2020
+ }
2021
+ if (changelogs.length === 1) {
2022
+ return changelogs[0];
2023
+ }
2024
+ let result = changelogs[0];
2025
+ for (let i = 1; i < changelogs.length; i++) {
2026
+ const mergeResult = mergeChangelogs(result, changelogs[i], options);
2027
+ result = mergeResult.changelog;
2028
+ }
2029
+ return result;
2030
+ }
2031
+
2032
+ /**
2033
+ * Maps section headings to their canonical types.
2034
+ * Used during parsing to normalize different heading styles.
2035
+ */
2036
+ /**
2037
+ * Standard section headings for serialization.
2038
+ * Maps section types to their preferred heading text.
2039
+ */
2040
+ const SECTION_HEADINGS = {
2041
+ breaking: 'Breaking Changes',
2042
+ features: 'Features',
2043
+ fixes: 'Bug Fixes',
2044
+ performance: 'Performance',
2045
+ documentation: 'Documentation',
2046
+ deprecations: 'Deprecations',
2047
+ refactoring: 'Refactoring',
2048
+ tests: 'Tests',
2049
+ build: 'Build',
2050
+ ci: 'CI',
2051
+ chores: 'Chores',
2052
+ other: 'Other',
2053
+ };
2054
+
2055
+ /**
2056
+ * Changelog Transformation Helpers
2057
+ *
2058
+ * Generic transformation functions for modifying changelogs.
2059
+ */
2060
+ /**
2061
+ * Transforms all entries in a changelog.
2062
+ *
2063
+ * @param changelog - The changelog to transform
2064
+ * @param transformer - Function to transform each entry
2065
+ * @returns A new changelog with transformed entries
2066
+ *
2067
+ * @example
2068
+ * ```ts
2069
+ * const transformed = transformEntries(changelog, (entry) => ({
2070
+ * ...entry,
2071
+ * date: entry.date?.toUpperCase()
2072
+ * }))
2073
+ * ```
2074
+ */
2075
+ function transformEntries(changelog, transformer) {
2076
+ const newEntries = changelog.entries.map(transformer);
2077
+ return {
2078
+ ...changelog,
2079
+ entries: newEntries,
2080
+ };
2081
+ }
2082
+ /**
2083
+ * Transforms all sections in all entries.
2084
+ *
2085
+ * @param changelog - The changelog to transform
2086
+ * @param transformer - Function to transform each section
2087
+ * @returns A new changelog with transformed sections
2088
+ */
2089
+ function transformSections(changelog, transformer) {
2090
+ const newEntries = changelog.entries.map((entry) => ({
2091
+ ...entry,
2092
+ sections: entry.sections.map((section) => transformer(section, entry)),
2093
+ }));
2094
+ return {
2095
+ ...changelog,
2096
+ entries: newEntries,
2097
+ };
2098
+ }
2099
+ /**
2100
+ * Transforms all items in all sections.
2101
+ *
2102
+ * @param changelog - The changelog to transform
2103
+ * @param transformer - Function to transform each item
2104
+ * @returns A new changelog with transformed items
2105
+ */
2106
+ function transformItems(changelog, transformer) {
2107
+ const newEntries = changelog.entries.map((entry) => ({
2108
+ ...entry,
2109
+ sections: entry.sections.map((section) => ({
2110
+ ...section,
2111
+ items: section.items.map((item) => transformer(item, section, entry)),
2112
+ })),
2113
+ }));
2114
+ return {
2115
+ ...changelog,
2116
+ entries: newEntries,
2117
+ };
2118
+ }
2119
+ /**
2120
+ * Updates the header of a changelog.
2121
+ *
2122
+ * @param changelog - The changelog to update
2123
+ * @param updates - Partial header updates
2124
+ * @returns A new changelog with updated header
2125
+ */
2126
+ function updateHeader(changelog, updates) {
2127
+ return {
2128
+ ...changelog,
2129
+ header: {
2130
+ ...changelog.header,
2131
+ ...updates,
2132
+ },
2133
+ };
2134
+ }
2135
+ /**
2136
+ * Updates the metadata of a changelog.
2137
+ *
2138
+ * @param changelog - The changelog to update
2139
+ * @param updates - Partial metadata updates
2140
+ * @returns A new changelog with updated metadata
2141
+ */
2142
+ function updateMetadata(changelog, updates) {
2143
+ return {
2144
+ ...changelog,
2145
+ metadata: {
2146
+ ...changelog.metadata,
2147
+ ...updates,
2148
+ },
2149
+ };
2150
+ }
2151
+ /**
2152
+ * Updates a specific entry by version.
2153
+ *
2154
+ * @param changelog - The changelog to update
2155
+ * @param version - Version of entry to update
2156
+ * @param updates - Partial entry updates or transformer function
2157
+ * @returns A new changelog with updated entry
2158
+ */
2159
+ function updateEntry(changelog, version, updates) {
2160
+ const entryIndex = changelog.entries.findIndex((e) => e.version === version);
2161
+ if (entryIndex === -1) {
2162
+ throw createError(`Entry with version "${version}" not found`);
2163
+ }
2164
+ const entry = changelog.entries[entryIndex];
2165
+ const updatedEntry = typeof updates === 'function' ? updates(entry) : { ...entry, ...updates };
2166
+ const newEntries = [...changelog.entries];
2167
+ newEntries[entryIndex] = updatedEntry;
2168
+ return {
2169
+ ...changelog,
2170
+ entries: newEntries,
2171
+ };
2172
+ }
2173
+ /**
2174
+ * Sorts entries by version (descending - newest first).
2175
+ *
2176
+ * @param changelog - The changelog to sort
2177
+ * @returns A new changelog with sorted entries
2178
+ */
2179
+ function sortEntries(changelog) {
2180
+ const sorted = [...changelog.entries].sort((a, b) => {
2181
+ // Unreleased always comes first
2182
+ if (a.unreleased && !b.unreleased)
2183
+ return -1;
2184
+ if (!a.unreleased && b.unreleased)
2185
+ return 1;
2186
+ if (a.unreleased && b.unreleased)
2187
+ return 0;
2188
+ const aResult = parseVersion(a.version);
2189
+ const bResult = parseVersion(b.version);
2190
+ if (!aResult.success || !bResult.success) {
2191
+ return b.version.localeCompare(a.version);
2192
+ }
2193
+ return compare(bResult.version, aResult.version);
2194
+ });
2195
+ return {
2196
+ ...changelog,
2197
+ entries: sorted,
2198
+ };
2199
+ }
2200
+ /**
2201
+ * Sorts entries by date (newest first).
2202
+ *
2203
+ * @param changelog - The changelog to sort
2204
+ * @returns A new changelog with sorted entries
2205
+ */
2206
+ function sortEntriesByDate(changelog) {
2207
+ const sorted = [...changelog.entries].sort((a, b) => {
2208
+ // Unreleased always comes first
2209
+ if (a.unreleased && !b.unreleased)
2210
+ return -1;
2211
+ if (!a.unreleased && b.unreleased)
2212
+ return 1;
2213
+ if (a.unreleased && b.unreleased)
2214
+ return 0;
2215
+ // Entries without dates go to the end
2216
+ if (!a.date && b.date)
2217
+ return 1;
2218
+ if (a.date && !b.date)
2219
+ return -1;
2220
+ if (!a.date && !b.date)
2221
+ return 0;
2222
+ // Compare dates (descending)
2223
+ return b.date.localeCompare(a.date);
2224
+ });
2225
+ return {
2226
+ ...changelog,
2227
+ entries: sorted,
2228
+ };
2229
+ }
2230
+ /**
2231
+ * Reverses the order of entries.
2232
+ *
2233
+ * @param changelog - The changelog to reverse
2234
+ * @returns A new changelog with reversed entries
2235
+ */
2236
+ function reverseEntries(changelog) {
2237
+ return {
2238
+ ...changelog,
2239
+ entries: [...changelog.entries].reverse(),
2240
+ };
2241
+ }
2242
+ /**
2243
+ * Sorts sections within each entry by a specified order.
2244
+ *
2245
+ * @param changelog - The changelog to sort
2246
+ * @param order - Optional custom section order (defaults to standard order)
2247
+ * @returns A new changelog with sorted sections
2248
+ */
2249
+ function sortSections(changelog, order) {
2250
+ const defaultOrder = [
2251
+ 'breaking',
2252
+ 'features',
2253
+ 'fixes',
2254
+ 'performance',
2255
+ 'documentation',
2256
+ 'deprecations',
2257
+ 'refactoring',
2258
+ 'tests',
2259
+ 'build',
2260
+ 'ci',
2261
+ 'chores',
2262
+ 'other',
2263
+ ];
2264
+ const sectionOrder = order ?? defaultOrder;
2265
+ const orderMap = createMap();
2266
+ sectionOrder.forEach((type, index) => orderMap.set(type, index));
2267
+ const newEntries = changelog.entries.map((entry) => ({
2268
+ ...entry,
2269
+ sections: [...entry.sections].sort((a, b) => {
2270
+ const orderA = orderMap.get(a.type) ?? Number.MAX_SAFE_INTEGER;
2271
+ const orderB = orderMap.get(b.type) ?? Number.MAX_SAFE_INTEGER;
2272
+ return orderA - orderB;
2273
+ }),
2274
+ }));
2275
+ return {
2276
+ ...changelog,
2277
+ entries: newEntries,
2278
+ };
2279
+ }
2280
+ /**
2281
+ * Normalizes section headings to standard format.
2282
+ *
2283
+ * @param changelog - The changelog to normalize
2284
+ * @returns A new changelog with normalized section headings
2285
+ */
2286
+ function normalizeSectionHeadings(changelog) {
2287
+ return transformSections(changelog, (section) => ({
2288
+ ...section,
2289
+ heading: SECTION_HEADINGS[section.type] ?? section.heading,
2290
+ }));
2291
+ }
2292
+ /**
2293
+ * Removes duplicate items across all sections.
2294
+ *
2295
+ * @param changelog - The changelog to deduplicate
2296
+ * @returns A new changelog without duplicate items
2297
+ */
2298
+ function deduplicateItems(changelog) {
2299
+ const newEntries = changelog.entries.map((entry) => {
2300
+ const seenDescriptions = createSet();
2301
+ const newSections = entry.sections.map((section) => {
2302
+ const uniqueItems = [];
2303
+ for (const item of section.items) {
2304
+ const key = `${item.scope ?? ''}:${item.description}`;
2305
+ if (!seenDescriptions.has(key)) {
2306
+ seenDescriptions.add(key);
2307
+ uniqueItems.push(item);
2308
+ }
2309
+ }
2310
+ return {
2311
+ ...section,
2312
+ items: uniqueItems,
2313
+ };
2314
+ });
2315
+ return {
2316
+ ...entry,
2317
+ sections: newSections,
2318
+ };
2319
+ });
2320
+ return {
2321
+ ...changelog,
2322
+ entries: newEntries,
2323
+ };
2324
+ }
2325
+ /**
2326
+ * Compacts a changelog by removing empty sections and entries.
2327
+ *
2328
+ * @param changelog - The changelog to compact
2329
+ * @param keepUnreleased - Whether to keep empty unreleased entry (default: true)
2330
+ * @returns A new compacted changelog
2331
+ */
2332
+ function compact(changelog, keepUnreleased = true) {
2333
+ const newEntries = changelog.entries
2334
+ .map((entry) => ({
2335
+ ...entry,
2336
+ sections: entry.sections.filter((s) => s.items.length > 0),
2337
+ }))
2338
+ .filter((entry) => {
2339
+ const hasContent = entry.sections.length > 0 || entry.rawContent;
2340
+ if (hasContent)
2341
+ return true;
2342
+ if (keepUnreleased && entry.unreleased)
2343
+ return true;
2344
+ return false;
2345
+ });
2346
+ return {
2347
+ ...changelog,
2348
+ entries: newEntries,
2349
+ };
2350
+ }
2351
+ /**
2352
+ * Strips metadata from a changelog.
2353
+ *
2354
+ * @param changelog - The changelog to strip
2355
+ * @returns A new changelog with minimal metadata
2356
+ */
2357
+ function stripMetadata(changelog) {
2358
+ return {
2359
+ ...changelog,
2360
+ source: undefined,
2361
+ metadata: {
2362
+ format: changelog.metadata.format,
2363
+ isConventional: changelog.metadata.isConventional,
2364
+ warnings: [],
2365
+ },
2366
+ };
2367
+ }
2368
+ /**
2369
+ * Clones a changelog deeply (for modification without affecting original).
2370
+ *
2371
+ * @param changelog - The changelog to clone
2372
+ * @returns A deep copy of the changelog
2373
+ */
2374
+ function cloneChangelog(changelog) {
2375
+ return {
2376
+ source: changelog.source,
2377
+ header: {
2378
+ title: changelog.header.title,
2379
+ description: [...changelog.header.description],
2380
+ links: changelog.header.links.map((link) => ({ ...link })),
2381
+ },
2382
+ entries: changelog.entries.map((entry) => ({
2383
+ version: entry.version,
2384
+ date: entry.date,
2385
+ unreleased: entry.unreleased,
2386
+ compareUrl: entry.compareUrl,
2387
+ rawContent: entry.rawContent,
2388
+ sections: entry.sections.map((section) => ({
2389
+ type: section.type,
2390
+ heading: section.heading,
2391
+ items: section.items.map((item) => ({
2392
+ scope: item.scope,
2393
+ description: item.description,
2394
+ breaking: item.breaking,
2395
+ commits: item.commits.map((commit) => ({ ...commit })),
2396
+ references: item.references.map((ref) => ({ ...ref })),
2397
+ })),
2398
+ })),
2399
+ })),
2400
+ metadata: {
2401
+ format: changelog.metadata.format,
2402
+ isConventional: changelog.metadata.isConventional,
2403
+ repositoryUrl: changelog.metadata.repositoryUrl,
2404
+ packageName: changelog.metadata.packageName,
2405
+ warnings: [...changelog.metadata.warnings],
2406
+ },
2407
+ };
2408
+ }
2409
+
2410
+ export { DEFAULT_MERGE_OPTIONS, addEntry, addItemToEntry, addUnreleasedEntry, appendChangelog, cloneChangelog, combineChangelogs, compact, deduplicateItems, excludeByScope, filterBreakingChanges, filterByDateRange, filterByScope, filterByVersionRange, filterEntries, filterFromVersion, filterItems, filterRecentEntries, filterSectionTypes, filterSections, filterToVersion, filterVersionRange, mergeChangelogs, normalizeSectionHeadings, releaseUnreleased, removeEmptyEntries, removeEmptySections, removeEntries, removeEntry, removeItem, removeSection, removeUnreleased, reverseEntries, sortEntries, sortEntriesByDate, sortSections, stripMetadata, transformEntries, transformItems, transformSections, updateEntry, updateHeader, updateMetadata };
2411
+ //# sourceMappingURL=index.esm.js.map