@hominis/fireforge 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE.md +294 -0
  3. package/README.md +435 -0
  4. package/dist/bin/fireforge.d.ts +10 -0
  5. package/dist/bin/fireforge.js +29 -0
  6. package/dist/src/cli.d.ts +33 -0
  7. package/dist/src/cli.js +180 -0
  8. package/dist/src/commands/bootstrap.d.ts +9 -0
  9. package/dist/src/commands/bootstrap.js +73 -0
  10. package/dist/src/commands/build.d.ts +11 -0
  11. package/dist/src/commands/build.js +102 -0
  12. package/dist/src/commands/config.d.ts +13 -0
  13. package/dist/src/commands/config.js +135 -0
  14. package/dist/src/commands/discard.d.ts +12 -0
  15. package/dist/src/commands/discard.js +84 -0
  16. package/dist/src/commands/doctor.d.ts +18 -0
  17. package/dist/src/commands/doctor.js +356 -0
  18. package/dist/src/commands/download.d.ts +11 -0
  19. package/dist/src/commands/download.js +127 -0
  20. package/dist/src/commands/export-all.d.ts +11 -0
  21. package/dist/src/commands/export-all.js +122 -0
  22. package/dist/src/commands/export-shared.d.ts +48 -0
  23. package/dist/src/commands/export-shared.js +208 -0
  24. package/dist/src/commands/export.d.ts +13 -0
  25. package/dist/src/commands/export.js +178 -0
  26. package/dist/src/commands/furnace/apply.d.ts +7 -0
  27. package/dist/src/commands/furnace/apply.js +80 -0
  28. package/dist/src/commands/furnace/create.d.ts +8 -0
  29. package/dist/src/commands/furnace/create.js +377 -0
  30. package/dist/src/commands/furnace/deploy.d.ts +8 -0
  31. package/dist/src/commands/furnace/deploy.js +338 -0
  32. package/dist/src/commands/furnace/diff.d.ts +7 -0
  33. package/dist/src/commands/furnace/diff.js +119 -0
  34. package/dist/src/commands/furnace/index.d.ts +16 -0
  35. package/dist/src/commands/furnace/index.js +121 -0
  36. package/dist/src/commands/furnace/list.d.ts +5 -0
  37. package/dist/src/commands/furnace/list.js +65 -0
  38. package/dist/src/commands/furnace/override.d.ts +8 -0
  39. package/dist/src/commands/furnace/override.js +188 -0
  40. package/dist/src/commands/furnace/preview.d.ts +7 -0
  41. package/dist/src/commands/furnace/preview.js +96 -0
  42. package/dist/src/commands/furnace/remove.d.ts +8 -0
  43. package/dist/src/commands/furnace/remove.js +159 -0
  44. package/dist/src/commands/furnace/scan.d.ts +5 -0
  45. package/dist/src/commands/furnace/scan.js +112 -0
  46. package/dist/src/commands/furnace/status.d.ts +7 -0
  47. package/dist/src/commands/furnace/status.js +137 -0
  48. package/dist/src/commands/furnace/validate.d.ts +6 -0
  49. package/dist/src/commands/furnace/validate.js +91 -0
  50. package/dist/src/commands/furnace/validation-output.d.ts +7 -0
  51. package/dist/src/commands/furnace/validation-output.js +22 -0
  52. package/dist/src/commands/import.d.ts +11 -0
  53. package/dist/src/commands/import.js +241 -0
  54. package/dist/src/commands/lint.d.ts +10 -0
  55. package/dist/src/commands/lint.js +118 -0
  56. package/dist/src/commands/package.d.ts +11 -0
  57. package/dist/src/commands/package.js +80 -0
  58. package/dist/src/commands/re-export.d.ts +12 -0
  59. package/dist/src/commands/re-export.js +242 -0
  60. package/dist/src/commands/rebase/abort.d.ts +7 -0
  61. package/dist/src/commands/rebase/abort.js +49 -0
  62. package/dist/src/commands/rebase/confirm.d.ts +18 -0
  63. package/dist/src/commands/rebase/confirm.js +33 -0
  64. package/dist/src/commands/rebase/continue.d.ts +7 -0
  65. package/dist/src/commands/rebase/continue.js +81 -0
  66. package/dist/src/commands/rebase/index.d.ts +22 -0
  67. package/dist/src/commands/rebase/index.js +127 -0
  68. package/dist/src/commands/rebase/patch-loop.d.ts +9 -0
  69. package/dist/src/commands/rebase/patch-loop.js +135 -0
  70. package/dist/src/commands/rebase/summary.d.ts +12 -0
  71. package/dist/src/commands/rebase/summary.js +43 -0
  72. package/dist/src/commands/rebase.d.ts +4 -0
  73. package/dist/src/commands/rebase.js +6 -0
  74. package/dist/src/commands/register.d.ts +13 -0
  75. package/dist/src/commands/register.js +67 -0
  76. package/dist/src/commands/reset.d.ts +11 -0
  77. package/dist/src/commands/reset.js +83 -0
  78. package/dist/src/commands/resolve.d.ts +9 -0
  79. package/dist/src/commands/resolve.js +124 -0
  80. package/dist/src/commands/run.d.ts +9 -0
  81. package/dist/src/commands/run.js +91 -0
  82. package/dist/src/commands/setup-support.d.ts +23 -0
  83. package/dist/src/commands/setup-support.js +310 -0
  84. package/dist/src/commands/setup.d.ts +11 -0
  85. package/dist/src/commands/setup.js +94 -0
  86. package/dist/src/commands/status.d.ts +11 -0
  87. package/dist/src/commands/status.js +268 -0
  88. package/dist/src/commands/test.d.ts +12 -0
  89. package/dist/src/commands/test.js +182 -0
  90. package/dist/src/commands/token-coverage.d.ts +5 -0
  91. package/dist/src/commands/token-coverage.js +57 -0
  92. package/dist/src/commands/token.d.ts +14 -0
  93. package/dist/src/commands/token.js +121 -0
  94. package/dist/src/commands/watch.d.ts +9 -0
  95. package/dist/src/commands/watch.js +112 -0
  96. package/dist/src/commands/wire.d.ts +13 -0
  97. package/dist/src/commands/wire.js +149 -0
  98. package/dist/src/core/ast-utils.d.ts +47 -0
  99. package/dist/src/core/ast-utils.js +57 -0
  100. package/dist/src/core/brand-validation.d.ts +7 -0
  101. package/dist/src/core/brand-validation.js +15 -0
  102. package/dist/src/core/branding.d.ts +49 -0
  103. package/dist/src/core/branding.js +229 -0
  104. package/dist/src/core/browser-wire.d.ts +40 -0
  105. package/dist/src/core/browser-wire.js +66 -0
  106. package/dist/src/core/build-prepare.d.ts +25 -0
  107. package/dist/src/core/build-prepare.js +93 -0
  108. package/dist/src/core/config-mutate.d.ts +15 -0
  109. package/dist/src/core/config-mutate.js +51 -0
  110. package/dist/src/core/config-paths.d.ts +28 -0
  111. package/dist/src/core/config-paths.js +65 -0
  112. package/dist/src/core/config-state.d.ts +28 -0
  113. package/dist/src/core/config-state.js +152 -0
  114. package/dist/src/core/config-validate.d.ts +11 -0
  115. package/dist/src/core/config-validate.js +141 -0
  116. package/dist/src/core/config.d.ts +39 -0
  117. package/dist/src/core/config.js +70 -0
  118. package/dist/src/core/file-lock.d.ts +11 -0
  119. package/dist/src/core/file-lock.js +80 -0
  120. package/dist/src/core/firefox-archive.d.ts +40 -0
  121. package/dist/src/core/firefox-archive.js +63 -0
  122. package/dist/src/core/firefox-cache.d.ts +23 -0
  123. package/dist/src/core/firefox-cache.js +134 -0
  124. package/dist/src/core/firefox-download.d.ts +21 -0
  125. package/dist/src/core/firefox-download.js +129 -0
  126. package/dist/src/core/firefox-extract.d.ts +21 -0
  127. package/dist/src/core/firefox-extract.js +53 -0
  128. package/dist/src/core/firefox.d.ts +34 -0
  129. package/dist/src/core/firefox.js +78 -0
  130. package/dist/src/core/furnace-apply-helpers.d.ts +21 -0
  131. package/dist/src/core/furnace-apply-helpers.js +244 -0
  132. package/dist/src/core/furnace-apply.d.ts +16 -0
  133. package/dist/src/core/furnace-apply.js +147 -0
  134. package/dist/src/core/furnace-config.d.ts +94 -0
  135. package/dist/src/core/furnace-config.js +372 -0
  136. package/dist/src/core/furnace-constants.d.ts +4 -0
  137. package/dist/src/core/furnace-constants.js +6 -0
  138. package/dist/src/core/furnace-registration-ast.d.ts +24 -0
  139. package/dist/src/core/furnace-registration-ast.js +218 -0
  140. package/dist/src/core/furnace-registration-remove.d.ts +14 -0
  141. package/dist/src/core/furnace-registration-remove.js +89 -0
  142. package/dist/src/core/furnace-registration-validate.d.ts +20 -0
  143. package/dist/src/core/furnace-registration-validate.js +40 -0
  144. package/dist/src/core/furnace-registration.d.ts +29 -0
  145. package/dist/src/core/furnace-registration.js +96 -0
  146. package/dist/src/core/furnace-rollback.d.ts +20 -0
  147. package/dist/src/core/furnace-rollback.js +66 -0
  148. package/dist/src/core/furnace-scanner.d.ts +40 -0
  149. package/dist/src/core/furnace-scanner.js +143 -0
  150. package/dist/src/core/furnace-stories.d.ts +37 -0
  151. package/dist/src/core/furnace-stories.js +185 -0
  152. package/dist/src/core/furnace-validate-accessibility.d.ts +6 -0
  153. package/dist/src/core/furnace-validate-accessibility.js +32 -0
  154. package/dist/src/core/furnace-validate-checks.d.ts +4 -0
  155. package/dist/src/core/furnace-validate-checks.js +7 -0
  156. package/dist/src/core/furnace-validate-compatibility.d.ts +6 -0
  157. package/dist/src/core/furnace-validate-compatibility.js +57 -0
  158. package/dist/src/core/furnace-validate-helpers.d.ts +28 -0
  159. package/dist/src/core/furnace-validate-helpers.js +129 -0
  160. package/dist/src/core/furnace-validate-registration.d.ts +37 -0
  161. package/dist/src/core/furnace-validate-registration.js +220 -0
  162. package/dist/src/core/furnace-validate-structure.d.ts +6 -0
  163. package/dist/src/core/furnace-validate-structure.js +66 -0
  164. package/dist/src/core/furnace-validate.d.ts +16 -0
  165. package/dist/src/core/furnace-validate.js +103 -0
  166. package/dist/src/core/git-base.d.ts +47 -0
  167. package/dist/src/core/git-base.js +50 -0
  168. package/dist/src/core/git-diff.d.ts +63 -0
  169. package/dist/src/core/git-diff.js +246 -0
  170. package/dist/src/core/git-file-ops.d.ts +65 -0
  171. package/dist/src/core/git-file-ops.js +141 -0
  172. package/dist/src/core/git-status.d.ts +65 -0
  173. package/dist/src/core/git-status.js +163 -0
  174. package/dist/src/core/git.d.ts +113 -0
  175. package/dist/src/core/git.js +363 -0
  176. package/dist/src/core/license-headers.d.ts +36 -0
  177. package/dist/src/core/license-headers.js +83 -0
  178. package/dist/src/core/mach-build-artifacts.d.ts +29 -0
  179. package/dist/src/core/mach-build-artifacts.js +117 -0
  180. package/dist/src/core/mach-mozconfig.d.ts +17 -0
  181. package/dist/src/core/mach-mozconfig.js +50 -0
  182. package/dist/src/core/mach-python.d.ts +16 -0
  183. package/dist/src/core/mach-python.js +126 -0
  184. package/dist/src/core/mach.d.ts +106 -0
  185. package/dist/src/core/mach.js +166 -0
  186. package/dist/src/core/manifest-helpers.d.ts +25 -0
  187. package/dist/src/core/manifest-helpers.js +96 -0
  188. package/dist/src/core/manifest-register.d.ts +30 -0
  189. package/dist/src/core/manifest-register.js +65 -0
  190. package/dist/src/core/manifest-rules.d.ts +39 -0
  191. package/dist/src/core/manifest-rules.js +151 -0
  192. package/dist/src/core/manifest-tokenizers.d.ts +34 -0
  193. package/dist/src/core/manifest-tokenizers.js +84 -0
  194. package/dist/src/core/parser-fallback.d.ts +36 -0
  195. package/dist/src/core/parser-fallback.js +43 -0
  196. package/dist/src/core/patch-apply-fuzz.d.ts +29 -0
  197. package/dist/src/core/patch-apply-fuzz.js +70 -0
  198. package/dist/src/core/patch-apply.d.ts +46 -0
  199. package/dist/src/core/patch-apply.js +235 -0
  200. package/dist/src/core/patch-export.d.ts +99 -0
  201. package/dist/src/core/patch-export.js +314 -0
  202. package/dist/src/core/patch-files.d.ts +11 -0
  203. package/dist/src/core/patch-files.js +51 -0
  204. package/dist/src/core/patch-lint.d.ts +72 -0
  205. package/dist/src/core/patch-lint.js +403 -0
  206. package/dist/src/core/patch-lock.d.ts +8 -0
  207. package/dist/src/core/patch-lock.js +29 -0
  208. package/dist/src/core/patch-manifest-consistency.d.ts +24 -0
  209. package/dist/src/core/patch-manifest-consistency.js +135 -0
  210. package/dist/src/core/patch-manifest-io.d.ts +36 -0
  211. package/dist/src/core/patch-manifest-io.js +77 -0
  212. package/dist/src/core/patch-manifest-query.d.ts +48 -0
  213. package/dist/src/core/patch-manifest-query.js +124 -0
  214. package/dist/src/core/patch-manifest-validate.d.ts +22 -0
  215. package/dist/src/core/patch-manifest-validate.js +72 -0
  216. package/dist/src/core/patch-manifest.d.ts +11 -0
  217. package/dist/src/core/patch-manifest.js +12 -0
  218. package/dist/src/core/patch-parse.d.ts +43 -0
  219. package/dist/src/core/patch-parse.js +143 -0
  220. package/dist/src/core/patch-transform.d.ts +21 -0
  221. package/dist/src/core/patch-transform.js +138 -0
  222. package/dist/src/core/rebase-session.d.ts +47 -0
  223. package/dist/src/core/rebase-session.js +65 -0
  224. package/dist/src/core/register-browser-content.d.ts +11 -0
  225. package/dist/src/core/register-browser-content.js +116 -0
  226. package/dist/src/core/register-module.d.ts +11 -0
  227. package/dist/src/core/register-module.js +76 -0
  228. package/dist/src/core/register-shared-css.d.ts +11 -0
  229. package/dist/src/core/register-shared-css.js +117 -0
  230. package/dist/src/core/register-test-manifest.d.ts +18 -0
  231. package/dist/src/core/register-test-manifest.js +99 -0
  232. package/dist/src/core/state-file.d.ts +4 -0
  233. package/dist/src/core/state-file.js +25 -0
  234. package/dist/src/core/token-coverage.d.ts +12 -0
  235. package/dist/src/core/token-coverage.js +74 -0
  236. package/dist/src/core/token-manager.d.ts +55 -0
  237. package/dist/src/core/token-manager.js +387 -0
  238. package/dist/src/core/wire-destroy.d.ts +21 -0
  239. package/dist/src/core/wire-destroy.js +103 -0
  240. package/dist/src/core/wire-dom-fragment.d.ts +23 -0
  241. package/dist/src/core/wire-dom-fragment.js +129 -0
  242. package/dist/src/core/wire-init.d.ts +23 -0
  243. package/dist/src/core/wire-init.js +201 -0
  244. package/dist/src/core/wire-subscript.d.ts +20 -0
  245. package/dist/src/core/wire-subscript.js +134 -0
  246. package/dist/src/core/wire-targets.d.ts +7 -0
  247. package/dist/src/core/wire-targets.js +9 -0
  248. package/dist/src/core/wire-utils.d.ts +88 -0
  249. package/dist/src/core/wire-utils.js +279 -0
  250. package/dist/src/errors/base.d.ts +60 -0
  251. package/dist/src/errors/base.js +87 -0
  252. package/dist/src/errors/build.d.ts +52 -0
  253. package/dist/src/errors/build.js +114 -0
  254. package/dist/src/errors/codes.d.ts +29 -0
  255. package/dist/src/errors/codes.js +30 -0
  256. package/dist/src/errors/config.d.ts +31 -0
  257. package/dist/src/errors/config.js +61 -0
  258. package/dist/src/errors/download.d.ts +42 -0
  259. package/dist/src/errors/download.js +95 -0
  260. package/dist/src/errors/furnace.d.ts +10 -0
  261. package/dist/src/errors/furnace.js +22 -0
  262. package/dist/src/errors/git.d.ts +41 -0
  263. package/dist/src/errors/git.js +99 -0
  264. package/dist/src/errors/patch.d.ts +10 -0
  265. package/dist/src/errors/patch.js +26 -0
  266. package/dist/src/errors/rebase.d.ts +20 -0
  267. package/dist/src/errors/rebase.js +30 -0
  268. package/dist/src/index.d.ts +21 -0
  269. package/dist/src/index.js +21 -0
  270. package/dist/src/types/cli.d.ts +14 -0
  271. package/dist/src/types/cli.js +2 -0
  272. package/dist/src/types/commands/index.d.ts +6 -0
  273. package/dist/src/types/commands/index.js +6 -0
  274. package/dist/src/types/commands/options.d.ts +239 -0
  275. package/dist/src/types/commands/options.js +6 -0
  276. package/dist/src/types/commands/patches.d.ts +89 -0
  277. package/dist/src/types/commands/patches.js +6 -0
  278. package/dist/src/types/commands/project.d.ts +71 -0
  279. package/dist/src/types/commands/project.js +6 -0
  280. package/dist/src/types/config.d.ts +101 -0
  281. package/dist/src/types/config.js +2 -0
  282. package/dist/src/types/furnace.d.ts +158 -0
  283. package/dist/src/types/furnace.js +2 -0
  284. package/dist/src/types/index.d.ts +6 -0
  285. package/dist/src/types/index.js +6 -0
  286. package/dist/src/utils/errors.d.ts +2 -0
  287. package/dist/src/utils/errors.js +15 -0
  288. package/dist/src/utils/fs.d.ts +72 -0
  289. package/dist/src/utils/fs.js +179 -0
  290. package/dist/src/utils/logger.d.ts +58 -0
  291. package/dist/src/utils/logger.js +120 -0
  292. package/dist/src/utils/options.d.ts +8 -0
  293. package/dist/src/utils/options.js +16 -0
  294. package/dist/src/utils/package-root.d.ts +10 -0
  295. package/dist/src/utils/package-root.js +53 -0
  296. package/dist/src/utils/parse.d.ts +110 -0
  297. package/dist/src/utils/parse.js +200 -0
  298. package/dist/src/utils/paths.d.ts +10 -0
  299. package/dist/src/utils/paths.js +43 -0
  300. package/dist/src/utils/platform.d.ts +38 -0
  301. package/dist/src/utils/platform.js +56 -0
  302. package/dist/src/utils/process.d.ts +80 -0
  303. package/dist/src/utils/process.js +188 -0
  304. package/dist/src/utils/regex.d.ts +24 -0
  305. package/dist/src/utils/regex.js +40 -0
  306. package/dist/src/utils/validation.d.ts +133 -0
  307. package/dist/src/utils/validation.js +250 -0
  308. package/package.json +106 -0
  309. package/templates/configs/common.mozconfig +24 -0
  310. package/templates/configs/darwin.mozconfig +10 -0
  311. package/templates/configs/linux.mozconfig +12 -0
  312. package/templates/configs/win32.mozconfig +14 -0
  313. package/templates/licenses/0BSD.md +14 -0
  314. package/templates/licenses/EUPL-1.2.md +294 -0
  315. package/templates/licenses/GPL-2.0-or-later.md +339 -0
  316. package/templates/licenses/MPL-2.0.md +383 -0
@@ -0,0 +1,6 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Re-exports all type definitions.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ /** Normalizes unknown throwables into an Error instance. */
2
+ export declare function toError(error: unknown): Error;
@@ -0,0 +1,15 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /** Normalizes unknown throwables into an Error instance. */
3
+ export function toError(error) {
4
+ if (error instanceof Error) {
5
+ return error;
6
+ }
7
+ if (typeof error === 'object' &&
8
+ error !== null &&
9
+ 'message' in error &&
10
+ typeof error.message === 'string') {
11
+ return new Error(error.message, { cause: error });
12
+ }
13
+ return new Error(typeof error === 'string' ? error : String(error), { cause: error });
14
+ }
15
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Checks if a path exists.
3
+ * @param path - Path to check
4
+ */
5
+ export declare function pathExists(path: string): Promise<boolean>;
6
+ /**
7
+ * Ensures a directory exists, creating it recursively if needed.
8
+ * @param path - Directory path to ensure
9
+ */
10
+ export declare function ensureDir(path: string): Promise<void>;
11
+ /**
12
+ * Ensures the parent directory of a file exists.
13
+ * @param filePath - Path to a file
14
+ */
15
+ export declare function ensureParentDir(filePath: string): Promise<void>;
16
+ /**
17
+ * Removes a directory recursively.
18
+ * @param path - Directory path to remove
19
+ */
20
+ export declare function removeDir(path: string): Promise<void>;
21
+ /**
22
+ * Removes a file.
23
+ * @param path - File path to remove
24
+ */
25
+ export declare function removeFile(path: string): Promise<void>;
26
+ /**
27
+ * Copies a file from source to destination.
28
+ * Creates parent directories if needed.
29
+ * @param src - Source file path
30
+ * @param dest - Destination file path
31
+ */
32
+ export declare function copyFile(src: string, dest: string): Promise<void>;
33
+ /**
34
+ * Reads a JSON file and parses it.
35
+ * @param path - Path to JSON file
36
+ * @returns Parsed JSON content
37
+ * @throws Error if file doesn't exist or contains invalid JSON
38
+ */
39
+ export declare function readJson<T>(path: string): Promise<T>;
40
+ /**
41
+ * Writes data to a JSON file with pretty formatting.
42
+ * Creates parent directories if needed.
43
+ * @param path - Path to JSON file
44
+ * @param data - Data to write
45
+ */
46
+ export declare function writeJson(path: string, data: unknown): Promise<void>;
47
+ /**
48
+ * Reads a text file.
49
+ * @param path - Path to text file
50
+ * @returns File content as string
51
+ */
52
+ export declare function readText(path: string): Promise<string>;
53
+ /**
54
+ * Writes text to a file.
55
+ * Creates parent directories if needed.
56
+ * @param path - Path to text file
57
+ * @param content - Content to write
58
+ */
59
+ export declare function writeText(path: string, content: string): Promise<void>;
60
+ /**
61
+ * Writes content atomically using a temp-file-and-rename strategy.
62
+ * Temp files are created in the destination directory so rename stays atomic.
63
+ * @param path - Destination file path
64
+ * @param content - Content to write
65
+ */
66
+ export declare function writeFileAtomic(path: string, content: string | Buffer): Promise<void>;
67
+ /**
68
+ * Copies a directory recursively.
69
+ * @param src - Source directory path
70
+ * @param dest - Destination directory path
71
+ */
72
+ export declare function copyDir(src: string, dest: string): Promise<void>;
@@ -0,0 +1,179 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { randomUUID } from 'node:crypto';
3
+ import { access, copyFile as fsCopyFile, mkdir, open, readdir, readFile, rename, rm, } from 'node:fs/promises';
4
+ import { dirname, join } from 'node:path';
5
+ const RETRIABLE_REMOVE_ERRORS = new Set(['ENOTEMPTY', 'EBUSY', 'EPERM']);
6
+ function sleep(ms) {
7
+ return new Promise((resolve) => {
8
+ setTimeout(resolve, ms);
9
+ });
10
+ }
11
+ /**
12
+ * Checks if a path exists.
13
+ * @param path - Path to check
14
+ */
15
+ export async function pathExists(path) {
16
+ try {
17
+ await access(path);
18
+ return true;
19
+ }
20
+ catch (error) {
21
+ void error;
22
+ return false;
23
+ }
24
+ }
25
+ /**
26
+ * Ensures a directory exists, creating it recursively if needed.
27
+ * @param path - Directory path to ensure
28
+ */
29
+ export async function ensureDir(path) {
30
+ await mkdir(path, { recursive: true });
31
+ }
32
+ /**
33
+ * Ensures the parent directory of a file exists.
34
+ * @param filePath - Path to a file
35
+ */
36
+ export async function ensureParentDir(filePath) {
37
+ const parent = dirname(filePath);
38
+ await ensureDir(parent);
39
+ }
40
+ /**
41
+ * Removes a directory recursively.
42
+ * @param path - Directory path to remove
43
+ */
44
+ export async function removeDir(path) {
45
+ for (let attempt = 0; attempt < 5; attempt++) {
46
+ try {
47
+ await rm(path, { recursive: true, force: true });
48
+ return;
49
+ }
50
+ catch (error) {
51
+ const code = error instanceof Error && 'code' in error && typeof error.code === 'string'
52
+ ? error.code
53
+ : undefined;
54
+ if (!code || !RETRIABLE_REMOVE_ERRORS.has(code) || attempt === 4) {
55
+ throw error;
56
+ }
57
+ await sleep(50 * (attempt + 1));
58
+ }
59
+ }
60
+ }
61
+ /**
62
+ * Removes a file.
63
+ * @param path - File path to remove
64
+ */
65
+ export async function removeFile(path) {
66
+ await rm(path, { force: true });
67
+ }
68
+ /**
69
+ * Copies a file from source to destination.
70
+ * Creates parent directories if needed.
71
+ * @param src - Source file path
72
+ * @param dest - Destination file path
73
+ */
74
+ export async function copyFile(src, dest) {
75
+ await ensureParentDir(dest);
76
+ await fsCopyFile(src, dest);
77
+ }
78
+ /**
79
+ * Reads a JSON file and parses it.
80
+ * @param path - Path to JSON file
81
+ * @returns Parsed JSON content
82
+ * @throws Error if file doesn't exist or contains invalid JSON
83
+ */
84
+ export async function readJson(path) {
85
+ const content = await readFile(path, 'utf-8');
86
+ return JSON.parse(content, (key, value) => key === '__proto__' || key === 'constructor' || key === 'prototype' ? undefined : value);
87
+ }
88
+ /**
89
+ * Writes data to a JSON file with pretty formatting.
90
+ * Creates parent directories if needed.
91
+ * @param path - Path to JSON file
92
+ * @param data - Data to write
93
+ */
94
+ export async function writeJson(path, data) {
95
+ const content = JSON.stringify(data, null, 2) + '\n';
96
+ await writeText(path, content);
97
+ }
98
+ /**
99
+ * Reads a text file.
100
+ * @param path - Path to text file
101
+ * @returns File content as string
102
+ */
103
+ export async function readText(path) {
104
+ return readFile(path, 'utf-8');
105
+ }
106
+ /**
107
+ * Writes text to a file.
108
+ * Creates parent directories if needed.
109
+ * @param path - Path to text file
110
+ * @param content - Content to write
111
+ */
112
+ export async function writeText(path, content) {
113
+ await writeFileAtomic(path, content);
114
+ }
115
+ /**
116
+ * Writes content atomically using a temp-file-and-rename strategy.
117
+ * Temp files are created in the destination directory so rename stays atomic.
118
+ * @param path - Destination file path
119
+ * @param content - Content to write
120
+ */
121
+ export async function writeFileAtomic(path, content) {
122
+ await ensureParentDir(path);
123
+ const tempPath = createAtomicTempPath(path);
124
+ const handle = await open(tempPath, 'w');
125
+ try {
126
+ await handle.writeFile(content);
127
+ await handle.sync();
128
+ }
129
+ catch (error) {
130
+ await handle.close();
131
+ await rm(tempPath, { force: true });
132
+ throw error;
133
+ }
134
+ await handle.close();
135
+ try {
136
+ await rename(tempPath, path);
137
+ }
138
+ catch (error) {
139
+ await rm(tempPath, { force: true });
140
+ throw error;
141
+ }
142
+ }
143
+ /**
144
+ * Copies a directory recursively.
145
+ * @param src - Source directory path
146
+ * @param dest - Destination directory path
147
+ */
148
+ export async function copyDir(src, dest) {
149
+ await ensureDir(dest);
150
+ const entries = await readdir(src, { withFileTypes: true });
151
+ for (const entry of entries) {
152
+ const srcPath = join(src, entry.name);
153
+ const destPath = join(dest, entry.name);
154
+ if (entry.isSymbolicLink()) {
155
+ // Skip symlinks to avoid circular recursion and symlink attacks
156
+ continue;
157
+ }
158
+ if (entry.isDirectory()) {
159
+ await copyDir(srcPath, destPath);
160
+ }
161
+ else {
162
+ await fsCopyFile(srcPath, destPath);
163
+ }
164
+ }
165
+ }
166
+ /**
167
+ * Generates a unique temp file path for atomic writes.
168
+ *
169
+ * Each invocation gets its own path via PID + UUID, so concurrent writers
170
+ * targeting the same destination never interfere with each other. Cleanup of
171
+ * the temp file is the caller's responsibility on error; we intentionally do
172
+ * NOT glob-delete peer temp files here to avoid racing with other writers.
173
+ */
174
+ function createAtomicTempPath(path) {
175
+ const directory = dirname(path);
176
+ const filename = path.slice(directory.length + 1);
177
+ return join(directory, `.${filename}.fireforge-tmp-${process.pid}-${randomUUID()}`);
178
+ }
179
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Enables or disables verbose mode.
3
+ * @param enabled - Whether to enable verbose output
4
+ */
5
+ export declare function setVerbose(enabled: boolean): void;
6
+ /**
7
+ * Checks if verbose mode is enabled.
8
+ * @returns True if verbose mode is enabled
9
+ */
10
+ export declare function isVerbose(): boolean;
11
+ /**
12
+ * Displays a verbose/debug message (only shown if verbose mode is enabled).
13
+ * @param message - Message to display
14
+ */
15
+ export declare function verbose(message: string): void;
16
+ /**
17
+ * Handle returned by the spinner function.
18
+ */
19
+ export interface SpinnerHandle {
20
+ /** Update the spinner message */
21
+ message: (msg: string) => void;
22
+ /** Stop the spinner with a success message */
23
+ stop: (msg?: string) => void;
24
+ /** Stop the spinner with an error message */
25
+ error: (msg?: string) => void;
26
+ }
27
+ /** Displays the top-level intro banner for a command. */
28
+ export declare function intro(message: string): void;
29
+ /** Displays the closing outro banner for a command. */
30
+ export declare function outro(message: string): void;
31
+ /** Logs an informational message. */
32
+ export declare function info(message: string): void;
33
+ /** Logs a success message. */
34
+ export declare function success(message: string): void;
35
+ /** Logs a warning message. */
36
+ export declare function warn(message: string): void;
37
+ /** Logs an error message. */
38
+ export declare function error(message: string): void;
39
+ /** Logs an in-progress step message. */
40
+ export declare function step(message: string): void;
41
+ /** Logs a plain message without a status prefix. */
42
+ export declare function message(message: string): void;
43
+ /** Formats text using the success color without logging it. */
44
+ export declare function formatSuccessText(message: string): string;
45
+ /** Formats text using the error color without logging it. */
46
+ export declare function formatErrorText(message: string): string;
47
+ /**
48
+ * Creates a spinner for long-running operations.
49
+ * @param initialMessage - Initial message to display
50
+ * @returns Spinner handle with message(), stop(), and error() methods
51
+ */
52
+ export declare function spinner(initialMessage: string): SpinnerHandle;
53
+ /** Emits a cancellation message. */
54
+ export declare function cancel(message: string): void;
55
+ /** Checks whether a prompt result represents a user cancellation. */
56
+ export declare function isCancel(value: unknown): boolean;
57
+ /** Displays a titled note block for follow-up details. */
58
+ export declare function note(message: string, title?: string): void;
@@ -0,0 +1,120 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import * as p from '@clack/prompts';
3
+ import pc from 'picocolors';
4
+ /** Whether verbose mode is enabled */
5
+ let verboseMode = false;
6
+ /**
7
+ * Enables or disables verbose mode.
8
+ * @param enabled - Whether to enable verbose output
9
+ */
10
+ export function setVerbose(enabled) {
11
+ verboseMode = enabled;
12
+ }
13
+ /**
14
+ * Checks if verbose mode is enabled.
15
+ * @returns True if verbose mode is enabled
16
+ */
17
+ export function isVerbose() {
18
+ return verboseMode;
19
+ }
20
+ /**
21
+ * Displays a verbose/debug message (only shown if verbose mode is enabled).
22
+ * @param message - Message to display
23
+ */
24
+ export function verbose(message) {
25
+ if (verboseMode) {
26
+ p.log.info(`[debug] ${message}`);
27
+ }
28
+ }
29
+ function supportsInteractiveSpinner() {
30
+ return process.stdout.isTTY && process.stderr.isTTY;
31
+ }
32
+ /** Displays the top-level intro banner for a command. */
33
+ export function intro(message) {
34
+ p.intro(message);
35
+ }
36
+ /** Displays the closing outro banner for a command. */
37
+ export function outro(message) {
38
+ p.outro(message);
39
+ }
40
+ /** Logs an informational message. */
41
+ export function info(message) {
42
+ p.log.info(message);
43
+ }
44
+ /** Logs a success message. */
45
+ export function success(message) {
46
+ p.log.success(message);
47
+ }
48
+ /** Logs a warning message. */
49
+ export function warn(message) {
50
+ p.log.warn(message);
51
+ }
52
+ /** Logs an error message. */
53
+ export function error(message) {
54
+ p.log.error(message);
55
+ }
56
+ /** Logs an in-progress step message. */
57
+ export function step(message) {
58
+ p.log.step(message);
59
+ }
60
+ /** Logs a plain message without a status prefix. */
61
+ export function message(message) {
62
+ p.log.message(message);
63
+ }
64
+ /** Formats text using the success color without logging it. */
65
+ export function formatSuccessText(message) {
66
+ return pc.green(message);
67
+ }
68
+ /** Formats text using the error color without logging it. */
69
+ export function formatErrorText(message) {
70
+ return pc.red(message);
71
+ }
72
+ /**
73
+ * Creates a spinner for long-running operations.
74
+ * @param initialMessage - Initial message to display
75
+ * @returns Spinner handle with message(), stop(), and error() methods
76
+ */
77
+ export function spinner(initialMessage) {
78
+ if (!supportsInteractiveSpinner()) {
79
+ let latestMessage = initialMessage;
80
+ return {
81
+ message: (msg) => {
82
+ latestMessage = msg;
83
+ p.log.step(msg);
84
+ },
85
+ stop: (msg) => {
86
+ p.log.step(msg ?? latestMessage);
87
+ },
88
+ error: (msg) => {
89
+ p.log.error(msg ?? 'Failed');
90
+ },
91
+ };
92
+ }
93
+ const s = p.spinner();
94
+ s.start(initialMessage);
95
+ return {
96
+ message: (msg) => {
97
+ s.message(msg);
98
+ },
99
+ stop: (msg) => {
100
+ s.stop(msg ?? initialMessage);
101
+ },
102
+ error: (msg) => {
103
+ s.stop();
104
+ p.log.error(msg ?? 'Failed');
105
+ },
106
+ };
107
+ }
108
+ /** Emits a cancellation message. */
109
+ export function cancel(message) {
110
+ p.cancel(message);
111
+ }
112
+ /** Checks whether a prompt result represents a user cancellation. */
113
+ export function isCancel(value) {
114
+ return p.isCancel(value);
115
+ }
116
+ /** Displays a titled note block for follow-up details. */
117
+ export function note(message, title) {
118
+ p.note(message, title);
119
+ }
120
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Filters an object to only include keys whose values are not undefined.
3
+ * Designed for use with exactOptionalPropertyTypes — the result can be
4
+ * spread into typed option objects without assigning undefined to optional properties.
5
+ */
6
+ export declare function pickDefined<T extends Record<string, unknown>>(obj: T): {
7
+ [K in keyof T]+?: Exclude<T[K], undefined>;
8
+ };
@@ -0,0 +1,16 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Filters an object to only include keys whose values are not undefined.
4
+ * Designed for use with exactOptionalPropertyTypes — the result can be
5
+ * spread into typed option objects without assigning undefined to optional properties.
6
+ */
7
+ export function pickDefined(obj) {
8
+ const result = {};
9
+ for (const key of Object.keys(obj)) {
10
+ if (obj[key] !== undefined) {
11
+ result[key] = obj[key];
12
+ }
13
+ }
14
+ return result;
15
+ }
16
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Finds the fireforge package root by walking up from the current module.
3
+ *
4
+ * Works from both the source tree (`src/utils/`) and the compiled
5
+ * tree (`dist/src/utils/`) by looking for a `package.json` whose
6
+ * `name` field is `"@hominis/fireforge"`.
7
+ */
8
+ export declare function getPackageRoot(): string;
9
+ /** Reads the current package version from the repository root package manifest. */
10
+ export declare function getPackageVersion(): string;
@@ -0,0 +1,53 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { readFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ function validatePackageMetadata(data, filePath) {
6
+ if (typeof data !== 'object' || data === null) {
7
+ throw new Error(`Invalid package metadata in ${filePath}: expected an object`);
8
+ }
9
+ const name = 'name' in data ? data.name : undefined;
10
+ const version = 'version' in data ? data.version : undefined;
11
+ if (typeof name !== 'string' || typeof version !== 'string') {
12
+ throw new Error(`Invalid package metadata in ${filePath}: expected string "name" and "version" fields`);
13
+ }
14
+ return { name, version };
15
+ }
16
+ function readPackageMetadata(filePath) {
17
+ const raw = readFileSync(filePath, 'utf-8');
18
+ return validatePackageMetadata(JSON.parse(raw), filePath);
19
+ }
20
+ /**
21
+ * Finds the fireforge package root by walking up from the current module.
22
+ *
23
+ * Works from both the source tree (`src/utils/`) and the compiled
24
+ * tree (`dist/src/utils/`) by looking for a `package.json` whose
25
+ * `name` field is `"@hominis/fireforge"`.
26
+ */
27
+ export function getPackageRoot() {
28
+ let current = dirname(fileURLToPath(import.meta.url));
29
+ for (;;) {
30
+ try {
31
+ const packagePath = join(current, 'package.json');
32
+ const pkg = readPackageMetadata(packagePath);
33
+ if (pkg.name === '@hominis/fireforge') {
34
+ return current;
35
+ }
36
+ }
37
+ catch (error) {
38
+ void error;
39
+ // no package.json here — keep walking
40
+ }
41
+ const parent = dirname(current);
42
+ if (parent === current) {
43
+ throw new Error('Could not locate the fireforge package root');
44
+ }
45
+ current = parent;
46
+ }
47
+ }
48
+ /** Reads the current package version from the repository root package manifest. */
49
+ export function getPackageVersion() {
50
+ const packageRoot = getPackageRoot();
51
+ return readPackageMetadata(join(packageRoot, 'package.json')).version;
52
+ }
53
+ //# sourceMappingURL=package-root.js.map
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Lightweight runtime object parsing and field extraction helpers.
3
+ *
4
+ * Reduces repetitive "validate unknown → cast" boilerplate across
5
+ * config validation, manifest parsing, and metadata validation.
6
+ */
7
+ /**
8
+ * A parsed record wrapper that provides typed field extraction
9
+ * with clear error messages. Construct via {@link parseObject}.
10
+ */
11
+ export declare class ParsedRecord {
12
+ #private;
13
+ constructor(data: Record<string, unknown>, label: string);
14
+ /**
15
+ * Extracts a required string field.
16
+ * @param key - Field name
17
+ * @returns The string value
18
+ * @throws Error if the field is missing or not a string
19
+ */
20
+ string(key: string): string;
21
+ /**
22
+ * Extracts an optional string field.
23
+ * @param key - Field name
24
+ * @returns The string value or undefined
25
+ * @throws Error if the field is present but not a string
26
+ */
27
+ optionalString(key: string): string | undefined;
28
+ /**
29
+ * Extracts a required number field.
30
+ * @param key - Field name
31
+ * @returns The number value
32
+ * @throws Error if the field is missing or not a number
33
+ */
34
+ number(key: string): number;
35
+ /**
36
+ * Extracts an optional number field.
37
+ * @param key - Field name
38
+ * @returns The number value or undefined
39
+ * @throws Error if the field is present but not a number
40
+ */
41
+ optionalNumber(key: string): number | undefined;
42
+ /**
43
+ * Extracts a required non-negative integer field.
44
+ * @param key - Field name
45
+ * @returns The integer value
46
+ * @throws Error if the field is missing, not a number, or negative
47
+ */
48
+ nonNegativeInteger(key: string): number;
49
+ /**
50
+ * Extracts an optional non-negative integer field.
51
+ * @param key - Field name
52
+ * @returns The integer value or undefined
53
+ * @throws Error if the field is present but not a non-negative integer
54
+ */
55
+ optionalNonNegativeInteger(key: string): number | undefined;
56
+ /**
57
+ * Extracts a required string field and validates it against a predicate.
58
+ * @param key - Field name
59
+ * @param predicate - Validation function
60
+ * @param allowed - Description of allowed values for the error message
61
+ * @returns The validated string value
62
+ */
63
+ stringEnum<T extends string>(key: string, predicate: (value: string) => value is T, allowed: string): T;
64
+ /**
65
+ * Extracts a required string field and validates it with a custom check.
66
+ * @param key - Field name
67
+ * @param check - Validation function returning true if valid
68
+ * @param constraint - Description of the constraint for the error message
69
+ * @returns The validated string value
70
+ */
71
+ validatedString(key: string, check: (value: string) => boolean, constraint: string): string;
72
+ /**
73
+ * Extracts a required array-of-strings field.
74
+ * @param key - Field name
75
+ * @returns The string array
76
+ * @throws Error if the field is missing or not an array of strings
77
+ */
78
+ stringArray(key: string): string[];
79
+ /**
80
+ * Extracts a required nested object field.
81
+ * @param key - Field name
82
+ * @returns A new ParsedRecord wrapping the nested object
83
+ * @throws Error if the field is missing or not an object
84
+ */
85
+ object(key: string): ParsedRecord;
86
+ /**
87
+ * Extracts an optional nested object field.
88
+ * @param key - Field name
89
+ * @returns A new ParsedRecord wrapping the nested object, or undefined
90
+ * @throws Error if the field is present but not an object
91
+ */
92
+ optionalObject(key: string): ParsedRecord | undefined;
93
+ /**
94
+ * Returns the raw value of a field without validation.
95
+ * @param key - Field name
96
+ */
97
+ raw(key: string): unknown;
98
+ /**
99
+ * Returns all keys in the underlying record.
100
+ */
101
+ keys(): string[];
102
+ }
103
+ /**
104
+ * Wraps an unknown value as a ParsedRecord after verifying it is an object.
105
+ * @param data - The unknown value to parse
106
+ * @param label - Label for error messages (e.g. "Config", "patches[0]")
107
+ * @returns A ParsedRecord for typed field extraction
108
+ * @throws Error if data is not a plain object
109
+ */
110
+ export declare function parseObject(data: unknown, label: string): ParsedRecord;