limina 0.0.2 → 0.0.4

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/cli.js CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import "./chunks/dep-lkQg1P9Q.js";
3
- import { d as normalizeSlashes, f as normalizeWorkspacePath, h as toRelativePath, i as loadConfig, l as isPathInsideDirectory, m as toPosixPath, n as getActiveCheckerExtensions, p as toAbsolutePath, r as getActiveCheckers, u as normalizeAbsolutePath } from "./chunks/dep-jgc7X0zw.js";
4
- import { A as shouldResolveThroughGraph$1, B as createExtensionPattern, C as formatArtifactDependencyPolicy, D as isRelativeSpecifier, E as isDtsProjectConfig, F as collectCheckerEntryProjectRoutes, G as isDtsConfigPath, H as formatReferences, I as collectGraphProjectRoute, J as parseProjectFileNamesForExtensions, K as isOrdinaryTypecheckConfigPath, L as collectGraphProjectRouteFromRoot, M as collectWorkspacePackages, N as findPackageForSpecifier, O as parseProject$1, P as getPackageRootSpecifier, R as collectGraphProjectRoutes, S as findTargetProject, T as inferPackageProject$1, U as getDtsCompanionConfigPath, V as createFormatHost, W as getRawReferencePaths, X as resolveProjectConfigPath, Y as readJsonConfig, Z as resolveReferencePath, _ as normalizeGraphRules, a as runSourceCheck, b as findImporterForFile$1, c as GraphLogger, d as ProofLogger, f as clearCliScreen, g as getDeniedRefRule, h as getDeniedDepRuleForSpecifier, i as runCheckerTypecheck, j as collectImporters, k as resolveInternalImport$1, l as PackageLogger, m as getDeniedDepRuleForPackage, n as createLiminaFlowReporter, o as runInit, p as formatErrorMessage$1, q as parseProjectFileNames, r as runCheckerBuild, s as CliLogger, u as PathsLogger, v as collectImportsFromFile$1, w as getTypecheckConfigPath, x as findPackageForFile, y as createFileOwnerLookup$1 } from "./chunks/dep-uPXyoC0V.js";
5
- import { builtinModules } from "node:module";
3
+ import { c as getCheckerAdapter, d as normalizeAbsolutePath, f as normalizeSlashes, g as toRelativePath, h as toPosixPath, i as loadConfig, l as normalizeExtensions, m as toAbsolutePath, n as getActiveCheckerExtensions, p as normalizeWorkspacePath, r as getActiveCheckers, u as isPathInsideDirectory } from "./chunks/dep-DzYrmtQJ.js";
4
+ import { $ as resolveReferencePath, A as resolveInternalImport$1, B as collectGraphProjectRouteFromRoot, C as findTargetProject, D as isDtsProjectConfig, E as inferPackageProject$1, F as getPackageRootSpecifier, G as createFormatHost, H as collectSourceGraphProjectExtensions, I as getPublishDependencySections, J as getRawReferencePaths, K as formatReferences, L as isWorkspaceDependencySpecifier, M as collectImporters, N as collectWorkspacePackages, O as isRelativeSpecifier, P as findPackageForSpecifier, Q as resolveProjectConfigPath, R as collectCheckerEntryProjectRoutes, S as findPackageForFile, T as getTypecheckConfigPath, U as createExtensionPattern, V as collectGraphProjectRoutes, W as createExtraFileExtensions, X as parseProjectFileNamesForExtensions, Y as isOrdinaryTypecheckConfigPath, Z as readJsonConfig, _ as getDeniedRefRule, a as runSourceCheck, b as createFileOwnerLookup$1, c as GraphLogger, d as ProofLogger, f as ReleaseLogger, g as getDeniedDepRuleForSpecifier, h as getDeniedDepRuleForPackage, i as runCheckerTypecheck, j as shouldResolveThroughGraph$1, k as parseProject$1, l as PackageLogger, m as formatErrorMessage$1, n as createLiminaFlowReporter, o as runInit, p as clearCliScreen, q as getDtsCompanionConfigPath, r as runCheckerBuild, s as CliLogger, u as PathsLogger, v as normalizeGraphRules, w as formatArtifactDependencyPolicy, x as findImporterForFile$1, y as collectImportsFromFile$1, z as collectGraphProjectRoute } from "./chunks/dep-ZRIm_-Zk.js";
5
+ import { builtinModules, createRequire } from "node:module";
6
6
  import { cac } from "cac";
7
7
  import { createElapsedTimer } from "logaria/helper";
8
8
  import { existsSync, readFileSync } from "node:fs";
9
9
  import path from "node:path";
10
10
  import ts from "typescript";
11
- import { spawn, spawnSync } from "node:child_process";
11
+ import { execFile, spawn, spawnSync } from "node:child_process";
12
12
  import { glob } from "tinyglobby";
13
13
  import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
14
14
  import { checkPackage, createPackageFromTarballData } from "@arethetypeswrong/core";
15
- import { pack } from "@publint/pack";
15
+ import { pack, unpack } from "@publint/pack";
16
16
  import { init, parse } from "es-module-lexer";
17
17
  import { tmpdir } from "node:os";
18
18
  import { publint } from "publint";
@@ -112,7 +112,7 @@ function addTypecheckParityProblems(config, dtsProject, problems) {
112
112
  ].join("\n"));
113
113
  return;
114
114
  }
115
- const typecheckProject = parseProject$1(config, typecheckConfigPath);
115
+ const typecheckProject = parseProject$1(config, typecheckConfigPath, dtsProject.extensions);
116
116
  for (const optionName of comparableTypecheckOptions) {
117
117
  const buildValue = dtsProject.options[optionName];
118
118
  const typecheckValue = typecheckProject.options[optionName];
@@ -224,9 +224,9 @@ function addWorkspaceReferenceDependencyProblems(config, project, projectsByPath
224
224
  }
225
225
  }
226
226
  async function runGraphCheckInternal(config, options = {}) {
227
- const graphRoute = collectGraphProjectRoute(config);
228
- const projectPaths = graphRoute.projectPaths;
229
- const projects = projectPaths.map((projectPath) => parseProject$1(config, projectPath));
227
+ const graphRoute = collectSourceGraphProjectExtensions(config);
228
+ const projectPaths = [...graphRoute.projectExtensionsByPath.keys()].sort();
229
+ const projects = projectPaths.map((projectPath) => parseProject$1(config, projectPath, graphRoute.projectExtensionsByPath.get(projectPath)));
230
230
  const projectsByPath = new Map(projects.map((project) => [project.configPath, project]));
231
231
  const fileOwnerLookup = createFileOwnerLookup$1(projects);
232
232
  const packages = await collectWorkspacePackages(config);
@@ -255,7 +255,7 @@ async function runGraphCheckInternal(config, options = {}) {
255
255
  rules: graphRules
256
256
  });
257
257
  addWorkspaceReferenceDependencyProblems(config, project, projectsByPath, packages, importers, problems);
258
- for (const filePath of project.fileNames) for (const importRecord of collectImportsFromFile$1(filePath)) {
258
+ for (const filePath of project.fileNames) for (const importRecord of collectImportsFromFile$1(filePath, config.rootDir)) {
259
259
  const rawDeniedDepRule = getDeniedDepRuleForSpecifier(graphRules, project.label, importRecord.specifier);
260
260
  if (rawDeniedDepRule) {
261
261
  addDeniedDepImportProblem({
@@ -267,7 +267,7 @@ async function runGraphCheckInternal(config, options = {}) {
267
267
  });
268
268
  continue;
269
269
  }
270
- const resolvedFilePath = resolveInternalImport$1(importRecord.specifier, filePath, project.options);
270
+ const resolvedFilePath = resolveInternalImport$1(importRecord.specifier, filePath, project.options, project.extensions);
271
271
  const targetPackage = findPackageForSpecifier(importRecord.specifier, packages);
272
272
  const importer = findImporterForFile$1(importRecord.filePath, importers);
273
273
  if (!resolvedFilePath) {
@@ -436,8 +436,7 @@ const ATTW_PROFILE_IGNORED_RESOLUTIONS = {
436
436
  "esm-only": ["node16-cjs"]
437
437
  };
438
438
  const nodeBuiltinSpecifiers = new Set(builtinModules.flatMap((specifier) => specifier.startsWith("node:") ? [specifier, specifier.slice(5)] : [specifier, `node:${specifier}`]));
439
- const REQUIRED_PUBLIC_PACKAGE_FILES = ["README.md", "LICENSE.md"];
440
- function isRecord(value) {
439
+ function isRecord$1(value) {
441
440
  return typeof value === "object" && value !== null && !Array.isArray(value);
442
441
  }
443
442
  function isPackageCheckTool(value) {
@@ -452,7 +451,7 @@ function toArrayBuffer(buffer) {
452
451
  function collectSelfSpecifierMatchers(packageName, exportsField) {
453
452
  const exact = new Set([packageName]);
454
453
  const prefixes = [];
455
- if (!isRecord(exportsField)) return {
454
+ if (!isRecord$1(exportsField)) return {
456
455
  exact,
457
456
  prefixes
458
457
  };
@@ -535,8 +534,8 @@ function formatAttwProblem(problem) {
535
534
  default: return `Unknown ATTW problem: ${JSON.stringify(problem)}`;
536
535
  }
537
536
  }
538
- function normalizeTargetChecks(target) {
539
- const checks = target.checks ?? DEFAULT_PACKAGE_CHECKS;
537
+ function normalizeEntryChecks(entry) {
538
+ const checks = entry.checks ?? DEFAULT_PACKAGE_CHECKS;
540
539
  const normalizedChecks = [];
541
540
  for (const check of checks) {
542
541
  if (!isPackageCheckTool(check)) throw new Error(`Invalid package check "${check}". Expected one of: publint, attw, boundary.`);
@@ -544,8 +543,8 @@ function normalizeTargetChecks(target) {
544
543
  }
545
544
  return normalizedChecks;
546
545
  }
547
- function selectTargetChecks(target, requestedTool) {
548
- const configuredChecks = normalizeTargetChecks(target);
546
+ function selectEntryChecks(entry, requestedTool) {
547
+ const configuredChecks = normalizeEntryChecks(entry);
549
548
  if (!requestedTool || requestedTool === "all") return configuredChecks;
550
549
  return configuredChecks.includes(requestedTool) ? [requestedTool] : [];
551
550
  }
@@ -573,61 +572,84 @@ function readCwdPackageName(cwd, rootDir) {
573
572
  throw new Error(`Unable to read package name from ${packageJsonPath}: ${formatErrorMessage$1(error)}`);
574
573
  }
575
574
  }
576
- function formatConfiguredTargetNames(targets) {
577
- const names = targets.map((target) => target.name).filter((name) => Boolean(name));
575
+ function formatConfiguredPackageEntryNames(entries) {
576
+ const names = entries.map((entry) => entry.name).filter(Boolean);
578
577
  return names.length > 0 ? names.join(", ") : "(none)";
579
578
  }
580
- function resolveTargetOutDir(options) {
581
- const outDir = options.target.outDir;
582
- if (typeof outDir !== "string" || outDir.trim().length === 0) throw new Error(`Invalid package check target at packageChecks.targets[${options.targetIndex}].outDir. Expected a non-empty string.`);
579
+ function getConfiguredPackageEntries(config) {
580
+ return config.package?.entries ?? [];
581
+ }
582
+ function normalizePackageNameFilters(packageNames) {
583
+ const normalizedNames = [];
584
+ for (const packageName of packageNames ?? []) {
585
+ const normalizedName = packageName.trim();
586
+ if (normalizedName && !normalizedNames.includes(normalizedName)) normalizedNames.push(normalizedName);
587
+ }
588
+ return normalizedNames;
589
+ }
590
+ function resolvePackageEntryOutDir(options) {
591
+ const outDir = options.entry.outDir;
592
+ if (typeof outDir !== "string" || outDir.trim().length === 0) throw new Error(`Invalid package entry at package.entries[${options.entryIndex}].outDir. Expected a non-empty string.`);
583
593
  return path.resolve(options.config.rootDir, outDir);
584
594
  }
585
- function getTargetLabel(config, target, outDir) {
586
- return target.name ?? toRelativePath(config.rootDir, outDir);
595
+ function getPackageEntryLabel(options) {
596
+ const name = options.entry.name;
597
+ if (typeof name !== "string" || name.trim().length === 0) throw new Error(`Invalid package entry at package.entries[${options.entryIndex}].name. Expected a non-empty string.`);
598
+ return name.trim();
587
599
  }
588
- function createTargetPlan(options) {
589
- const outDir = resolveTargetOutDir({
600
+ function createEntryPlan(options) {
601
+ const outDir = resolvePackageEntryOutDir({
590
602
  config: options.config,
591
- target: options.target,
592
- targetIndex: options.targetIndex
603
+ entry: options.entry,
604
+ entryIndex: options.entryIndex
593
605
  });
594
606
  return {
595
- checks: selectTargetChecks(options.target, options.requestedTool),
596
- label: getTargetLabel(options.config, options.target, outDir),
607
+ checks: selectEntryChecks(options.entry, options.requestedTool),
608
+ entryIndex: options.entryIndex,
609
+ label: getPackageEntryLabel({
610
+ entry: options.entry,
611
+ entryIndex: options.entryIndex
612
+ }),
597
613
  outDir,
598
- rawTarget: options.target
614
+ rawEntry: options.entry
599
615
  };
600
616
  }
601
- function createPackageCheckPlan(options) {
602
- const targets = options.config.packageChecks?.targets ?? [];
603
- if (targets.length === 0) throw new Error("No package check targets are configured.");
604
- let selectedTargets;
617
+ function createPackageEntrySelectionPlan(options) {
618
+ const entries = getConfiguredPackageEntries(options.config);
619
+ if (entries.length === 0) throw new Error("No package entries are configured.");
620
+ let selectedEntries;
605
621
  let selectionReason;
606
- if (options.targetName) {
607
- selectedTargets = targets.filter((target) => target.name === options.targetName);
608
- if (selectedTargets.length === 0) throw new Error([`No package check target named "${options.targetName}" is configured.`, `Configured target names: ${formatConfiguredTargetNames(targets)}.`].join(" "));
609
- selectionReason = `--package "${options.targetName}" matched configured target name.`;
622
+ const packageNames = normalizePackageNameFilters(options.packageNames);
623
+ if (packageNames.length > 0) {
624
+ selectedEntries = packageNames.map((packageName) => {
625
+ const entry = entries.find((candidate) => candidate.name === packageName);
626
+ if (!entry) throw new Error([`No package entry named "${packageName}" is configured.`, `Configured package entries: ${formatConfiguredPackageEntryNames(entries)}.`].join(" "));
627
+ return entry;
628
+ });
629
+ selectionReason = `--package matched configured package entry name(s): ${packageNames.join(", ")}.`;
610
630
  } else {
611
631
  const cwdPackageName = readCwdPackageName(options.cwd, options.config.rootDir);
612
632
  if (cwdPackageName) {
613
- selectedTargets = targets.filter((target) => target.name === cwdPackageName);
614
- if (selectedTargets.length > 0) selectionReason = `nearest package.json name "${cwdPackageName}" matched configured target name.`;
633
+ selectedEntries = entries.filter((entry) => entry.name === cwdPackageName);
634
+ if (selectedEntries.length > 0) selectionReason = `nearest package.json name "${cwdPackageName}" matched configured package entry name.`;
635
+ else if (options.strictCwd) throw new Error([`Nearest package.json name "${cwdPackageName}" does not match a configured package entry.`, `Configured package entries: ${formatConfiguredPackageEntryNames(entries)}.`].join(" "));
615
636
  else {
616
- selectedTargets = targets;
617
- selectionReason = `nearest package.json name "${cwdPackageName}" did not match configured target names; running all configured targets.`;
637
+ selectedEntries = entries;
638
+ selectionReason = `nearest package.json name "${cwdPackageName}" did not match configured package entries; running all configured entries.`;
618
639
  }
619
- } else {
620
- selectedTargets = targets;
621
- selectionReason = "No package name was found from cwd up to the workspace root; running all configured targets.";
640
+ } else if (options.strictCwd) throw new Error(["No package name was found from cwd up to the workspace root.", "Run from a configured package directory or pass --package <name>."].join(" "));
641
+ else {
642
+ selectedEntries = entries;
643
+ selectionReason = "No package name was found from cwd up to the workspace root; running all configured entries.";
622
644
  }
623
645
  }
624
646
  return {
625
647
  selectionReason,
626
- targets: selectedTargets.map((target) => createTargetPlan({
648
+ entries: selectedEntries.map((entry) => createEntryPlan({
627
649
  config: options.config,
628
- requestedTool: options.tool,
629
- target,
630
- targetIndex: targets.indexOf(target)
650
+ entry,
651
+ entryIndex: entries.indexOf(entry),
652
+ requestedTool: options.tool
631
653
  }))
632
654
  };
633
655
  }
@@ -637,11 +659,11 @@ function logPackageCheckPlan(options) {
637
659
  ` config: ${toRelativePath(options.config.rootDir, options.config.configPath)}`,
638
660
  ` cwd: ${toRelativePath(options.config.rootDir, options.cwd)}`,
639
661
  ` selection: ${options.plan.selectionReason}`,
640
- " targets:",
641
- ...options.plan.targets.map((target) => [
642
- ` - ${target.label}`,
643
- ` outDir: ${toRelativePath(options.config.rootDir, target.outDir)}`,
644
- ` checks: ${target.checks.length > 0 ? target.checks.join(", ") : "(none)"}`
662
+ " entries:",
663
+ ...options.plan.entries.map((entry) => [
664
+ ` - ${entry.label}`,
665
+ ` outDir: ${toRelativePath(options.config.rootDir, entry.outDir)}`,
666
+ ` checks: ${entry.checks.length > 0 ? entry.checks.join(", ") : "(none)"}`
645
667
  ].join("\n"))
646
668
  ].join("\n"));
647
669
  }
@@ -665,16 +687,6 @@ async function readDistPackageJson(options) {
665
687
  if (!existsSync(options.packageJsonPath)) throw new Error(`outDir package.json not found${options.label ? ` for ${options.label}` : ""} at ${options.config ? toRelativePath(options.config.rootDir, options.packageJsonPath) : options.packageJsonPath}. Run the package build first.`);
666
688
  return JSON.parse(await readFile(options.packageJsonPath, "utf8"));
667
689
  }
668
- async function assertPublicPackageMetadata(options) {
669
- if ((await readDistPackageJson({
670
- config: options.config,
671
- label: options.label,
672
- packageJsonPath: options.packageJsonPath
673
- })).private === true) return;
674
- const missingFiles = REQUIRED_PUBLIC_PACKAGE_FILES.filter((fileName) => !existsSync(path.join(options.outDir, fileName)));
675
- if (missingFiles.length === 0) return;
676
- throw new Error(`publishable package output for ${options.label} at ${toRelativePath(options.config.rootDir, options.outDir)} is missing required file(s): ${missingFiles.join(", ")}. Add them to the built output or set "private": true in the output package.json.`);
677
- }
678
690
  async function runPublintCheck(options) {
679
691
  const task = options.flow?.start(`publint: ${options.label}`, { depth: options.flowDepth ?? 0 });
680
692
  PackageLogger.info(`publint started: ${options.label}`);
@@ -745,20 +757,19 @@ async function runBoundaryCheck(target, label, options = {}) {
745
757
  task?.fail(`package boundary found ${violations.length} issue(s): ${label}`);
746
758
  return false;
747
759
  }
748
- async function runPackageCheckTarget(options) {
749
- const target = {
750
- ...options.rawTarget,
760
+ async function runPackageCheckEntry(options) {
761
+ const entry = {
762
+ ...options.rawEntry,
751
763
  outDir: options.outDir
752
764
  };
753
765
  const label = options.label;
754
- const outputPackageJsonPath = path.join(target.outDir, "package.json");
755
- const task = options.flow?.start(`package target: ${label}`, { depth: options.flowDepth ?? 0 });
766
+ const outputPackageJsonPath = path.join(entry.outDir, "package.json");
767
+ const task = options.flow?.start(`package entry: ${label}`, { depth: options.flowDepth ?? 0 });
756
768
  let packedDist;
757
769
  try {
758
- await assertPublicPackageMetadata({
770
+ await readDistPackageJson({
759
771
  config: options.config,
760
772
  label,
761
- outDir: target.outDir,
762
773
  packageJsonPath: outputPackageJsonPath
763
774
  });
764
775
  if (options.checks.includes("publint") || options.checks.includes("attw")) {
@@ -766,7 +777,7 @@ async function runPackageCheckTarget(options) {
766
777
  PackageLogger.info(`package tarball packing started: ${label}`);
767
778
  const packElapsed = createElapsedTimer();
768
779
  try {
769
- packedDist = await packOutputTarball(target.outDir);
780
+ packedDist = await packOutputTarball(entry.outDir);
770
781
  } catch (error) {
771
782
  PackageLogger.error(`package tarball failed: ${label}: ${formatErrorMessage$1(error)}`, packElapsed());
772
783
  packTask?.fail(`package tarball failed: ${label}`, { error });
@@ -780,19 +791,19 @@ async function runPackageCheckTarget(options) {
780
791
  flow: options.flow,
781
792
  flowDepth: (options.flowDepth ?? 0) + 1,
782
793
  label,
783
- strict: target.publint?.strict ?? true,
794
+ strict: entry.publint?.strict ?? true,
784
795
  tarball: packedDist.tarball
785
796
  }) && passed;
786
797
  if (options.checks.includes("attw")) passed = await runAttwCheck({
787
798
  flow: options.flow,
788
799
  flowDepth: (options.flowDepth ?? 0) + 1,
789
800
  label,
790
- profile: options.attwProfile ?? target.attw?.profile ?? "esm-only",
801
+ profile: options.attwProfile ?? entry.attw?.profile ?? "esm-only",
791
802
  tarball: packedDist.tarball
792
803
  }) && passed;
793
804
  if (options.checks.includes("boundary")) passed = await runBoundaryCheck({
794
- ...target.boundary,
795
- outDir: target.outDir
805
+ ...entry.boundary,
806
+ outDir: entry.outDir
796
807
  }, label, {
797
808
  flow: options.flow,
798
809
  flowDepth: (options.flowDepth ?? 0) + 1
@@ -859,10 +870,11 @@ async function runPackageCheck(options) {
859
870
  const task = options.flow?.start("package check", { depth: options.flowDepth ?? 0 });
860
871
  try {
861
872
  PackageLogger.info("package check started");
862
- const plan = createPackageCheckPlan({
873
+ const plan = createPackageEntrySelectionPlan({
863
874
  config: options.config,
864
875
  cwd,
865
- targetName: options.targetName,
876
+ packageNames: options.packageNames,
877
+ strictCwd: false,
866
878
  tool: options.tool
867
879
  });
868
880
  logPackageCheckPlan({
@@ -870,18 +882,18 @@ async function runPackageCheck(options) {
870
882
  cwd,
871
883
  plan
872
884
  });
873
- const runnableTargets = plan.targets.filter((target) => target.checks.length > 0);
874
- if (runnableTargets.length === 0) throw new Error(options.tool && options.tool !== "all" ? `No package check targets have "${options.tool}" enabled.` : "No package checks are enabled.");
885
+ const runnableEntries = plan.entries.filter((entry) => entry.checks.length > 0);
886
+ if (runnableEntries.length === 0) throw new Error(options.tool && options.tool !== "all" ? `No package entries have "${options.tool}" enabled.` : "No package checks are enabled.");
875
887
  let passed = true;
876
- for (const target of runnableTargets) passed = await runPackageCheckTarget({
888
+ for (const entry of runnableEntries) passed = await runPackageCheckEntry({
877
889
  attwProfile: options.attwProfile,
878
- checks: target.checks,
890
+ checks: entry.checks,
879
891
  config: options.config,
880
892
  flow: options.flow,
881
893
  flowDepth: (options.flowDepth ?? 0) + 1,
882
- label: target.label,
883
- outDir: target.outDir,
884
- rawTarget: target.rawTarget
894
+ label: entry.label,
895
+ outDir: entry.outDir,
896
+ rawEntry: entry.rawEntry
885
897
  }) && passed;
886
898
  if (passed) {
887
899
  if (!options.flow?.interactive) PackageLogger.success("package check finished", elapsed());
@@ -1444,6 +1456,14 @@ const ignoredSemanticCompilerOptions = new Set([
1444
1456
  "sourceRoot",
1445
1457
  "tsBuildInfoFile"
1446
1458
  ]);
1459
+ const typeScriptCheckerExtensions = getCheckerAdapter("tsc")?.defaultExtensions ?? [];
1460
+ function getFirstClassCoverageExtensions(extensions) {
1461
+ return normalizeExtensions([...typeScriptCheckerExtensions, ...extensions]);
1462
+ }
1463
+ function getCheckerCoverageExtensions(checker) {
1464
+ if (!getCheckerAdapter(checker.preset)?.sourceGraph) return checker.extensions;
1465
+ return getFirstClassCoverageExtensions(checker.extensions);
1466
+ }
1447
1467
  async function collectTsconfigPaths(config, pattern) {
1448
1468
  return (await glob(pattern, {
1449
1469
  cwd: config.rootDir,
@@ -1605,24 +1625,19 @@ function addCoverage(coverageByFile, filePath, source) {
1605
1625
  }
1606
1626
  function collectCoverage(options) {
1607
1627
  const coverageByFile = /* @__PURE__ */ new Map();
1608
- const proofFilePattern = createExtensionPattern(getActiveCheckerExtensions(options.config));
1609
- const typeScriptChecker = getActiveCheckers(options.config).find((checker) => checker.preset === "tsc");
1610
- for (const graphProjectPath of options.graphProjectPaths) for (const filePath of parseProjectFileNames(options.config, graphProjectPath, proofFilePattern)) {
1628
+ for (const route of options.graphRoutes) for (const graphProjectPath of route.projectPaths) for (const filePath of parseProjectFileNamesForExtensions(options.config, graphProjectPath, getFirstClassCoverageExtensions(route.extensions))) {
1611
1629
  if (!options.sourceFiles.has(filePath)) continue;
1612
1630
  addCoverage(coverageByFile, filePath, {
1613
1631
  label: toRelativePath(options.config.rootDir, graphProjectPath),
1614
1632
  type: "graph"
1615
1633
  });
1616
1634
  }
1617
- for (const checkerTarget of options.checkerTargets) {
1618
- const checkerExtensions = [...typeScriptChecker?.extensions ?? [], ...checkerTarget.checker.extensions];
1619
- for (const configPath of checkerTarget.coverageConfigPaths) for (const filePath of parseProjectFileNamesForExtensions(options.config, configPath, checkerExtensions)) {
1620
- if (!options.sourceFiles.has(filePath)) continue;
1621
- addCoverage(coverageByFile, filePath, {
1622
- label: `${toRelativePath(options.config.rootDir, configPath)} via ${checkerTarget.label}`,
1623
- type: "checker"
1624
- });
1625
- }
1635
+ for (const checkerTarget of options.checkerTargets) for (const configPath of checkerTarget.coverageConfigPaths) for (const filePath of parseProjectFileNamesForExtensions(options.config, configPath, getCheckerCoverageExtensions(checkerTarget.checker))) {
1636
+ if (!options.sourceFiles.has(filePath)) continue;
1637
+ addCoverage(coverageByFile, filePath, {
1638
+ label: `${toRelativePath(options.config.rootDir, configPath)} via ${checkerTarget.label}`,
1639
+ type: "checker"
1640
+ });
1626
1641
  }
1627
1642
  if (options.includeAllowlist !== false) for (const entry of options.allowlistEntries) {
1628
1643
  if (!options.sourceFiles.has(entry.filePath)) continue;
@@ -1633,15 +1648,19 @@ function collectCoverage(options) {
1633
1648
  }
1634
1649
  return coverageByFile;
1635
1650
  }
1636
- function parseConfig(config, configPath) {
1651
+ function collectProjectExtensionsByPath(routes) {
1652
+ const projectExtensionsByPath = /* @__PURE__ */ new Map();
1653
+ for (const route of routes) for (const projectPath of route.projectPaths) {
1654
+ const extensions = new Set([...projectExtensionsByPath.get(projectPath) ?? [], ...route.extensions]);
1655
+ projectExtensionsByPath.set(projectPath, [...extensions].sort());
1656
+ }
1657
+ return projectExtensionsByPath;
1658
+ }
1659
+ function parseConfig(config, configPath, extensions = []) {
1637
1660
  const diagnostics = [];
1638
- const parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, {
1639
- ...ts.sys,
1640
- onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
1641
- diagnostics.push(diagnostic);
1642
- }
1643
- });
1644
- if (!parsed) throw new Error(ts.formatDiagnosticsWithColorAndContext(diagnostics, createFormatHost(config.rootDir)));
1661
+ const configObject = readJsonConfig(config, configPath);
1662
+ const parsed = ts.parseJsonConfigFileContent(configObject, ts.sys, path.dirname(configPath), {}, configPath, void 0, createExtraFileExtensions(extensions));
1663
+ if (diagnostics.length > 0) throw new Error(ts.formatDiagnosticsWithColorAndContext(diagnostics, createFormatHost(config.rootDir)));
1645
1664
  if (parsed.errors.length > 0) throw new Error(ts.formatDiagnosticsWithColorAndContext(parsed.errors, createFormatHost(config.rootDir)));
1646
1665
  return {
1647
1666
  fileNames: parsed.fileNames.map(normalizeAbsolutePath).sort(),
@@ -1704,8 +1723,9 @@ function addDtsConfigProblems(options) {
1704
1723
  ].join("\n"));
1705
1724
  continue;
1706
1725
  }
1707
- const dtsConfig = parseConfig(options.config, configPath);
1708
- const localConfig = parseConfig(options.config, localConfigPath);
1726
+ const extensions = options.projectExtensionsByPath.get(configPath) ?? [];
1727
+ const dtsConfig = parseConfig(options.config, configPath, extensions);
1728
+ const localConfig = parseConfig(options.config, localConfigPath, extensions);
1709
1729
  if (dtsConfig.options.composite !== true) options.problems.push([
1710
1730
  "DTS config is not valid for tsc -b:",
1711
1731
  ` config: ${toRelativePath(options.config.rootDir, configPath)}`,
@@ -1846,12 +1866,11 @@ function addDefaultTsconfigEnvironmentProblems(options) {
1846
1866
  ].join("\n"));
1847
1867
  }
1848
1868
  }
1849
- function collectConfigFileOwners(config, configPaths, sourceFiles) {
1869
+ function collectConfigFileOwners(config, graphRoutes, sourceFiles) {
1850
1870
  const ownersByFile = /* @__PURE__ */ new Map();
1851
- const proofFilePattern = createExtensionPattern(getActiveCheckerExtensions(config));
1852
- for (const configPath of configPaths) {
1871
+ for (const route of graphRoutes) for (const configPath of route.projectPaths) {
1853
1872
  if (!existsSync(configPath)) continue;
1854
- for (const filePath of parseProjectFileNames(config, configPath, proofFilePattern)) {
1873
+ for (const filePath of parseProjectFileNamesForExtensions(config, configPath, route.extensions)) {
1855
1874
  if (!sourceFiles.has(filePath)) continue;
1856
1875
  const owners = ownersByFile.get(filePath) ?? [];
1857
1876
  owners.push(configPath);
@@ -1873,19 +1892,6 @@ function addDuplicateGraphCoverageProblems(options) {
1873
1892
  ].join("\n"));
1874
1893
  }
1875
1894
  }
1876
- function addDuplicateGraphOwnerProblems(options) {
1877
- for (const [configPath, ownerCheckerNames] of options.graphOwnersByConfigPath.entries()) {
1878
- const uniqueOwnerCheckerNames = [...new Set(ownerCheckerNames)].sort();
1879
- if (uniqueOwnerCheckerNames.length <= 1) continue;
1880
- options.problems.push([
1881
- "Duplicate checker graph declaration owner:",
1882
- ` config: ${toRelativePath(options.config.rootDir, configPath)}`,
1883
- " owned by:",
1884
- ...uniqueOwnerCheckerNames.map((checkerName) => ` - ${checkerName}`),
1885
- " reason: each tsconfig*.dts.json must be reached by exactly one graph-capable checker entry."
1886
- ].join("\n"));
1887
- }
1888
- }
1889
1895
  function addAllowlistProblems(options) {
1890
1896
  for (const entry of options.allowlistEntries) {
1891
1897
  if (!existsSync(entry.filePath)) {
@@ -1913,34 +1919,25 @@ function addUncoveredSourceProblems(options) {
1913
1919
  " reason: every file in config.source must be covered by a checker entry or an explicit allowlist entry."
1914
1920
  ].filter(Boolean).join("\n"));
1915
1921
  }
1916
- function addGraphOwner(ownersByConfigPath, configPath, checkerName) {
1917
- const owners = ownersByConfigPath.get(configPath) ?? [];
1918
- owners.push(checkerName);
1919
- ownersByConfigPath.set(configPath, owners);
1920
- }
1921
1922
  async function runProofCheckInternal(config, options = {}) {
1922
1923
  const problems = [];
1923
1924
  const graphRouteCollection = collectGraphProjectRoutes(config);
1924
1925
  const entryRouteCollection = collectCheckerEntryProjectRoutes(config);
1925
- const graphProjectPaths = [...new Set(graphRouteCollection.routes.flatMap((route) => route.projectPaths))].sort();
1926
1926
  const entryProjectPaths = [...new Set(entryRouteCollection.routes.flatMap((route) => route.projectPaths))].sort();
1927
1927
  const entryProjectPathSet = new Set(entryProjectPaths);
1928
+ const entryProjectExtensionsByPath = collectProjectExtensionsByPath(entryRouteCollection.routes);
1928
1929
  const dtsConfigPaths = await collectDtsConfigPaths(config);
1929
1930
  const buildGraphConfigPaths = await collectBuildGraphConfigPaths(config);
1930
1931
  const defaultTsconfigPaths = await collectDefaultTsconfigPaths(config);
1931
1932
  const ordinaryTypecheckConfigPaths = await collectOrdinaryTypecheckConfigPaths(config);
1932
- const graphOwnersByConfigPath = /* @__PURE__ */ new Map();
1933
1933
  problems.push(...graphRouteCollection.problems);
1934
1934
  problems.push(...entryRouteCollection.problems);
1935
- for (const route of graphRouteCollection.routes) for (const projectPath of route.projectPaths) {
1936
- if (!isDtsConfigPath(projectPath)) continue;
1937
- addGraphOwner(graphOwnersByConfigPath, projectPath, route.checkerName);
1938
- }
1939
1935
  addDtsConfigProblems({
1940
1936
  config,
1941
1937
  dtsConfigPaths,
1942
1938
  graphProjectPaths: entryProjectPathSet,
1943
- problems
1939
+ problems,
1940
+ projectExtensionsByPath: entryProjectExtensionsByPath
1944
1941
  });
1945
1942
  addBuildGraphConfigProblems({
1946
1943
  buildGraphConfigPaths,
@@ -1957,11 +1954,6 @@ async function runProofCheckInternal(config, options = {}) {
1957
1954
  ordinaryConfigPaths: ordinaryTypecheckConfigPaths,
1958
1955
  problems
1959
1956
  });
1960
- addDuplicateGraphOwnerProblems({
1961
- config,
1962
- graphOwnersByConfigPath,
1963
- problems
1964
- });
1965
1957
  if (problems.length > 0) {
1966
1958
  ProofLogger.error(problems.join("\n\n"));
1967
1959
  return false;
@@ -1981,7 +1973,7 @@ async function runProofCheckInternal(config, options = {}) {
1981
1973
  allowlistEntries,
1982
1974
  checkerTargets,
1983
1975
  config,
1984
- graphProjectPaths,
1976
+ graphRoutes: graphRouteCollection.routes,
1985
1977
  includeAllowlist: false,
1986
1978
  sourceFiles
1987
1979
  });
@@ -1989,12 +1981,12 @@ async function runProofCheckInternal(config, options = {}) {
1989
1981
  allowlistEntries,
1990
1982
  checkerTargets,
1991
1983
  config,
1992
- graphProjectPaths,
1984
+ graphRoutes: graphRouteCollection.routes,
1993
1985
  sourceFiles
1994
1986
  });
1995
1987
  addDuplicateGraphCoverageProblems({
1996
1988
  config,
1997
- ownersByFile: collectConfigFileOwners(config, graphProjectPaths, sourceFiles),
1989
+ ownersByFile: collectConfigFileOwners(config, graphRouteCollection.routes, sourceFiles),
1998
1990
  problems
1999
1991
  });
2000
1992
  addAllowlistProblems({
@@ -2047,6 +2039,545 @@ async function runProofCheck(config, options = {}) {
2047
2039
  }
2048
2040
  }
2049
2041
 
2042
+ //#endregion
2043
+ //#region src/package-release-consistency.ts
2044
+ var PackageReleaseConsistencyError = class extends Error {
2045
+ name = "PackageReleaseConsistencyError";
2046
+ };
2047
+ const semver = createRequire(import.meta.url)("semver");
2048
+ const REQUIRED_RELEASE_FILES = ["README.md", "LICENSE.md"];
2049
+ const SOURCE_MAPPING_URL_PATTERN = /(?:\/\/\s*#\s*sourceMappingURL\s*=|\/\*\s*#\s*sourceMappingURL\s*=)/u;
2050
+ function isRecord(value) {
2051
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2052
+ }
2053
+ function isLinkDependencySpecifier(specifier) {
2054
+ return specifier.startsWith("link:");
2055
+ }
2056
+ function createReleaseConsistencyState() {
2057
+ return {
2058
+ directWorkspaceDependencies: [],
2059
+ edges: /* @__PURE__ */ new Map(),
2060
+ missingWorkspaceDependencies: [],
2061
+ packedManifestProblems: [],
2062
+ privateWorkspaceDependencies: [],
2063
+ releaseHygieneProblems: [],
2064
+ registryMetadataCache: /* @__PURE__ */ new Map(),
2065
+ registryProblems: [],
2066
+ sourceLinkDependencies: [],
2067
+ unpublishedPackageNames: /* @__PURE__ */ new Set(),
2068
+ visitedPackages: /* @__PURE__ */ new Set()
2069
+ };
2070
+ }
2071
+ function collectPublishDependencyEntries(manifest) {
2072
+ const entries = [];
2073
+ for (const { dependencies, name } of getPublishDependencySections(manifest)) for (const [dependencyName, specifier] of Object.entries(dependencies)) entries.push({
2074
+ dependencyName,
2075
+ sectionName: name,
2076
+ specifier
2077
+ });
2078
+ return entries;
2079
+ }
2080
+ function addEdge(edges, importerName, dependencyName) {
2081
+ const dependencies = edges.get(importerName) ?? /* @__PURE__ */ new Set();
2082
+ dependencies.add(dependencyName);
2083
+ edges.set(importerName, dependencies);
2084
+ }
2085
+ function formatDependencyLocation(problem) {
2086
+ const dependency = problem.dependencyName ? ` -> ${problem.dependencyName}` : "";
2087
+ const section = problem.sectionName ? ` [${problem.sectionName}]` : "";
2088
+ const specifier = problem.specifier ? ` (${problem.specifier})` : "";
2089
+ return `${problem.importerName}${dependency}${section}${specifier}`;
2090
+ }
2091
+ function formatProblemLines(title, problems) {
2092
+ if (problems.length === 0) return [];
2093
+ return [
2094
+ "",
2095
+ title,
2096
+ ...problems.map((problem) => ` - ${formatDependencyLocation(problem)}: ${problem.message}`)
2097
+ ];
2098
+ }
2099
+ function getPackedDependencySpecifier(manifest, dependencyName) {
2100
+ for (const { dependencies } of getPublishDependencySections(manifest)) {
2101
+ const specifier = dependencies[dependencyName];
2102
+ if (specifier) return specifier;
2103
+ }
2104
+ }
2105
+ function getNpmPackageMetadataUrl(packageName) {
2106
+ return `https://registry.npmjs.org/${encodeURIComponent(packageName)}`;
2107
+ }
2108
+ async function fetchRegistryPackageMetadata(packageName, state) {
2109
+ if (state.registryMetadataCache.has(packageName)) return state.registryMetadataCache.get(packageName) ?? null;
2110
+ const response = await fetch(getNpmPackageMetadataUrl(packageName), { headers: { accept: "application/json" } });
2111
+ if (!response.ok) {
2112
+ state.registryMetadataCache.set(packageName, null);
2113
+ return null;
2114
+ }
2115
+ const metadata = await response.json();
2116
+ const registryMetadata = isRecord(metadata) ? metadata : null;
2117
+ state.registryMetadataCache.set(packageName, registryMetadata);
2118
+ return registryMetadata;
2119
+ }
2120
+ function findRegistryVersionMetadata(metadata, version) {
2121
+ if (!isRecord(metadata.versions)) return null;
2122
+ const versionMetadata = metadata.versions[version];
2123
+ return isRecord(versionMetadata) ? versionMetadata : null;
2124
+ }
2125
+ function execGitCommand(config, args) {
2126
+ return new Promise((resolve, reject) => {
2127
+ execFile("git", [
2128
+ "-C",
2129
+ config.rootDir,
2130
+ ...args
2131
+ ], {
2132
+ encoding: "utf8",
2133
+ maxBuffer: 10 * 1024 * 1024
2134
+ }, (error, stdout) => {
2135
+ if (error) {
2136
+ reject(error);
2137
+ return;
2138
+ }
2139
+ resolve(stdout);
2140
+ });
2141
+ });
2142
+ }
2143
+ async function hasWorkspacePackageChangesSinceGitHead(options) {
2144
+ const relativeDirectory = toRelativePath(options.config.rootDir, options.workspacePackage.directory);
2145
+ try {
2146
+ await execGitCommand(options.config, [
2147
+ "diff",
2148
+ "--quiet",
2149
+ options.gitHead,
2150
+ "--",
2151
+ relativeDirectory
2152
+ ]);
2153
+ } catch (error) {
2154
+ if (isRecord(error) && error.code === 1) return true;
2155
+ throw error;
2156
+ }
2157
+ return (await execGitCommand(options.config, [
2158
+ "ls-files",
2159
+ "--others",
2160
+ "--exclude-standard",
2161
+ "--",
2162
+ relativeDirectory
2163
+ ])).trim().length > 0;
2164
+ }
2165
+ async function verifyWorkspacePackagePublished(options) {
2166
+ const { state, workspacePackage } = options;
2167
+ const version = workspacePackage.manifest.version;
2168
+ if (!version || !semver.valid(version)) {
2169
+ state.unpublishedPackageNames.add(workspacePackage.name);
2170
+ state.registryProblems.push({
2171
+ importerName: workspacePackage.name,
2172
+ message: ["workspace package must declare a valid semver version", "before another publishable package can depend on it"].join(" "),
2173
+ packageName: workspacePackage.name
2174
+ });
2175
+ return;
2176
+ }
2177
+ let metadata;
2178
+ try {
2179
+ metadata = await fetchRegistryPackageMetadata(workspacePackage.name, state);
2180
+ } catch (error) {
2181
+ state.unpublishedPackageNames.add(workspacePackage.name);
2182
+ state.registryProblems.push({
2183
+ importerName: workspacePackage.name,
2184
+ message: [`unable to read npm registry metadata for ${workspacePackage.name}@${version}:`, formatErrorMessage$1(error)].join(" "),
2185
+ packageName: workspacePackage.name
2186
+ });
2187
+ return;
2188
+ }
2189
+ if (!metadata) {
2190
+ state.unpublishedPackageNames.add(workspacePackage.name);
2191
+ state.registryProblems.push({
2192
+ importerName: workspacePackage.name,
2193
+ message: `${workspacePackage.name}@${version} is not published to the npm registry`,
2194
+ packageName: workspacePackage.name
2195
+ });
2196
+ return;
2197
+ }
2198
+ const versionMetadata = findRegistryVersionMetadata(metadata, version);
2199
+ if (!versionMetadata) {
2200
+ state.unpublishedPackageNames.add(workspacePackage.name);
2201
+ state.registryProblems.push({
2202
+ importerName: workspacePackage.name,
2203
+ message: `${workspacePackage.name}@${version} is not published to the npm registry`,
2204
+ packageName: workspacePackage.name
2205
+ });
2206
+ return;
2207
+ }
2208
+ if (typeof versionMetadata.gitHead !== "string" || versionMetadata.gitHead.trim().length === 0) {
2209
+ state.unpublishedPackageNames.add(workspacePackage.name);
2210
+ state.registryProblems.push({
2211
+ importerName: workspacePackage.name,
2212
+ message: [`${workspacePackage.name}@${version} registry metadata has no gitHead,`, "so limina cannot prove the published source baseline"].join(" "),
2213
+ packageName: workspacePackage.name
2214
+ });
2215
+ return;
2216
+ }
2217
+ let hasChanges;
2218
+ try {
2219
+ hasChanges = await hasWorkspacePackageChangesSinceGitHead({
2220
+ config: options.config,
2221
+ gitHead: versionMetadata.gitHead,
2222
+ workspacePackage
2223
+ });
2224
+ } catch (error) {
2225
+ state.unpublishedPackageNames.add(workspacePackage.name);
2226
+ state.registryProblems.push({
2227
+ importerName: workspacePackage.name,
2228
+ message: [
2229
+ `unable to compare ${workspacePackage.name}@${version}`,
2230
+ `against published gitHead ${versionMetadata.gitHead}:`,
2231
+ formatErrorMessage$1(error)
2232
+ ].join(" "),
2233
+ packageName: workspacePackage.name
2234
+ });
2235
+ return;
2236
+ }
2237
+ if (hasChanges) {
2238
+ state.unpublishedPackageNames.add(workspacePackage.name);
2239
+ state.registryProblems.push({
2240
+ importerName: workspacePackage.name,
2241
+ message: [`${workspacePackage.name}@${version} has workspace changes`, `after the published npm registry gitHead ${versionMetadata.gitHead}`].join(" "),
2242
+ packageName: workspacePackage.name
2243
+ });
2244
+ }
2245
+ }
2246
+ async function visitWorkspacePackageDependencies(options) {
2247
+ const { config, importerName, isRoot, manifest, state, workspacePackagesByName } = options;
2248
+ for (const entry of collectPublishDependencyEntries(manifest)) {
2249
+ if (isLinkDependencySpecifier(entry.specifier)) {
2250
+ state.sourceLinkDependencies.push({
2251
+ dependencyName: entry.dependencyName,
2252
+ importerName,
2253
+ message: "publishable dependency sections must not use link:",
2254
+ sectionName: entry.sectionName,
2255
+ specifier: entry.specifier
2256
+ });
2257
+ continue;
2258
+ }
2259
+ if (!isWorkspaceDependencySpecifier(entry.specifier)) continue;
2260
+ const targetPackage = workspacePackagesByName.get(entry.dependencyName);
2261
+ if (!targetPackage) {
2262
+ state.missingWorkspaceDependencies.push({
2263
+ dependencyName: entry.dependencyName,
2264
+ importerName,
2265
+ message: "workspace: publish dependency does not match a named workspace package",
2266
+ sectionName: entry.sectionName,
2267
+ specifier: entry.specifier
2268
+ });
2269
+ continue;
2270
+ }
2271
+ addEdge(state.edges, importerName, targetPackage.name);
2272
+ if (isRoot) state.directWorkspaceDependencies.push({
2273
+ dependencyName: entry.dependencyName,
2274
+ sectionName: entry.sectionName,
2275
+ targetPackage
2276
+ });
2277
+ if (targetPackage.manifest.private === true) {
2278
+ state.privateWorkspaceDependencies.push({
2279
+ dependencyName: entry.dependencyName,
2280
+ importerName,
2281
+ message: "publishable packages cannot depend on a private workspace package",
2282
+ packageName: targetPackage.name,
2283
+ sectionName: entry.sectionName,
2284
+ specifier: entry.specifier
2285
+ });
2286
+ continue;
2287
+ }
2288
+ if (!state.visitedPackages.has(targetPackage.name)) {
2289
+ state.visitedPackages.add(targetPackage.name);
2290
+ await verifyWorkspacePackagePublished({
2291
+ config,
2292
+ state,
2293
+ workspacePackage: targetPackage
2294
+ });
2295
+ await visitWorkspacePackageDependencies({
2296
+ config,
2297
+ importerName: targetPackage.name,
2298
+ isRoot: false,
2299
+ manifest: targetPackage.manifest,
2300
+ state,
2301
+ workspacePackagesByName
2302
+ });
2303
+ }
2304
+ }
2305
+ }
2306
+ async function unpackPackedPackage(tarball) {
2307
+ return await unpack(tarball);
2308
+ }
2309
+ function getPackedContentFiles(packedPackage) {
2310
+ const rootPrefix = `${packedPackage.rootDir}/`;
2311
+ const files = [];
2312
+ for (const file of packedPackage.files) {
2313
+ if (!file.name.startsWith(rootPrefix)) continue;
2314
+ files.push({
2315
+ data: file.data,
2316
+ relativePath: file.name.slice(rootPrefix.length).replaceAll("\\", "/")
2317
+ });
2318
+ }
2319
+ return files;
2320
+ }
2321
+ function readPackedPackageJson(options) {
2322
+ const packageJsonFile = options.contentFiles.find((file) => file.relativePath === "package.json");
2323
+ if (!packageJsonFile) {
2324
+ options.state.releaseHygieneProblems.push({
2325
+ importerName: options.rootPackageName,
2326
+ message: "tarball does not contain package.json"
2327
+ });
2328
+ return null;
2329
+ }
2330
+ try {
2331
+ return JSON.parse(Buffer.from(packageJsonFile.data).toString("utf8"));
2332
+ } catch (error) {
2333
+ options.state.releaseHygieneProblems.push({
2334
+ importerName: options.rootPackageName,
2335
+ message: `tarball package.json is not valid JSON: ${formatErrorMessage$1(error)}`
2336
+ });
2337
+ return null;
2338
+ }
2339
+ }
2340
+ function isJavaScriptPackageFile(relativePath) {
2341
+ return /\.(?:cjs|mjs|js)$/u.test(relativePath);
2342
+ }
2343
+ function validateReleaseTarballHygiene(options) {
2344
+ const filePaths = new Set(options.contentFiles.map((file) => file.relativePath));
2345
+ const missingReleaseFiles = REQUIRED_RELEASE_FILES.filter((fileName) => !filePaths.has(fileName));
2346
+ if (missingReleaseFiles.length > 0) options.state.releaseHygieneProblems.push({
2347
+ importerName: options.rootPackageName,
2348
+ message: `tarball is missing required file(s): ${missingReleaseFiles.join(", ")}`
2349
+ });
2350
+ for (const file of options.contentFiles) {
2351
+ if (/\.map$/u.test(file.relativePath)) {
2352
+ options.state.releaseHygieneProblems.push({
2353
+ importerName: options.rootPackageName,
2354
+ message: `tarball contains source map file: ${file.relativePath}`
2355
+ });
2356
+ continue;
2357
+ }
2358
+ if (!isJavaScriptPackageFile(file.relativePath)) continue;
2359
+ const source = Buffer.from(file.data).toString("utf8");
2360
+ if (SOURCE_MAPPING_URL_PATTERN.test(source)) options.state.releaseHygieneProblems.push({
2361
+ importerName: options.rootPackageName,
2362
+ message: `tarball JavaScript file contains sourceMappingURL directive: ${file.relativePath}`
2363
+ });
2364
+ }
2365
+ }
2366
+ function validatePackedManifest(options) {
2367
+ const { manifest, rootPackageName, state } = options;
2368
+ for (const entry of collectPublishDependencyEntries(manifest)) if (isWorkspaceDependencySpecifier(entry.specifier) || isLinkDependencySpecifier(entry.specifier)) state.packedManifestProblems.push({
2369
+ dependencyName: entry.dependencyName,
2370
+ importerName: rootPackageName,
2371
+ message: "packed package manifest must not expose workspace: or link: dependency specifiers",
2372
+ sectionName: entry.sectionName,
2373
+ specifier: entry.specifier
2374
+ });
2375
+ for (const dependency of state.directWorkspaceDependencies) {
2376
+ const packedSpecifier = getPackedDependencySpecifier(manifest, dependency.dependencyName);
2377
+ if (!packedSpecifier) {
2378
+ state.packedManifestProblems.push({
2379
+ dependencyName: dependency.dependencyName,
2380
+ importerName: rootPackageName,
2381
+ message: "packed package manifest must keep every source workspace publish dependency",
2382
+ sectionName: dependency.sectionName
2383
+ });
2384
+ continue;
2385
+ }
2386
+ if (isWorkspaceDependencySpecifier(packedSpecifier) || isLinkDependencySpecifier(packedSpecifier)) continue;
2387
+ const targetVersion = dependency.targetPackage.manifest.version;
2388
+ if (!targetVersion || !semver.satisfies(targetVersion, packedSpecifier, { includePrerelease: true })) state.packedManifestProblems.push({
2389
+ dependencyName: dependency.dependencyName,
2390
+ importerName: rootPackageName,
2391
+ message: `packed dependency range must include ${dependency.targetPackage.name}@${targetVersion ?? "(missing version)"}`,
2392
+ sectionName: dependency.sectionName,
2393
+ specifier: packedSpecifier
2394
+ });
2395
+ }
2396
+ }
2397
+ function createPublishOrder(rootPackageName, state) {
2398
+ const publishOrder = [];
2399
+ const seen = /* @__PURE__ */ new Set();
2400
+ function visit(packageName) {
2401
+ if (seen.has(packageName)) return;
2402
+ seen.add(packageName);
2403
+ for (const dependencyName of state.edges.get(packageName) ?? []) visit(dependencyName);
2404
+ if (packageName === rootPackageName || state.unpublishedPackageNames.has(packageName)) publishOrder.push(packageName);
2405
+ }
2406
+ visit(rootPackageName);
2407
+ return publishOrder;
2408
+ }
2409
+ function createReleaseConsistencyError(options) {
2410
+ const { config, label, outDir, rootPackageName, state } = options;
2411
+ if (state.sourceLinkDependencies.length + state.privateWorkspaceDependencies.length + state.missingWorkspaceDependencies.length + state.registryProblems.length + state.releaseHygieneProblems.length + state.packedManifestProblems.length === 0) return null;
2412
+ const publishOrder = createPublishOrder(rootPackageName, state);
2413
+ const lines = [
2414
+ `package release check failed for ${label}:`,
2415
+ ` output: ${toRelativePath(config.rootDir, outDir)}`,
2416
+ ...formatProblemLines("Release tarball is not publishable:", state.releaseHygieneProblems),
2417
+ ...formatProblemLines("Source manifest contains local link: publish dependencies:", state.sourceLinkDependencies),
2418
+ ...formatProblemLines("Source manifest depends on private workspace packages:", state.privateWorkspaceDependencies),
2419
+ ...formatProblemLines("Source manifest has invalid workspace: publish dependencies:", state.missingWorkspaceDependencies),
2420
+ ...formatProblemLines("Workspace packages must be published before this package:", state.registryProblems),
2421
+ ...formatProblemLines("Packed package manifest is inconsistent with workspace publish dependencies:", state.packedManifestProblems)
2422
+ ];
2423
+ if (publishOrder.length > 1) lines.push("", `Suggested publish order: ${publishOrder.join(" -> ")}`);
2424
+ return new PackageReleaseConsistencyError(lines.join("\n"));
2425
+ }
2426
+ async function assertPackageReleaseConsistency(options) {
2427
+ const workspacePackages = await collectWorkspacePackages(options.config);
2428
+ const sourcePackage = workspacePackages.find((workspacePackage) => workspacePackage.name === options.outputManifest.name);
2429
+ const state = createReleaseConsistencyState();
2430
+ if (sourcePackage) {
2431
+ state.visitedPackages.add(sourcePackage.name);
2432
+ await visitWorkspacePackageDependencies({
2433
+ config: options.config,
2434
+ importerName: sourcePackage.name,
2435
+ isRoot: true,
2436
+ manifest: sourcePackage.manifest,
2437
+ state,
2438
+ workspacePackagesByName: new Map(workspacePackages.map((workspacePackage) => [workspacePackage.name, workspacePackage]))
2439
+ });
2440
+ }
2441
+ const contentFiles = getPackedContentFiles(await unpackPackedPackage(options.packedTarball));
2442
+ validateReleaseTarballHygiene({
2443
+ contentFiles,
2444
+ rootPackageName: options.outputManifest.name,
2445
+ state
2446
+ });
2447
+ const packedManifest = readPackedPackageJson({
2448
+ contentFiles,
2449
+ rootPackageName: options.outputManifest.name,
2450
+ state
2451
+ });
2452
+ if (packedManifest) validatePackedManifest({
2453
+ manifest: packedManifest,
2454
+ rootPackageName: options.outputManifest.name,
2455
+ state
2456
+ });
2457
+ const error = createReleaseConsistencyError({
2458
+ config: options.config,
2459
+ label: options.label,
2460
+ outDir: options.outDir,
2461
+ rootPackageName: options.outputManifest.name,
2462
+ state
2463
+ });
2464
+ if (error) throw error;
2465
+ }
2466
+
2467
+ //#endregion
2468
+ //#region src/commands/release.ts
2469
+ function logReleaseCheckPlan(options) {
2470
+ ReleaseLogger.info([
2471
+ "Release check plan:",
2472
+ ` config: ${toRelativePath(options.config.rootDir, options.config.configPath)}`,
2473
+ ` cwd: ${toRelativePath(options.config.rootDir, options.cwd)}`,
2474
+ ` selection: ${options.plan.selectionReason}`,
2475
+ " entries:",
2476
+ ...options.plan.entries.map((entry) => [` - ${entry.label}`, ` outDir: ${toRelativePath(options.config.rootDir, entry.outDir)}`].join("\n"))
2477
+ ].join("\n"));
2478
+ }
2479
+ async function packReleaseTarball(options) {
2480
+ const packTask = options.flow?.start(`release tarball: ${options.label}`, { depth: options.flowDepth ?? 0 });
2481
+ ReleaseLogger.info(`release tarball packing started: ${options.label}`);
2482
+ const packElapsed = createElapsedTimer();
2483
+ try {
2484
+ const packedDist = await packOutputTarball(options.outDir);
2485
+ if (!options.flow?.interactive) ReleaseLogger.success(`release tarball packed: ${options.label}`, packElapsed());
2486
+ packTask?.pass();
2487
+ return packedDist;
2488
+ } catch (error) {
2489
+ ReleaseLogger.error(`release tarball failed: ${options.label}: ${formatErrorMessage$1(error)}`, packElapsed());
2490
+ packTask?.fail(`release tarball failed: ${options.label}`, { error });
2491
+ throw error;
2492
+ }
2493
+ }
2494
+ async function runReleaseCheckEntry(options) {
2495
+ const task = options.flow?.start(`release entry: ${options.label}`, { depth: options.flowDepth ?? 0 });
2496
+ let packedDist;
2497
+ try {
2498
+ const outputPackageJsonPath = path.join(options.outDir, "package.json");
2499
+ const outputManifest = await readDistPackageJson({
2500
+ config: options.config,
2501
+ label: options.label,
2502
+ packageJsonPath: outputPackageJsonPath
2503
+ });
2504
+ if (outputManifest.private === true) throw new PackageReleaseConsistencyError([
2505
+ `package release check failed for ${options.label}:`,
2506
+ ` output: ${toRelativePath(options.config.rootDir, options.outDir)}`,
2507
+ "",
2508
+ "Release tarball is not publishable:",
2509
+ ` - ${outputManifest.name}: selected release package has "private": true; npm publish would reject it`
2510
+ ].join("\n"));
2511
+ packedDist = await packReleaseTarball({
2512
+ flow: options.flow,
2513
+ flowDepth: (options.flowDepth ?? 0) + 1,
2514
+ label: options.label,
2515
+ outDir: options.outDir
2516
+ });
2517
+ await assertPackageReleaseConsistency({
2518
+ config: options.config,
2519
+ label: options.label,
2520
+ outDir: options.outDir,
2521
+ outputManifest,
2522
+ packedTarball: packedDist.tarball
2523
+ });
2524
+ if (!options.flow?.interactive) ReleaseLogger.success(`release checks passed: ${options.label}`);
2525
+ task?.pass();
2526
+ return true;
2527
+ } catch (error) {
2528
+ if (error instanceof PackageReleaseConsistencyError) {
2529
+ ReleaseLogger.error(formatErrorMessage$1(error));
2530
+ task?.fail(`release checks failed: ${options.label}`);
2531
+ return false;
2532
+ }
2533
+ ReleaseLogger.error(`release checks failed: ${options.label}: ${formatErrorMessage$1(error)}`);
2534
+ task?.fail(`release checks failed: ${options.label}`, { error });
2535
+ throw error;
2536
+ } finally {
2537
+ if (packedDist) await packedDist.cleanup();
2538
+ }
2539
+ }
2540
+ async function runReleaseCheck(options) {
2541
+ if (options.clearScreen ?? true) clearCliScreen();
2542
+ const elapsed = createElapsedTimer();
2543
+ const cwd = path.resolve(options.cwd ?? process.cwd());
2544
+ const task = options.flow?.start("release check", { depth: options.flowDepth ?? 0 });
2545
+ try {
2546
+ ReleaseLogger.info("release check started");
2547
+ const plan = createPackageEntrySelectionPlan({
2548
+ config: options.config,
2549
+ cwd,
2550
+ packageNames: options.packageNames,
2551
+ strictCwd: true
2552
+ });
2553
+ logReleaseCheckPlan({
2554
+ config: options.config,
2555
+ cwd,
2556
+ plan
2557
+ });
2558
+ let passed = true;
2559
+ for (const entry of plan.entries) passed = await runReleaseCheckEntry({
2560
+ config: options.config,
2561
+ flow: options.flow,
2562
+ flowDepth: (options.flowDepth ?? 0) + 1,
2563
+ label: entry.label,
2564
+ outDir: entry.outDir
2565
+ }) && passed;
2566
+ if (passed) {
2567
+ if (!options.flow?.interactive) ReleaseLogger.success("release check finished", elapsed());
2568
+ task?.pass();
2569
+ } else {
2570
+ ReleaseLogger.error("release check finished with failures", elapsed());
2571
+ task?.fail("release check finished with failures");
2572
+ }
2573
+ return passed;
2574
+ } catch (error) {
2575
+ ReleaseLogger.error(`release check failed: ${formatErrorMessage$1(error)}`, elapsed());
2576
+ task?.fail("release check failed", { error });
2577
+ throw error;
2578
+ }
2579
+ }
2580
+
2050
2581
  //#endregion
2051
2582
  //#region src/pipeline.ts
2052
2583
  const builtInTaskNames = new Set([
@@ -2055,14 +2586,33 @@ const builtInTaskNames = new Set([
2055
2586
  "graph:check",
2056
2587
  "package:check",
2057
2588
  "proof:check",
2589
+ "release:check",
2058
2590
  "source:check"
2059
2591
  ]);
2060
2592
  const defaultCheckPipeline = [
2061
2593
  "graph:check",
2062
2594
  "source:check",
2063
2595
  "proof:check",
2596
+ "checker:build",
2064
2597
  "checker:typecheck"
2065
2598
  ];
2599
+ function reportCheckerCapabilities(config, flow) {
2600
+ if (!flow) return;
2601
+ const firstClass = [];
2602
+ const sourceOnly = [];
2603
+ for (const checker of getActiveCheckers(config)) {
2604
+ const adapter = getCheckerAdapter(checker.preset);
2605
+ const label = `${checker.name} (${checker.preset})`;
2606
+ if (adapter?.tier === "first-class") firstClass.push(label);
2607
+ else if (adapter?.tier === "source-only") sourceOnly.push(label);
2608
+ }
2609
+ flow.info([
2610
+ "checker capability summary:",
2611
+ ` first-class: ${firstClass.length > 0 ? firstClass.join(", ") : "(none)"}`,
2612
+ ` source-only: ${sourceOnly.length > 0 ? sourceOnly.join(", ") : "(none)"}`,
2613
+ ...sourceOnly.length > 0 ? [" note: source-only checkers get coverage proof and direct typecheck, but Limina does not parse their internal import graph."] : []
2614
+ ].join("\n"), { depth: 1 });
2615
+ }
2066
2616
  function isBuiltinTaskName(value) {
2067
2617
  return builtInTaskNames.has(value);
2068
2618
  }
@@ -2092,7 +2642,16 @@ async function runBuiltinTask(config, taskName, options = {}) {
2092
2642
  config,
2093
2643
  cwd: options.cwd,
2094
2644
  flow: options.flow,
2095
- flowDepth: 1
2645
+ flowDepth: 1,
2646
+ packageNames: options.packageNames
2647
+ });
2648
+ case "release:check": return runReleaseCheck({
2649
+ clearScreen: false,
2650
+ config,
2651
+ cwd: options.cwd,
2652
+ flow: options.flow,
2653
+ flowDepth: 1,
2654
+ packageNames: options.packageNames
2096
2655
  });
2097
2656
  case "checker:typecheck": return (await runCheckerTypecheck({
2098
2657
  clearScreen: false,
@@ -2191,6 +2750,7 @@ async function runPipeline(config, pipelineName, options = {}) {
2191
2750
  async function runDefaultCheck(config, options = {}) {
2192
2751
  const normalizedSteps = defaultCheckPipeline.map(normalizePipelineStep);
2193
2752
  const pipelineTask = options.flow?.start("default check", { collapseOnSuccess: false });
2753
+ reportCheckerCapabilities(config, options.flow);
2194
2754
  for (const [stepIndex, step] of normalizedSteps.entries()) if (!(step.type === "task" ? await runBuiltinTask(config, step.name, options) : await runCommandStep(config, step, options))) {
2195
2755
  const label = getPipelineStepLabel(step);
2196
2756
  pipelineTask?.fail(`default check blocked at ${label}`);
@@ -2221,11 +2781,10 @@ function parsePackageAttwProfile(profile) {
2221
2781
  if (profile === "strict" || profile === "node16" || profile === "esm-only") return profile;
2222
2782
  throw new Error(`Invalid package check --attw-profile "${profile}". Expected one of: strict, node16, esm-only.`);
2223
2783
  }
2224
- function parseConcurrency(value) {
2225
- if (value === void 0) return;
2226
- const parsed = Number(value);
2227
- if (!Number.isInteger(parsed) || parsed < 1) throw new Error("Invalid --concurrency value. Expected a positive integer.");
2228
- return parsed;
2784
+ function parsePackageNames(packageName) {
2785
+ if (!packageName) return;
2786
+ const packageNames = (Array.isArray(packageName) ? packageName : [packageName]).map((name) => name.trim()).filter(Boolean);
2787
+ return packageNames.length > 0 ? packageNames : void 0;
2229
2788
  }
2230
2789
  function createCliFlow() {
2231
2790
  clearCliScreen();
@@ -2247,13 +2806,14 @@ async function main() {
2247
2806
  });
2248
2807
  flow.outro("limina init finished");
2249
2808
  });
2250
- cli.command("check [pipeline]", "Run the default check or a configured pipeline").action(async (pipeline, flags) => {
2809
+ cli.command("check [pipeline]", "Run the default check or a configured pipeline").option("-p, --package <name>", "Run package-aware pipeline tasks for one package entry").action(async (pipeline, flags) => {
2251
2810
  const flow = createCliFlow();
2252
2811
  flow.intro("limina check");
2253
2812
  const config = await load(flags, "check");
2254
2813
  const passed = pipeline ? await runPipeline(config, pipeline, {
2255
2814
  cwd: process.cwd(),
2256
- flow
2815
+ flow,
2816
+ packageNames: parsePackageNames(flags.package)
2257
2817
  }) : await runDefaultCheck(config, {
2258
2818
  cwd: process.cwd(),
2259
2819
  flow
@@ -2306,8 +2866,8 @@ async function main() {
2306
2866
  if (!passed) process.exitCode = 1;
2307
2867
  flow.outro(passed ? "limina source passed" : "limina source failed");
2308
2868
  });
2309
- cli.command("checker <action>", "Run configured checker typecheck or build entries").option("--concurrency <n>", "Maximum concurrent checker processes").action(async (action, flags) => {
2310
- if (action !== "typecheck" && action !== "build") throw new Error(`Unknown checker action "${action}". Expected typecheck or build.`);
2869
+ cli.command("checker <action>", "Run configured checker build or typecheck entries").action(async (action, flags) => {
2870
+ if (action !== "typecheck" && action !== "build") throw new Error(`Unknown checker action "${action}". Expected build or typecheck.`);
2311
2871
  const flow = createCliFlow();
2312
2872
  flow.intro(`limina checker ${action}`);
2313
2873
  if (action === "build") {
@@ -2324,14 +2884,13 @@ async function main() {
2324
2884
  const result = await runCheckerTypecheck({
2325
2885
  clearScreen: false,
2326
2886
  config: await load(flags, "check"),
2327
- concurrency: parseConcurrency(flags.concurrency),
2328
2887
  cwd: process.cwd(),
2329
2888
  flow
2330
2889
  });
2331
2890
  if (!result.passed) process.exitCode = 1;
2332
2891
  flow.outro(result.passed ? "limina checker passed" : "limina checker failed");
2333
2892
  });
2334
- cli.command("package <action>", "Check configured published package outputs").option("-p, --package <name>", "Run a single package check target").option("--tool <tool>", "Run one package check tool").option("--attw-profile <profile>", "Override the configured ATTW profile").action(async (action, flags) => {
2893
+ cli.command("package <action>", "Check configured published package outputs").option("-p, --package <name>", "Run one package check entry").option("--tool <tool>", "Run one package check tool").option("--attw-profile <profile>", "Override the configured ATTW profile").action(async (action, flags) => {
2335
2894
  if (action !== "check") throw new Error(`Unknown package action "${action}". Expected check.`);
2336
2895
  const flow = createCliFlow();
2337
2896
  flow.intro("limina package check");
@@ -2342,12 +2901,26 @@ async function main() {
2342
2901
  config,
2343
2902
  cwd: process.cwd(),
2344
2903
  flow,
2345
- targetName: flags.package,
2904
+ packageNames: parsePackageNames(flags.package),
2346
2905
  tool: parsePackageTool(flags.tool)
2347
2906
  });
2348
2907
  if (!passed) process.exitCode = 1;
2349
2908
  flow.outro(passed ? "limina package passed" : "limina package failed");
2350
2909
  });
2910
+ cli.command("release <action>", "Check package release readiness").option("-p, --package <name>", "Run one release check package entry").action(async (action, flags) => {
2911
+ if (action !== "check") throw new Error(`Unknown release action "${action}". Expected check.`);
2912
+ const flow = createCliFlow();
2913
+ flow.intro("limina release check");
2914
+ const passed = await runReleaseCheck({
2915
+ clearScreen: false,
2916
+ config: await load(flags, "release"),
2917
+ cwd: process.cwd(),
2918
+ flow,
2919
+ packageNames: parsePackageNames(flags.package)
2920
+ });
2921
+ if (!passed) process.exitCode = 1;
2922
+ flow.outro(passed ? "limina release passed" : "limina release failed");
2923
+ });
2351
2924
  cli.parse(process.argv, { run: false });
2352
2925
  try {
2353
2926
  await cli.runMatchedCommand();