limina 0.1.3 → 0.2.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.
@@ -6,16 +6,14 @@ import { pathToFileURL } from "node:url";
6
6
  function uniqueValues(values) {
7
7
  return [...new Set(values)];
8
8
  }
9
- function uniqueBy(values, getKey) {
10
- const seen = /* @__PURE__ */ new Set();
11
- const result = [];
9
+ function countDefinedBy(values, getKey) {
10
+ const counts = /* @__PURE__ */ new Map();
12
11
  for (const value of values) {
13
12
  const key = getKey(value);
14
- if (seen.has(key)) continue;
15
- seen.add(key);
16
- result.push(value);
13
+ if (!key) continue;
14
+ counts.set(key, (counts.get(key) ?? 0) + 1);
17
15
  }
18
- return result;
16
+ return counts;
19
17
  }
20
18
  function uniqueSortedStrings(values) {
21
19
  return uniqueValues(values).sort((left, right) => left.localeCompare(right));
@@ -605,6 +603,11 @@ function getNativeTypeScriptProjectExtensions() {
605
603
  };
606
604
  return normalizeExtensions(flattenTypeScriptExtensionGroups(api.getSupportedExtensionsWithJsonIfResolveJsonModule(options, api.getSupportedExtensions(options))));
607
605
  }
606
+ /** Uses the active compiler's supported-extension metadata, not a hard-coded list. */
607
+ function isNativeTypeScriptProjectInput(fileName) {
608
+ const normalizedFileName = fileName.toLowerCase();
609
+ return getNativeTypeScriptProjectExtensions().some((extension) => normalizedFileName.endsWith(extension.toLowerCase()));
610
+ }
608
611
  function getBuildCheckerSupportedExtensions(preset) {
609
612
  const adapter = getCheckerAdapter(preset);
610
613
  if (!adapter || adapter.execution !== "build") return [];
@@ -615,6 +618,18 @@ function getSvelteCheckerExtensions() {
615
618
  return normalizeExtensions([...getTypeScriptCheckerExtensions(), ".svelte"]);
616
619
  }
617
620
  const parsedProjectConfigCache = /* @__PURE__ */ new Map();
621
+ function createProjectParseHost(virtualFiles) {
622
+ if (!virtualFiles) return ts.sys;
623
+ return {
624
+ ...ts.sys,
625
+ fileExists(fileName) {
626
+ return virtualFiles.has(normalizeAbsolutePath(fileName)) || ts.sys.fileExists(fileName);
627
+ },
628
+ readFile(fileName, encoding) {
629
+ return virtualFiles.get(normalizeAbsolutePath(fileName)) ?? ts.sys.readFile(fileName, encoding);
630
+ }
631
+ };
632
+ }
618
633
  function createFormatHost(rootDir) {
619
634
  return {
620
635
  getCanonicalFileName: (fileName) => fileName,
@@ -624,8 +639,9 @@ function createFormatHost(rootDir) {
624
639
  }
625
640
  function readTypeScriptProjectConfig(options) {
626
641
  const diagnostics = [];
642
+ const host = createProjectParseHost(options.virtualFiles);
627
643
  const parsed = ts.getParsedCommandLineOfConfigFile(options.configPath, {}, {
628
- ...ts.sys,
644
+ ...host,
629
645
  onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
630
646
  diagnostics.push(diagnostic);
631
647
  }
@@ -654,19 +670,18 @@ function resolveContextCheckerPresets(context) {
654
670
  return context.checkerPresets.length > 0 ? uniqueSortedStrings(context.checkerPresets) : ["tsc"];
655
671
  }
656
672
  function createParsedProjectConfigCacheKey(options) {
657
- const configStat = statSync(options.configPath);
673
+ const normalizedConfigPath = normalizeAbsolutePath(options.configPath);
674
+ const virtualContent = options.virtualFiles?.get(normalizedConfigPath);
675
+ const configStat = virtualContent === void 0 ? statSync(options.configPath) : null;
658
676
  return JSON.stringify({
659
677
  checkerPresets: options.checkerPresets,
660
- configPath: normalizeAbsolutePath(options.configPath),
661
- configSize: configStat.size,
662
- configTime: configStat.mtimeMs,
678
+ configPath: normalizedConfigPath,
679
+ configSize: virtualContent?.length ?? configStat?.size,
680
+ configTime: virtualContent ?? configStat?.mtimeMs,
663
681
  extensions: normalizeExtensions(options.extensions),
664
682
  projectRootDir: normalizeAbsolutePath(options.projectRootDir)
665
683
  });
666
684
  }
667
- function clearCheckerProjectConfigCache() {
668
- parsedProjectConfigCache.clear();
669
- }
670
685
  function createExtraFileExtensions(extensions) {
671
686
  const nativeExtensions = new Set(getNativeTypeScriptProjectExtensions());
672
687
  return extensions.filter((extension) => !nativeExtensions.has(extension)).map((extension) => ({
@@ -677,8 +692,9 @@ function createExtraFileExtensions(extensions) {
677
692
  }
678
693
  function parseProjectConfigWithExtraFileExtensions(options, extensions) {
679
694
  const diagnostics = [];
695
+ const host = createProjectParseHost(options.virtualFiles);
680
696
  const parsed = ts.getParsedCommandLineOfConfigFile(options.configPath, {}, {
681
- ...ts.sys,
697
+ ...host,
682
698
  onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
683
699
  diagnostics.push(diagnostic);
684
700
  }
@@ -743,7 +759,7 @@ function createVueParsedCommandLine(options) {
743
759
  });
744
760
  const configPath = normalizeAbsolutePath(options.configPath);
745
761
  return {
746
- commandLine: vueLanguageCore.createParsedCommandLine(ts, ts.sys, configPath),
762
+ commandLine: vueLanguageCore.createParsedCommandLine(ts, createProjectParseHost(options.virtualFiles), configPath),
747
763
  configPath,
748
764
  vueLanguageCore
749
765
  };
@@ -752,7 +768,8 @@ function resolveVueProjectExtensions(options, packageName) {
752
768
  const { commandLine, vueLanguageCore } = createVueParsedCommandLine({
753
769
  configPath: options.configPath,
754
770
  packageName,
755
- projectRootDir: options.projectRootDir
771
+ projectRootDir: options.projectRootDir,
772
+ virtualFiles: options.virtualFiles
756
773
  });
757
774
  try {
758
775
  return normalizeExtensions([...getTypeScriptCheckerExtensions(), ...vueLanguageCore.getAllExtensions(commandLine.vueOptions)]);
@@ -773,15 +790,17 @@ function parseVueProjectConfig(options, packageName) {
773
790
  const { commandLine, configPath, vueLanguageCore } = createVueParsedCommandLine({
774
791
  configPath: options.configPath,
775
792
  packageName,
776
- projectRootDir: options.projectRootDir
793
+ projectRootDir: options.projectRootDir,
794
+ virtualFiles: options.virtualFiles
777
795
  });
778
796
  const extensions = normalizeExtensions([
779
797
  ...options.extensions ?? [],
780
798
  ...getTypeScriptCheckerExtensions(),
781
799
  ...vueLanguageCore.getAllExtensions(commandLine.vueOptions)
782
800
  ]);
783
- const configFile = ts.readJsonConfigFile(configPath, ts.sys.readFile);
784
- const parsed = ts.parseJsonSourceFileConfigFileContent(configFile, ts.sys, posix.dirname(configPath), {}, configPath, void 0, createExtraFileExtensions(extensions));
801
+ const host = createProjectParseHost(options.virtualFiles);
802
+ const configFile = ts.readJsonConfigFile(configPath, host.readFile);
803
+ const parsed = ts.parseJsonSourceFileConfigFileContent(configFile, host, posix.dirname(configPath), {}, configPath, void 0, createExtraFileExtensions(extensions));
785
804
  const errors = parsed.errors;
786
805
  if (errors.length > 0) throw new Error(ts.formatDiagnosticsWithColorAndContext(errors, createFormatHost(options.projectRootDir)));
787
806
  return createParsedCheckerProjectConfig({
@@ -800,15 +819,26 @@ function resolveTypeScriptModuleName(options) {
800
819
  return resolveTypeScriptModuleNameDetailed(options)?.resolvedFileName ?? null;
801
820
  }
802
821
  function resolveTypeScriptModuleNameDetailed(options) {
803
- const resolved = ts.resolveModuleName(options.specifier, options.containingFile, options.compilerOptions, ts.sys).resolvedModule;
822
+ options.metrics?.record({
823
+ kind: "request",
824
+ name: "typescript-resolution",
825
+ provider: "module-resolution"
826
+ });
827
+ const nativeCacheHit = Boolean(options.moduleResolutionCache && ts.resolveModuleNameFromCache(options.specifier, options.containingFile, options.moduleResolutionCache) !== void 0);
828
+ options.metrics?.record({
829
+ kind: "module-resolution",
830
+ name: nativeCacheHit ? "typescript-module-resolution-cache-hit" : "typescript-module-resolution-cache-miss",
831
+ provider: "typescript"
832
+ });
833
+ const resolved = ts.resolveModuleName(options.specifier, options.containingFile, options.compilerOptions, ts.sys, options.moduleResolutionCache).resolvedModule;
804
834
  if (resolved?.resolvedFileName) return {
805
835
  isExternalLibraryImport: resolved.isExternalLibraryImport === true,
806
836
  resolvedBy: "typescript",
807
837
  resolvedFileName: normalizeAbsolutePath(resolved.resolvedFileName)
808
838
  };
809
- return resolveCheckerExtensionModuleName(options);
839
+ return resolveCheckerSourceModuleName(options);
810
840
  }
811
- function resolveCheckerExtensionModuleName(options) {
841
+ function resolveCheckerSourceModuleName(options) {
812
842
  const typeScriptExtensions = new Set(getTypeScriptCheckerExtensions());
813
843
  const checkerOnlyExtensions = options.extensions.filter((extension) => !typeScriptExtensions.has(extension));
814
844
  if (checkerOnlyExtensions.length === 0) return null;
@@ -825,9 +855,11 @@ function resolveCheckerExtensionModuleName(options) {
825
855
  extensions: checkerOnlyExtensions,
826
856
  specifier: options.specifier
827
857
  });
828
- return resolvedFileName ? {
858
+ const normalizedResolvedFileName = resolvedFileName?.toLowerCase();
859
+ const isDeclaredCheckerSource = checkerOnlyExtensions.some((extension) => normalizedResolvedFileName?.endsWith(extension.toLowerCase()));
860
+ return resolvedFileName && isDeclaredCheckerSource ? {
829
861
  isExternalLibraryImport: false,
830
- resolvedBy: "checker-extension",
862
+ resolvedBy: "checker-source",
831
863
  resolvedFileName
832
864
  } : null;
833
865
  }
@@ -916,7 +948,8 @@ function parseCheckerProjectConfigForContext(options) {
916
948
  checkerPresets,
917
949
  configPath: options.configPath,
918
950
  extensions: options.context.extensions,
919
- projectRootDir: options.projectRootDir
951
+ projectRootDir: options.projectRootDir,
952
+ virtualFiles: options.virtualFiles
920
953
  });
921
954
  const cached = parsedProjectConfigCache.get(cacheKey);
922
955
  if (cached) return cloneParsedCheckerProjectConfig(cached);
@@ -926,35 +959,31 @@ function parseCheckerProjectConfigForContext(options) {
926
959
  return adapter.parseProjectConfig({
927
960
  configPath: options.configPath,
928
961
  extensions: options.context.extensions,
929
- projectRootDir: options.projectRootDir
962
+ projectRootDir: options.projectRootDir,
963
+ virtualFiles: options.virtualFiles
930
964
  });
931
965
  }), options.context.extensions);
932
966
  parsedProjectConfigCache.set(cacheKey, cloneParsedCheckerProjectConfig(parsedConfig));
933
967
  return cloneParsedCheckerProjectConfig(parsedConfig);
934
968
  }
935
- function resolveModuleNameWithCheckers(options) {
936
- return resolveModuleNameWithCheckersDetailed(options)?.resolvedFileName ?? null;
937
- }
938
969
  function resolveModuleNameWithCheckersDetailed(options) {
939
- const checkerPresets = options.context.checkerPresets.length > 0 ? options.context.checkerPresets : ["tsc"];
940
- for (const preset of checkerPresets) {
941
- if (!getCheckerAdapter(preset)) continue;
942
- const resolved = resolveTypeScriptModuleNameDetailed({
943
- compilerOptions: options.compilerOptions,
944
- containingFile: options.containingFile,
945
- extensions: options.context.extensions,
946
- specifier: options.specifier
947
- });
948
- if (resolved) return resolved;
949
- }
950
- return null;
970
+ if (!(options.context.checkerPresets.length > 0 ? options.context.checkerPresets : ["tsc"]).some((preset) => Boolean(getCheckerAdapter(preset)))) return null;
971
+ return resolveTypeScriptModuleNameDetailed({
972
+ compilerOptions: options.compilerOptions,
973
+ containingFile: options.containingFile,
974
+ extensions: options.context.extensions,
975
+ metrics: options.metrics,
976
+ moduleResolutionCache: options.moduleResolutionCache,
977
+ specifier: options.specifier
978
+ });
951
979
  }
952
980
  function resolveCheckerProjectExtensions(options) {
953
981
  const adapter = getCheckerAdapter(options.preset);
954
982
  if (!adapter) throw new Error(`Checker preset "${options.preset}" is not supported.`);
955
983
  return adapter.extensions({
956
984
  configPath: options.configPath,
957
- projectRootDir: options.projectRootDir
985
+ projectRootDir: options.projectRootDir,
986
+ virtualFiles: options.virtualFiles
958
987
  });
959
988
  }
960
989
  function createTscCommandTarget(options) {
@@ -1029,6 +1058,7 @@ const builtinCheckerAdapters = {
1029
1058
  createCommandTarget: createSvelteCheckCommandTarget,
1030
1059
  extensions: (options) => resolveExtensionsForChecker(options, getSvelteCheckerExtensions()),
1031
1060
  execution: "typecheck",
1061
+ emitProjection: "typescript",
1032
1062
  packageNames: ["svelte-check"],
1033
1063
  parseProjectConfig: (options) => parseProjectConfigWithExtensions(options, getSvelteCheckerExtensions()),
1034
1064
  preset: "svelte-check",
@@ -1039,6 +1069,7 @@ const builtinCheckerAdapters = {
1039
1069
  createCommandTarget: createTscCommandTarget,
1040
1070
  extensions: (options) => resolveExtensionsForChecker(options, getTypeScriptCheckerExtensions()),
1041
1071
  execution: "build",
1072
+ emitProjection: "typescript",
1042
1073
  packageNames: ["typescript"],
1043
1074
  parseProjectConfig: (options) => parseProjectConfigWithExtensions(options, getTypeScriptCheckerExtensions()),
1044
1075
  preset: "tsc",
@@ -1049,6 +1080,7 @@ const builtinCheckerAdapters = {
1049
1080
  createCommandTarget: createTsgoCommandTarget,
1050
1081
  extensions: (options) => resolveExtensionsForChecker(options, getTypeScriptCheckerExtensions()),
1051
1082
  execution: "build",
1083
+ emitProjection: "typescript",
1052
1084
  packageNames: ["@typescript/native-preview"],
1053
1085
  parseProjectConfig: (options) => parseProjectConfigWithExtensions(options, getTypeScriptCheckerExtensions()),
1054
1086
  preset: "tsgo",
@@ -1059,6 +1091,7 @@ const builtinCheckerAdapters = {
1059
1091
  createCommandTarget: createVueTscCommandTarget,
1060
1092
  extensions: (options) => resolveVueProjectExtensionsForChecker(options, "vue-tsc"),
1061
1093
  execution: "build",
1094
+ emitProjection: "vue-bounded",
1062
1095
  packageNames: ["vue-tsc"],
1063
1096
  parseProjectConfig: (options) => parseVueProjectConfig(options, "vue-tsc"),
1064
1097
  preset: "vue-tsc",
@@ -1069,6 +1102,7 @@ const builtinCheckerAdapters = {
1069
1102
  createCommandTarget: createVueTsgoCommandTarget,
1070
1103
  extensions: (options) => resolveVueProjectExtensionsForChecker(options, "vue-tsgo"),
1071
1104
  execution: "typecheck",
1105
+ emitProjection: "vue-bounded",
1072
1106
  packageNames: ["vue-tsgo", "@typescript/native-preview"],
1073
1107
  parseProjectConfig: (options) => parseVueProjectConfig(options, "vue-tsgo"),
1074
1108
  preset: "vue-tsgo",
@@ -1082,6 +1116,19 @@ function isBuiltinCheckerPreset(value) {
1082
1116
  function getCheckerAdapter(preset) {
1083
1117
  return isBuiltinCheckerPreset(preset) ? builtinCheckerAdapters[preset] : null;
1084
1118
  }
1119
+ const checkerBuildEngines = {
1120
+ "svelte-check": "typecheck-only",
1121
+ tsc: "tsc",
1122
+ tsgo: "tsgo",
1123
+ "vue-tsc": "vue-tsc",
1124
+ "vue-tsgo": "typecheck-only"
1125
+ };
1126
+ function getCheckerBuildEngine(preset) {
1127
+ return checkerBuildEngines[preset] ?? "unknown";
1128
+ }
1129
+ function isBuildCapablePreset(preset) {
1130
+ return getCheckerAdapter(preset)?.execution === "build";
1131
+ }
1085
1132
  function isVueCheckerPreset(preset) {
1086
1133
  return preset === "vue-tsc" || preset === "vue-tsgo";
1087
1134
  }
@@ -1255,6 +1302,17 @@ function cleanRegex(source) {
1255
1302
  const end = source.endsWith("$") ? source.length - 1 : source.length;
1256
1303
  return source.slice(start, end);
1257
1304
  }
1305
+ function floatSafeRemainder(val, step) {
1306
+ const valDecCount = (val.toString().split(".")[1] || "").length;
1307
+ const stepString = step.toString();
1308
+ let stepDecCount = (stepString.split(".")[1] || "").length;
1309
+ if (stepDecCount === 0 && /\d?e-\d?/.test(stepString)) {
1310
+ const match = stepString.match(/\d?e-(\d?)/);
1311
+ if (match?.[1]) stepDecCount = Number.parseInt(match[1]);
1312
+ }
1313
+ const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;
1314
+ return Number.parseInt(val.toFixed(decCount).replace(".", "")) % Number.parseInt(step.toFixed(decCount).replace(".", "")) / 10 ** decCount;
1315
+ }
1258
1316
  const EVALUATING = Symbol("evaluating");
1259
1317
  function defineLazy(object, key, getter) {
1260
1318
  let value = void 0;
@@ -1353,7 +1411,13 @@ function optionalKeys(shape) {
1353
1411
  return shape[k]._zod.optin === "optional" && shape[k]._zod.optout === "optional";
1354
1412
  });
1355
1413
  }
1356
- Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, -Number.MAX_VALUE, Number.MAX_VALUE;
1414
+ const NUMBER_FORMAT_RANGES = {
1415
+ safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
1416
+ int32: [-2147483648, 2147483647],
1417
+ uint32: [0, 4294967295],
1418
+ float32: [-34028234663852886e22, 34028234663852886e22],
1419
+ float64: [-Number.MAX_VALUE, Number.MAX_VALUE]
1420
+ };
1357
1421
  function pick(schema, mask) {
1358
1422
  const currDef = schema._zod.def;
1359
1423
  const checks = currDef.checks;
@@ -1670,6 +1734,8 @@ const _safeEncodeAsync = (_Err) => async (schema, value, _ctx) => {
1670
1734
  const _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {
1671
1735
  return _safeParseAsync(_Err)(schema, value, _ctx);
1672
1736
  };
1737
+ const integer = /^-?\d+$/;
1738
+ const number$1 = /^-?\d+(?:\.\d+)?$/;
1673
1739
  //#endregion
1674
1740
  //#region ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/checks.js
1675
1741
  const $ZodCheck = /*@__PURE__*/ $constructor("$ZodCheck", (inst, def) => {
@@ -1678,6 +1744,145 @@ const $ZodCheck = /*@__PURE__*/ $constructor("$ZodCheck", (inst, def) => {
1678
1744
  inst._zod.def = def;
1679
1745
  (_a = inst._zod).onattach ?? (_a.onattach = []);
1680
1746
  });
1747
+ const numericOriginMap = {
1748
+ number: "number",
1749
+ bigint: "bigint",
1750
+ object: "date"
1751
+ };
1752
+ const $ZodCheckLessThan = /*@__PURE__*/ $constructor("$ZodCheckLessThan", (inst, def) => {
1753
+ $ZodCheck.init(inst, def);
1754
+ const origin = numericOriginMap[typeof def.value];
1755
+ inst._zod.onattach.push((inst) => {
1756
+ const bag = inst._zod.bag;
1757
+ const curr = (def.inclusive ? bag.maximum : bag.exclusiveMaximum) ?? Number.POSITIVE_INFINITY;
1758
+ if (def.value < curr) if (def.inclusive) bag.maximum = def.value;
1759
+ else bag.exclusiveMaximum = def.value;
1760
+ });
1761
+ inst._zod.check = (payload) => {
1762
+ if (def.inclusive ? payload.value <= def.value : payload.value < def.value) return;
1763
+ payload.issues.push({
1764
+ origin,
1765
+ code: "too_big",
1766
+ maximum: typeof def.value === "object" ? def.value.getTime() : def.value,
1767
+ input: payload.value,
1768
+ inclusive: def.inclusive,
1769
+ inst,
1770
+ continue: !def.abort
1771
+ });
1772
+ };
1773
+ });
1774
+ const $ZodCheckGreaterThan = /*@__PURE__*/ $constructor("$ZodCheckGreaterThan", (inst, def) => {
1775
+ $ZodCheck.init(inst, def);
1776
+ const origin = numericOriginMap[typeof def.value];
1777
+ inst._zod.onattach.push((inst) => {
1778
+ const bag = inst._zod.bag;
1779
+ const curr = (def.inclusive ? bag.minimum : bag.exclusiveMinimum) ?? Number.NEGATIVE_INFINITY;
1780
+ if (def.value > curr) if (def.inclusive) bag.minimum = def.value;
1781
+ else bag.exclusiveMinimum = def.value;
1782
+ });
1783
+ inst._zod.check = (payload) => {
1784
+ if (def.inclusive ? payload.value >= def.value : payload.value > def.value) return;
1785
+ payload.issues.push({
1786
+ origin,
1787
+ code: "too_small",
1788
+ minimum: typeof def.value === "object" ? def.value.getTime() : def.value,
1789
+ input: payload.value,
1790
+ inclusive: def.inclusive,
1791
+ inst,
1792
+ continue: !def.abort
1793
+ });
1794
+ };
1795
+ });
1796
+ const $ZodCheckMultipleOf = /*@__PURE__*/ $constructor("$ZodCheckMultipleOf", (inst, def) => {
1797
+ $ZodCheck.init(inst, def);
1798
+ inst._zod.onattach.push((inst) => {
1799
+ var _a;
1800
+ (_a = inst._zod.bag).multipleOf ?? (_a.multipleOf = def.value);
1801
+ });
1802
+ inst._zod.check = (payload) => {
1803
+ if (typeof payload.value !== typeof def.value) throw new Error("Cannot mix number and bigint in multiple_of check.");
1804
+ if (typeof payload.value === "bigint" ? payload.value % def.value === BigInt(0) : floatSafeRemainder(payload.value, def.value) === 0) return;
1805
+ payload.issues.push({
1806
+ origin: typeof payload.value,
1807
+ code: "not_multiple_of",
1808
+ divisor: def.value,
1809
+ input: payload.value,
1810
+ inst,
1811
+ continue: !def.abort
1812
+ });
1813
+ };
1814
+ });
1815
+ const $ZodCheckNumberFormat = /*@__PURE__*/ $constructor("$ZodCheckNumberFormat", (inst, def) => {
1816
+ $ZodCheck.init(inst, def);
1817
+ def.format = def.format || "float64";
1818
+ const isInt = def.format?.includes("int");
1819
+ const origin = isInt ? "int" : "number";
1820
+ const [minimum, maximum] = NUMBER_FORMAT_RANGES[def.format];
1821
+ inst._zod.onattach.push((inst) => {
1822
+ const bag = inst._zod.bag;
1823
+ bag.format = def.format;
1824
+ bag.minimum = minimum;
1825
+ bag.maximum = maximum;
1826
+ if (isInt) bag.pattern = integer;
1827
+ });
1828
+ inst._zod.check = (payload) => {
1829
+ const input = payload.value;
1830
+ if (isInt) {
1831
+ if (!Number.isInteger(input)) {
1832
+ payload.issues.push({
1833
+ expected: origin,
1834
+ format: def.format,
1835
+ code: "invalid_type",
1836
+ continue: false,
1837
+ input,
1838
+ inst
1839
+ });
1840
+ return;
1841
+ }
1842
+ if (!Number.isSafeInteger(input)) {
1843
+ if (input > 0) payload.issues.push({
1844
+ input,
1845
+ code: "too_big",
1846
+ maximum: Number.MAX_SAFE_INTEGER,
1847
+ note: "Integers must be within the safe integer range.",
1848
+ inst,
1849
+ origin,
1850
+ inclusive: true,
1851
+ continue: !def.abort
1852
+ });
1853
+ else payload.issues.push({
1854
+ input,
1855
+ code: "too_small",
1856
+ minimum: Number.MIN_SAFE_INTEGER,
1857
+ note: "Integers must be within the safe integer range.",
1858
+ inst,
1859
+ origin,
1860
+ inclusive: true,
1861
+ continue: !def.abort
1862
+ });
1863
+ return;
1864
+ }
1865
+ }
1866
+ if (input < minimum) payload.issues.push({
1867
+ origin: "number",
1868
+ input,
1869
+ code: "too_small",
1870
+ minimum,
1871
+ inclusive: true,
1872
+ inst,
1873
+ continue: !def.abort
1874
+ });
1875
+ if (input > maximum) payload.issues.push({
1876
+ origin: "number",
1877
+ input,
1878
+ code: "too_big",
1879
+ maximum,
1880
+ inclusive: true,
1881
+ inst,
1882
+ continue: !def.abort
1883
+ });
1884
+ };
1885
+ });
1681
1886
  const $ZodCheckMaxLength = /*@__PURE__*/ $constructor("$ZodCheckMaxLength", (inst, def) => {
1682
1887
  var _a;
1683
1888
  $ZodCheck.init(inst, def);
@@ -1900,6 +2105,30 @@ const $ZodType = /*@__PURE__*/ $constructor("$ZodType", (inst, def) => {
1900
2105
  version: 1
1901
2106
  }));
1902
2107
  });
2108
+ const $ZodNumber = /*@__PURE__*/ $constructor("$ZodNumber", (inst, def) => {
2109
+ $ZodType.init(inst, def);
2110
+ inst._zod.pattern = inst._zod.bag.pattern ?? number$1;
2111
+ inst._zod.parse = (payload, _ctx) => {
2112
+ if (def.coerce) try {
2113
+ payload.value = Number(payload.value);
2114
+ } catch (_) {}
2115
+ const input = payload.value;
2116
+ if (typeof input === "number" && !Number.isNaN(input) && Number.isFinite(input)) return payload;
2117
+ const received = typeof input === "number" ? Number.isNaN(input) ? "NaN" : !Number.isFinite(input) ? "Infinity" : void 0 : void 0;
2118
+ payload.issues.push({
2119
+ expected: "number",
2120
+ code: "invalid_type",
2121
+ input,
2122
+ inst,
2123
+ ...received ? { received } : {}
2124
+ });
2125
+ return payload;
2126
+ };
2127
+ });
2128
+ const $ZodNumberFormat = /*@__PURE__*/ $constructor("$ZodNumberFormat", (inst, def) => {
2129
+ $ZodCheckNumberFormat.init(inst, def);
2130
+ $ZodNumber.init(inst, def);
2131
+ });
1903
2132
  const $ZodUnknown = /*@__PURE__*/ $constructor("$ZodUnknown", (inst, def) => {
1904
2133
  $ZodType.init(inst, def);
1905
2134
  inst._zod.parse = (payload) => payload;
@@ -2321,6 +2550,24 @@ const $ZodEnum = /*@__PURE__*/ $constructor("$ZodEnum", (inst, def) => {
2321
2550
  return payload;
2322
2551
  };
2323
2552
  });
2553
+ const $ZodLiteral = /*@__PURE__*/ $constructor("$ZodLiteral", (inst, def) => {
2554
+ $ZodType.init(inst, def);
2555
+ if (def.values.length === 0) throw new Error("Cannot create literal schema with no valid values");
2556
+ const values = new Set(def.values);
2557
+ inst._zod.values = values;
2558
+ inst._zod.pattern = new RegExp(`^(${def.values.map((o) => typeof o === "string" ? escapeRegex(o) : o ? escapeRegex(o.toString()) : String(o)).join("|")})$`);
2559
+ inst._zod.parse = (payload, _ctx) => {
2560
+ const input = payload.value;
2561
+ if (values.has(input)) return payload;
2562
+ payload.issues.push({
2563
+ code: "invalid_value",
2564
+ values: def.values,
2565
+ input,
2566
+ inst
2567
+ });
2568
+ return payload;
2569
+ };
2570
+ });
2324
2571
  const $ZodTransform = /*@__PURE__*/ $constructor("$ZodTransform", (inst, def) => {
2325
2572
  $ZodType.init(inst, def);
2326
2573
  inst._zod.parse = (payload, ctx) => {
@@ -2592,6 +2839,24 @@ const globalRegistry = globalThis.__zod_globalRegistry;
2592
2839
  //#endregion
2593
2840
  //#region ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/api.js
2594
2841
  // @__NO_SIDE_EFFECTS__
2842
+ function _number(Class, params) {
2843
+ return new Class({
2844
+ type: "number",
2845
+ checks: [],
2846
+ ...normalizeParams(params)
2847
+ });
2848
+ }
2849
+ // @__NO_SIDE_EFFECTS__
2850
+ function _int(Class, params) {
2851
+ return new Class({
2852
+ type: "number",
2853
+ check: "number_format",
2854
+ abort: false,
2855
+ format: "safeint",
2856
+ ...normalizeParams(params)
2857
+ });
2858
+ }
2859
+ // @__NO_SIDE_EFFECTS__
2595
2860
  function _unknown(Class) {
2596
2861
  return new Class({ type: "unknown" });
2597
2862
  }
@@ -2603,6 +2868,50 @@ function _never(Class, params) {
2603
2868
  });
2604
2869
  }
2605
2870
  // @__NO_SIDE_EFFECTS__
2871
+ function _lt(value, params) {
2872
+ return new $ZodCheckLessThan({
2873
+ check: "less_than",
2874
+ ...normalizeParams(params),
2875
+ value,
2876
+ inclusive: false
2877
+ });
2878
+ }
2879
+ // @__NO_SIDE_EFFECTS__
2880
+ function _lte(value, params) {
2881
+ return new $ZodCheckLessThan({
2882
+ check: "less_than",
2883
+ ...normalizeParams(params),
2884
+ value,
2885
+ inclusive: true
2886
+ });
2887
+ }
2888
+ // @__NO_SIDE_EFFECTS__
2889
+ function _gt(value, params) {
2890
+ return new $ZodCheckGreaterThan({
2891
+ check: "greater_than",
2892
+ ...normalizeParams(params),
2893
+ value,
2894
+ inclusive: false
2895
+ });
2896
+ }
2897
+ // @__NO_SIDE_EFFECTS__
2898
+ function _gte(value, params) {
2899
+ return new $ZodCheckGreaterThan({
2900
+ check: "greater_than",
2901
+ ...normalizeParams(params),
2902
+ value,
2903
+ inclusive: true
2904
+ });
2905
+ }
2906
+ // @__NO_SIDE_EFFECTS__
2907
+ function _multipleOf(value, params) {
2908
+ return new $ZodCheckMultipleOf({
2909
+ check: "multiple_of",
2910
+ ...normalizeParams(params),
2911
+ value
2912
+ });
2913
+ }
2914
+ // @__NO_SIDE_EFFECTS__
2606
2915
  function _maxLength(maximum, params) {
2607
2916
  return new $ZodCheckMaxLength({
2608
2917
  check: "max_length",
@@ -2965,6 +3274,31 @@ const createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params)
2965
3274
  };
2966
3275
  //#endregion
2967
3276
  //#region ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/json-schema-processors.js
3277
+ const numberProcessor = (schema, ctx, _json, _params) => {
3278
+ const json = _json;
3279
+ const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
3280
+ if (typeof format === "string" && format.includes("int")) json.type = "integer";
3281
+ else json.type = "number";
3282
+ if (typeof exclusiveMinimum === "number") if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") {
3283
+ json.minimum = exclusiveMinimum;
3284
+ json.exclusiveMinimum = true;
3285
+ } else json.exclusiveMinimum = exclusiveMinimum;
3286
+ if (typeof minimum === "number") {
3287
+ json.minimum = minimum;
3288
+ if (typeof exclusiveMinimum === "number" && ctx.target !== "draft-04") if (exclusiveMinimum >= minimum) delete json.minimum;
3289
+ else delete json.exclusiveMinimum;
3290
+ }
3291
+ if (typeof exclusiveMaximum === "number") if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") {
3292
+ json.maximum = exclusiveMaximum;
3293
+ json.exclusiveMaximum = true;
3294
+ } else json.exclusiveMaximum = exclusiveMaximum;
3295
+ if (typeof maximum === "number") {
3296
+ json.maximum = maximum;
3297
+ if (typeof exclusiveMaximum === "number" && ctx.target !== "draft-04") if (exclusiveMaximum <= maximum) delete json.maximum;
3298
+ else delete json.exclusiveMaximum;
3299
+ }
3300
+ if (typeof multipleOf === "number") json.multipleOf = multipleOf;
3301
+ };
2968
3302
  const neverProcessor = (_schema, _ctx, json, _params) => {
2969
3303
  json.not = {};
2970
3304
  };
@@ -2975,6 +3309,27 @@ const enumProcessor = (schema, _ctx, json, _params) => {
2975
3309
  if (values.every((v) => typeof v === "string")) json.type = "string";
2976
3310
  json.enum = values;
2977
3311
  };
3312
+ const literalProcessor = (schema, ctx, json, _params) => {
3313
+ const def = schema._zod.def;
3314
+ const vals = [];
3315
+ for (const val of def.values) if (val === void 0) {
3316
+ if (ctx.unrepresentable === "throw") throw new Error("Literal `undefined` cannot be represented in JSON Schema");
3317
+ } else if (typeof val === "bigint") if (ctx.unrepresentable === "throw") throw new Error("BigInt literals cannot be represented in JSON Schema");
3318
+ else vals.push(Number(val));
3319
+ else vals.push(val);
3320
+ if (vals.length === 0) {} else if (vals.length === 1) {
3321
+ const val = vals[0];
3322
+ json.type = val === null ? "null" : typeof val;
3323
+ if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") json.enum = [val];
3324
+ else json.const = val;
3325
+ } else {
3326
+ if (vals.every((v) => typeof v === "number")) json.type = "number";
3327
+ if (vals.every((v) => typeof v === "string")) json.type = "string";
3328
+ if (vals.every((v) => typeof v === "boolean")) json.type = "boolean";
3329
+ if (vals.every((v) => v === null)) json.type = "null";
3330
+ json.enum = vals;
3331
+ }
3332
+ };
2978
3333
  const customProcessor = (_schema, ctx, _json, _params) => {
2979
3334
  if (ctx.unrepresentable === "throw") throw new Error("Custom types cannot be represented in JSON Schema");
2980
3335
  };
@@ -3234,6 +3589,42 @@ const ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
3234
3589
  inst.apply = (fn) => fn(inst);
3235
3590
  return inst;
3236
3591
  });
3592
+ const ZodNumber = /*@__PURE__*/ $constructor("ZodNumber", (inst, def) => {
3593
+ $ZodNumber.init(inst, def);
3594
+ ZodType.init(inst, def);
3595
+ inst._zod.processJSONSchema = (ctx, json, params) => numberProcessor(inst, ctx, json, params);
3596
+ inst.gt = (value, params) => inst.check(/* @__PURE__ */ _gt(value, params));
3597
+ inst.gte = (value, params) => inst.check(/* @__PURE__ */ _gte(value, params));
3598
+ inst.min = (value, params) => inst.check(/* @__PURE__ */ _gte(value, params));
3599
+ inst.lt = (value, params) => inst.check(/* @__PURE__ */ _lt(value, params));
3600
+ inst.lte = (value, params) => inst.check(/* @__PURE__ */ _lte(value, params));
3601
+ inst.max = (value, params) => inst.check(/* @__PURE__ */ _lte(value, params));
3602
+ inst.int = (params) => inst.check(int(params));
3603
+ inst.safe = (params) => inst.check(int(params));
3604
+ inst.positive = (params) => inst.check(/* @__PURE__ */ _gt(0, params));
3605
+ inst.nonnegative = (params) => inst.check(/* @__PURE__ */ _gte(0, params));
3606
+ inst.negative = (params) => inst.check(/* @__PURE__ */ _lt(0, params));
3607
+ inst.nonpositive = (params) => inst.check(/* @__PURE__ */ _lte(0, params));
3608
+ inst.multipleOf = (value, params) => inst.check(/* @__PURE__ */ _multipleOf(value, params));
3609
+ inst.step = (value, params) => inst.check(/* @__PURE__ */ _multipleOf(value, params));
3610
+ inst.finite = () => inst;
3611
+ const bag = inst._zod.bag;
3612
+ inst.minValue = Math.max(bag.minimum ?? Number.NEGATIVE_INFINITY, bag.exclusiveMinimum ?? Number.NEGATIVE_INFINITY) ?? null;
3613
+ inst.maxValue = Math.min(bag.maximum ?? Number.POSITIVE_INFINITY, bag.exclusiveMaximum ?? Number.POSITIVE_INFINITY) ?? null;
3614
+ inst.isInt = (bag.format ?? "").includes("int") || Number.isSafeInteger(bag.multipleOf ?? .5);
3615
+ inst.isFinite = true;
3616
+ inst.format = bag.format ?? null;
3617
+ });
3618
+ function number(params) {
3619
+ return /* @__PURE__ */ _number(ZodNumber, params);
3620
+ }
3621
+ const ZodNumberFormat = /*@__PURE__*/ $constructor("ZodNumberFormat", (inst, def) => {
3622
+ $ZodNumberFormat.init(inst, def);
3623
+ ZodNumber.init(inst, def);
3624
+ });
3625
+ function int(params) {
3626
+ return /* @__PURE__ */ _int(ZodNumberFormat, params);
3627
+ }
3237
3628
  const ZodUnknown = /*@__PURE__*/ $constructor("ZodUnknown", (inst, def) => {
3238
3629
  $ZodUnknown.init(inst, def);
3239
3630
  ZodType.init(inst, def);
@@ -3368,9 +3759,27 @@ const ZodEnum = /*@__PURE__*/ $constructor("ZodEnum", (inst, def) => {
3368
3759
  };
3369
3760
  });
3370
3761
  function _enum(values, params) {
3762
+ const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;
3371
3763
  return new ZodEnum({
3372
3764
  type: "enum",
3373
- entries: Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values,
3765
+ entries,
3766
+ ...normalizeParams(params)
3767
+ });
3768
+ }
3769
+ const ZodLiteral = /*@__PURE__*/ $constructor("ZodLiteral", (inst, def) => {
3770
+ $ZodLiteral.init(inst, def);
3771
+ ZodType.init(inst, def);
3772
+ inst._zod.processJSONSchema = (ctx, json, params) => literalProcessor(inst, ctx, json, params);
3773
+ inst.values = new Set(def.values);
3774
+ Object.defineProperty(inst, "value", { get() {
3775
+ if (def.values.length > 1) throw new Error("This schema contains multiple valid literal values. Use `.values` instead.");
3776
+ return def.values[0];
3777
+ } });
3778
+ });
3779
+ function literal(value, params) {
3780
+ return new ZodLiteral({
3781
+ type: "literal",
3782
+ values: Array.isArray(value) ? value : [value],
3374
3783
  ...normalizeParams(params)
3375
3784
  });
3376
3785
  }
@@ -3538,36 +3947,76 @@ function superRefine(fn) {
3538
3947
  return /* @__PURE__ */ _superRefine(fn);
3539
3948
  }
3540
3949
  //#endregion
3950
+ //#region src/domain/validation/errors.ts
3951
+ var ConfigurationError = class extends Error {
3952
+ name = "ConfigurationError";
3953
+ problems;
3954
+ constructor(message, problems = []) {
3955
+ super(message);
3956
+ this.problems = problems;
3957
+ }
3958
+ };
3959
+ var ExecutionFailure = class extends Error {
3960
+ name = "ExecutionFailure";
3961
+ cause;
3962
+ stage;
3963
+ constructor(message, options) {
3964
+ super(message);
3965
+ this.cause = options.cause;
3966
+ this.stage = options.stage;
3967
+ }
3968
+ };
3969
+ var CancelledFailure = class extends Error {
3970
+ name = "CancelledFailure";
3971
+ constructor(message = "Analysis was cancelled.") {
3972
+ super(message);
3973
+ }
3974
+ };
3975
+ //#endregion
3541
3976
  //#region src/config/schema.ts
3542
- const checkerExtensionsConfigReason = "checker extensions are fixed by built-in presets and cannot be configured.";
3543
- const checkerRoutesConfigReason = "checker routes are not supported; configure checker.include with source tsconfig selectors.";
3544
3977
  const unsupportedCheckerPresetReason = "configured checkers require a built-in checker adapter.";
3545
- const checkerEntryConfigReason = "checker.entry has been removed; configure checker.include with source tsconfig selectors.";
3546
3978
  const checkerConfigReason = "config.checkers must be an object auto config or an object keyed by checker name.";
3547
- const checkerAutoStringConfigReason = "checkers: \"auto\" has been removed; omit config.checkers or use { mode: \"auto\" }.";
3548
3979
  const autoCheckerMixedConfigReason = "auto checker config must not be mixed with named checker entries.";
3549
3980
  const autoCheckerModeConfigReason = "auto checker config requires mode: \"auto\".";
3550
3981
  const importAnalysisConfigReason = "config.imports must be an object when configured.";
3551
3982
  const vueImportParserConfigReason = "config.imports.vue must be \"heuristic\" or \"compiler-sfc\".";
3983
+ const checkerConfigKeys = /* @__PURE__ */ new Set([
3984
+ "exclude",
3985
+ "include",
3986
+ "preset"
3987
+ ]);
3988
+ const importAuthorityGrantKeys = /* @__PURE__ */ new Set([
3989
+ "include",
3990
+ "reason",
3991
+ "workspaceRootDependencies"
3992
+ ]);
3993
+ const liminaConfigKeys = /* @__PURE__ */ new Set([
3994
+ "config",
3995
+ "execution",
3996
+ "graph",
3997
+ "package",
3998
+ "pipelines",
3999
+ "proof",
4000
+ "release",
4001
+ "regions",
4002
+ "source"
4003
+ ]);
4004
+ function isAbsolutePublicSelector(value) {
4005
+ const selector = value.trim().replace(/^!+/u, "");
4006
+ return posix.isAbsolute(selector) || /^[A-Za-z]:[\\/]/u.test(selector);
4007
+ }
3552
4008
  const checkerConfigShapeSchema = looseObject({}).superRefine((checker, ctx) => {
3553
4009
  const preset = checker.preset;
3554
4010
  const include = checker.include;
3555
4011
  const exclude = checker.exclude;
3556
- if (Object.hasOwn(checker, "entry")) ctx.addIssue({
3557
- code: "custom",
3558
- message: checkerEntryConfigReason,
3559
- path: ["entry"]
3560
- });
3561
- if (Object.hasOwn(checker, "extensions")) ctx.addIssue({
3562
- code: "custom",
3563
- message: checkerExtensionsConfigReason,
3564
- path: ["extensions"]
3565
- });
3566
- if (Object.hasOwn(checker, "routes")) ctx.addIssue({
3567
- code: "custom",
3568
- message: checkerRoutesConfigReason,
3569
- path: ["routes"]
3570
- });
4012
+ for (const key of Object.keys(checker)) {
4013
+ if (checkerConfigKeys.has(key)) continue;
4014
+ ctx.addIssue({
4015
+ code: "custom",
4016
+ message: "unknown checker config field.",
4017
+ path: [key]
4018
+ });
4019
+ }
3571
4020
  if (typeof preset !== "string" || preset.trim().length === 0) ctx.addIssue({
3572
4021
  code: "custom",
3573
4022
  message: "checker preset must be a non-empty string.",
@@ -3588,6 +4037,11 @@ const checkerConfigShapeSchema = looseObject({}).superRefine((checker, ctx) => {
3588
4037
  message: "checker include entries must be non-empty string paths.",
3589
4038
  path: ["include", index]
3590
4039
  });
4040
+ else if (isAbsolutePublicSelector(value)) ctx.addIssue({
4041
+ code: "custom",
4042
+ message: "checker include entries must be config.rootDir-relative paths; ../ is allowed.",
4043
+ path: ["include", index]
4044
+ });
3591
4045
  if (exclude === void 0) return;
3592
4046
  if (!Array.isArray(exclude)) {
3593
4047
  ctx.addIssue({
@@ -3602,20 +4056,65 @@ const checkerConfigShapeSchema = looseObject({}).superRefine((checker, ctx) => {
3602
4056
  message: "checker exclude entries must be non-empty string paths.",
3603
4057
  path: ["exclude", index]
3604
4058
  });
4059
+ else if (isAbsolutePublicSelector(value)) ctx.addIssue({
4060
+ code: "custom",
4061
+ message: "checker exclude entries must be config.rootDir-relative paths; ../ is allowed.",
4062
+ path: ["exclude", index]
4063
+ });
3605
4064
  });
3606
4065
  function isPlainConfigRecord(value) {
3607
4066
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3608
4067
  }
4068
+ function validateSourcePatternConfig(options) {
4069
+ const patterns = options.source[options.field];
4070
+ if (patterns === void 0) return;
4071
+ if (!Array.isArray(patterns) || patterns.length === 0) {
4072
+ options.ctx.addIssue({
4073
+ code: "custom",
4074
+ message: `config.source.${options.field} must be a non-empty string array.`,
4075
+ path: ["source", options.field]
4076
+ });
4077
+ return;
4078
+ }
4079
+ let defaultTokenCount = 0;
4080
+ for (const [index, value] of patterns.entries()) {
4081
+ if (typeof value !== "string" || value.trim().length === 0) {
4082
+ options.ctx.addIssue({
4083
+ code: "custom",
4084
+ message: `config.source.${options.field} entries must be non-empty strings.`,
4085
+ path: [
4086
+ "source",
4087
+ options.field,
4088
+ index
4089
+ ]
4090
+ });
4091
+ continue;
4092
+ }
4093
+ if (value !== "..." && isAbsolutePublicSelector(value)) {
4094
+ options.ctx.addIssue({
4095
+ code: "custom",
4096
+ message: `config.source.${options.field} entries must be config.rootDir-relative paths; ../ is allowed.`,
4097
+ path: [
4098
+ "source",
4099
+ options.field,
4100
+ index
4101
+ ]
4102
+ });
4103
+ continue;
4104
+ }
4105
+ if (value === "...") defaultTokenCount += 1;
4106
+ }
4107
+ if (defaultTokenCount > 1) options.ctx.addIssue({
4108
+ code: "custom",
4109
+ message: `config.source.${options.field} may contain "..." at most once.`,
4110
+ path: ["source", options.field]
4111
+ });
4112
+ }
3609
4113
  const sharedLiminaConfigShapeSchema = looseObject({}).superRefine((sharedConfig, ctx) => {
3610
4114
  const checkers = sharedConfig.checkers;
3611
4115
  const imports = sharedConfig.imports;
3612
4116
  const source = sharedConfig.source;
3613
- if (checkers !== void 0) if (checkers === "auto") ctx.addIssue({
3614
- code: "custom",
3615
- message: checkerAutoStringConfigReason,
3616
- path: ["checkers"]
3617
- });
3618
- else if (!isPlainConfigRecord(checkers)) ctx.addIssue({
4117
+ if (checkers !== void 0) if (!isPlainConfigRecord(checkers)) ctx.addIssue({
3619
4118
  code: "custom",
3620
4119
  message: checkerConfigReason,
3621
4120
  path: ["checkers"]
@@ -3642,6 +4141,15 @@ const sharedLiminaConfigShapeSchema = looseObject({}).superRefine((sharedConfig,
3642
4141
  index
3643
4142
  ]
3644
4143
  });
4144
+ else if (isAbsolutePublicSelector(value)) ctx.addIssue({
4145
+ code: "custom",
4146
+ message: "auto checker exclude entries must be config.rootDir-relative paths; ../ is allowed.",
4147
+ path: [
4148
+ "checkers",
4149
+ "exclude",
4150
+ index
4151
+ ]
4152
+ });
3645
4153
  }
3646
4154
  for (const key of Object.keys(checkers)) {
3647
4155
  if (key === "mode" || key === "exclude") continue;
@@ -3652,6 +4160,14 @@ const sharedLiminaConfigShapeSchema = looseObject({}).superRefine((sharedConfig,
3652
4160
  });
3653
4161
  }
3654
4162
  } else for (const [checkerName, checker] of Object.entries(checkers)) {
4163
+ if (checkerName.length === 0 || checkerName === "." || checkerName === ".." || checkerName.includes("/") || checkerName.includes("\\")) {
4164
+ ctx.addIssue({
4165
+ code: "custom",
4166
+ message: "checker names must be non-empty path-safe identifiers without slash segments.",
4167
+ path: ["checkers", checkerName]
4168
+ });
4169
+ continue;
4170
+ }
3655
4171
  const result = checkerConfigShapeSchema.safeParse(checker);
3656
4172
  if (result.success) continue;
3657
4173
  for (const issue of result.error.issues) ctx.addIssue({
@@ -3692,8 +4208,18 @@ const sharedLiminaConfigShapeSchema = looseObject({}).superRefine((sharedConfig,
3692
4208
  path: ["source", key]
3693
4209
  });
3694
4210
  }
4211
+ validateSourcePatternConfig({
4212
+ ctx,
4213
+ field: "include",
4214
+ source
4215
+ });
4216
+ validateSourcePatternConfig({
4217
+ ctx,
4218
+ field: "exclude",
4219
+ source
4220
+ });
3695
4221
  });
3696
- const releaseConfigShapeSchema = looseObject({ contentHash: looseObject({}).superRefine((contentHash, ctx) => {
4222
+ const releaseContentHashShapeSchema = looseObject({}).superRefine((contentHash, ctx) => {
3697
4223
  const baselineTag = contentHash.baselineTag;
3698
4224
  if (baselineTag !== void 0 && typeof baselineTag !== "function" && (typeof baselineTag !== "string" || baselineTag.trim().length === 0)) ctx.addIssue({
3699
4225
  code: "custom",
@@ -3724,40 +4250,84 @@ const releaseConfigShapeSchema = looseObject({ contentHash: looseObject({}).supe
3724
4250
  path: ["ignore", index]
3725
4251
  });
3726
4252
  }
3727
- }).optional() });
3728
- const executionConcurrencyFields = [
3729
- "tasks",
3730
- "checkerBuild",
3731
- "checkerTypecheck",
3732
- "packageEntries",
3733
- "releaseEntries"
3734
- ];
3735
- function isValidExecutionConcurrencyValue(value) {
3736
- return value === "auto" || typeof value === "number" && Number.isInteger(value) && value > 0;
3737
- }
3738
- const executionConfigShapeSchema = looseObject({}).superRefine((execution, ctx) => {
3739
- for (const key of Object.keys(execution)) {
3740
- if (key === "failFast" || executionConcurrencyFields.includes(key)) continue;
4253
+ });
4254
+ const releaseNpmPackageJsonLintSeverities = /* @__PURE__ */ new Set([
4255
+ "error",
4256
+ "off",
4257
+ "warning"
4258
+ ]);
4259
+ function isReleaseNpmPackageJsonLintRuleConfig(value) {
4260
+ if (typeof value === "string" && releaseNpmPackageJsonLintSeverities.has(value)) return true;
4261
+ if (!Array.isArray(value) || value.length !== 2) return false;
4262
+ const [severity, ruleOptions] = value;
4263
+ return typeof severity === "string" && releaseNpmPackageJsonLintSeverities.has(severity) && (Array.isArray(ruleOptions) || isPlainConfigRecord(ruleOptions));
4264
+ }
4265
+ const releaseNpmPackageJsonLintShapeSchema = unknown().superRefine((npmPackageJsonLint, ctx) => {
4266
+ if (typeof npmPackageJsonLint === "boolean") return;
4267
+ if (!isPlainConfigRecord(npmPackageJsonLint)) {
3741
4268
  ctx.addIssue({
3742
4269
  code: "custom",
3743
- message: "unknown execution config field.",
4270
+ message: "npmPackageJsonLint must be a boolean or object."
4271
+ });
4272
+ return;
4273
+ }
4274
+ for (const key of Object.keys(npmPackageJsonLint)) {
4275
+ if (key === "rules") continue;
4276
+ ctx.addIssue({
4277
+ code: "custom",
4278
+ message: "unknown npmPackageJsonLint config field.",
3744
4279
  path: [key]
3745
4280
  });
3746
4281
  }
3747
- for (const key of executionConcurrencyFields) {
3748
- const value = execution[key];
3749
- if (value === void 0 || isValidExecutionConcurrencyValue(value)) continue;
4282
+ const rules = npmPackageJsonLint.rules;
4283
+ if (rules === void 0) return;
4284
+ if (!isPlainConfigRecord(rules)) {
3750
4285
  ctx.addIssue({
3751
4286
  code: "custom",
3752
- message: "execution concurrency must be a positive integer or \"auto\".",
4287
+ message: "npmPackageJsonLint.rules must be an object.",
4288
+ path: ["rules"]
4289
+ });
4290
+ return;
4291
+ }
4292
+ for (const [ruleName, ruleConfig] of Object.entries(rules)) {
4293
+ if (ruleName.trim().length === 0) {
4294
+ ctx.addIssue({
4295
+ code: "custom",
4296
+ message: "npmPackageJsonLint rule names must be non-empty strings.",
4297
+ path: ["rules"]
4298
+ });
4299
+ continue;
4300
+ }
4301
+ if (isReleaseNpmPackageJsonLintRuleConfig(ruleConfig)) continue;
4302
+ ctx.addIssue({
4303
+ code: "custom",
4304
+ message: "rule config must be \"off\", \"warning\", \"error\", or a [severity, options] tuple.",
4305
+ path: ["rules", ruleName]
4306
+ });
4307
+ }
4308
+ });
4309
+ const releaseConfigShapeSchema = looseObject({
4310
+ contentHash: releaseContentHashShapeSchema.optional(),
4311
+ npmPackageJsonLint: releaseNpmPackageJsonLintShapeSchema.optional()
4312
+ });
4313
+ const executionConcurrencyError = "execution concurrency must be a positive integer or \"auto\".";
4314
+ const executionConcurrencySchema = union([literal("auto", { error: executionConcurrencyError }), number({ error: executionConcurrencyError }).int({ error: executionConcurrencyError }).positive({ error: executionConcurrencyError })], { error: executionConcurrencyError });
4315
+ const executionConfigShape = {
4316
+ checkerBuild: executionConcurrencySchema.optional(),
4317
+ checkerTypecheck: executionConcurrencySchema.optional(),
4318
+ packageEntries: executionConcurrencySchema.optional(),
4319
+ releaseEntries: executionConcurrencySchema.optional(),
4320
+ tasks: executionConcurrencySchema.optional()
4321
+ };
4322
+ const executionConfigShapeSchema = looseObject(executionConfigShape).superRefine((execution, ctx) => {
4323
+ for (const key of Object.keys(execution)) {
4324
+ if (Object.hasOwn(executionConfigShape, key)) continue;
4325
+ ctx.addIssue({
4326
+ code: "custom",
4327
+ message: "unknown execution config field.",
3753
4328
  path: [key]
3754
4329
  });
3755
4330
  }
3756
- if (execution.failFast !== void 0 && typeof execution.failFast !== "boolean") ctx.addIssue({
3757
- code: "custom",
3758
- message: "execution failFast must be a boolean.",
3759
- path: ["failFast"]
3760
- });
3761
4331
  });
3762
4332
  function validateStringArrayField(options) {
3763
4333
  if (options.value === void 0) {
@@ -3798,10 +4368,10 @@ function validateSourceImportAuthorityConfig(value, ctx) {
3798
4368
  }
3799
4369
  const allow = value.allow;
3800
4370
  if (allow === void 0) return;
3801
- if (!Array.isArray(allow)) {
4371
+ if (Array.isArray(allow) || !isPlainConfigRecord(allow)) {
3802
4372
  ctx.addIssue({
3803
4373
  code: "custom",
3804
- message: "importAuthority.allow must be an array.",
4374
+ message: "allow must be an object keyed by source owner identity.\n fix: use allow: { \"@scope/package\": [{ include: [\"test/**/*.ts\"], workspaceRootDependencies: [\"@example/fixture\"], reason: \"...\" }] }.",
3805
4375
  path: [
3806
4376
  "source",
3807
4377
  "importAuthority",
@@ -3810,69 +4380,258 @@ function validateSourceImportAuthorityConfig(value, ctx) {
3810
4380
  });
3811
4381
  return;
3812
4382
  }
3813
- for (const [index, entry] of allow.entries()) {
3814
- const path = [
4383
+ for (const [ownerIdentity, grants] of Object.entries(allow)) {
4384
+ const ownerPath = [
3815
4385
  "source",
3816
4386
  "importAuthority",
3817
4387
  "allow",
3818
- index
4388
+ ownerIdentity
3819
4389
  ];
3820
- if (!isPlainConfigRecord(entry)) {
4390
+ if (ownerIdentity.trim().length === 0) ctx.addIssue({
4391
+ code: "custom",
4392
+ message: "allow keys must be non-empty source owner identities.",
4393
+ path: ownerPath
4394
+ });
4395
+ if (!Array.isArray(grants)) {
4396
+ ctx.addIssue({
4397
+ code: "custom",
4398
+ message: "allow owner entries must be arrays of grants.",
4399
+ path: ownerPath
4400
+ });
4401
+ continue;
4402
+ }
4403
+ for (const [index, grant] of grants.entries()) {
4404
+ const grantPath = [...ownerPath, index];
4405
+ if (!isPlainConfigRecord(grant)) {
4406
+ ctx.addIssue({
4407
+ code: "custom",
4408
+ message: "importAuthority allow grants must be objects with workspaceRootDependencies and reason fields.",
4409
+ path: grantPath
4410
+ });
4411
+ continue;
4412
+ }
4413
+ for (const key of Object.keys(grant)) {
4414
+ if (importAuthorityGrantKeys.has(key)) continue;
4415
+ ctx.addIssue({
4416
+ code: "custom",
4417
+ message: "unknown source import authority grant field.",
4418
+ path: [...grantPath, key]
4419
+ });
4420
+ }
4421
+ validateStringArrayField({
4422
+ ctx,
4423
+ path: [...grantPath, "workspaceRootDependencies"],
4424
+ required: true,
4425
+ value: grant.workspaceRootDependencies,
4426
+ valueName: "workspaceRootDependencies"
4427
+ });
4428
+ validateStringArrayField({
4429
+ ctx,
4430
+ path: [...grantPath, "include"],
4431
+ value: grant.include,
4432
+ valueName: "include"
4433
+ });
4434
+ if (Array.isArray(grant.include)) {
4435
+ for (const [includeIndex, include] of grant.include.entries()) if (typeof include === "string" && include.trim().length > 0 && isAbsolutePublicSelector(include)) ctx.addIssue({
4436
+ code: "custom",
4437
+ message: "source.importAuthority allow include entries must be config.rootDir-relative paths; ../ is allowed.",
4438
+ path: [
4439
+ ...grantPath,
4440
+ "include",
4441
+ includeIndex
4442
+ ]
4443
+ });
4444
+ }
4445
+ if (typeof grant.reason !== "string" || grant.reason.trim().length === 0) ctx.addIssue({
4446
+ code: "custom",
4447
+ message: "reason must be a non-empty string.",
4448
+ path: [...grantPath, "reason"]
4449
+ });
4450
+ }
4451
+ }
4452
+ }
4453
+ function validateSourceDeclarationsConfig(value, ctx) {
4454
+ if (value === void 0) return;
4455
+ const declarationsPath = ["source", "declarations"];
4456
+ if (!isPlainConfigRecord(value)) {
4457
+ ctx.addIssue({
4458
+ code: "custom",
4459
+ message: "declarations must be an object.",
4460
+ path: declarationsPath
4461
+ });
4462
+ return;
4463
+ }
4464
+ for (const key of Object.keys(value)) if (key !== "ambient") ctx.addIssue({
4465
+ code: "custom",
4466
+ message: "unknown source declarations config field.",
4467
+ path: [...declarationsPath, key]
4468
+ });
4469
+ if (value.ambient === void 0) return;
4470
+ const ambientPath = [...declarationsPath, "ambient"];
4471
+ if (!Array.isArray(value.ambient)) {
4472
+ ctx.addIssue({
4473
+ code: "custom",
4474
+ message: "ambient must be an array.",
4475
+ path: ambientPath
4476
+ });
4477
+ return;
4478
+ }
4479
+ for (const [index, rule] of value.ambient.entries()) {
4480
+ const rulePath = [...ambientPath, index];
4481
+ if (!isPlainConfigRecord(rule)) {
3821
4482
  ctx.addIssue({
3822
4483
  code: "custom",
3823
- message: "importAuthority allow entries must be objects with files, packages or specifiers, and reason fields.",
3824
- path
4484
+ message: "ambient declaration rules must be objects.",
4485
+ path: rulePath
3825
4486
  });
3826
4487
  continue;
3827
4488
  }
4489
+ for (const key of Object.keys(rule)) if (![
4490
+ "include",
4491
+ "allowSharedAcrossOwners",
4492
+ "allowTripleSlashReferences",
4493
+ "reason"
4494
+ ].includes(key)) ctx.addIssue({
4495
+ code: "custom",
4496
+ message: "unknown ambient declaration rule field.",
4497
+ path: [...rulePath, key]
4498
+ });
3828
4499
  validateStringArrayField({
3829
4500
  ctx,
3830
- path: [...path, "files"],
4501
+ path: [...rulePath, "include"],
3831
4502
  required: true,
3832
- value: entry.files,
3833
- valueName: "files"
4503
+ value: rule.include,
4504
+ valueName: "ambient declaration include"
3834
4505
  });
3835
- const hasPackages = validateStringArrayField({
3836
- ctx,
3837
- path: [...path, "packages"],
3838
- value: entry.packages,
3839
- valueName: "packages"
4506
+ if (Array.isArray(rule.include)) {
4507
+ for (const [includeIndex, include] of rule.include.entries()) if (typeof include === "string" && include.trim().length > 0 && isAbsolutePublicSelector(include)) ctx.addIssue({
4508
+ code: "custom",
4509
+ message: "ambient declaration include entries must be config.rootDir-relative paths; ../ is allowed.",
4510
+ path: [
4511
+ ...rulePath,
4512
+ "include",
4513
+ includeIndex
4514
+ ]
4515
+ });
4516
+ }
4517
+ if (typeof rule.reason !== "string" || rule.reason.trim().length === 0) ctx.addIssue({
4518
+ code: "custom",
4519
+ message: "ambient declaration reason must be a non-empty string.",
4520
+ path: [...rulePath, "reason"]
3840
4521
  });
3841
- const hasSpecifiers = validateStringArrayField({
3842
- ctx,
3843
- path: [...path, "specifiers"],
3844
- value: entry.specifiers,
3845
- valueName: "specifiers"
4522
+ for (const key of ["allowSharedAcrossOwners", "allowTripleSlashReferences"]) if (rule[key] !== void 0 && typeof rule[key] !== "boolean") ctx.addIssue({
4523
+ code: "custom",
4524
+ message: `${key} must be a boolean.`,
4525
+ path: [...rulePath, key]
4526
+ });
4527
+ }
4528
+ }
4529
+ const regionExcludeKinds = ["workspace-package", "package-scope"];
4530
+ function validateRegionsConfig(value, ctx) {
4531
+ if (value === void 0) return;
4532
+ if (!isPlainConfigRecord(value)) {
4533
+ ctx.addIssue({
4534
+ code: "custom",
4535
+ message: "regions config must be an object.",
4536
+ path: ["regions"]
4537
+ });
4538
+ return;
4539
+ }
4540
+ for (const key of Object.keys(value)) {
4541
+ if (key === "exclude" || key === "extendNestedPackageScopes") continue;
4542
+ ctx.addIssue({
4543
+ code: "custom",
4544
+ message: "unknown regions config field.",
4545
+ path: ["regions", key]
4546
+ });
4547
+ }
4548
+ if (value.extendNestedPackageScopes !== void 0 && typeof value.extendNestedPackageScopes !== "boolean") ctx.addIssue({
4549
+ code: "custom",
4550
+ message: "regions.extendNestedPackageScopes must be a boolean.",
4551
+ path: ["regions", "extendNestedPackageScopes"]
4552
+ });
4553
+ const exclude = value.exclude;
4554
+ if (exclude === void 0) return;
4555
+ if (!Array.isArray(exclude)) {
4556
+ ctx.addIssue({
4557
+ code: "custom",
4558
+ message: "regions.exclude must be an array.",
4559
+ path: ["regions", "exclude"]
4560
+ });
4561
+ return;
4562
+ }
4563
+ for (const [index, entry] of exclude.entries()) {
4564
+ const entryPath = [
4565
+ "regions",
4566
+ "exclude",
4567
+ index
4568
+ ];
4569
+ if (!isPlainConfigRecord(entry)) {
4570
+ ctx.addIssue({
4571
+ code: "custom",
4572
+ message: "regions.exclude entries must be objects with kind, include, and reason fields.",
4573
+ path: entryPath
4574
+ });
4575
+ continue;
4576
+ }
4577
+ for (const key of Object.keys(entry)) {
4578
+ if (key === "include" || key === "kind" || key === "reason") continue;
4579
+ ctx.addIssue({
4580
+ code: "custom",
4581
+ message: "unknown regions.exclude entry field.",
4582
+ path: [...entryPath, key]
4583
+ });
4584
+ }
4585
+ if (!Object.hasOwn(entry, "kind")) ctx.addIssue({
4586
+ code: "custom",
4587
+ message: `regions.exclude[${index}].kind is required.`,
4588
+ path: [...entryPath, "kind"]
3846
4589
  });
3847
- if (!hasPackages && !hasSpecifiers) ctx.addIssue({
4590
+ else if (typeof entry.kind !== "string" || !regionExcludeKinds.includes(entry.kind)) ctx.addIssue({
3848
4591
  code: "custom",
3849
- message: "importAuthority allow entries must declare packages or specifiers.",
3850
- path
4592
+ message: [`regions.exclude[${index}].kind must be one of:`, ...regionExcludeKinds.map((kind) => ` ${kind}`)].join("\n"),
4593
+ path: [...entryPath, "kind"]
3851
4594
  });
3852
- if (entry.owner !== void 0) {
3853
- if (typeof entry.owner !== "string" || entry.owner.trim().length === 0) ctx.addIssue({
4595
+ validateStringArrayField({
4596
+ ctx,
4597
+ path: [...entryPath, "include"],
4598
+ required: true,
4599
+ value: entry.include,
4600
+ valueName: "regions.exclude.include"
4601
+ });
4602
+ if (Array.isArray(entry.include)) {
4603
+ for (const [includeIndex, include] of entry.include.entries()) if (typeof include === "string" && include.trim().length > 0 && isAbsolutePublicSelector(include)) ctx.addIssue({
3854
4604
  code: "custom",
3855
- message: "owner must be a non-empty string when configured.",
3856
- path: [...path, "owner"]
4605
+ message: "regions.exclude.include entries must be config.rootDir-relative paths; ../ is allowed.",
4606
+ path: [
4607
+ ...entryPath,
4608
+ "include",
4609
+ includeIndex
4610
+ ]
3857
4611
  });
3858
4612
  }
3859
4613
  if (typeof entry.reason !== "string" || entry.reason.trim().length === 0) ctx.addIssue({
3860
4614
  code: "custom",
3861
4615
  message: "reason must be a non-empty string.",
3862
- path: [...path, "reason"]
4616
+ path: [...entryPath, "reason"]
3863
4617
  });
3864
4618
  }
3865
4619
  }
3866
4620
  const liminaConfigShapeSchema = looseObject({
3867
4621
  config: sharedLiminaConfigShapeSchema.optional(),
3868
4622
  execution: executionConfigShapeSchema.optional(),
4623
+ regions: unknown().optional(),
3869
4624
  release: releaseConfigShapeSchema.optional()
3870
4625
  }).superRefine((config, ctx) => {
3871
- if (Object.hasOwn(config, "paths")) ctx.addIssue({
3872
- code: "custom",
3873
- message: "paths config has been removed; use graph/proof/source checks instead.",
3874
- path: ["paths"]
3875
- });
4626
+ for (const key of Object.keys(config)) {
4627
+ if (liminaConfigKeys.has(key)) continue;
4628
+ ctx.addIssue({
4629
+ code: "custom",
4630
+ message: "unknown Limina config field.",
4631
+ path: [key]
4632
+ });
4633
+ }
4634
+ validateRegionsConfig(config.regions, ctx);
3876
4635
  const source = config.source;
3877
4636
  if (source === void 0) return;
3878
4637
  if (!isPlainConfigRecord(source)) {
@@ -3884,7 +4643,7 @@ const liminaConfigShapeSchema = looseObject({
3884
4643
  return;
3885
4644
  }
3886
4645
  for (const key of Object.keys(source)) {
3887
- if (key === "knip" || key === "importAuthority") continue;
4646
+ if (key === "knip" || key === "importAuthority" || key === "declarations") continue;
3888
4647
  ctx.addIssue({
3889
4648
  code: "custom",
3890
4649
  message: "unknown source config field.",
@@ -3892,9 +4651,10 @@ const liminaConfigShapeSchema = looseObject({
3892
4651
  });
3893
4652
  }
3894
4653
  validateSourceImportAuthorityConfig(source.importAuthority, ctx);
4654
+ validateSourceDeclarationsConfig(source.declarations, ctx);
3895
4655
  });
3896
4656
  function formatZodPath(pathSegments) {
3897
- return pathSegments.map((segment) => typeof segment === "number" ? `[${segment}]` : `.${String(segment)}`).join("").replace(/^\./u, "");
4657
+ return pathSegments.map((segment) => typeof segment === "number" ? `[${segment}]` : /^[A-Za-z_$][\w$]*$/u.test(String(segment)) ? `.${String(segment)}` : `[${JSON.stringify(String(segment))}]`).join("").replace(/^\./u, "");
3898
4658
  }
3899
4659
  function getValueAtPath(value, pathSegments) {
3900
4660
  let current = value;
@@ -3914,24 +4674,12 @@ function formatLiminaConfigShapeIssue(value, issue) {
3914
4674
  ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
3915
4675
  " reason: config must be an object."
3916
4676
  ].join("\n");
3917
- if (field === "paths") return [
3918
- "Invalid Limina paths config:",
3919
- " field: paths",
3920
- ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
3921
- ` reason: ${issue.message}`
3922
- ].join("\n");
3923
4677
  if (field === "execution") return [
3924
4678
  "Invalid Limina execution config:",
3925
4679
  " field: execution",
3926
4680
  ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
3927
4681
  " reason: execution must be an object."
3928
4682
  ].join("\n");
3929
- if (field === "execution.failFast") return [
3930
- "Invalid Limina execution config:",
3931
- " field: execution.failFast",
3932
- ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
3933
- " reason: execution failFast must be a boolean."
3934
- ].join("\n");
3935
4683
  if (field.startsWith("execution.")) return [
3936
4684
  "Invalid Limina execution config:",
3937
4685
  ` field: ${field}`,
@@ -3968,6 +4716,12 @@ function formatLiminaConfigShapeIssue(value, issue) {
3968
4716
  ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
3969
4717
  ` reason: ${issue.message}`
3970
4718
  ].join("\n");
4719
+ if (field === "source.importAuthority" || field.startsWith("source.importAuthority.")) return [
4720
+ "Invalid source import authority config:",
4721
+ ` field: ${field}`,
4722
+ ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
4723
+ ` reason: ${issue.message}`
4724
+ ].join("\n");
3971
4725
  if (field === "source" || field.startsWith("source.")) return [
3972
4726
  "Invalid Limina source config:",
3973
4727
  ` field: ${field}`,
@@ -3997,21 +4751,9 @@ function formatLiminaConfigShapeIssue(value, issue) {
3997
4751
  " reason: checker preset must be a non-empty string."
3998
4752
  ].join("\n");
3999
4753
  }
4000
- if (pathSegments[3] === "entry") return [
4001
- "Invalid Limina checker entry config:",
4002
- ` field: ${checkerField}.entry`,
4003
- ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
4004
- ` reason: ${issue.message}`
4005
- ].join("\n");
4006
- if (pathSegments[3] === "extensions") return [
4754
+ return [
4007
4755
  "Invalid Limina checker config:",
4008
- ` field: ${checkerField}.extensions`,
4009
- ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
4010
- ` reason: ${issue.message}`
4011
- ].join("\n");
4012
- if (pathSegments[3] === "routes") return [
4013
- "Invalid Limina checker config:",
4014
- ` field: ${checkerField}.routes`,
4756
+ ` field: ${field}`,
4015
4757
  ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
4016
4758
  ` reason: ${issue.message}`
4017
4759
  ].join("\n");
@@ -4028,6 +4770,12 @@ function formatLiminaConfigShapeIssue(value, issue) {
4028
4770
  ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
4029
4771
  " reason: release.contentHash must be an object."
4030
4772
  ].join("\n");
4773
+ if (field === "release.npmPackageJsonLint" || field.startsWith("release.npmPackageJsonLint.")) return [
4774
+ "Invalid Limina release config:",
4775
+ ` field: ${field}`,
4776
+ ` value: ${formatUnknownValue(getValueAtPath(value, pathSegments))}`,
4777
+ ` reason: ${issue.message}`
4778
+ ].join("\n");
4031
4779
  if (field === "release.contentHash.baselineTag") return [
4032
4780
  "Invalid Limina release config:",
4033
4781
  " field: release.contentHash.baselineTag",
@@ -4066,18 +4814,32 @@ function collectLiminaConfigShapeProblems(value) {
4066
4814
  }
4067
4815
  function validateLiminaConfig(config) {
4068
4816
  const problems = collectLiminaConfigShapeProblems(config);
4069
- if (problems.length > 0) throw new Error(problems.join("\n\n"));
4817
+ if (problems.length > 0) throw new ConfigurationError(problems.join("\n\n"), problems.map((message) => ({
4818
+ message,
4819
+ path: []
4820
+ })));
4070
4821
  }
4071
4822
  //#endregion
4072
4823
  //#region src/config/runner.ts
4073
4824
  function isAutoCheckerConfigMode(checkers) {
4074
4825
  return Boolean(checkers && checkers.mode === "auto");
4075
4826
  }
4827
+ const DEFAULT_LIMINA_CONFIG_FILES = [
4828
+ "limina.config.mts",
4829
+ "limina.config.mjs",
4830
+ "limina.config.ts",
4831
+ "limina.config.js"
4832
+ ];
4076
4833
  function defineConfig(config) {
4077
4834
  return config;
4078
4835
  }
4079
4836
  function getActiveCheckers(config) {
4080
- validateLiminaConfig(config);
4837
+ if ("configPath" in config && "rootDir" in config) {
4838
+ const userConfig = { ...config };
4839
+ delete userConfig.configPath;
4840
+ delete userConfig.rootDir;
4841
+ validateLiminaConfig(userConfig);
4842
+ } else validateLiminaConfig(config);
4081
4843
  return getResolvedCheckers(config);
4082
4844
  }
4083
4845
  function normalizeConfig(value) {
@@ -4104,8 +4866,10 @@ function findLiminaConfigPath(startDir, rootDir) {
4104
4866
  let currentDir = posix.resolve(startDir);
4105
4867
  const workspaceRootDir = posix.resolve(rootDir);
4106
4868
  while (isPathInsideDirectory(currentDir, workspaceRootDir)) {
4107
- const candidatePath = posix.join(currentDir, "limina.config.mjs");
4108
- if (existsSync(candidatePath)) return candidatePath;
4869
+ for (const fileName of DEFAULT_LIMINA_CONFIG_FILES) {
4870
+ const candidatePath = posix.join(currentDir, fileName);
4871
+ if (existsSync(candidatePath)) return candidatePath;
4872
+ }
4109
4873
  if (currentDir === workspaceRootDir) return null;
4110
4874
  const parentDir = posix.dirname(currentDir);
4111
4875
  if (parentDir === currentDir) return null;
@@ -4125,32 +4889,91 @@ function validateConfigPathInsideWorkspace(configPath, rootDir) {
4125
4889
  async function resolveConfigExport(configExport, configEnv) {
4126
4890
  return normalizeConfig(typeof configExport === "function" ? await configExport(configEnv) : await configExport);
4127
4891
  }
4128
- function hasSourceImportAuthorityPackageRules(config) {
4129
- return Boolean(config.source?.importAuthority?.allow?.some((rule) => rule.packages?.some((packageName) => packageName.trim().length > 0)));
4892
+ function hasSourceImportAuthorityWorkspaceRootGrants(config) {
4893
+ const allow = config.source?.importAuthority?.allow;
4894
+ if (!allow) return false;
4895
+ return Boolean(Object.values(allow).some((grants) => grants.some((grant) => grant.workspaceRootDependencies.some((packageName) => packageName.trim().length > 0))));
4130
4896
  }
4131
4897
  function validateRootPackageImportAuthorityConfig(config, rootDir) {
4132
- if (!hasSourceImportAuthorityPackageRules(config)) return;
4898
+ if (!hasSourceImportAuthorityWorkspaceRootGrants(config)) return;
4133
4899
  if (existsSync(posix.join(rootDir, "package.json"))) return;
4134
4900
  throw new Error([
4135
- "Invalid Limina source config:",
4136
- " field: source.importAuthority.allow[].packages",
4137
- ` value: ${JSON.stringify(config.source?.importAuthority?.allow)}`,
4138
- " reason: package allow rules enable workspace root package.json as a dependency authority manifest, but no package.json exists at the workspace root."
4901
+ "Invalid source import authority config:",
4902
+ " field: source.importAuthority.allow",
4903
+ " reason: workspaceRootDependencies grants require a workspace root package.json.",
4904
+ " fix: create a workspace root package.json, or remove workspaceRootDependencies grants."
4139
4905
  ].join("\n"));
4140
4906
  }
4907
+ function resolveConfigLoader(configLoader) {
4908
+ if (configLoader === void 0 || configLoader === "native") return "native";
4909
+ if (configLoader === "tsx") return "tsx";
4910
+ throw new Error(`Unsupported Limina config loader "${String(configLoader)}". Expected one of: native, tsx.`);
4911
+ }
4912
+ function getErrorMessage(error) {
4913
+ return error instanceof Error ? error.message : String(error);
4914
+ }
4915
+ function getErrorCode(error) {
4916
+ return typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
4917
+ }
4918
+ function shouldSuggestTsxLoader(error) {
4919
+ const code = getErrorCode(error);
4920
+ const message = getErrorMessage(error);
4921
+ const stack = error instanceof Error ? error.stack : void 0;
4922
+ return code === "ERR_UNKNOWN_FILE_EXTENSION" || code === "ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX" || message.includes("Unknown file extension \".ts\"") || message.includes("Unknown file extension \".mts\"") || message.includes("Cannot find module") || typeof stack === "string" && stack.includes("node:internal/modules/esm/translators");
4923
+ }
4924
+ function unwrapModuleDefault(module) {
4925
+ if (typeof module === "object" && module !== null && "default" in module && Object.prototype.toString.call(module) === "[object Module]") return module.default;
4926
+ return module;
4927
+ }
4928
+ function unwrapTsxConfigExport(module) {
4929
+ const value = unwrapModuleDefault(module);
4930
+ if (typeof value === "object" && value !== null && "default" in value && Object.prototype.toString.call(value) === "[object Object]" && Object.keys(value).length === 1) return value.default;
4931
+ return value;
4932
+ }
4933
+ async function nativeImportConfig(configPath) {
4934
+ const url = pathToFileURL(configPath);
4935
+ url.searchParams.set("t", String(Date.now()));
4936
+ try {
4937
+ return unwrapModuleDefault(await import(url.href));
4938
+ } catch (error) {
4939
+ if (shouldSuggestTsxLoader(error)) throw new Error([
4940
+ "Failed to load the Limina config file with the native loader.",
4941
+ "Try setting the --config-loader CLI flag to `tsx`.",
4942
+ "",
4943
+ getErrorMessage(error)
4944
+ ].join("\n"), { cause: error });
4945
+ throw error;
4946
+ }
4947
+ }
4948
+ async function tsxImportConfig(configPath) {
4949
+ let tsxApi;
4950
+ try {
4951
+ tsxApi = await import("tsx/esm/api");
4952
+ } catch (error) {
4953
+ throw new Error(["Failed to load the Limina config file with the tsx loader.", "Please install `tsx` in the current workspace before using --config-loader tsx."].join("\n"), { cause: error });
4954
+ }
4955
+ return unwrapTsxConfigExport(await tsxApi.tsImport(pathToFileURL(configPath).href, import.meta.url));
4956
+ }
4957
+ async function loadConfigModule(configPath, configLoader) {
4958
+ return resolveConfigLoader(configLoader) === "native" ? nativeImportConfig(configPath) : tsxImportConfig(configPath);
4959
+ }
4960
+ function formatDefaultConfigFileList() {
4961
+ return DEFAULT_LIMINA_CONFIG_FILES.map((fileName) => `"${fileName}"`).join(", ");
4962
+ }
4141
4963
  async function loadConfig(options = {}) {
4142
4964
  const cwd = options.cwd ? posix.resolve(options.cwd) : process.cwd();
4143
- const rootDir = inferWorkspaceRoot(cwd);
4144
- const configPath = options.configPath ? posix.resolve(cwd, options.configPath) : findLiminaConfigPath(cwd, rootDir);
4145
- if (configPath) validateConfigPathInsideWorkspace(configPath, rootDir);
4146
- if (!configPath || !existsSync(configPath)) throw new Error(options.configPath ? `Unable to find limina config at ${configPath}` : `Unable to find limina config. Searched for limina.config.mjs from ${cwd} up to the pnpm workspace root at ${rootDir}.`);
4147
- const config = await resolveConfigExport((await import(`${pathToFileURL(configPath).href}?t=${Date.now()}`)).default, createConfigEnv(options));
4965
+ const configPath = options.configPath ? posix.resolve(cwd, options.configPath) : void 0;
4966
+ const rootDir = configPath ? inferWorkspaceRoot(posix.dirname(configPath)) : inferWorkspaceRoot(cwd);
4967
+ const resolvedConfigPath = configPath ?? findLiminaConfigPath(cwd, rootDir);
4968
+ if (resolvedConfigPath) validateConfigPathInsideWorkspace(resolvedConfigPath, rootDir);
4969
+ if (!resolvedConfigPath || !existsSync(resolvedConfigPath)) throw new Error(options.configPath ? `Unable to find limina config at ${resolvedConfigPath}` : `Unable to find limina config. Searched for ${formatDefaultConfigFileList()} from ${cwd} up to the pnpm workspace root at ${rootDir}.`);
4970
+ const config = await resolveConfigExport(await loadConfigModule(resolvedConfigPath, options.configLoader), createConfigEnv(options));
4148
4971
  validateRootPackageImportAuthorityConfig(config, rootDir);
4149
4972
  return {
4150
4973
  ...config,
4151
- configPath,
4974
+ configPath: resolvedConfigPath,
4152
4975
  rootDir
4153
4976
  };
4154
4977
  }
4155
4978
  //#endregion
4156
- export { isPackageImportSpecifier as A, resolveRelativeModuleCandidate as C, toPosixPath as D, normalizeSlashes as E, uniqueBy as F, uniqueSortedStrings as I, uniqueTrimmedNonEmptySortedStrings as L, isUrlOrDataOrFileSpecifier as M, isVirtualModuleSpecifier as N, toRelativePath as O, posix as P, uniqueValues as R, resolvePathMappedModuleCandidate as S, normalizeAbsolutePath as T, resolveModuleNameWithCheckers as _, formatUnknownValue as a, resolveBaseUrlModuleCandidate as b, clearCheckerProjectConfigCache as c, getBuildCheckerSupportedExtensions as d, getCheckerAdapter as f, resolveCheckerProjectExtensions as g, parseCheckerProjectConfigForContext as h, loadConfig as i, isRelativeSpecifier as j, isBarePackageSpecifier as k, collectMissingCheckerPeerDependencies as l, normalizeExtensions as m, getActiveCheckers as n, isNonEmptyString as o, getCheckerExtensions as p, isAutoCheckerConfigMode as r, isPlainRecord as s, defineConfig as t, formatMissingCheckerPeerDependencies as u, resolveModuleNameWithCheckersDetailed as v, isPathInsideDirectory as w, resolveExistingFilePath as x, candidatePathsForBasePath as y };
4979
+ export { normalizeAbsolutePathIdentity as A, countDefinedBy as B, candidatePathsForBasePath as C, resolveRelativeModuleCandidate as D, resolvePathMappedModuleCandidate as E, isPackageImportSpecifier as F, uniqueTrimmedNonEmptySortedStrings as H, isRelativeSpecifier as I, isUrlOrDataOrFileSpecifier as L, toPosixPath as M, toRelativePath as N, isPathInsideDirectory as O, isBarePackageSpecifier as P, isVirtualModuleSpecifier as R, resolveModuleNameWithCheckersDetailed as S, resolveExistingFilePath as T, uniqueValues as U, uniqueSortedStrings as V, isBuildCapablePreset as _, CancelledFailure as a, parseCheckerProjectConfigForContext as b, formatUnknownValue as c, collectMissingCheckerPeerDependencies as d, formatMissingCheckerPeerDependencies as f, getCheckerExtensions as g, getCheckerBuildEngine as h, loadConfig as i, normalizeSlashes as j, normalizeAbsolutePath as k, isNonEmptyString as l, getCheckerAdapter as m, getActiveCheckers as n, ConfigurationError as o, getBuildCheckerSupportedExtensions as p, isAutoCheckerConfigMode as r, ExecutionFailure as s, defineConfig as t, isPlainRecord as u, isNativeTypeScriptProjectInput as v, resolveBaseUrlModuleCandidate as w, resolveCheckerProjectExtensions as x, normalizeExtensions as y, posix as z };