json-sort-cli 4.3.0 → 4.3.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.
@@ -0,0 +1,793 @@
1
+ // This is the canonical source. `lect` copies it verbatim into every CLI.
2
+
3
+ import { spawn } from "node:child_process";
4
+ import {
5
+ mkdirSync,
6
+ readFileSync,
7
+ renameSync,
8
+ statSync,
9
+ unlinkSync,
10
+ writeFileSync,
11
+ } from "node:fs";
12
+ import { homedir } from "node:os";
13
+ import path from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ const CHECK_INTERVAL = 24 * 60 * 60 * 1000;
17
+ const FAILURE_BACKOFF = 60 * 60 * 1000;
18
+ const FETCH_TIMEOUT = 10 * 1000;
19
+ const LOCK_STALE_AFTER = FETCH_TIMEOUT + 20 * 1000;
20
+ const MAX_JSON_BYTES = 64 * 1024;
21
+ const MODULE_FILENAME = fileURLToPath(import.meta.url);
22
+ const NOTIFICATION_LEASE = 60 * 60 * 1000;
23
+ const STATE_SCHEMA_VERSION = 1;
24
+ const WORKER_FLAG = "--codsen-update-check-worker";
25
+
26
+ const SEMVER_PATTERN =
27
+ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
28
+ const PACKAGE_NAME_PART = /^[a-z0-9][a-z0-9._~-]*$/;
29
+
30
+ function parseSemver(version) {
31
+ if (typeof version !== "string") {
32
+ return null;
33
+ }
34
+ const match = SEMVER_PATTERN.exec(version);
35
+ if (!match) {
36
+ return null;
37
+ }
38
+ const prerelease = match[4] ? match[4].split(".") : [];
39
+ if (
40
+ prerelease.some(
41
+ (identifier) => /^\d+$/.test(identifier) && /^0\d+/.test(identifier),
42
+ )
43
+ ) {
44
+ return null;
45
+ }
46
+ return {
47
+ core: [match[1], match[2], match[3]],
48
+ prerelease,
49
+ };
50
+ }
51
+
52
+ function compareNumericStrings(left, right) {
53
+ if (left.length !== right.length) {
54
+ return left.length < right.length ? -1 : 1;
55
+ }
56
+ if (left === right) {
57
+ return 0;
58
+ }
59
+ return left < right ? -1 : 1;
60
+ }
61
+
62
+ function compareSemver(leftVersion, rightVersion) {
63
+ const left = parseSemver(leftVersion);
64
+ const right = parseSemver(rightVersion);
65
+ if (!left || !right) {
66
+ return null;
67
+ }
68
+ for (let index = 0; index < left.core.length; index += 1) {
69
+ const comparison = compareNumericStrings(
70
+ left.core[index],
71
+ right.core[index],
72
+ );
73
+ if (comparison) {
74
+ return comparison;
75
+ }
76
+ }
77
+ if (!left.prerelease.length || !right.prerelease.length) {
78
+ if (left.prerelease.length === right.prerelease.length) {
79
+ return 0;
80
+ }
81
+ return left.prerelease.length ? -1 : 1;
82
+ }
83
+ const length = Math.max(left.prerelease.length, right.prerelease.length);
84
+ for (let index = 0; index < length; index += 1) {
85
+ const leftIdentifier = left.prerelease[index];
86
+ const rightIdentifier = right.prerelease[index];
87
+ if (leftIdentifier === undefined || rightIdentifier === undefined) {
88
+ return leftIdentifier === undefined ? -1 : 1;
89
+ }
90
+ if (leftIdentifier === rightIdentifier) {
91
+ continue;
92
+ }
93
+ const leftIsNumeric = /^\d+$/.test(leftIdentifier);
94
+ const rightIsNumeric = /^\d+$/.test(rightIdentifier);
95
+ if (leftIsNumeric && rightIsNumeric) {
96
+ return compareNumericStrings(leftIdentifier, rightIdentifier);
97
+ }
98
+ if (leftIsNumeric !== rightIsNumeric) {
99
+ return leftIsNumeric ? -1 : 1;
100
+ }
101
+ return leftIdentifier < rightIdentifier ? -1 : 1;
102
+ }
103
+ return 0;
104
+ }
105
+
106
+ function isValidPackageName(packageName) {
107
+ if (
108
+ typeof packageName !== "string" ||
109
+ !packageName ||
110
+ packageName.length > 214
111
+ ) {
112
+ return false;
113
+ }
114
+ if (!packageName.startsWith("@")) {
115
+ return PACKAGE_NAME_PART.test(packageName);
116
+ }
117
+ const parts = packageName.slice(1).split("/");
118
+ return (
119
+ parts.length === 2 && parts.every((part) => PACKAGE_NAME_PART.test(part))
120
+ );
121
+ }
122
+
123
+ function validTimestamp(value) {
124
+ return Number.isSafeInteger(value) && value >= 0 ? value : 0;
125
+ }
126
+
127
+ function initialState(now, legacy = {}) {
128
+ const legacyCheck = validTimestamp(legacy.lastUpdateCheck);
129
+ return {
130
+ schemaVersion: STATE_SCHEMA_VERSION,
131
+ createdAt: legacyCheck || now,
132
+ lastAttempt: 0,
133
+ lastSuccess: 0,
134
+ latestVersion: parseSemver(legacy.latestVersion)
135
+ ? legacy.latestVersion
136
+ : null,
137
+ lastNotification: null,
138
+ pendingNotification: null,
139
+ };
140
+ }
141
+
142
+ function normalizeState(value, now) {
143
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
144
+ return null;
145
+ }
146
+ const latestVersion = parseSemver(value.latestVersion)
147
+ ? value.latestVersion
148
+ : null;
149
+ const notification = value.lastNotification;
150
+ const lastNotification =
151
+ notification &&
152
+ typeof notification === "object" &&
153
+ parseSemver(notification.current) &&
154
+ parseSemver(notification.latest)
155
+ ? {
156
+ current: notification.current,
157
+ latest: notification.latest,
158
+ at: validTimestamp(notification.at),
159
+ }
160
+ : null;
161
+ const pending = value.pendingNotification;
162
+ const pendingNotification =
163
+ pending &&
164
+ typeof pending === "object" &&
165
+ parseSemver(pending.current) &&
166
+ parseSemver(pending.latest) &&
167
+ typeof pending.token === "string" &&
168
+ pending.token.length <= 256
169
+ ? {
170
+ current: pending.current,
171
+ latest: pending.latest,
172
+ token: pending.token,
173
+ at: validTimestamp(pending.at),
174
+ }
175
+ : null;
176
+ return {
177
+ schemaVersion: STATE_SCHEMA_VERSION,
178
+ createdAt: validTimestamp(value.createdAt) || now,
179
+ lastAttempt: validTimestamp(value.lastAttempt),
180
+ lastSuccess: validTimestamp(value.lastSuccess),
181
+ latestVersion,
182
+ lastNotification,
183
+ pendingNotification,
184
+ };
185
+ }
186
+
187
+ function readSmallJson(filename) {
188
+ if (!filename) {
189
+ return null;
190
+ }
191
+ try {
192
+ const stats = statSync(filename);
193
+ if (!stats.isFile() || stats.size > MAX_JSON_BYTES) {
194
+ return null;
195
+ }
196
+ return JSON.parse(readFileSync(filename, "utf8"));
197
+ } catch {
198
+ return null;
199
+ }
200
+ }
201
+
202
+ function readState(filename, now) {
203
+ return normalizeState(readSmallJson(filename), now);
204
+ }
205
+
206
+ function writeState(filename, state) {
207
+ let temporaryFilename;
208
+ try {
209
+ mkdirSync(path.dirname(filename), { mode: 0o700, recursive: true });
210
+ temporaryFilename = `${filename}.${process.pid}.${Date.now().toString(36)}.${Math.random().toString(36).slice(2)}.tmp`;
211
+ writeFileSync(temporaryFilename, `${JSON.stringify(state, null, 2)}\n`, {
212
+ encoding: "utf8",
213
+ flag: "wx",
214
+ mode: 0o600,
215
+ });
216
+ renameSync(temporaryFilename, filename);
217
+ return true;
218
+ } catch {
219
+ if (temporaryFilename) {
220
+ try {
221
+ unlinkSync(temporaryFilename);
222
+ } catch {}
223
+ }
224
+ return false;
225
+ }
226
+ }
227
+
228
+ function acquireLock(lockFile, now, staleAfter = LOCK_STALE_AFTER) {
229
+ try {
230
+ mkdirSync(path.dirname(lockFile), { mode: 0o700, recursive: true });
231
+ } catch {
232
+ return false;
233
+ }
234
+ const token = `${process.pid}:${Date.now()}:${Math.random()}`;
235
+ for (let attempt = 0; attempt < 2; attempt += 1) {
236
+ try {
237
+ writeFileSync(lockFile, token, {
238
+ encoding: "utf8",
239
+ flag: "wx",
240
+ mode: 0o600,
241
+ });
242
+ return token;
243
+ } catch (error) {
244
+ if (error?.code !== "EEXIST") {
245
+ return false;
246
+ }
247
+ try {
248
+ const observedToken = readFileSync(lockFile, "utf8");
249
+ const observedStats = statSync(lockFile);
250
+ const age = now - observedStats.mtimeMs;
251
+ if (!Number.isFinite(age) || age <= staleAfter) {
252
+ return false;
253
+ }
254
+ const currentStats = statSync(lockFile);
255
+ if (
256
+ readFileSync(lockFile, "utf8") !== observedToken ||
257
+ currentStats.dev !== observedStats.dev ||
258
+ currentStats.ino !== observedStats.ino ||
259
+ currentStats.mtimeMs !== observedStats.mtimeMs
260
+ ) {
261
+ return false;
262
+ }
263
+ if (readFileSync(lockFile, "utf8") !== observedToken) {
264
+ return false;
265
+ }
266
+ unlinkSync(lockFile);
267
+ } catch {
268
+ return false;
269
+ }
270
+ }
271
+ }
272
+ return false;
273
+ }
274
+
275
+ function releaseLock(lockFile, token) {
276
+ try {
277
+ if (readFileSync(lockFile, "utf8") === token) {
278
+ unlinkSync(lockFile);
279
+ }
280
+ } catch {}
281
+ }
282
+
283
+ function updateStateWithLock(
284
+ paths,
285
+ now,
286
+ update,
287
+ staleAfter = LOCK_STALE_AFTER,
288
+ ) {
289
+ const lockToken = acquireLock(paths.lockFile, now, staleAfter);
290
+ if (!lockToken) {
291
+ return null;
292
+ }
293
+ try {
294
+ const existingState = readState(paths.cacheFile, now);
295
+ const nextState = update(existingState || initialState(now), {
296
+ exists: Boolean(existingState),
297
+ });
298
+ return nextState && writeState(paths.cacheFile, nextState)
299
+ ? nextState
300
+ : null;
301
+ } finally {
302
+ releaseLock(paths.lockFile, lockToken);
303
+ }
304
+ }
305
+
306
+ function resolveCacheRoot(env, home, platform) {
307
+ if (
308
+ typeof env.XDG_CACHE_HOME === "string" &&
309
+ path.isAbsolute(env.XDG_CACHE_HOME)
310
+ ) {
311
+ return env.XDG_CACHE_HOME;
312
+ }
313
+ if (platform === "win32") {
314
+ if (
315
+ typeof env.LOCALAPPDATA === "string" &&
316
+ path.isAbsolute(env.LOCALAPPDATA)
317
+ ) {
318
+ return env.LOCALAPPDATA;
319
+ }
320
+ return home && path.isAbsolute(home)
321
+ ? path.join(home, "AppData", "Local")
322
+ : null;
323
+ }
324
+ if (!home || !path.isAbsolute(home)) {
325
+ return null;
326
+ }
327
+ return platform === "darwin"
328
+ ? path.join(home, "Library", "Caches")
329
+ : path.join(home, ".cache");
330
+ }
331
+
332
+ function resolvePaths(packageName, runtime = {}) {
333
+ const env = runtime.env ?? process.env;
334
+ const home = runtime.home ?? homedir();
335
+ const platform = runtime.platform ?? process.platform;
336
+ const cacheRoot = runtime.cacheRoot ?? resolveCacheRoot(env, home, platform);
337
+ if (!cacheRoot || !path.isAbsolute(cacheRoot)) {
338
+ return null;
339
+ }
340
+ const encodedName = Buffer.from(packageName).toString("base64url");
341
+ const directory = path.join(cacheRoot, "codsen", "update-notifier");
342
+ const cacheFile =
343
+ runtime.cacheFile ?? path.join(directory, `${encodedName}.json`);
344
+ const lockFile = runtime.lockFile ?? `${cacheFile}.lock`;
345
+ const configRoot =
346
+ env.XDG_CONFIG_HOME || (home && path.join(home, ".config"));
347
+ const legacyConfigFile =
348
+ "legacyConfigFile" in runtime
349
+ ? runtime.legacyConfigFile
350
+ : configRoot &&
351
+ path.join(
352
+ configRoot,
353
+ "configstore",
354
+ `update-notifier-${packageName}.json`,
355
+ );
356
+ return { cacheFile, legacyConfigFile, lockFile };
357
+ }
358
+
359
+ function readLegacyConfig(filename) {
360
+ const value = readSmallJson(filename);
361
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
362
+ return {};
363
+ }
364
+ return {
365
+ optOut: Boolean(value.optOut),
366
+ lastUpdateCheck: validTimestamp(value.lastUpdateCheck),
367
+ latestVersion: value.update?.latest,
368
+ };
369
+ }
370
+
371
+ function isCi(env) {
372
+ return (
373
+ env.CI !== "0" &&
374
+ env.CI !== "false" &&
375
+ ("CI" in env ||
376
+ "CONTINUOUS_INTEGRATION" in env ||
377
+ Object.keys(env).some((key) => key.startsWith("CI_")))
378
+ );
379
+ }
380
+
381
+ function isPackageManagerInvocation(env) {
382
+ const userAgent = env.npm_config_user_agent;
383
+ return (
384
+ (typeof env.npm_package_json === "string" &&
385
+ env.npm_package_json.endsWith("package.json")) ||
386
+ (typeof userAgent === "string" &&
387
+ ["npm", "yarn", "pnpm", "bun"].some((name) => userAgent.startsWith(name)))
388
+ );
389
+ }
390
+
391
+ function isDisabled({
392
+ argv,
393
+ enabled,
394
+ env,
395
+ packageName,
396
+ packageVersion,
397
+ stderr,
398
+ }) {
399
+ return (
400
+ enabled === false ||
401
+ !isValidPackageName(packageName) ||
402
+ !parseSemver(packageVersion) ||
403
+ stderr?.isTTY !== true ||
404
+ "NO_UPDATE_NOTIFIER" in env ||
405
+ env.NODE_ENV === "test" ||
406
+ argv.includes("--no-update-notifier") ||
407
+ isCi(env) ||
408
+ isPackageManagerInvocation(env)
409
+ );
410
+ }
411
+
412
+ function isCheckDue(state, now, options = {}) {
413
+ const checkInterval = options.checkInterval ?? CHECK_INTERVAL;
414
+ const failureBackoff = options.failureBackoff ?? FAILURE_BACKOFF;
415
+ const successOrCreation = state.lastSuccess || state.createdAt;
416
+ return (
417
+ now - successOrCreation >= checkInterval &&
418
+ (!state.lastAttempt || now - state.lastAttempt >= failureBackoff)
419
+ );
420
+ }
421
+
422
+ function isNotificationDue(state, currentVersion) {
423
+ const latestVersion = state.latestVersion;
424
+ if (compareSemver(latestVersion, currentVersion) !== 1) {
425
+ return false;
426
+ }
427
+ return !(
428
+ state.lastNotification?.current === currentVersion &&
429
+ state.lastNotification?.latest === latestVersion
430
+ );
431
+ }
432
+
433
+ function formatCliUpdateNotification({
434
+ currentVersion,
435
+ latestVersion,
436
+ packageName,
437
+ }) {
438
+ return `\nUpdate available for ${packageName}: ${currentVersion} → ${latestVersion}\nhttps://www.npmjs.com/package/${encodeURIComponent(packageName)}\n\n`;
439
+ }
440
+
441
+ function hasActiveNotificationReservation(
442
+ state,
443
+ currentVersion,
444
+ latestVersion,
445
+ now,
446
+ lease,
447
+ ) {
448
+ const pending = state.pendingNotification;
449
+ return (
450
+ pending?.current === currentVersion &&
451
+ pending.latest === latestVersion &&
452
+ now - pending.at < lease
453
+ );
454
+ }
455
+
456
+ function registerNotification({
457
+ currentVersion,
458
+ now,
459
+ onExit,
460
+ packageName,
461
+ paths,
462
+ reservationToken,
463
+ staleAfter,
464
+ stderr,
465
+ }) {
466
+ const finish = (code) => {
467
+ const notificationTime = now();
468
+ updateStateWithLock(
469
+ paths,
470
+ notificationTime,
471
+ (state) => {
472
+ if (state.pendingNotification?.token !== reservationToken) {
473
+ return null;
474
+ }
475
+ if (code !== 0 || stderr?.isTTY !== true) {
476
+ return { ...state, pendingNotification: null };
477
+ }
478
+ const notificationLatest = state.latestVersion;
479
+ if (compareSemver(notificationLatest, currentVersion) !== 1) {
480
+ return { ...state, pendingNotification: null };
481
+ }
482
+ try {
483
+ stderr.write(
484
+ formatCliUpdateNotification({
485
+ currentVersion,
486
+ latestVersion: notificationLatest,
487
+ packageName,
488
+ }),
489
+ );
490
+ } catch {
491
+ return { ...state, pendingNotification: null };
492
+ }
493
+ return {
494
+ ...state,
495
+ lastNotification: {
496
+ current: currentVersion,
497
+ latest: notificationLatest,
498
+ at: notificationTime,
499
+ },
500
+ pendingNotification: null,
501
+ };
502
+ },
503
+ staleAfter,
504
+ );
505
+ };
506
+ try {
507
+ onExit(finish);
508
+ return true;
509
+ } catch {
510
+ finish(1);
511
+ return false;
512
+ }
513
+ }
514
+
515
+ function spawnWorker(packageName, attemptToken, spawnImpl = spawn) {
516
+ try {
517
+ const child = spawnImpl(
518
+ process.execPath,
519
+ [MODULE_FILENAME, WORKER_FLAG, packageName, String(attemptToken)],
520
+ {
521
+ detached: true,
522
+ stdio: "ignore",
523
+ windowsHide: true,
524
+ },
525
+ );
526
+ child.once?.("error", () => {});
527
+ child.unref?.();
528
+ return true;
529
+ } catch {
530
+ return false;
531
+ }
532
+ }
533
+
534
+ function notifyOfCliUpdate({ enabled = true, pkg } = {}, runtime = {}) {
535
+ try {
536
+ const argv = runtime.argv ?? process.argv.slice(2);
537
+ const env = runtime.env ?? process.env;
538
+ const stderr = runtime.stderr ?? process.stderr;
539
+ const packageName = pkg?.name;
540
+ const packageVersion = pkg?.version;
541
+ if (
542
+ isDisabled({
543
+ argv,
544
+ enabled,
545
+ env,
546
+ packageName,
547
+ packageVersion,
548
+ stderr,
549
+ })
550
+ ) {
551
+ return false;
552
+ }
553
+ const paths = resolvePaths(packageName, runtime);
554
+ if (!paths) {
555
+ return false;
556
+ }
557
+ const legacy = readLegacyConfig(paths.legacyConfigFile);
558
+ if (legacy.optOut) {
559
+ return false;
560
+ }
561
+ const now = runtime.now ?? Date.now;
562
+ const currentTime = now();
563
+ const staleAfter = runtime.lockStaleAfter ?? LOCK_STALE_AFTER;
564
+ let state = readState(paths.cacheFile, currentTime);
565
+ if (!state) {
566
+ state = updateStateWithLock(
567
+ paths,
568
+ currentTime,
569
+ (current, { exists }) =>
570
+ exists ? current : initialState(currentTime, legacy),
571
+ staleAfter,
572
+ );
573
+ if (!state) {
574
+ return false;
575
+ }
576
+ }
577
+ if (isNotificationDue(state, packageVersion)) {
578
+ const reservationToken = `${process.pid}:${currentTime}:${Math.random()}`;
579
+ const claimedState = updateStateWithLock(
580
+ paths,
581
+ currentTime,
582
+ (current) => {
583
+ const currentLatest = current.latestVersion;
584
+ return isNotificationDue(current, packageVersion) &&
585
+ !hasActiveNotificationReservation(
586
+ current,
587
+ packageVersion,
588
+ currentLatest,
589
+ currentTime,
590
+ runtime.notificationLease ?? NOTIFICATION_LEASE,
591
+ )
592
+ ? {
593
+ ...current,
594
+ pendingNotification: {
595
+ current: packageVersion,
596
+ latest: currentLatest,
597
+ token: reservationToken,
598
+ at: currentTime,
599
+ },
600
+ }
601
+ : null;
602
+ },
603
+ staleAfter,
604
+ );
605
+ if (!claimedState) {
606
+ return false;
607
+ }
608
+ return registerNotification({
609
+ currentVersion: packageVersion,
610
+ now,
611
+ onExit:
612
+ runtime.onExit ?? ((listener) => process.once("exit", listener)),
613
+ packageName,
614
+ paths,
615
+ reservationToken,
616
+ staleAfter,
617
+ stderr,
618
+ });
619
+ }
620
+ if (
621
+ !isCheckDue(state, currentTime, {
622
+ checkInterval: runtime.checkInterval,
623
+ failureBackoff: runtime.failureBackoff,
624
+ })
625
+ ) {
626
+ return false;
627
+ }
628
+ const scheduledState = updateStateWithLock(
629
+ paths,
630
+ currentTime,
631
+ (current) =>
632
+ isCheckDue(current, currentTime, {
633
+ checkInterval: runtime.checkInterval,
634
+ failureBackoff: runtime.failureBackoff,
635
+ })
636
+ ? { ...current, lastAttempt: currentTime }
637
+ : null,
638
+ staleAfter,
639
+ );
640
+ if (!scheduledState) {
641
+ return false;
642
+ }
643
+ const startWorker = runtime.spawnWorker ?? spawnWorker;
644
+ return Boolean(startWorker(packageName, currentTime, runtime.spawnImpl));
645
+ } catch {
646
+ return false;
647
+ }
648
+ }
649
+
650
+ async function readLimitedBody(response) {
651
+ const contentLength = Number(response.headers?.get?.("content-length"));
652
+ if (Number.isFinite(contentLength) && contentLength > MAX_JSON_BYTES) {
653
+ return null;
654
+ }
655
+ if (!response.body?.getReader) {
656
+ const text = await response.text();
657
+ return Buffer.byteLength(text) <= MAX_JSON_BYTES ? text : null;
658
+ }
659
+ const reader = response.body.getReader();
660
+ const chunks = [];
661
+ let total = 0;
662
+ try {
663
+ while (true) {
664
+ const { done, value } = await reader.read();
665
+ if (done) {
666
+ break;
667
+ }
668
+ const chunk = Buffer.from(value);
669
+ total += chunk.length;
670
+ if (total > MAX_JSON_BYTES) {
671
+ await reader.cancel();
672
+ return null;
673
+ }
674
+ chunks.push(chunk);
675
+ }
676
+ } finally {
677
+ reader.releaseLock();
678
+ }
679
+ return Buffer.concat(chunks, total).toString("utf8");
680
+ }
681
+
682
+ async function fetchLatestVersion(packageName, runtime = {}) {
683
+ const fetchImpl = runtime.fetchImpl ?? globalThis.fetch;
684
+ if (typeof fetchImpl !== "function") {
685
+ return null;
686
+ }
687
+ const timeoutMs = runtime.fetchTimeout ?? FETCH_TIMEOUT;
688
+ const controller = new AbortController();
689
+ let timeout;
690
+ const request = Promise.resolve()
691
+ .then(async () => {
692
+ const response = await fetchImpl(
693
+ `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`,
694
+ {
695
+ headers: { accept: "application/json" },
696
+ redirect: "follow",
697
+ signal: controller.signal,
698
+ },
699
+ );
700
+ if (!response?.ok) {
701
+ return null;
702
+ }
703
+ const body = await readLimitedBody(response);
704
+ if (body === null) {
705
+ return null;
706
+ }
707
+ const version = JSON.parse(body).version;
708
+ return parseSemver(version) ? version : null;
709
+ })
710
+ .catch(() => null);
711
+ const timedOut = new Promise((resolve) => {
712
+ timeout = setTimeout(() => {
713
+ controller.abort();
714
+ resolve(null);
715
+ }, timeoutMs);
716
+ });
717
+ try {
718
+ return await Promise.race([request, timedOut]);
719
+ } finally {
720
+ clearTimeout(timeout);
721
+ }
722
+ }
723
+
724
+ async function runUpdateCheck(packageName, runtime = {}) {
725
+ try {
726
+ if (!isValidPackageName(packageName)) {
727
+ return false;
728
+ }
729
+ const paths = resolvePaths(packageName, runtime);
730
+ if (!paths) {
731
+ return false;
732
+ }
733
+ const latestVersion = await fetchLatestVersion(packageName, runtime);
734
+ if (!latestVersion) {
735
+ return false;
736
+ }
737
+ const now = runtime.now ?? Date.now;
738
+ const currentTime = now();
739
+ return Boolean(
740
+ updateStateWithLock(
741
+ paths,
742
+ currentTime,
743
+ (state) =>
744
+ runtime.expectedAttempt !== undefined &&
745
+ state.lastAttempt !== runtime.expectedAttempt
746
+ ? null
747
+ : {
748
+ ...state,
749
+ lastSuccess: currentTime,
750
+ latestVersion,
751
+ },
752
+ runtime.lockStaleAfter,
753
+ ),
754
+ );
755
+ } catch {
756
+ return false;
757
+ }
758
+ }
759
+
760
+ function isWorkerInvocation(argv = process.argv) {
761
+ return (
762
+ argv[1] &&
763
+ path.resolve(argv[1]) === path.resolve(MODULE_FILENAME) &&
764
+ argv[2] === WORKER_FLAG
765
+ );
766
+ }
767
+
768
+ if (isWorkerInvocation()) {
769
+ const attemptToken = Number(process.argv[4]);
770
+ await runUpdateCheck(process.argv[3], {
771
+ expectedAttempt: Number.isSafeInteger(attemptToken)
772
+ ? attemptToken
773
+ : undefined,
774
+ });
775
+ }
776
+
777
+ export {
778
+ compareSemver,
779
+ fetchLatestVersion,
780
+ formatCliUpdateNotification,
781
+ isCheckDue,
782
+ isCi,
783
+ isDisabled,
784
+ isNotificationDue,
785
+ isPackageManagerInvocation,
786
+ notifyOfCliUpdate,
787
+ parseSemver,
788
+ readLegacyConfig,
789
+ readState,
790
+ resolveCacheRoot,
791
+ resolvePaths,
792
+ runUpdateCheck,
793
+ };