@kb-labs/release-manager-core 2.119.0-canary.e120bc0ee → 2.119.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.
package/dist/index.d.ts CHANGED
@@ -124,6 +124,16 @@ interface CustomCheckConfig {
124
124
  * If omitted, check runs in each package directory (original behaviour).
125
125
  */
126
126
  runIn?: 'repoRoot' | 'scopePath' | 'perPackage';
127
+ /**
128
+ * Package name patterns (same matcher as PackagesFilter.exclude) skipped
129
+ * by this check only — unlike `packages.exclude`, the package still gets
130
+ * discovered, packed, versioned, and published normally by the rest of
131
+ * the release. Use this for a check whose assumption doesn't hold for a
132
+ * specific package (e.g. `pack-install`'s bare top-level import against a
133
+ * config-only or test-harness-only package), not as a substitute for
134
+ * excluding the package from the release itself.
135
+ */
136
+ skipPackages?: string[];
127
137
  }
128
138
  interface ReleaseResult {
129
139
  ok: boolean;
@@ -664,6 +674,60 @@ declare function resolveFlowFromTag(config: ReleaseConfig, tag: string): {
664
674
  channel: ReleaseChannel;
665
675
  } | null;
666
676
 
677
+ /**
678
+ * Release status — a read-only "what is actually true right now" view for a
679
+ * flow, cross-checking the surfaces that the 2026-08-13 release-system audit
680
+ * found drifting apart independently: the latest stable git tag, and the
681
+ * real npm dist-tags packages currently resolve to.
682
+ *
683
+ * This does not replace the receipt/state-machine model from the breaking
684
+ * control-plane cutover plan — it is the cheap, non-breaking piece of that
685
+ * diagnosis that can ship today: make "a tag/npm entry exists" and "this was
686
+ * actually verified and promoted" visibly different without any new storage.
687
+ */
688
+
689
+ interface NpmDistTagInfo {
690
+ name: string;
691
+ stableVersion: string | null;
692
+ canaryVersion: string | null;
693
+ error?: string;
694
+ }
695
+ interface StableTagInfo {
696
+ tag: string | null;
697
+ version: string | null;
698
+ commit: string | null;
699
+ committedAt: string | null;
700
+ }
701
+ interface FlowReleaseStatus {
702
+ flow: string;
703
+ packages: string[];
704
+ git: StableTagInfo;
705
+ npm: {
706
+ registry: string;
707
+ stableDistTag: string;
708
+ canaryDistTag: string;
709
+ perPackage: NpmDistTagInfo[];
710
+ /** Agreed version across all sampled packages, or null if they disagree or none resolved. */
711
+ stableVersion: string | null;
712
+ canaryVersion: string | null;
713
+ stableDrift: boolean;
714
+ canaryDrift: boolean;
715
+ };
716
+ verdict: {
717
+ ok: boolean;
718
+ warnings: string[];
719
+ };
720
+ }
721
+ interface ComputeFlowReleaseStatusOptions {
722
+ cwd: string;
723
+ config: ReleaseConfig;
724
+ flow: string;
725
+ shell: ShellAPI;
726
+ /** How many of the flow's packages to sample against npm. Lockstep flows only need enough to detect drift. Default 3. */
727
+ maxPackagesToCheck?: number;
728
+ }
729
+ declare function computeFlowReleaseStatus(opts: ComputeFlowReleaseStatusOptions): Promise<FlowReleaseStatus>;
730
+
667
731
  /**
668
732
  * Unified release pipeline — single orchestrator for CLI and REST.
669
733
  *
@@ -733,8 +797,11 @@ interface CheckRunnerOptions {
733
797
  * the tarball and runs a real `npm install` of it into a throwaway consumer
734
798
  * per package. At higher concurrency those installs contend for CPU/disk/npm
735
799
  * registry and start blowing their own per-check timeout under load.
800
+ *
801
+ * Overridable via KB_RELEASE_CHECKS_CONCURRENCY for profiling/tuning without
802
+ * a source edit + rebuild; the default (2) is unchanged when unset.
736
803
  */
737
- declare const CHECKS_CONCURRENCY = 2;
804
+ declare const CHECKS_CONCURRENCY: number;
738
805
  /**
739
806
  * Run all configured checks against packages.
740
807
  * Handles: parser evaluation, script path resolution, perPackage/scopePath/repoRoot routing.
@@ -820,6 +887,13 @@ interface VerifyAgainstRegistryOptions {
820
887
  * Default: 0 (single attempt, matches pre-existing Verdaccio behavior).
821
888
  */
822
889
  retries?: number;
890
+ /**
891
+ * Total time to wait for npm metadata and tarballs to propagate after a
892
+ * publish. When provided this supersedes the legacy fixed retry count, so
893
+ * an idempotent re-run resumes verification of already-published tarballs
894
+ * instead of requiring a new publish or a workflow-owned sleep.
895
+ */
896
+ visibilityDeadlineMs?: number;
823
897
  retryDelaysMs?: readonly number[];
824
898
  logger?: Pick<PluginLogger, 'info' | 'warn'>;
825
899
  }
@@ -853,8 +927,15 @@ interface CleanInstallResult {
853
927
  * Install `tarballPath` into a throwaway consumer project (outside the
854
928
  * monorepo workspace, so pnpm's workspace resolution can't mask a problem)
855
929
  * and confirm `packageName` can actually be imported afterward.
930
+ *
931
+ * `registry`, when set, overrides where the consumer resolves ALL
932
+ * dependencies from (not just `packageName` itself) — used by the
933
+ * `pack-install` release gate to point at a local staging registry that
934
+ * carries the release plan's not-yet-published internal sibling versions.
935
+ * Third-party dependencies still resolve correctly through that registry's
936
+ * uplink to the real npm registry.
856
937
  */
857
- declare function verifyCleanInstall(tarballPath: string, packageName: string, additionalTarballs?: string[], packageManager?: 'pnpm' | 'npm'): Promise<CleanInstallResult>;
938
+ declare function verifyCleanInstall(tarballPath: string, packageName: string, additionalTarballs?: string[], packageManager?: 'pnpm' | 'npm', registry?: string): Promise<CleanInstallResult>;
858
939
 
859
940
  /**
860
941
  * Scope utilities — resolve scope name to filesystem path.
@@ -880,4 +961,4 @@ declare function verifyCleanInstall(tarballPath: string, packageName: string, ad
880
961
  */
881
962
  declare function resolveScopePath(repoRoot: string, scope: string): Promise<string>;
882
963
 
883
- export { type AuditSummary, type BuildConfig, type BuildResult, CHECKS_CONCURRENCY, type ChangelogGenerator, type CheckId, type CheckResult, type CheckResultDetails, type CleanInstallResult, type CustomCheckConfig, DEFAULT_ROOT_CHANGELOG_PATH, DEFAULT_TAG_PATTERN, type FlowConfig, type PackagePublisher, type PackageVersion, type PackagesFilter, type PipelineOptions, type PipelineResult, type PlannerOptions, type PluginLogger, type PublishResult, type PublishablePackage, type ReleaseChannel, type ReleaseConfig, type ReleaseContext, type ReleasePlan, type ReleaseReport, type ReleaseResult, type ReleaseShell, type ReleaseStage, type RollbackSnapshot, type StrategyOptions, type VerifyResult, type VersionBump, type VersionStrategy, applyCanarySuffix, applyVersionStrategy, buildPackages, buildReleaseTag, commitAndTagRelease, copyChangelogToPackages, createExecaShellAdapter, discoverCurrentPackages, findForbiddenDependencyProtocols, isBuildCommand, isVersionPublished, matchesPackagePattern, mergeConfigWithFlow, mergeRootChangelog, planRelease, renderJson, renderMarkdown, renderText, resolveFlowFromTag, resolvePublishRegistry, resolvePublishTag, resolveRootChangelogRelPath, resolveScopePath, restoreSnapshot, runReleaseChecks, runReleasePipeline, runSafeBuild, saveSnapshot, updatePackageVersion, updatePackageVersions, verifyAgainstRegistry, verifyCleanInstall, verifyExtractedTarball, verifyPackage, verifyPackages };
964
+ export { type AuditSummary, type BuildConfig, type BuildResult, CHECKS_CONCURRENCY, type ChangelogGenerator, type CheckId, type CheckResult, type CheckResultDetails, type CleanInstallResult, type ComputeFlowReleaseStatusOptions, type CustomCheckConfig, DEFAULT_ROOT_CHANGELOG_PATH, DEFAULT_TAG_PATTERN, type FlowConfig, type FlowReleaseStatus, type NpmDistTagInfo, type PackagePublisher, type PackageVersion, type PackagesFilter, type PipelineOptions, type PipelineResult, type PlannerOptions, type PluginLogger, type PublishResult, type PublishablePackage, type ReleaseChannel, type ReleaseConfig, type ReleaseContext, type ReleasePlan, type ReleaseReport, type ReleaseResult, type ReleaseShell, type ReleaseStage, type RollbackSnapshot, type StableTagInfo, type StrategyOptions, type VerifyResult, type VersionBump, type VersionStrategy, applyCanarySuffix, applyVersionStrategy, buildPackages, buildReleaseTag, commitAndTagRelease, computeFlowReleaseStatus, copyChangelogToPackages, createExecaShellAdapter, discoverCurrentPackages, findForbiddenDependencyProtocols, isBuildCommand, isVersionPublished, matchesPackagePattern, mergeConfigWithFlow, mergeRootChangelog, planRelease, renderJson, renderMarkdown, renderText, resolveFlowFromTag, resolvePublishRegistry, resolvePublishTag, resolveRootChangelogRelPath, resolveScopePath, restoreSnapshot, runReleaseChecks, runReleasePipeline, runSafeBuild, saveSnapshot, updatePackageVersion, updatePackageVersions, verifyAgainstRegistry, verifyCleanInstall, verifyExtractedTarball, verifyPackage, verifyPackages };
package/dist/index.js CHANGED
@@ -1050,6 +1050,133 @@ function resolvePublishRegistry(config, channel) {
1050
1050
  }
1051
1051
  return config.registry ?? DEFAULT_NPM_REGISTRY;
1052
1052
  }
1053
+
1054
+ // src/status.ts
1055
+ var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/;
1056
+ function compareSemver(a, b) {
1057
+ const ma = a.match(SEMVER_RE);
1058
+ const mb = b.match(SEMVER_RE);
1059
+ if (!ma || !mb) {
1060
+ return 0;
1061
+ }
1062
+ for (let i = 1; i <= 3; i++) {
1063
+ const diff = Number(ma[i]) - Number(mb[i]);
1064
+ if (diff !== 0) {
1065
+ return diff;
1066
+ }
1067
+ }
1068
+ return 0;
1069
+ }
1070
+ function escapeRegex2(str) {
1071
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1072
+ }
1073
+ async function fetchDistTags(name, registry) {
1074
+ try {
1075
+ const encoded = name.startsWith("@") ? `@${encodeURIComponent(name.slice(1))}` : name;
1076
+ const url = `${registry.replace(/\/$/, "")}/${encoded}`;
1077
+ const res = await fetch(url, {
1078
+ headers: { Accept: "application/vnd.npm.install-v1+json" },
1079
+ signal: AbortSignal.timeout(8e3)
1080
+ });
1081
+ if (!res.ok) {
1082
+ return { error: `HTTP ${res.status}` };
1083
+ }
1084
+ const body = await res.json();
1085
+ return body["dist-tags"] ?? {};
1086
+ } catch (err) {
1087
+ return { error: err instanceof Error ? err.message : String(err) };
1088
+ }
1089
+ }
1090
+ async function findLatestStableTag(shell, cwd, flowName, tagPattern) {
1091
+ const empty = { tag: null, version: null, commit: null, committedAt: null };
1092
+ const prefix = tagPattern.split("{version}")[0]?.replace("{flow}", flowName) ?? `${flowName}-v`;
1093
+ const listRes = await shell.exec("git", ["tag", "-l", `${prefix}*`], { cwd });
1094
+ const tags = listRes.stdout.split("\n").map((t) => t.trim()).filter(Boolean);
1095
+ if (tags.length === 0) {
1096
+ return empty;
1097
+ }
1098
+ const versionRe = new RegExp(`^${escapeRegex2(prefix)}(\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?)$`);
1099
+ const parsed = tags.map((t) => ({ tag: t, version: t.match(versionRe)?.[1] })).filter((t) => Boolean(t.version));
1100
+ if (parsed.length === 0) {
1101
+ return empty;
1102
+ }
1103
+ parsed.sort((a, b) => compareSemver(a.version, b.version));
1104
+ const latest = parsed[parsed.length - 1];
1105
+ const [shaRes, dateRes] = await Promise.all([
1106
+ shell.exec("git", ["rev-list", "-n", "1", latest.tag], { cwd }),
1107
+ shell.exec("git", ["log", "-1", "--format=%cI", latest.tag], { cwd })
1108
+ ]);
1109
+ return {
1110
+ tag: latest.tag,
1111
+ version: latest.version,
1112
+ commit: shaRes.stdout.trim() || null,
1113
+ committedAt: dateRes.stdout.trim() || null
1114
+ };
1115
+ }
1116
+ async function computeFlowReleaseStatus(opts) {
1117
+ const { cwd, config, flow, shell, maxPackagesToCheck = 3 } = opts;
1118
+ const flowConfig = mergeConfigWithFlow(config, flow);
1119
+ const tagPattern = config.flows?.[flow]?.tagPattern ?? DEFAULT_TAG_PATTERN;
1120
+ const packages = await discoverCurrentPackages(cwd, void 0, flowConfig);
1121
+ const registry = resolvePublishRegistry(config, "canary");
1122
+ const stableDistTag = resolvePublishTag(config, "stable");
1123
+ const canaryDistTag = resolvePublishTag(config, "canary");
1124
+ const sampled = packages.slice(0, maxPackagesToCheck);
1125
+ const perPackage = [];
1126
+ for (const pkg of sampled) {
1127
+ const result = await fetchDistTags(pkg.name, registry);
1128
+ if ("error" in result) {
1129
+ perPackage.push({ name: pkg.name, stableVersion: null, canaryVersion: null, error: result.error });
1130
+ } else {
1131
+ perPackage.push({
1132
+ name: pkg.name,
1133
+ stableVersion: result[stableDistTag] ?? null,
1134
+ canaryVersion: result[canaryDistTag] ?? null
1135
+ });
1136
+ }
1137
+ }
1138
+ const stableVersions = new Set(perPackage.map((p) => p.stableVersion).filter((v) => Boolean(v)));
1139
+ const canaryVersions = new Set(perPackage.map((p) => p.canaryVersion).filter((v) => Boolean(v)));
1140
+ const git = await findLatestStableTag(shell, cwd, flow, tagPattern);
1141
+ const warnings = [];
1142
+ if (stableVersions.size > 1) {
1143
+ warnings.push(`Packages disagree on npm "${stableDistTag}" version: ${[...stableVersions].join(", ")}`);
1144
+ }
1145
+ if (canaryVersions.size > 1) {
1146
+ warnings.push(`Packages disagree on npm "${canaryDistTag}" version: ${[...canaryVersions].join(", ")}`);
1147
+ }
1148
+ const stableVersion = stableVersions.size === 1 ? [...stableVersions][0] : null;
1149
+ const canaryVersion = canaryVersions.size === 1 ? [...canaryVersions][0] : null;
1150
+ if (git.version && stableVersion && git.version !== stableVersion) {
1151
+ warnings.push(
1152
+ `Latest git tag ${git.tag} (${git.version}) does not match npm "${stableDistTag}" (${stableVersion})`
1153
+ );
1154
+ }
1155
+ if (stableVersion && canaryVersion && compareSemver(canaryVersion, stableVersion) > 0) {
1156
+ warnings.push(
1157
+ `npm "${canaryDistTag}" (${canaryVersion}) is ahead of "${stableDistTag}" (${stableVersion}) \u2014 treat as an unverified candidate, not a release, until it has a green delivery + smoke run and has been explicitly promoted`
1158
+ );
1159
+ }
1160
+ return {
1161
+ flow,
1162
+ packages: packages.map((p) => p.name),
1163
+ git,
1164
+ npm: {
1165
+ registry,
1166
+ stableDistTag,
1167
+ canaryDistTag,
1168
+ perPackage,
1169
+ stableVersion,
1170
+ canaryVersion,
1171
+ stableDrift: stableVersions.size > 1,
1172
+ canaryDrift: canaryVersions.size > 1
1173
+ },
1174
+ verdict: {
1175
+ ok: warnings.length === 0,
1176
+ warnings
1177
+ }
1178
+ };
1179
+ }
1053
1180
  function buildIntraDeps(packages, nameSet) {
1054
1181
  const deps = /* @__PURE__ */ new Map();
1055
1182
  for (const pkg of packages) {
@@ -1198,7 +1325,7 @@ async function executeCommand(shell, command, args, cwd, timeoutMs, env) {
1198
1325
  exitCode
1199
1326
  };
1200
1327
  }
1201
- var CHECKS_CONCURRENCY = 2;
1328
+ var CHECKS_CONCURRENCY = Number(process.env.KB_RELEASE_CHECKS_CONCURRENCY) || 2;
1202
1329
  async function runReleaseChecks(checks, options) {
1203
1330
  const results = [];
1204
1331
  for (const check of checks) {
@@ -1221,33 +1348,72 @@ async function runSingleCheck(check, options) {
1221
1348
  } else {
1222
1349
  pathsToRun = options.packagePaths.length > 0 ? options.packagePaths : [options.repoRoot];
1223
1350
  }
1351
+ if (runIn === "perPackage" && check.skipPackages?.length) {
1352
+ const skipPatterns = check.skipPackages;
1353
+ pathsToRun = pathsToRun.filter((pkgPath) => {
1354
+ const name = readPackageName(pkgPath);
1355
+ const skip = name != null && matchesPackagePattern(name, pkgPath, skipPatterns);
1356
+ if (skip) {
1357
+ options.logger?.info?.(`Check ${check.id}: skipping ${name} (matches skipPackages)`);
1358
+ }
1359
+ return !skip;
1360
+ });
1361
+ }
1224
1362
  const resolvedArgs = (check.args ?? []).map(
1225
1363
  (arg) => arg.match(/\.(sh|js|ts|mjs|cjs)$/) ? join(options.repoRoot, arg) : arg
1226
1364
  );
1227
1365
  const timeoutMs = check.timeoutMs ?? 12e4;
1228
- async function runForPath(pkgPath) {
1366
+ async function runForPath(pkgPath, attempt = 1) {
1229
1367
  const startedAt = Date.now();
1230
- const result = await options.shell.exec(check.command, resolvedArgs, { cwd: pkgPath, timeout: timeoutMs });
1231
- const ok = evaluateParser(check, result.stdout, result.stderr, result.code);
1232
- return {
1233
- path: pkgPath,
1234
- ok,
1235
- durationMs: Date.now() - startedAt,
1236
- details: {
1237
- packagePath: pkgPath,
1238
- stdout: result.stdout || void 0,
1239
- stderr: result.stderr || void 0,
1240
- exitCode: result.code,
1241
- error: !ok ? `exit code ${result.code}` : void 0
1368
+ try {
1369
+ const result = await options.shell.exec(check.command, resolvedArgs, { cwd: pkgPath, timeout: timeoutMs });
1370
+ const ok = evaluateParser(check, result.stdout, result.stderr, result.code);
1371
+ return {
1372
+ path: pkgPath,
1373
+ ok,
1374
+ durationMs: Date.now() - startedAt,
1375
+ details: {
1376
+ packagePath: pkgPath,
1377
+ stdout: result.stdout || void 0,
1378
+ stderr: result.stderr || void 0,
1379
+ exitCode: result.code,
1380
+ error: !ok ? `exit code ${result.code}` : void 0
1381
+ }
1382
+ };
1383
+ } catch (error) {
1384
+ if (attempt === 1 && isTimeoutError(error)) {
1385
+ const partial2 = partialResultOf(error);
1386
+ options.logger?.warn?.(
1387
+ `Check ${check.id}: ${pkgPath} timed out, retrying once` + (partial2?.stdout || partial2?.stderr ? ` (captured ${partial2.stdout?.length ?? 0}B stdout / ${partial2.stderr?.length ?? 0}B stderr before kill)` : "")
1388
+ );
1389
+ return runForPath(pkgPath, attempt + 1);
1242
1390
  }
1243
- };
1391
+ const partial = partialResultOf(error);
1392
+ return {
1393
+ path: pkgPath,
1394
+ ok: false,
1395
+ durationMs: Date.now() - startedAt,
1396
+ details: {
1397
+ packagePath: pkgPath,
1398
+ stdout: partial?.stdout || void 0,
1399
+ stderr: partial?.stderr || void 0,
1400
+ exitCode: partial?.exitCode,
1401
+ error: error instanceof Error ? error.message : String(error)
1402
+ }
1403
+ };
1404
+ }
1244
1405
  }
1245
1406
  let pkgResults;
1246
- if (runIn === "perPackage" && pathsToRun.length > 1) {
1407
+ if (pathsToRun.length === 0) {
1408
+ pkgResults = [];
1409
+ } else if (runIn === "perPackage" && pathsToRun.length > 1) {
1247
1410
  pkgResults = [];
1248
1411
  for (let i = 0; i < pathsToRun.length; i += CHECKS_CONCURRENCY) {
1249
1412
  const batch = pathsToRun.slice(i, i + CHECKS_CONCURRENCY);
1250
1413
  pkgResults.push(...await Promise.all(batch.map(runForPath)));
1414
+ options.logger?.info?.(
1415
+ `Check ${check.id}: ${Math.min(i + CHECKS_CONCURRENCY, pathsToRun.length)}/${pathsToRun.length} packages checked`
1416
+ );
1251
1417
  }
1252
1418
  } else {
1253
1419
  pkgResults = [await runForPath(pathsToRun[0])];
@@ -1266,6 +1432,36 @@ async function runSingleCheck(check, options) {
1266
1432
  packages: perPackage && perPackage.length > 0 ? perPackage : void 0
1267
1433
  };
1268
1434
  }
1435
+ function isTimeoutError(error) {
1436
+ return typeof error === "object" && error !== null && error.code === "PROCESS_TIMEOUT";
1437
+ }
1438
+ function partialResultOf(error) {
1439
+ if (typeof error !== "object" || error === null) {
1440
+ return void 0;
1441
+ }
1442
+ const details = error.details;
1443
+ if (typeof details !== "object" || details === null) {
1444
+ return void 0;
1445
+ }
1446
+ const result = details.result;
1447
+ if (typeof result !== "object" || result === null) {
1448
+ return void 0;
1449
+ }
1450
+ const r = result;
1451
+ return {
1452
+ stdout: typeof r.stdout === "string" ? r.stdout : void 0,
1453
+ stderr: typeof r.stderr === "string" ? r.stderr : void 0,
1454
+ exitCode: typeof r.code === "number" ? r.code : void 0
1455
+ };
1456
+ }
1457
+ function readPackageName(pkgPath) {
1458
+ try {
1459
+ const pkg = JSON.parse(readFileSync(join(pkgPath, "package.json"), "utf8"));
1460
+ return typeof pkg.name === "string" ? pkg.name : void 0;
1461
+ } catch {
1462
+ return void 0;
1463
+ }
1464
+ }
1269
1465
  function evaluateParser(check, stdout, stderr, exitCode) {
1270
1466
  const parser = check.parser ?? "exitcode";
1271
1467
  if (parser === "exitcode") {
@@ -1486,31 +1682,35 @@ async function verifyAgainstRegistry(packages, options) {
1486
1682
  const { registry, timeout = DEFAULT_TIMEOUT_MS, retries = 0, retryDelaysMs = DEFAULT_POLL_RETRY_DELAYS_MS, logger } = options;
1487
1683
  const results = [];
1488
1684
  for (const pkg of packages) {
1489
- results.push(await verifyOneAgainstRegistry(pkg, registry, timeout, retries, retryDelaysMs, logger));
1685
+ results.push(await verifyOneAgainstRegistry(pkg, registry, timeout, retries, retryDelaysMs, options.visibilityDeadlineMs, logger));
1490
1686
  }
1491
1687
  return results;
1492
1688
  }
1493
- async function waitUntilPublished(pkg, registry, retries, retryDelaysMs, logger) {
1494
- for (let attempt = 0; attempt <= retries; attempt++) {
1689
+ async function waitUntilPublished(pkg, registry, retries, retryDelaysMs, visibilityDeadlineMs, logger) {
1690
+ const deadline = visibilityDeadlineMs === void 0 ? void 0 : Date.now() + visibilityDeadlineMs;
1691
+ for (let attempt = 0; deadline === void 0 ? attempt <= retries : Date.now() <= deadline; attempt++) {
1495
1692
  if (await isVersionPublished(pkg.name, pkg.version, registry)) {
1496
1693
  return true;
1497
1694
  }
1498
- if (attempt < retries) {
1695
+ if (deadline === void 0 ? attempt < retries : Date.now() < deadline) {
1499
1696
  const delay = retryDelaysMs[Math.min(attempt, retryDelaysMs.length - 1)];
1697
+ const remaining = deadline === void 0 ? void 0 : deadline - Date.now();
1698
+ const waitMs = remaining === void 0 ? delay : Math.min(delay, Math.max(0, remaining));
1500
1699
  logger?.warn?.(
1501
- `${pkg.name}@${pkg.version} not yet visible on ${registry} (attempt ${attempt + 1}/${retries + 1}), retrying in ${(delay / 1e3).toFixed(0)}s \u2014 likely registry propagation lag`
1700
+ `${pkg.name}@${pkg.version} not yet visible on ${registry} (attempt ${attempt + 1}${deadline === void 0 ? `/${retries + 1}` : ""}), retrying in ${(waitMs / 1e3).toFixed(0)}s${remaining === void 0 ? "" : `; ${(Math.max(0, remaining) / 1e3).toFixed(0)}s remain`} \u2014 likely registry propagation lag`
1502
1701
  );
1503
1702
  await new Promise((r) => {
1504
- setTimeout(r, delay);
1703
+ setTimeout(r, waitMs);
1505
1704
  });
1506
1705
  }
1507
1706
  }
1508
1707
  return false;
1509
1708
  }
1510
- async function verifyOneAgainstRegistry(pkg, registry, timeout, retries, retryDelaysMs, logger) {
1511
- const published = await waitUntilPublished(pkg, registry, retries, retryDelaysMs, logger);
1709
+ async function verifyOneAgainstRegistry(pkg, registry, timeout, retries, retryDelaysMs, visibilityDeadlineMs, logger) {
1710
+ const published = await waitUntilPublished(pkg, registry, retries, retryDelaysMs, visibilityDeadlineMs, logger);
1512
1711
  if (!published) {
1513
- return { name: pkg.name, success: false, issues: [`${pkg.name}@${pkg.version} was not found on ${registry} after publish (waited through ${retries} retr${retries === 1 ? "y" : "ies"})`] };
1712
+ const waited = visibilityDeadlineMs === void 0 ? `waited through ${retries} retr${retries === 1 ? "y" : "ies"}` : `visibility deadline of ${(visibilityDeadlineMs / 1e3).toFixed(0)}s elapsed`;
1713
+ return { name: pkg.name, success: false, issues: [`${pkg.name}@${pkg.version} was not found on ${registry} after publish (${waited})`] };
1514
1714
  }
1515
1715
  logger?.info?.(`${pkg.name}@${pkg.version} confirmed on ${registry}`);
1516
1716
  const tmpDir = join(tmpdir(), `kb-verdaccio-verify-${randomBytes(6).toString("hex")}`);
@@ -1995,7 +2195,7 @@ function buildReport(stage, plan, repoRoot, dryRun, startTime, result) {
1995
2195
  result: { ...result, timingMs: result.timingMs ?? Date.now() - startTime }
1996
2196
  };
1997
2197
  }
1998
- async function verifyCleanInstall(tarballPath, packageName, additionalTarballs = [], packageManager = "npm") {
2198
+ async function verifyCleanInstall(tarballPath, packageName, additionalTarballs = [], packageManager = "npm", registry) {
1999
2199
  const consumerDir = mkdtempSync(join(tmpdir(), "kb-clean-install-"));
2000
2200
  try {
2001
2201
  if (packageManager === "pnpm") {
@@ -2017,6 +2217,10 @@ async function verifyCleanInstall(tarballPath, packageName, additionalTarballs =
2017
2217
  join(consumerDir, "package.json"),
2018
2218
  JSON.stringify({ name: "kb-release-consumer", private: true, dependencies, pnpm: { overrides } }, null, 2) + "\n"
2019
2219
  );
2220
+ if (registry) {
2221
+ writeFileSync(join(consumerDir, ".npmrc"), `registry=${registry}
2222
+ `);
2223
+ }
2020
2224
  const install = spawnSync(
2021
2225
  "pnpm",
2022
2226
  ["install", "--ignore-scripts", "--no-lockfile", "--config.auto-install-peers=true"],
@@ -2028,7 +2232,7 @@ async function verifyCleanInstall(tarballPath, packageName, additionalTarballs =
2028
2232
  } else {
2029
2233
  writeFileSync(join(consumerDir, "package.json"), JSON.stringify({ name: "kb-release-consumer", private: true }) + "\n");
2030
2234
  const { Arborist } = await import('@npmcli/arborist');
2031
- const arb = new Arborist({ path: consumerDir, ignoreScripts: true });
2235
+ const arb = new Arborist({ path: consumerDir, ignoreScripts: true, audit: false, ...registry ? { registry } : {} });
2032
2236
  try {
2033
2237
  await arb.reify({ add: [tarballPath, ...additionalTarballs], save: false });
2034
2238
  } catch (err) {
@@ -2097,6 +2301,6 @@ async function resolveScopePath(repoRoot, scope) {
2097
2301
  return join(repoRoot, scope);
2098
2302
  }
2099
2303
 
2100
- export { CHECKS_CONCURRENCY, DEFAULT_ROOT_CHANGELOG_PATH, DEFAULT_TAG_PATTERN, applyCanarySuffix, applyVersionStrategy, buildPackages, buildReleaseTag, commitAndTagRelease, copyChangelogToPackages, createExecaShellAdapter, discoverCurrentPackages, findForbiddenDependencyProtocols, isBuildCommand, isVersionPublished, matchesPackagePattern, mergeConfigWithFlow, mergeRootChangelog, planRelease, renderJson, renderMarkdown, renderText, resolveFlowFromTag, resolvePublishRegistry, resolvePublishTag, resolveRootChangelogRelPath, resolveScopePath, restoreSnapshot, runReleaseChecks, runReleasePipeline, runSafeBuild, saveSnapshot, updatePackageVersion, updatePackageVersions, verifyAgainstRegistry, verifyCleanInstall, verifyExtractedTarball, verifyPackage, verifyPackages };
2304
+ export { CHECKS_CONCURRENCY, DEFAULT_ROOT_CHANGELOG_PATH, DEFAULT_TAG_PATTERN, applyCanarySuffix, applyVersionStrategy, buildPackages, buildReleaseTag, commitAndTagRelease, computeFlowReleaseStatus, copyChangelogToPackages, createExecaShellAdapter, discoverCurrentPackages, findForbiddenDependencyProtocols, isBuildCommand, isVersionPublished, matchesPackagePattern, mergeConfigWithFlow, mergeRootChangelog, planRelease, renderJson, renderMarkdown, renderText, resolveFlowFromTag, resolvePublishRegistry, resolvePublishTag, resolveRootChangelogRelPath, resolveScopePath, restoreSnapshot, runReleaseChecks, runReleasePipeline, runSafeBuild, saveSnapshot, updatePackageVersion, updatePackageVersions, verifyAgainstRegistry, verifyCleanInstall, verifyExtractedTarball, verifyPackage, verifyPackages };
2101
2305
  //# sourceMappingURL=index.js.map
2102
2306
  //# sourceMappingURL=index.js.map