@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,200 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Lightweight runtime object parsing and field extraction helpers.
4
+ *
5
+ * Reduces repetitive "validate unknown → cast" boilerplate across
6
+ * config validation, manifest parsing, and metadata validation.
7
+ */
8
+ import { isArray, isNumber, isObject, isString } from './validation.js';
9
+ /**
10
+ * A parsed record wrapper that provides typed field extraction
11
+ * with clear error messages. Construct via {@link parseObject}.
12
+ */
13
+ export class ParsedRecord {
14
+ #data;
15
+ #label;
16
+ constructor(data, label) {
17
+ this.#data = data;
18
+ this.#label = label;
19
+ }
20
+ /**
21
+ * Extracts a required string field.
22
+ * @param key - Field name
23
+ * @returns The string value
24
+ * @throws Error if the field is missing or not a string
25
+ */
26
+ string(key) {
27
+ const value = this.#data[key];
28
+ if (!isString(value)) {
29
+ throw new Error(`${this.#label}.${key} must be a string`);
30
+ }
31
+ return value;
32
+ }
33
+ /**
34
+ * Extracts an optional string field.
35
+ * @param key - Field name
36
+ * @returns The string value or undefined
37
+ * @throws Error if the field is present but not a string
38
+ */
39
+ optionalString(key) {
40
+ const value = this.#data[key];
41
+ if (value === undefined)
42
+ return undefined;
43
+ if (!isString(value)) {
44
+ throw new Error(`${this.#label}.${key} must be a string`);
45
+ }
46
+ return value;
47
+ }
48
+ /**
49
+ * Extracts a required number field.
50
+ * @param key - Field name
51
+ * @returns The number value
52
+ * @throws Error if the field is missing or not a number
53
+ */
54
+ number(key) {
55
+ const value = this.#data[key];
56
+ if (!isNumber(value)) {
57
+ throw new Error(`${this.#label}.${key} must be a number`);
58
+ }
59
+ return value;
60
+ }
61
+ /**
62
+ * Extracts an optional number field.
63
+ * @param key - Field name
64
+ * @returns The number value or undefined
65
+ * @throws Error if the field is present but not a number
66
+ */
67
+ optionalNumber(key) {
68
+ const value = this.#data[key];
69
+ if (value === undefined)
70
+ return undefined;
71
+ if (!isNumber(value)) {
72
+ throw new Error(`${this.#label}.${key} must be a number`);
73
+ }
74
+ return value;
75
+ }
76
+ /**
77
+ * Extracts a required non-negative integer field.
78
+ * @param key - Field name
79
+ * @returns The integer value
80
+ * @throws Error if the field is missing, not a number, or negative
81
+ */
82
+ nonNegativeInteger(key) {
83
+ const value = this.#data[key];
84
+ if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
85
+ throw new Error(`${this.#label}.${key} must be a non-negative integer`);
86
+ }
87
+ return value;
88
+ }
89
+ /**
90
+ * Extracts an optional non-negative integer field.
91
+ * @param key - Field name
92
+ * @returns The integer value or undefined
93
+ * @throws Error if the field is present but not a non-negative integer
94
+ */
95
+ optionalNonNegativeInteger(key) {
96
+ const value = this.#data[key];
97
+ if (value === undefined)
98
+ return undefined;
99
+ if (!isNumber(value) || !Number.isInteger(value) || value < 0) {
100
+ throw new Error(`${this.#label}.${key} must be a non-negative integer`);
101
+ }
102
+ return value;
103
+ }
104
+ /**
105
+ * Extracts a required string field and validates it against a predicate.
106
+ * @param key - Field name
107
+ * @param predicate - Validation function
108
+ * @param allowed - Description of allowed values for the error message
109
+ * @returns The validated string value
110
+ */
111
+ stringEnum(key, predicate, allowed) {
112
+ const value = this.string(key);
113
+ if (!predicate(value)) {
114
+ throw new Error(`${this.#label}.${key} must be ${allowed}`);
115
+ }
116
+ return value;
117
+ }
118
+ /**
119
+ * Extracts a required string field and validates it with a custom check.
120
+ * @param key - Field name
121
+ * @param check - Validation function returning true if valid
122
+ * @param constraint - Description of the constraint for the error message
123
+ * @returns The validated string value
124
+ */
125
+ validatedString(key, check, constraint) {
126
+ const value = this.string(key);
127
+ if (!check(value)) {
128
+ throw new Error(`${this.#label}.${key} must be ${constraint}`);
129
+ }
130
+ return value;
131
+ }
132
+ /**
133
+ * Extracts a required array-of-strings field.
134
+ * @param key - Field name
135
+ * @returns The string array
136
+ * @throws Error if the field is missing or not an array of strings
137
+ */
138
+ stringArray(key) {
139
+ const value = this.#data[key];
140
+ if (!isArray(value) || !value.every(isString)) {
141
+ throw new Error(`${this.#label}.${key} must be an array of strings`);
142
+ }
143
+ return [...value];
144
+ }
145
+ /**
146
+ * Extracts a required nested object field.
147
+ * @param key - Field name
148
+ * @returns A new ParsedRecord wrapping the nested object
149
+ * @throws Error if the field is missing or not an object
150
+ */
151
+ object(key) {
152
+ const value = this.#data[key];
153
+ if (!isObject(value)) {
154
+ throw new Error(`${this.#label}.${key} must be an object`);
155
+ }
156
+ return new ParsedRecord(value, `${this.#label}.${key}`);
157
+ }
158
+ /**
159
+ * Extracts an optional nested object field.
160
+ * @param key - Field name
161
+ * @returns A new ParsedRecord wrapping the nested object, or undefined
162
+ * @throws Error if the field is present but not an object
163
+ */
164
+ optionalObject(key) {
165
+ const value = this.#data[key];
166
+ if (value === undefined)
167
+ return undefined;
168
+ if (!isObject(value)) {
169
+ throw new Error(`${this.#label}.${key} must be an object`);
170
+ }
171
+ return new ParsedRecord(value, `${this.#label}.${key}`);
172
+ }
173
+ /**
174
+ * Returns the raw value of a field without validation.
175
+ * @param key - Field name
176
+ */
177
+ raw(key) {
178
+ return this.#data[key];
179
+ }
180
+ /**
181
+ * Returns all keys in the underlying record.
182
+ */
183
+ keys() {
184
+ return Object.keys(this.#data);
185
+ }
186
+ }
187
+ /**
188
+ * Wraps an unknown value as a ParsedRecord after verifying it is an object.
189
+ * @param data - The unknown value to parse
190
+ * @param label - Label for error messages (e.g. "Config", "patches[0]")
191
+ * @returns A ParsedRecord for typed field extraction
192
+ * @throws Error if data is not a plain object
193
+ */
194
+ export function parseObject(data, label) {
195
+ if (!isObject(data)) {
196
+ throw new Error(`${label} must be an object`);
197
+ }
198
+ return new ParsedRecord(data, label);
199
+ }
200
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1,10 @@
1
+ /** Converts Windows path separators to forward slashes for stable comparisons. */
2
+ export declare function normalizePathSlashes(path: string): string;
3
+ /** Checks whether a path is explicitly absolute on either POSIX or Windows. */
4
+ export declare function isExplicitAbsolutePath(path: string): boolean;
5
+ /** Resolves a candidate path and returns whether it stays within the given root. */
6
+ export declare function isPathInsideRoot(root: string, candidate: string): boolean;
7
+ /** Checks whether a relative path stays contained within an arbitrary root. */
8
+ export declare function isContainedRelativePath(path: string): boolean;
9
+ /** Converts a candidate path to a normalized root-relative path, rejecting escapes. */
10
+ export declare function toRootRelativePath(root: string, candidate: string): string;
@@ -0,0 +1,43 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { isAbsolute, relative, resolve } from 'node:path';
3
+ const WINDOWS_ABSOLUTE_PATH = /^[a-zA-Z]:[\\/]/;
4
+ const RELATIVE_PATH_ROOT = resolve('/__fireforge_path_root__');
5
+ /** Converts Windows path separators to forward slashes for stable comparisons. */
6
+ export function normalizePathSlashes(path) {
7
+ return path.replace(/\\/g, '/');
8
+ }
9
+ /** Checks whether a path is explicitly absolute on either POSIX or Windows. */
10
+ export function isExplicitAbsolutePath(path) {
11
+ return isAbsolute(path) || WINDOWS_ABSOLUTE_PATH.test(path);
12
+ }
13
+ /** Resolves a candidate path and returns whether it stays within the given root. */
14
+ export function isPathInsideRoot(root, candidate) {
15
+ const resolvedRoot = resolve(root);
16
+ const resolvedCandidate = isExplicitAbsolutePath(candidate)
17
+ ? resolve(candidate)
18
+ : resolve(resolvedRoot, candidate);
19
+ const relativePath = relative(resolvedRoot, resolvedCandidate);
20
+ return (relativePath === '' ||
21
+ (!relativePath.startsWith('..') &&
22
+ !isAbsolute(relativePath) &&
23
+ !WINDOWS_ABSOLUTE_PATH.test(relativePath)));
24
+ }
25
+ /** Checks whether a relative path stays contained within an arbitrary root. */
26
+ export function isContainedRelativePath(path) {
27
+ if (isExplicitAbsolutePath(path)) {
28
+ return false;
29
+ }
30
+ return isPathInsideRoot(RELATIVE_PATH_ROOT, path);
31
+ }
32
+ /** Converts a candidate path to a normalized root-relative path, rejecting escapes. */
33
+ export function toRootRelativePath(root, candidate) {
34
+ const resolvedRoot = resolve(root);
35
+ const resolvedCandidate = isExplicitAbsolutePath(candidate)
36
+ ? resolve(candidate)
37
+ : resolve(resolvedRoot, candidate);
38
+ if (!isPathInsideRoot(resolvedRoot, resolvedCandidate)) {
39
+ throw new Error(`Path escapes root: ${candidate}`);
40
+ }
41
+ return normalizePathSlashes(relative(resolvedRoot, resolvedCandidate));
42
+ }
43
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Supported operating system platforms.
3
+ */
4
+ export type Platform = 'darwin' | 'linux' | 'win32';
5
+ /**
6
+ * Supported CPU architectures.
7
+ */
8
+ export type Arch = 'x64' | 'arm64';
9
+ /**
10
+ * Gets the current operating system platform.
11
+ * @throws Error if running on an unsupported platform
12
+ */
13
+ export declare function getPlatform(): Platform;
14
+ /**
15
+ * Gets the current CPU architecture.
16
+ * @throws Error if running on an unsupported architecture
17
+ */
18
+ export declare function getArch(): Arch;
19
+ /**
20
+ * Gets the mozconfig filename for the current platform.
21
+ */
22
+ export declare function getMozconfigName(): string;
23
+ /**
24
+ * Checks if the current platform is macOS.
25
+ */
26
+ export declare function isDarwin(): boolean;
27
+ /**
28
+ * Checks if the current platform is Linux.
29
+ */
30
+ export declare function isLinux(): boolean;
31
+ /**
32
+ * Checks if the current platform is Windows.
33
+ */
34
+ export declare function isWindows(): boolean;
35
+ /**
36
+ * Gets the appropriate file extension for executables on the current platform.
37
+ */
38
+ export declare function getExecutableExtension(): string;
@@ -0,0 +1,56 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { arch, platform } from 'node:os';
3
+ import { GeneralError } from '../errors/base.js';
4
+ /**
5
+ * Gets the current operating system platform.
6
+ * @throws Error if running on an unsupported platform
7
+ */
8
+ export function getPlatform() {
9
+ const p = platform();
10
+ if (p === 'darwin' || p === 'linux' || p === 'win32') {
11
+ return p;
12
+ }
13
+ throw new GeneralError(`Unsupported platform: ${p}. FireForge supports darwin, linux, and win32.`);
14
+ }
15
+ /**
16
+ * Gets the current CPU architecture.
17
+ * @throws Error if running on an unsupported architecture
18
+ */
19
+ export function getArch() {
20
+ const a = arch();
21
+ if (a === 'x64' || a === 'arm64') {
22
+ return a;
23
+ }
24
+ throw new GeneralError(`Unsupported architecture: ${a}. FireForge supports x64 and arm64.`);
25
+ }
26
+ /**
27
+ * Gets the mozconfig filename for the current platform.
28
+ */
29
+ export function getMozconfigName() {
30
+ return `${getPlatform()}.mozconfig`;
31
+ }
32
+ /**
33
+ * Checks if the current platform is macOS.
34
+ */
35
+ export function isDarwin() {
36
+ return platform() === 'darwin';
37
+ }
38
+ /**
39
+ * Checks if the current platform is Linux.
40
+ */
41
+ export function isLinux() {
42
+ return platform() === 'linux';
43
+ }
44
+ /**
45
+ * Checks if the current platform is Windows.
46
+ */
47
+ export function isWindows() {
48
+ return platform() === 'win32';
49
+ }
50
+ /**
51
+ * Gets the appropriate file extension for executables on the current platform.
52
+ */
53
+ export function getExecutableExtension() {
54
+ return isWindows() ? '.exe' : '';
55
+ }
56
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Result of executing a command.
3
+ */
4
+ export interface ExecResult {
5
+ /** Standard output content */
6
+ stdout: string;
7
+ /** Standard error content */
8
+ stderr: string;
9
+ /** Process exit code */
10
+ exitCode: number;
11
+ }
12
+ /**
13
+ * Options for command execution.
14
+ */
15
+ export interface ExecOptions {
16
+ /** Working directory for the command */
17
+ cwd?: string;
18
+ /** Environment variables */
19
+ env?: Record<string, string>;
20
+ /** Timeout in milliseconds */
21
+ timeout?: number;
22
+ }
23
+ /**
24
+ * Executes a command and returns its output.
25
+ * @param command - Command to execute
26
+ * @param args - Command arguments
27
+ * @param options - Execution options
28
+ * @returns Execution result with stdout, stderr, and exit code
29
+ */
30
+ export declare function exec(command: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
31
+ /**
32
+ * Callback for streaming output.
33
+ */
34
+ export type StreamCallback = (data: string) => void;
35
+ /**
36
+ * Options for streaming command execution.
37
+ */
38
+ export interface StreamOptions extends ExecOptions {
39
+ /** Callback for stdout data */
40
+ onStdout?: StreamCallback;
41
+ /** Callback for stderr data */
42
+ onStderr?: StreamCallback;
43
+ }
44
+ /**
45
+ * Executes a command and streams its output.
46
+ * @param command - Command to execute
47
+ * @param args - Command arguments
48
+ * @param options - Execution options
49
+ * @returns Exit code of the process
50
+ */
51
+ export declare function execStream(command: string, args: string[], options?: StreamOptions): Promise<number>;
52
+ /**
53
+ * Executes a command and inherits stdio (shows output directly).
54
+ * @param command - Command to execute
55
+ * @param args - Command arguments
56
+ * @param options - Execution options
57
+ * @returns Exit code of the process
58
+ */
59
+ export declare function execInherit(command: string, args: string[], options?: ExecOptions): Promise<number>;
60
+ /**
61
+ * Executes a command while inheriting stdin, streaming stdout/stderr live,
62
+ * and capturing the emitted output for diagnostics.
63
+ * @param command - Command to execute
64
+ * @param args - Command arguments
65
+ * @param options - Execution options
66
+ * @returns Execution result with stdout, stderr, and exit code
67
+ */
68
+ export declare function execInheritCapture(command: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
69
+ /**
70
+ * Finds an executable in the system PATH.
71
+ * @param name - Name of the executable
72
+ * @returns Full path to the executable, or undefined if not found
73
+ */
74
+ export declare function findExecutable(name: string): Promise<string | undefined>;
75
+ /**
76
+ * Checks if an executable exists in the system PATH.
77
+ * @param name - Name of the executable
78
+ * @returns True if the executable exists
79
+ */
80
+ export declare function executableExists(name: string): Promise<boolean>;
@@ -0,0 +1,188 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { spawn } from 'node:child_process';
3
+ import { constants as osConstants } from 'node:os';
4
+ // 50 MB cap per stream to prevent OOM — large toolchain builds (e.g. Firefox, Chromium)
5
+ // can easily blow past this, so we truncate rather than let the buffer grow unbounded.
6
+ const MAX_OUTPUT_SIZE = 50 * 1024 * 1024;
7
+ function createStreamCollector(mirror) {
8
+ const chunks = [];
9
+ let totalLength = 0;
10
+ let truncated = false;
11
+ return {
12
+ onData: (data) => {
13
+ const chunk = data.toString();
14
+ mirror?.write(chunk);
15
+ if (truncated)
16
+ return;
17
+ const remaining = MAX_OUTPUT_SIZE - totalLength;
18
+ if (chunk.length > remaining) {
19
+ chunks.push(chunk.slice(0, remaining));
20
+ chunks.push('\n[truncated — output exceeded 50 MB]');
21
+ totalLength = MAX_OUTPUT_SIZE;
22
+ truncated = true;
23
+ }
24
+ else {
25
+ chunks.push(chunk);
26
+ totalLength += chunk.length;
27
+ }
28
+ },
29
+ getText: () => chunks.join(''),
30
+ };
31
+ }
32
+ function exitCodeFromClose(code, signal) {
33
+ if (code !== null) {
34
+ return code;
35
+ }
36
+ if (signal) {
37
+ const signalNumber = osConstants.signals[signal];
38
+ if (typeof signalNumber === 'number') {
39
+ return 128 + signalNumber;
40
+ }
41
+ }
42
+ return 1;
43
+ }
44
+ /**
45
+ * Executes a command and returns its output.
46
+ * @param command - Command to execute
47
+ * @param args - Command arguments
48
+ * @param options - Execution options
49
+ * @returns Execution result with stdout, stderr, and exit code
50
+ */
51
+ export async function exec(command, args, options = {}) {
52
+ return new Promise((resolve, reject) => {
53
+ const child = spawn(command, args, {
54
+ cwd: options.cwd,
55
+ env: { ...process.env, ...options.env },
56
+ stdio: ['ignore', 'pipe', 'pipe'],
57
+ timeout: options.timeout,
58
+ });
59
+ const out = createStreamCollector();
60
+ const err = createStreamCollector();
61
+ child.stdout.on('data', out.onData);
62
+ child.stderr.on('data', err.onData);
63
+ child.on('error', (error) => {
64
+ reject(error);
65
+ });
66
+ child.on('close', (code, signal) => {
67
+ resolve({
68
+ stdout: out.getText(),
69
+ stderr: err.getText(),
70
+ exitCode: exitCodeFromClose(code, signal),
71
+ });
72
+ });
73
+ });
74
+ }
75
+ /**
76
+ * Executes a command and streams its output.
77
+ * @param command - Command to execute
78
+ * @param args - Command arguments
79
+ * @param options - Execution options
80
+ * @returns Exit code of the process
81
+ */
82
+ export async function execStream(command, args, options = {}) {
83
+ return new Promise((resolve, reject) => {
84
+ const child = spawn(command, args, {
85
+ cwd: options.cwd,
86
+ env: { ...process.env, ...options.env },
87
+ stdio: ['ignore', 'pipe', 'pipe'],
88
+ timeout: options.timeout,
89
+ });
90
+ child.stdout.on('data', (data) => {
91
+ options.onStdout?.(data.toString());
92
+ });
93
+ child.stderr.on('data', (data) => {
94
+ options.onStderr?.(data.toString());
95
+ });
96
+ child.on('error', (error) => {
97
+ reject(error);
98
+ });
99
+ child.on('close', (code, signal) => {
100
+ resolve(exitCodeFromClose(code, signal));
101
+ });
102
+ });
103
+ }
104
+ /**
105
+ * Executes a command and inherits stdio (shows output directly).
106
+ * @param command - Command to execute
107
+ * @param args - Command arguments
108
+ * @param options - Execution options
109
+ * @returns Exit code of the process
110
+ */
111
+ export async function execInherit(command, args, options = {}) {
112
+ return new Promise((resolve, reject) => {
113
+ const child = spawn(command, args, {
114
+ cwd: options.cwd,
115
+ env: { ...process.env, ...options.env },
116
+ stdio: 'inherit',
117
+ timeout: options.timeout,
118
+ });
119
+ child.on('error', (error) => {
120
+ reject(error);
121
+ });
122
+ child.on('close', (code, signal) => {
123
+ resolve(exitCodeFromClose(code, signal));
124
+ });
125
+ });
126
+ }
127
+ /**
128
+ * Executes a command while inheriting stdin, streaming stdout/stderr live,
129
+ * and capturing the emitted output for diagnostics.
130
+ * @param command - Command to execute
131
+ * @param args - Command arguments
132
+ * @param options - Execution options
133
+ * @returns Execution result with stdout, stderr, and exit code
134
+ */
135
+ export async function execInheritCapture(command, args, options = {}) {
136
+ return new Promise((resolve, reject) => {
137
+ const child = spawn(command, args, {
138
+ cwd: options.cwd,
139
+ env: { ...process.env, ...options.env },
140
+ stdio: ['inherit', 'pipe', 'pipe'],
141
+ timeout: options.timeout,
142
+ });
143
+ const out = createStreamCollector(process.stdout);
144
+ const err = createStreamCollector(process.stderr);
145
+ child.stdout.on('data', out.onData);
146
+ child.stderr.on('data', err.onData);
147
+ child.on('error', (error) => {
148
+ reject(error);
149
+ });
150
+ child.on('close', (code, signal) => {
151
+ resolve({
152
+ stdout: out.getText(),
153
+ stderr: err.getText(),
154
+ exitCode: exitCodeFromClose(code, signal),
155
+ });
156
+ });
157
+ });
158
+ }
159
+ /**
160
+ * Finds an executable in the system PATH.
161
+ * @param name - Name of the executable
162
+ * @returns Full path to the executable, or undefined if not found
163
+ */
164
+ export async function findExecutable(name) {
165
+ const command = process.platform === 'win32' ? 'where' : 'which';
166
+ try {
167
+ const result = await exec(command, [name]);
168
+ if (result.exitCode === 0 && result.stdout.trim()) {
169
+ // Return the first line (first match)
170
+ return result.stdout.trim().split('\n')[0];
171
+ }
172
+ return undefined;
173
+ }
174
+ catch (error) {
175
+ void error;
176
+ return undefined;
177
+ }
178
+ }
179
+ /**
180
+ * Checks if an executable exists in the system PATH.
181
+ * @param name - Name of the executable
182
+ * @returns True if the executable exists
183
+ */
184
+ export async function executableExists(name) {
185
+ const path = await findExecutable(name);
186
+ return path !== undefined;
187
+ }
188
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Escapes special regex characters in a string.
3
+ */
4
+ export declare function escapeRegex(str: string): string;
5
+ /** Matches hex color values like #fff, #ff00ff, #ff00ff80 (longest-first alternation) */
6
+ export declare const CSS_HEX_COLOR: RegExp;
7
+ /** Matches rgb() or rgba() function calls */
8
+ export declare const CSS_RGB_COLOR: RegExp;
9
+ /** Matches hsl() or hsla() function calls */
10
+ export declare const CSS_HSL_COLOR: RegExp;
11
+ /**
12
+ * Returns true if the content contains any raw CSS color values (hex, rgb, hsl).
13
+ */
14
+ export declare function hasRawCssColors(content: string): boolean;
15
+ /**
16
+ * Strips JS single-line and multi-line comments from source code, replacing them
17
+ * with spaces of equal length to preserve character offsets. String literals
18
+ * (single-quoted, double-quoted, and template) are preserved intact.
19
+ */
20
+ export declare function stripJsComments(source: string): string;
21
+ /**
22
+ * Counts the total number of raw CSS color values (hex, rgb, hsl) in content.
23
+ */
24
+ export declare function countRawCssColors(content: string): number;
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Escapes special regex characters in a string.
4
+ */
5
+ export function escapeRegex(str) {
6
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
7
+ }
8
+ /** Matches hex color values like #fff, #ff00ff, #ff00ff80 (longest-first alternation) */
9
+ export const CSS_HEX_COLOR = /#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{3,4})\b/;
10
+ /** Matches rgb() or rgba() function calls */
11
+ export const CSS_RGB_COLOR = /\brgba?\s*\(/;
12
+ /** Matches hsl() or hsla() function calls */
13
+ export const CSS_HSL_COLOR = /\bhsla?\s*\(/;
14
+ // Global variants for counting (cached to avoid re-creation per call)
15
+ const CSS_HEX_COLOR_G = new RegExp(CSS_HEX_COLOR.source, 'g');
16
+ const CSS_RGB_COLOR_G = new RegExp(CSS_RGB_COLOR.source, 'g');
17
+ const CSS_HSL_COLOR_G = new RegExp(CSS_HSL_COLOR.source, 'g');
18
+ /**
19
+ * Returns true if the content contains any raw CSS color values (hex, rgb, hsl).
20
+ */
21
+ export function hasRawCssColors(content) {
22
+ return CSS_HEX_COLOR.test(content) || CSS_RGB_COLOR.test(content) || CSS_HSL_COLOR.test(content);
23
+ }
24
+ /**
25
+ * Strips JS single-line and multi-line comments from source code, replacing them
26
+ * with spaces of equal length to preserve character offsets. String literals
27
+ * (single-quoted, double-quoted, and template) are preserved intact.
28
+ */
29
+ export function stripJsComments(source) {
30
+ return source.replace(/\/\/.*$|\/\*[\s\S]*?\*\/|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`/gm, (match) => (match.startsWith('/') ? ' '.repeat(match.length) : match));
31
+ }
32
+ /**
33
+ * Counts the total number of raw CSS color values (hex, rgb, hsl) in content.
34
+ */
35
+ export function countRawCssColors(content) {
36
+ return ((content.match(CSS_HEX_COLOR_G)?.length ?? 0) +
37
+ (content.match(CSS_RGB_COLOR_G)?.length ?? 0) +
38
+ (content.match(CSS_HSL_COLOR_G)?.length ?? 0));
39
+ }
40
+ //# sourceMappingURL=regex.js.map