browser-commander 0.16.1 → 0.17.1

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 (71) hide show
  1. package/.changeset/config.json +2 -1
  2. package/.jscpd-baseline.json +263 -0
  3. package/.jscpd.json +1 -3
  4. package/.prettierignore +1 -0
  5. package/CHANGELOG.md +19 -0
  6. package/eslint.config.js +23 -1
  7. package/package.json +13 -24
  8. package/scripts/changeset-version.mjs +2 -6
  9. package/scripts/check-release-format.mjs +80 -0
  10. package/scripts/create-github-release.mjs +33 -13
  11. package/scripts/create-manual-changeset.mjs +7 -8
  12. package/scripts/format-github-release.mjs +6 -7
  13. package/scripts/format-release-notes.mjs +7 -8
  14. package/scripts/instant-version-bump.mjs +7 -8
  15. package/scripts/publish-to-npm.mjs +7 -8
  16. package/scripts/setup-npm.mjs +2 -6
  17. package/scripts/version-and-commit.mjs +18 -7
  18. package/src/browser/browser-cookie-credentials.js +3 -1
  19. package/src/browser/browser-cookies.js +6 -2
  20. package/src/browser/browser-profiles.js +1 -1
  21. package/src/browser/launch-options.js +31 -8
  22. package/src/browser/launcher.js +16 -0
  23. package/src/exports.js +34 -0
  24. package/src/fingerprint/apply.js +116 -0
  25. package/src/fingerprint/automation-parity.js +224 -0
  26. package/src/fingerprint/cdp-overrides.js +176 -0
  27. package/src/fingerprint/derive.js +104 -0
  28. package/src/fingerprint/init-payload.js +181 -0
  29. package/src/fingerprint/init-script.js +104 -0
  30. package/src/fingerprint/limitations.js +107 -0
  31. package/src/fingerprint/limitations.json +96 -0
  32. package/src/fingerprint/presets.js +244 -0
  33. package/src/fingerprint/profile.js +428 -0
  34. package/src/tests/index.js +2 -1
  35. package/tests/e2e/fingerprint-parity.e2e.test.js +212 -0
  36. package/tests/helpers/mocks.js +4 -2
  37. package/tests/helpers/repo.js +74 -0
  38. package/tests/unit/browser/launch-options.test.js +9 -1
  39. package/tests/unit/experiments/fingerprint-parity-harness.test.js +97 -0
  40. package/tests/unit/fingerprint/apply.test.js +271 -0
  41. package/tests/unit/fingerprint/automation-parity.test.js +212 -0
  42. package/tests/unit/fingerprint/cdp-overrides.test.js +227 -0
  43. package/tests/unit/fingerprint/derive.test.js +105 -0
  44. package/tests/unit/fingerprint/init-script.test.js +357 -0
  45. package/tests/unit/fingerprint/limitations.test.js +184 -0
  46. package/tests/unit/fingerprint/presets.test.js +135 -0
  47. package/tests/unit/fingerprint/profile.test.js +282 -0
  48. package/tests/unit/scripts/cargo-lock-sync.test.js +110 -0
  49. package/tests/unit/scripts/changelog-collection-order.test.js +144 -0
  50. package/tests/unit/scripts/changeset-formatter.test.js +108 -0
  51. package/tests/unit/scripts/check-pipeline-status.test.js +133 -0
  52. package/tests/unit/scripts/check-release-format.test.js +131 -0
  53. package/tests/unit/scripts/check-web-archive.test.js +191 -0
  54. package/tests/unit/scripts/ci-timeout-budgets.test.js +311 -0
  55. package/tests/unit/scripts/ci-workflow-policy.test.js +292 -153
  56. package/tests/unit/scripts/command-error-text.test.js +83 -0
  57. package/tests/unit/scripts/command-stream-errexit.test.js +132 -0
  58. package/tests/unit/scripts/debug-print.test.js +67 -0
  59. package/tests/unit/scripts/jscpd-config.test.js +141 -0
  60. package/tests/unit/scripts/pre-commit-config.test.js +214 -0
  61. package/tests/unit/scripts/read-manifest.test.js +194 -0
  62. package/tests/unit/scripts/release-tags.test.js +96 -0
  63. package/tests/unit/scripts/required-docs.test.js +145 -0
  64. package/tests/unit/scripts/run-with-budget-warning.test.js +115 -0
  65. package/tests/unit/scripts/rust-changelog.test.js +147 -0
  66. package/tests/unit/scripts/use-module-adoption.test.js +74 -0
  67. package/tests/unit/scripts/use-module-integration.test.js +64 -0
  68. package/tests/unit/scripts/use-module.test.js +182 -0
  69. package/.husky/pre-commit +0 -1
  70. package/.prettierrc +0 -10
  71. package/deno.json +0 -7
@@ -1,6 +1,7 @@
1
1
  {
2
- "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
2
+ "$schema": "https://unpkg.com/@changesets/config@4.0.0/schema.json",
3
3
  "changelog": "@changesets/cli/changelog",
4
+ "format": "prettier",
4
5
  "commit": false,
5
6
  "fixed": [],
6
7
  "linked": [],
@@ -0,0 +1,263 @@
1
+ {
2
+ "version": 1,
3
+ "fingerprints": {
4
+ "00d381dd611ae8ac": 1,
5
+ "029a62ef1f48e856": 1,
6
+ "038170e72e0af1f5": 1,
7
+ "04b28d01d0bb47dc": 1,
8
+ "04f605be138bc350": 1,
9
+ "0796d358ad2562c3": 1,
10
+ "090a69d9528e4cdb": 1,
11
+ "09694dc8fb9d683c": 1,
12
+ "0a3459aad03492bb": 1,
13
+ "0b48b63049c42591": 1,
14
+ "0b8c71391c107a5e": 1,
15
+ "0ba2d165b56db0b3": 1,
16
+ "0c8a29320e45bdfa": 1,
17
+ "0ca2d9c2f2b44d6c": 1,
18
+ "0caa402c4eae32d8": 1,
19
+ "0fd23be8410c2c95": 1,
20
+ "0fe4f3b5e5e939b3": 1,
21
+ "1019ebbe7f07a5ad": 1,
22
+ "1273365073edd790": 1,
23
+ "1296adc3c3128048": 1,
24
+ "13c4b58d3ea0719b": 1,
25
+ "14beedf656e57fba": 1,
26
+ "150c8efe188ac225": 1,
27
+ "16340a16ed52849e": 1,
28
+ "17c26c0db2d35ad1": 1,
29
+ "193b46c0e7f72053": 1,
30
+ "1ac9fbd87efdb313": 1,
31
+ "1ad4dc7e19a77f68": 1,
32
+ "1c1baf1bc8ea0eae": 1,
33
+ "1d61f5c49f12d2dc": 1,
34
+ "1ef570563d4e5a08": 1,
35
+ "1f2104c950b12a30": 1,
36
+ "20bb8deee1481129": 1,
37
+ "21f9aab35441e4ff": 1,
38
+ "22755b35701c5bb6": 1,
39
+ "2296c06fa00da254": 1,
40
+ "229b903386b17538": 1,
41
+ "2357ae472525f6bf": 1,
42
+ "2394170c8a2a0e9a": 1,
43
+ "23d2486c0e269b77": 1,
44
+ "24b29d9d78441dbd": 1,
45
+ "266893ea7a511fe4": 1,
46
+ "266b9cb95f783cdf": 1,
47
+ "26bb09996580437c": 1,
48
+ "26ebb2e251c9a39e": 1,
49
+ "2a78ee4b05679c8d": 1,
50
+ "2b1a759e2cfd21a2": 1,
51
+ "2c0f8b5c6f28420b": 1,
52
+ "2d2d6fd6c7c0f30d": 1,
53
+ "2e70be24d2268673": 1,
54
+ "2f4c9810eae87d6f": 1,
55
+ "2f9e15467cc2f12a": 1,
56
+ "3101802d3ea65339": 1,
57
+ "33357ce94e07cc4b": 1,
58
+ "37bd4114d0089f49": 1,
59
+ "389df6fb4b8315a1": 1,
60
+ "39eaada6d1879f4a": 1,
61
+ "3a9f1672ecc1d55b": 1,
62
+ "3adca52a3f04837c": 1,
63
+ "3c1017def7b3216c": 1,
64
+ "3da23cbee214a1a5": 1,
65
+ "3dced48baf70eb36": 1,
66
+ "3fa2ebbb416120f0": 1,
67
+ "3fe61c0a7772f18b": 1,
68
+ "406fed8e5b39aafd": 1,
69
+ "40aa2953f783819c": 1,
70
+ "418db56376cc23bd": 1,
71
+ "441570d51d6f70a4": 1,
72
+ "457c71f32f53833d": 1,
73
+ "46e3f58cfb14a43a": 1,
74
+ "4b0d3cc5c27227e7": 1,
75
+ "4c3b94caa28045d0": 1,
76
+ "4e090d6bb30e6551": 1,
77
+ "4f3c541b3f3e3f23": 1,
78
+ "4fe5663c69a426cb": 1,
79
+ "505d98bd14b63a6c": 1,
80
+ "5085765e1f103f3e": 1,
81
+ "5184c90c9f8406c3": 1,
82
+ "520a28ee2ced56c2": 1,
83
+ "53519ec61a6ba319": 1,
84
+ "53f7e3f3e444a37d": 1,
85
+ "5587ec0cdb7b96e6": 1,
86
+ "559b5241047cd753": 1,
87
+ "562ebfb882d4aa8f": 1,
88
+ "56b5df15ee61887d": 1,
89
+ "56b7675e4ce57335": 1,
90
+ "56d45d447baad2fd": 1,
91
+ "56deee57166c4369": 1,
92
+ "578ab1469dc998ec": 1,
93
+ "57c410b183671eec": 1,
94
+ "57e313e36a5ce81d": 1,
95
+ "58c08b8242c875ec": 1,
96
+ "58c1ca12d4d2b50f": 1,
97
+ "5a09e2677334ba05": 1,
98
+ "5ae9b1430fc38daf": 1,
99
+ "602bfd129bba900e": 1,
100
+ "608e1dd27138ec6f": 1,
101
+ "60d0c58a01fc8746": 1,
102
+ "61bcadcd8dc9f63e": 1,
103
+ "63a3b672c8d8b50b": 1,
104
+ "63ff613b1780c67a": 1,
105
+ "642cc89ed49f8a21": 1,
106
+ "6479c3604c153ec4": 1,
107
+ "66fb97eebd7ff0dc": 1,
108
+ "67a95e428b8fa31c": 1,
109
+ "6880d620e0e67229": 1,
110
+ "69d50e6f6a5f2ee9": 1,
111
+ "6a7ed9ad3fc00a39": 1,
112
+ "6af3c307939ef75e": 1,
113
+ "6b35b48512756192": 1,
114
+ "6cd00d6b6a039022": 1,
115
+ "6e2457cd3e1f69e7": 1,
116
+ "6fa62e2642cc73c3": 1,
117
+ "7009c5e9bd61dbe8": 1,
118
+ "709348ea44394b52": 1,
119
+ "731fbc5d4188839a": 1,
120
+ "73dbf56d17ab79ed": 1,
121
+ "760d9925f6e3ca85": 1,
122
+ "7623a3eea4680be4": 1,
123
+ "771db68809cce310": 1,
124
+ "771ddbb36a118cbb": 1,
125
+ "779517ed59b39aa6": 1,
126
+ "7850ccac58d09216": 1,
127
+ "785d557d0683185b": 1,
128
+ "785e57db939e1887": 1,
129
+ "786d7040e54017c7": 1,
130
+ "78c84b601cbc4ffe": 1,
131
+ "799dc9940577a15e": 1,
132
+ "79e705499ddaf44b": 1,
133
+ "7e6bad380644f9b0": 1,
134
+ "81a56c10bfd42e39": 1,
135
+ "837ba752ecdaf052": 1,
136
+ "8549c997a472cb6c": 1,
137
+ "86646647aab19e39": 1,
138
+ "8678919f1350f154": 1,
139
+ "86aae81b1aa0d21b": 1,
140
+ "87484a2744e09748": 1,
141
+ "87514cf1299c9c0e": 1,
142
+ "876d2c8edfc8c40a": 1,
143
+ "8b013ac20e436d76": 1,
144
+ "8c2b1408886c1db1": 1,
145
+ "8c36ed4fc291ae47": 1,
146
+ "8cb9a984bf19a062": 1,
147
+ "8ec18cdbd4a96518": 1,
148
+ "8ecf2e554a04d2ef": 1,
149
+ "91049efadc65169b": 1,
150
+ "9134185980de8a17": 1,
151
+ "916833bafca4fa63": 1,
152
+ "91a538e4a0c28f25": 1,
153
+ "929fba5228e9fdac": 1,
154
+ "92ddafa7e65be7eb": 1,
155
+ "94821a4d1b745dfd": 1,
156
+ "94d89888ac9da9a9": 1,
157
+ "97c5a112fdde7be8": 1,
158
+ "98cc22079feaa004": 1,
159
+ "990d87f21678fa96": 1,
160
+ "9a72b1c2f22156ca": 1,
161
+ "9b6ef4df04562546": 1,
162
+ "9c055dfd90c369bf": 1,
163
+ "9cb19f1b972e811e": 1,
164
+ "9ee57dcd5e7a6691": 1,
165
+ "9fbb8296259dedbe": 1,
166
+ "a0183465782b0b15": 1,
167
+ "a1e3a3a13b27dba4": 1,
168
+ "a1ef39a15701c2b5": 1,
169
+ "a2a00d268fb2cee4": 1,
170
+ "a305c6cac16f3af5": 1,
171
+ "a4c8498d4f2d2191": 1,
172
+ "a6ad7ab6c05656dc": 1,
173
+ "a78d76908162699e": 1,
174
+ "a9bd4ffef6da5faf": 1,
175
+ "a9d71c2e50f1b832": 1,
176
+ "abe5a58f2b2cc593": 1,
177
+ "acc95d694eb2f581": 1,
178
+ "ad4469f2e8131535": 1,
179
+ "ad4cfa76daa1b6fc": 1,
180
+ "adae1329361a81a1": 1,
181
+ "ae477ba64bafba3f": 1,
182
+ "aee1e5c65f783848": 1,
183
+ "aee45e8b58c2d87f": 1,
184
+ "af9d6a2f4eba146e": 1,
185
+ "b010686af23efa90": 1,
186
+ "b2a0686231220c8a": 1,
187
+ "b35ff46c365dcd18": 1,
188
+ "b41581af204af40e": 1,
189
+ "b62f3449cde7991d": 1,
190
+ "b71fa5250da28739": 1,
191
+ "b740ecac685a15fe": 1,
192
+ "b8c2d21f2a26f77d": 1,
193
+ "bb269582b94ed42d": 1,
194
+ "bb3e6d0f7e7edccd": 1,
195
+ "bccf0d5cc57c08df": 1,
196
+ "bd0ae42c13d5b4f9": 1,
197
+ "bd6c62a6e1c6a071": 1,
198
+ "bdb8ca169be36b80": 1,
199
+ "bdcd11c2bfb5fde0": 1,
200
+ "be0900fb3e87164e": 1,
201
+ "bf452b9431c092ad": 1,
202
+ "c0b027bebd7bd737": 1,
203
+ "c189fe5122a7dc91": 1,
204
+ "c2c155adbcb46f3c": 1,
205
+ "c30737d62069fc02": 1,
206
+ "c3c3569357673af8": 1,
207
+ "c415ffcd004d6518": 1,
208
+ "c461a83b51eb7740": 1,
209
+ "c9cf3c28bed33a5b": 1,
210
+ "cbd444e9a6a9072e": 1,
211
+ "cd49f39b43d14635": 1,
212
+ "cebff7d1cc87f646": 1,
213
+ "cf6116d195199f8a": 1,
214
+ "cfd68135b744c27b": 1,
215
+ "d097f41a6f43e634": 1,
216
+ "d0da8714c4b7de30": 1,
217
+ "d17201496391f6cc": 1,
218
+ "d17e8db5a2bab966": 1,
219
+ "d1a4a8e6a523b752": 1,
220
+ "d1e5c2961267399a": 1,
221
+ "d2ed3d02dd6a2d8c": 1,
222
+ "d4eec99f58906a63": 1,
223
+ "d6157a86fea41907": 1,
224
+ "d6fa9884eee815f0": 1,
225
+ "d86bafb915488255": 1,
226
+ "de0bf2317c53d427": 1,
227
+ "de1b481bfe8c3e58": 1,
228
+ "df2dc922f40485e7": 1,
229
+ "df3b85f6947db144": 1,
230
+ "e0b90bce5112bb9c": 1,
231
+ "e2ef7800deb0f464": 1,
232
+ "e3111b845acb7a0e": 1,
233
+ "e4b10b56d7e1edac": 1,
234
+ "e59733bafda785e9": 1,
235
+ "e5feedc0c24e103e": 1,
236
+ "e67726a913675120": 1,
237
+ "ea1195048ded896c": 1,
238
+ "ea88711c5deb1fe5": 1,
239
+ "eb9a70c2a21094ff": 1,
240
+ "ebd08637a2d7bcf3": 1,
241
+ "ebe03c5f8c2f5147": 1,
242
+ "ee19c8b9b332ee98": 1,
243
+ "eeca0ccf8d821f21": 1,
244
+ "ef2fc2e92ac07c6e": 1,
245
+ "f022b763ff342b2d": 1,
246
+ "f07f979f8c2706e8": 1,
247
+ "f38f72095a67c95e": 1,
248
+ "f40ba84763547f41": 1,
249
+ "f46509ed693574df": 1,
250
+ "f4bb6f4c83950fcd": 1,
251
+ "f4bfa7e503443cd1": 1,
252
+ "f6678d6748e9c902": 1,
253
+ "f67a07a611f39cb2": 1,
254
+ "f911821aa7dda47f": 1,
255
+ "f97bf515bfd95829": 1,
256
+ "fae844842fba705b": 1,
257
+ "faf76ec43b2dbbaa": 1,
258
+ "fb3a2e9ace145907": 1,
259
+ "fc6ed7d760c98f51": 1,
260
+ "fccfd8b004c12dd4": 1,
261
+ "fe859df9f97c75d4": 1
262
+ }
263
+ }
package/.jscpd.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
- "threshold": 0,
3
2
  "minTokens": 30,
4
3
  "minLines": 5,
5
- "skipComments": true,
4
+ "mode": "weak",
6
5
  "ignore": [
7
6
  "**/node_modules/**",
8
7
  "**/build/**",
@@ -14,7 +13,6 @@
14
13
  "**/pnpm-lock.yaml",
15
14
  "**/yarn.lock"
16
15
  ],
17
- "format": "console",
18
16
  "reporters": ["console", "html"],
19
17
  "output": "./reports/jscpd"
20
18
  }
package/.prettierignore CHANGED
@@ -6,3 +6,4 @@ docs-api
6
6
  package-lock.json
7
7
  .eslintcache
8
8
  CLAUDE.md
9
+ reports
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.17.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 43708ff: Make the release scripts fail when the commands they run fail.
8
+
9
+ `command-stream`'s `$` resolves rather than rejects on a non-zero exit, so every
10
+ `try/catch` around it in the release scripts was dead code. `loadCommandStream()`
11
+ now turns on `errexit`, the GitHub release tags are namespaced per language, and
12
+ the release job checks the formatting of the commit it is about to push.
13
+
14
+ ## 0.17.0
15
+
16
+ ### Minor Changes
17
+
18
+ - Restore the release pipeline. `const { $ } = await use('command-stream')` returned `undefined` on the Node 24 the release jobs pin, because Node 23 added a synthetic `'module.exports'` named export for CommonJS namespaces that use-m's unwrapping does not recognise, so every JS and Rust release died on its first shell call with `TypeError: $ is not a function`. Dependencies now load through `scripts/use-module.mjs`, which detects that namespace shape and unwraps it. Manifest fields are read by TOML table through `scripts/read-manifest.mjs` instead of a line-anchored `grep`, which matched duplicate keys in other tables and wrote two lines into `$GITHUB_OUTPUT`. The duplication gate had `format` set to a reporter name, so jscpd scanned zero files and passed unconditionally; it now scans the tree against a recorded baseline.
19
+
20
+ Add the `fingerprint` subsystem, which removes the differences between a browser this library controls and one started by hand. `navigator.webdriver` stays false because the `AutomationControlled` Blink feature is disabled at launch, and `launchBrowser()` no longer passes the engine defaults a real Chrome does not carry. `resolveFingerprintProfile()` validates the 19 environment fields a page can read -- user agent and client hints, languages and locale, time zone, platform, cores, memory, screen, viewport, touch, WebGL strings, geolocation and the media preferences -- `createFingerprintPreset()` builds internally consistent Windows, macOS, Linux and Android machines, and `applyFingerprint()` installs a profile over CDP for both Playwright and Puppeteer. `FINGERPRINT_LIMITATIONS` documents what still cannot be made identical, and `relevantFingerprintLimitations()` narrows it to the entries a given profile and browser actually hit. The excluded defaults include Playwright's unconditional `--enable-unsafe-swiftshader`, which would otherwise give an automated browser a SwiftShader WebGL context on a machine where a hand-started Chrome has none.
21
+
3
22
  ## 0.16.1
4
23
 
5
24
  ### Patch Changes
package/eslint.config.js CHANGED
@@ -45,9 +45,22 @@ export default [
45
45
  Bun: 'readonly',
46
46
  Deno: 'readonly',
47
47
  globalThis: 'readonly',
48
- // Browser globals (used in evaluate functions)
48
+ // Browser globals (used in evaluate functions and in the page
49
+ // scripts under experiments/fingerprint-parity/, which are shipped to
50
+ // Chrome verbatim rather than executed by Node).
49
51
  document: 'readonly',
50
52
  window: 'readonly',
53
+ navigator: 'readonly',
54
+ Navigator: 'readonly',
55
+ screen: 'readonly',
56
+ Screen: 'readonly',
57
+ Notification: 'readonly',
58
+ speechSynthesis: 'readonly',
59
+ HTMLCanvasElement: 'readonly',
60
+ CanvasRenderingContext2D: 'readonly',
61
+ WebGLRenderingContext: 'readonly',
62
+ Blob: 'readonly',
63
+ Worker: 'readonly',
51
64
  },
52
65
  },
53
66
  rules: {
@@ -104,6 +117,15 @@ export default [
104
117
  'max-lines': ['error', 1500], // Maximum lines per file - counts all lines including blank lines and comments
105
118
  },
106
119
  },
120
+ {
121
+ // The shared init payload is source text, not a module: it is read verbatim
122
+ // and sent to the page, so the declaration it exports to the page has no
123
+ // caller inside this package. See scripts/check-shared-fingerprint-assets.sh.
124
+ files: ['src/fingerprint/init-payload.js'],
125
+ rules: {
126
+ 'no-unused-vars': 'off',
127
+ },
128
+ },
107
129
  {
108
130
  // Test files have different requirements
109
131
  files: ['tests/**/*.js', '**/*.test.js'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-commander",
3
- "version": "0.16.1",
3
+ "version": "0.17.1",
4
4
  "description": "Universal browser automation library that supports both Playwright and Puppeteer with a unified API",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -15,19 +15,20 @@
15
15
  "test:e2e": "node scripts/run-tests.mjs --env RUN_E2E=true tests/e2e",
16
16
  "test:e2e:playwright": "node scripts/run-tests.mjs --env RUN_E2E=true tests/e2e/playwright.e2e.test.js",
17
17
  "test:e2e:puppeteer": "node scripts/run-tests.mjs --env RUN_E2E=true tests/e2e/puppeteer.e2e.test.js",
18
+ "test:e2e:parity": "node scripts/run-tests.mjs --env RUN_E2E=true tests/e2e/fingerprint-parity.e2e.test.js",
18
19
  "test:all": "npm run test:unit && npm run test:e2e",
19
20
  "lint": "eslint .",
20
21
  "lint:fix": "eslint . --fix",
21
22
  "format": "prettier --write .",
22
23
  "format:check": "prettier --check .",
23
24
  "docs:api": "jsdoc -c jsdoc.json",
24
- "check:duplication": "jscpd .",
25
+ "check:duplication": "jscpd . --baseline .jscpd-baseline.json --fail-on-new-clones",
25
26
  "check": "npm run lint && npm run format:check && npm run check:duplication",
26
- "prepare": "husky || true",
27
27
  "changeset": "changeset",
28
28
  "changeset:version": "node scripts/changeset-version.mjs",
29
29
  "changeset:publish": "changeset publish",
30
- "changeset:status": "changeset status --since=origin/main"
30
+ "changeset:status": "changeset status --since=origin/main",
31
+ "check:duplication:update": "jscpd . --baseline .jscpd-baseline.json --update-baseline"
31
32
  },
32
33
  "repository": {
33
34
  "type": "git",
@@ -68,27 +69,15 @@
68
69
  "test-anywhere": "^0.9.1"
69
70
  },
70
71
  "devDependencies": {
71
- "@changesets/cli": "^2.29.7",
72
- "eslint": "^9.39.0",
72
+ "@changesets/cli": "^3.0.2",
73
+ "@eslint/js": "^10.0.1",
74
+ "eslint": "^10.10.0",
73
75
  "eslint-config-prettier": "^10.1.8",
74
- "eslint-plugin-prettier": "^5.5.4",
75
- "husky": "^9.1.7",
76
- "jscpd": "^4.0.5",
76
+ "eslint-plugin-prettier": "^5.5.6",
77
+ "jscpd": "^5.1.2",
77
78
  "jsdoc": "^4.0.4",
78
- "lint-staged": "^16.2.6",
79
- "playwright": "^1.56.1",
80
- "prettier": "^3.6.2",
81
- "puppeteer": "^24.27.0"
82
- },
83
- "lint-staged": {
84
- "*.{js,mjs,cjs}": [
85
- "eslint --fix",
86
- "prettier --write",
87
- "prettier --check"
88
- ],
89
- "*.md": [
90
- "prettier --write",
91
- "prettier --check"
92
- ]
79
+ "playwright": "^1.62.1",
80
+ "prettier": "^3.9.6",
81
+ "puppeteer": "^25.10.0"
93
82
  }
94
83
  }
@@ -13,13 +13,9 @@
13
13
  * - command-stream: Modern shell command execution with streaming support
14
14
  */
15
15
 
16
- // Load use-m dynamically
17
- const { use } = eval(
18
- await (await fetch('https://unpkg.com/use-m/use.js')).text()
19
- );
16
+ import { loadCommandStream } from '../../scripts/use-module.mjs';
20
17
 
21
- // Import command-stream for shell command execution
22
- const { $ } = await use('command-stream');
18
+ const { $ } = await loadCommandStream();
23
19
 
24
20
  try {
25
21
  console.log('Running changeset version...');
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Guard the release commit against the formatting gate that never sees it.
3
+ *
4
+ * `version-and-commit.mjs` pushes the bump with `GITHUB_TOKEN`, and a push
5
+ * authenticated with `GITHUB_TOKEN` does not start a workflow run. Nothing
6
+ * therefore inspects the files the bump generated -- CHANGELOG.md,
7
+ * package.json, package-lock.json. When `changeset version` wrote a
8
+ * CHANGELOG.md that Prettier rejects, main's `format:check` went red and
9
+ * stayed red, because the only runs that could have reported it were the ones
10
+ * the push never triggered.
11
+ *
12
+ * This module has no side effects on import: the shell is passed in, so the
13
+ * behaviour is testable without running a release.
14
+ */
15
+
16
+ export const FORMAT_CHECK_COMMAND = 'npm run format:check';
17
+
18
+ export const FORMAT_FAILURE_TITLE = 'Release commit is not formatted';
19
+
20
+ /**
21
+ * Build the annotation shown when the generated files fail the gate.
22
+ *
23
+ * A workflow command ends at the first newline, so the message is a single
24
+ * line and the captured output is printed separately.
25
+ * @returns {string}
26
+ */
27
+ export function formatFailureAnnotation() {
28
+ return (
29
+ `::error title=${FORMAT_FAILURE_TITLE}::The version bump wrote files that ` +
30
+ 'Prettier rejects. Pushing them would leave main failing format:check, ' +
31
+ 'and the push does not trigger a run that would report it. Fix the ' +
32
+ 'generator (see .changeset/config.json "format") rather than reformatting ' +
33
+ 'by hand.'
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Extract the reason a command-stream failure carries.
39
+ *
40
+ * With `errexit` on, `error.message` is only "Command failed with exit code
41
+ * N"; the diagnosis is in the captured streams.
42
+ * @param {unknown} error
43
+ * @returns {string}
44
+ */
45
+ export function formatFailureDetails(error) {
46
+ const streams = error && typeof error === 'object' ? error : {};
47
+ return [streams.stdout, streams.stderr]
48
+ .map((stream) => String(stream ?? '').trim())
49
+ .filter(Boolean)
50
+ .join('\n');
51
+ }
52
+
53
+ /**
54
+ * Run the repository's formatting gate over the generated release commit.
55
+ *
56
+ * Rethrows on failure so the release stops before the push.
57
+ * @param {() => Promise<unknown>} runCheck
58
+ * @param {{log?: (message: string) => void, error?: (message: string) => void}} [console_]
59
+ * @returns {Promise<void>}
60
+ */
61
+ export async function assertGeneratedFilesAreFormatted(
62
+ runCheck,
63
+ console_ = console
64
+ ) {
65
+ const log = console_.log ?? (() => {});
66
+ const error = console_.error ?? (() => {});
67
+
68
+ log('Checking that the release commit is formatted...');
69
+ try {
70
+ await runCheck();
71
+ } catch (failure) {
72
+ error(formatFailureAnnotation());
73
+ const details = formatFailureDetails(failure);
74
+ if (details) {
75
+ error(details);
76
+ }
77
+ throw failure;
78
+ }
79
+ log('Release commit is formatted');
80
+ }
@@ -14,14 +14,15 @@
14
14
 
15
15
  import { readFileSync } from 'fs';
16
16
 
17
- // Load use-m dynamically
18
- const { use } = eval(
19
- await (await fetch('https://unpkg.com/use-m/use.js')).text()
20
- );
17
+ import { releaseTag } from '../../scripts/release-tags.mjs';
18
+ import {
19
+ commandErrorText,
20
+ loadCommandStream,
21
+ loadLinoArguments,
22
+ } from '../../scripts/use-module.mjs';
21
23
 
22
- // Import link-foundation libraries
23
- const { $ } = await use('command-stream');
24
- const { makeConfig } = await use('lino-arguments');
24
+ const { $ } = await loadCommandStream();
25
+ const { makeConfig } = await loadLinoArguments();
25
26
 
26
27
  // Parse CLI arguments using lino-arguments
27
28
  // Note: Using --release-version instead of --version to avoid conflict with yargs' built-in --version flag
@@ -50,7 +51,9 @@ if (!version || !repository) {
50
51
  process.exit(1);
51
52
  }
52
53
 
53
- const tag = `v${version}`;
54
+ // `v<version>` is this package's namespace only; the crate uses `rust-v` and
55
+ // the wheel `python-v`. See scripts/release-tags.mjs.
56
+ const tag = releaseTag('js', version);
54
57
 
55
58
  console.log(`Creating GitHub release for ${tag}...`);
56
59
 
@@ -69,7 +72,14 @@ try {
69
72
  releaseNotes = match[0].replace(`## ${version}`, '').trim();
70
73
  }
71
74
 
75
+ // Falling back to a bare "Release <version>" body is a silent downgrade:
76
+ // the release still goes out and the job still passes, so this annotation is
77
+ // what makes an empty release body visible in the run summary.
72
78
  if (!releaseNotes) {
79
+ console.warn(
80
+ `::warning::Version ${version} has no "## ${version}" section in ` +
81
+ 'CHANGELOG.md, releasing without notes'
82
+ );
73
83
  releaseNotes = `Release ${version}`;
74
84
  }
75
85
 
@@ -82,11 +92,21 @@ try {
82
92
  body: releaseNotes,
83
93
  });
84
94
 
85
- await $`gh api repos/${repository}/releases -X POST --input -`.run({
86
- stdin: payload,
87
- });
88
-
89
- console.log(`\u2705 Created GitHub release: ${tag}`);
95
+ try {
96
+ await $`gh api repos/${repository}/releases -X POST --input -`.run({
97
+ stdin: payload,
98
+ });
99
+ console.log(`\u2705 Created GitHub release: ${tag}`);
100
+ } catch (error) {
101
+ // Re-running a release over a tag that already has one is not a failure.
102
+ // The rust script has always tolerated it; the JS one did not need to
103
+ // while a failed `gh` call resolved instead of rejecting.
104
+ if (commandErrorText(error).includes('already exists')) {
105
+ console.log(`Release ${tag} already exists, skipping`);
106
+ } else {
107
+ throw error;
108
+ }
109
+ }
90
110
  } catch (error) {
91
111
  console.error('Error creating release:', error.message);
92
112
  process.exit(1);
@@ -15,14 +15,13 @@ import { randomBytes } from 'crypto';
15
15
 
16
16
  const PACKAGE_NAME = 'browser-commander';
17
17
 
18
- // Load use-m dynamically
19
- const { use } = eval(
20
- await (await fetch('https://unpkg.com/use-m/use.js')).text()
21
- );
22
-
23
- // Import link-foundation libraries
24
- const { $ } = await use('command-stream');
25
- const { makeConfig } = await use('lino-arguments');
18
+ import {
19
+ loadCommandStream,
20
+ loadLinoArguments,
21
+ } from '../../scripts/use-module.mjs';
22
+
23
+ const { $ } = await loadCommandStream();
24
+ const { makeConfig } = await loadLinoArguments();
26
25
 
27
26
  // Parse CLI arguments using lino-arguments
28
27
  const config = makeConfig({
@@ -13,14 +13,13 @@
13
13
  * - lino-arguments: Unified configuration from CLI args, env vars, and .lenv files
14
14
  */
15
15
 
16
- // Load use-m dynamically
17
- const { use } = eval(
18
- await (await fetch('https://unpkg.com/use-m/use.js')).text()
19
- );
16
+ import {
17
+ loadCommandStream,
18
+ loadLinoArguments,
19
+ } from '../../scripts/use-module.mjs';
20
20
 
21
- // Import link-foundation libraries
22
- const { $ } = await use('command-stream');
23
- const { makeConfig } = await use('lino-arguments');
21
+ const { $ } = await loadCommandStream();
22
+ const { makeConfig } = await loadLinoArguments();
24
23
 
25
24
  // Parse CLI arguments using lino-arguments
26
25
  // Note: Using --release-version instead of --version to avoid conflict with yargs' built-in --version flag
@@ -23,14 +23,13 @@
23
23
 
24
24
  const PACKAGE_NAME = 'browser-commander';
25
25
 
26
- // Load use-m dynamically
27
- const { use } = eval(
28
- await (await fetch('https://unpkg.com/use-m/use.js')).text()
29
- );
30
-
31
- // Import link-foundation libraries
32
- const { $ } = await use('command-stream');
33
- const { makeConfig } = await use('lino-arguments');
26
+ import {
27
+ loadCommandStream,
28
+ loadLinoArguments,
29
+ } from '../../scripts/use-module.mjs';
30
+
31
+ const { $ } = await loadCommandStream();
32
+ const { makeConfig } = await loadLinoArguments();
34
33
 
35
34
  // Parse CLI arguments using lino-arguments
36
35
  // Note: Using --release-version instead of --version to avoid conflict with yargs' built-in --version flag