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