@outcomeeng/spx 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +569 -320
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1598,6 +1598,18 @@ import { join as join5 } from "path";
|
|
|
1598
1598
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
1599
1599
|
import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
|
|
1600
1600
|
|
|
1601
|
+
// src/lib/error-message.ts
|
|
1602
|
+
function toMessage(error) {
|
|
1603
|
+
if (error instanceof Error) return error.message;
|
|
1604
|
+
if (typeof error === "string") return error;
|
|
1605
|
+
const fallback = `[${typeof error}]`;
|
|
1606
|
+
try {
|
|
1607
|
+
return JSON.stringify(error) ?? fallback;
|
|
1608
|
+
} catch {
|
|
1609
|
+
return fallback;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1601
1613
|
// src/domains/agent-environment/config.ts
|
|
1602
1614
|
var AGENT_ENVIRONMENT_SECTION = "agentEnvironment";
|
|
1603
1615
|
var AGENT_RUNTIME = {
|
|
@@ -1794,15 +1806,15 @@ function validateRuntimes(raw) {
|
|
|
1794
1806
|
}
|
|
1795
1807
|
return { ok: true, value: runtimes };
|
|
1796
1808
|
}
|
|
1797
|
-
function validateRuntimeConfig(path7, raw,
|
|
1809
|
+
function validateRuntimeConfig(path7, raw, defaults5) {
|
|
1798
1810
|
if (!isRecord(raw)) return { ok: false, error: `${path7} must be an object` };
|
|
1799
1811
|
const unknown = rejectUnknownFields(path7, raw, AGENT_ENVIRONMENT_RUNTIME_CONFIG_ALLOWED_FIELDS);
|
|
1800
1812
|
if (!unknown.ok) return unknown;
|
|
1801
1813
|
const enabledRaw = raw[AGENT_ENVIRONMENT_CONFIG_FIELDS.ENABLED];
|
|
1802
|
-
if (enabledRaw === void 0) return { ok: true, value:
|
|
1814
|
+
if (enabledRaw === void 0) return { ok: true, value: defaults5 };
|
|
1803
1815
|
const enabled = validateBoolean(`${path7}.${AGENT_ENVIRONMENT_CONFIG_FIELDS.ENABLED}`, enabledRaw);
|
|
1804
1816
|
if (!enabled.ok) return enabled;
|
|
1805
|
-
return { ok: true, value: { ...
|
|
1817
|
+
return { ok: true, value: { ...defaults5, enabled: enabled.value } };
|
|
1806
1818
|
}
|
|
1807
1819
|
function validatePluginBootstrap(raw) {
|
|
1808
1820
|
const sectionPath = `${AGENT_ENVIRONMENT_SECTION}.${AGENT_ENVIRONMENT_CONFIG_FIELDS.PLUGIN_BOOTSTRAP}`;
|
|
@@ -1866,8 +1878,8 @@ function validatePluginBootstrap(raw) {
|
|
|
1866
1878
|
}
|
|
1867
1879
|
};
|
|
1868
1880
|
}
|
|
1869
|
-
function validateEntryArray(path7, raw, validateEntry2,
|
|
1870
|
-
if (raw === void 0) return { ok: true, value:
|
|
1881
|
+
function validateEntryArray(path7, raw, validateEntry2, defaults5) {
|
|
1882
|
+
if (raw === void 0) return { ok: true, value: defaults5 };
|
|
1871
1883
|
if (!Array.isArray(raw)) return { ok: false, error: `${path7} must be an array` };
|
|
1872
1884
|
const entries = [];
|
|
1873
1885
|
for (const [index, entry] of raw.entries()) {
|
|
@@ -2050,6 +2062,82 @@ var agentEnvironmentConfigDescriptor = {
|
|
|
2050
2062
|
validate
|
|
2051
2063
|
};
|
|
2052
2064
|
|
|
2065
|
+
// src/domains/diagnose/facts.ts
|
|
2066
|
+
function isRecord2(value) {
|
|
2067
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2068
|
+
}
|
|
2069
|
+
function isNonEmptyString(value) {
|
|
2070
|
+
return typeof value === "string" && value.length > 0;
|
|
2071
|
+
}
|
|
2072
|
+
function isNonEmptyStringArray(value) {
|
|
2073
|
+
return Array.isArray(value) && value.length > 0 && value.every(isNonEmptyString);
|
|
2074
|
+
}
|
|
2075
|
+
function validateMarketplaceIdentity(value, field) {
|
|
2076
|
+
if (!isRecord2(value) || !isNonEmptyString(value.name) || !isNonEmptyString(value.source)) {
|
|
2077
|
+
return { ok: false, error: `${field} must carry a non-empty \`name\` and \`source\`` };
|
|
2078
|
+
}
|
|
2079
|
+
return { ok: true, value: { name: value.name, source: value.source } };
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
// src/domains/diagnose/config.ts
|
|
2083
|
+
var DIAGNOSE_SECTION = "diagnose";
|
|
2084
|
+
var DIAGNOSE_CONFIG_FIELDS = {
|
|
2085
|
+
SPX_FLOOR: "spxFloor",
|
|
2086
|
+
MARKETPLACE: "marketplace",
|
|
2087
|
+
EXPECTED_PLUGINS: "expectedPlugins",
|
|
2088
|
+
CHECKS: "checks"
|
|
2089
|
+
};
|
|
2090
|
+
var defaults = {};
|
|
2091
|
+
function validate2(value) {
|
|
2092
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2093
|
+
return { ok: false, error: `${DIAGNOSE_SECTION} section must be an object` };
|
|
2094
|
+
}
|
|
2095
|
+
const candidate = value;
|
|
2096
|
+
const resolved = {};
|
|
2097
|
+
const spxFloor = candidate[DIAGNOSE_CONFIG_FIELDS.SPX_FLOOR];
|
|
2098
|
+
if (spxFloor !== void 0) {
|
|
2099
|
+
if (!isNonEmptyString(spxFloor)) {
|
|
2100
|
+
return { ok: false, error: `${DIAGNOSE_SECTION}.${DIAGNOSE_CONFIG_FIELDS.SPX_FLOOR} must be a non-empty string` };
|
|
2101
|
+
}
|
|
2102
|
+
resolved.spxFloor = spxFloor;
|
|
2103
|
+
}
|
|
2104
|
+
const marketplace = candidate[DIAGNOSE_CONFIG_FIELDS.MARKETPLACE];
|
|
2105
|
+
if (marketplace !== void 0) {
|
|
2106
|
+
const result = validateMarketplaceIdentity(
|
|
2107
|
+
marketplace,
|
|
2108
|
+
`${DIAGNOSE_SECTION}.${DIAGNOSE_CONFIG_FIELDS.MARKETPLACE}`
|
|
2109
|
+
);
|
|
2110
|
+
if (!result.ok) return result;
|
|
2111
|
+
resolved.marketplace = result.value;
|
|
2112
|
+
}
|
|
2113
|
+
const expectedPlugins = candidate[DIAGNOSE_CONFIG_FIELDS.EXPECTED_PLUGINS];
|
|
2114
|
+
if (expectedPlugins !== void 0) {
|
|
2115
|
+
if (!isNonEmptyStringArray(expectedPlugins)) {
|
|
2116
|
+
return {
|
|
2117
|
+
ok: false,
|
|
2118
|
+
error: `${DIAGNOSE_SECTION}.${DIAGNOSE_CONFIG_FIELDS.EXPECTED_PLUGINS} must be a non-empty array of non-empty strings`
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
resolved.expectedPlugins = expectedPlugins;
|
|
2122
|
+
}
|
|
2123
|
+
const checks = candidate[DIAGNOSE_CONFIG_FIELDS.CHECKS];
|
|
2124
|
+
if (checks !== void 0) {
|
|
2125
|
+
if (!isNonEmptyStringArray(checks)) {
|
|
2126
|
+
return {
|
|
2127
|
+
ok: false,
|
|
2128
|
+
error: `${DIAGNOSE_SECTION}.${DIAGNOSE_CONFIG_FIELDS.CHECKS} must be a non-empty array of non-empty strings`
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2131
|
+
resolved.checks = checks;
|
|
2132
|
+
}
|
|
2133
|
+
return { ok: true, value: resolved };
|
|
2134
|
+
}
|
|
2135
|
+
var diagnoseConfigDescriptor = {
|
|
2136
|
+
section: DIAGNOSE_SECTION,
|
|
2137
|
+
defaults,
|
|
2138
|
+
validate: validate2
|
|
2139
|
+
};
|
|
2140
|
+
|
|
2053
2141
|
// src/lib/spec-tree/config.ts
|
|
2054
2142
|
var SPEC_TREE_KIND_CATEGORY_VALUES = {
|
|
2055
2143
|
NODE: "node",
|
|
@@ -2229,7 +2317,7 @@ var SPEC_TREE_NODE_STATE = {
|
|
|
2229
2317
|
};
|
|
2230
2318
|
var SPEC_TREE_SECTION = SPEC_TREE_CONFIG.SECTION;
|
|
2231
2319
|
function isKind(value) {
|
|
2232
|
-
return Object.
|
|
2320
|
+
return Object.hasOwn(KIND_REGISTRY, value);
|
|
2233
2321
|
}
|
|
2234
2322
|
function buildDefaults() {
|
|
2235
2323
|
return { kinds: { ...KIND_REGISTRY } };
|
|
@@ -2238,7 +2326,7 @@ function buildConfigFromKindNames(kindNames) {
|
|
|
2238
2326
|
const entries = kindNames.map((kind) => [kind, KIND_REGISTRY[kind]]);
|
|
2239
2327
|
return { kinds: Object.fromEntries(entries) };
|
|
2240
2328
|
}
|
|
2241
|
-
function
|
|
2329
|
+
function validate3(value) {
|
|
2242
2330
|
if (typeof value !== "object" || value === null) {
|
|
2243
2331
|
return { ok: false, error: `${SPEC_TREE_SECTION} section must be an object` };
|
|
2244
2332
|
}
|
|
@@ -2337,7 +2425,7 @@ function validateKindDefinitionMap(kindEntries) {
|
|
|
2337
2425
|
var specTreeConfigDescriptor = {
|
|
2338
2426
|
section: SPEC_TREE_SECTION,
|
|
2339
2427
|
defaults: buildDefaults(),
|
|
2340
|
-
validate:
|
|
2428
|
+
validate: validate3
|
|
2341
2429
|
};
|
|
2342
2430
|
|
|
2343
2431
|
// src/lib/file-inclusion/adapters/eslint.ts
|
|
@@ -2513,11 +2601,11 @@ var DEFAULT_SCOPE_CONFIG = {
|
|
|
2513
2601
|
var DEFAULT_TOOLS_CONFIG = Object.fromEntries(
|
|
2514
2602
|
Object.entries(TOOL_DEFAULT_FLAGS).map(([name, flag]) => [name, { ignoreFlag: flag }])
|
|
2515
2603
|
);
|
|
2516
|
-
var
|
|
2604
|
+
var defaults2 = {
|
|
2517
2605
|
scope: DEFAULT_SCOPE_CONFIG,
|
|
2518
2606
|
tools: DEFAULT_TOOLS_CONFIG
|
|
2519
2607
|
};
|
|
2520
|
-
function
|
|
2608
|
+
function isRecord3(value) {
|
|
2521
2609
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2522
2610
|
}
|
|
2523
2611
|
function validateStringField(section, field, value) {
|
|
@@ -2540,7 +2628,7 @@ function rejectUnknownFields2(section, value, allowed) {
|
|
|
2540
2628
|
return { ok: true, value: void 0 };
|
|
2541
2629
|
}
|
|
2542
2630
|
function validateScope(raw) {
|
|
2543
|
-
if (!
|
|
2631
|
+
if (!isRecord3(raw)) {
|
|
2544
2632
|
return {
|
|
2545
2633
|
ok: false,
|
|
2546
2634
|
error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.SCOPE} must be an object`
|
|
@@ -2597,7 +2685,7 @@ function validateScope(raw) {
|
|
|
2597
2685
|
};
|
|
2598
2686
|
}
|
|
2599
2687
|
function validateTools(raw) {
|
|
2600
|
-
if (!
|
|
2688
|
+
if (!isRecord3(raw)) {
|
|
2601
2689
|
return {
|
|
2602
2690
|
ok: false,
|
|
2603
2691
|
error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.TOOLS} must be an object`
|
|
@@ -2612,7 +2700,7 @@ function validateTools(raw) {
|
|
|
2612
2700
|
error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.TOOLS}.${toolName} is not a recognized tool`
|
|
2613
2701
|
};
|
|
2614
2702
|
}
|
|
2615
|
-
if (!
|
|
2703
|
+
if (!isRecord3(adapterRaw)) {
|
|
2616
2704
|
return {
|
|
2617
2705
|
ok: false,
|
|
2618
2706
|
error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.TOOLS}.${toolName} must be an object`
|
|
@@ -2632,7 +2720,7 @@ function validateTools(raw) {
|
|
|
2632
2720
|
}
|
|
2633
2721
|
return { ok: true, value: tools };
|
|
2634
2722
|
}
|
|
2635
|
-
function
|
|
2723
|
+
function validate4(value) {
|
|
2636
2724
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2637
2725
|
return { ok: false, error: `${FILE_INCLUSION_SECTION} section must be an object` };
|
|
2638
2726
|
}
|
|
@@ -2659,8 +2747,8 @@ function validate3(value) {
|
|
|
2659
2747
|
}
|
|
2660
2748
|
var fileInclusionConfigDescriptor = {
|
|
2661
2749
|
section: FILE_INCLUSION_SECTION,
|
|
2662
|
-
defaults,
|
|
2663
|
-
validate:
|
|
2750
|
+
defaults: defaults2,
|
|
2751
|
+
validate: validate4
|
|
2664
2752
|
};
|
|
2665
2753
|
|
|
2666
2754
|
// src/lib/precommit/config.ts
|
|
@@ -2669,7 +2757,7 @@ var PRECOMMIT_DEFAULTS = {
|
|
|
2669
2757
|
sourceDirs: ["src/"],
|
|
2670
2758
|
testPattern: ".test.ts"
|
|
2671
2759
|
};
|
|
2672
|
-
function
|
|
2760
|
+
function validate5(value) {
|
|
2673
2761
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2674
2762
|
return { ok: false, error: `${PRECOMMIT_SECTION} section must be an object` };
|
|
2675
2763
|
}
|
|
@@ -2699,7 +2787,7 @@ function validate4(value) {
|
|
|
2699
2787
|
var precommitConfigDescriptor = {
|
|
2700
2788
|
section: PRECOMMIT_SECTION,
|
|
2701
2789
|
defaults: PRECOMMIT_DEFAULTS,
|
|
2702
|
-
validate:
|
|
2790
|
+
validate: validate5
|
|
2703
2791
|
};
|
|
2704
2792
|
|
|
2705
2793
|
// src/config/primitives/path-filter.ts
|
|
@@ -2736,7 +2824,11 @@ function validatePathFilterConfig(raw, path7) {
|
|
|
2736
2824
|
}
|
|
2737
2825
|
var PATH_SEGMENT_SEPARATOR = "/";
|
|
2738
2826
|
function normalizePathPrefix(value) {
|
|
2739
|
-
|
|
2827
|
+
let end = value.length;
|
|
2828
|
+
while (end > 0 && value[end - 1] === PATH_SEGMENT_SEPARATOR) {
|
|
2829
|
+
end--;
|
|
2830
|
+
}
|
|
2831
|
+
return value.slice(0, end);
|
|
2740
2832
|
}
|
|
2741
2833
|
function pathMatchesPrefix(path7, prefix) {
|
|
2742
2834
|
const normalizedPath = normalizePathPrefix(path7);
|
|
@@ -2760,7 +2852,7 @@ var TESTING_CONFIG_FIELDS = {
|
|
|
2760
2852
|
PASSING_SCOPE: "passingScope"
|
|
2761
2853
|
};
|
|
2762
2854
|
var DEFAULT_PASSING_SCOPE = resolveDefaultPassingScope();
|
|
2763
|
-
var
|
|
2855
|
+
var defaults3 = {
|
|
2764
2856
|
passingScope: DEFAULT_PASSING_SCOPE
|
|
2765
2857
|
};
|
|
2766
2858
|
function resolveDefaultPassingScope() {
|
|
@@ -2773,14 +2865,14 @@ function resolveDefaultPassingScope() {
|
|
|
2773
2865
|
}
|
|
2774
2866
|
return result.value;
|
|
2775
2867
|
}
|
|
2776
|
-
function
|
|
2868
|
+
function validate6(value) {
|
|
2777
2869
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2778
2870
|
return { ok: false, error: `${TESTING_SECTION} section must be an object` };
|
|
2779
2871
|
}
|
|
2780
2872
|
const candidate = value;
|
|
2781
2873
|
const passingScopeRaw = candidate[TESTING_CONFIG_FIELDS.PASSING_SCOPE];
|
|
2782
2874
|
if (passingScopeRaw === void 0) {
|
|
2783
|
-
return { ok: true, value:
|
|
2875
|
+
return { ok: true, value: defaults3 };
|
|
2784
2876
|
}
|
|
2785
2877
|
const passingScopeResult = validatePathFilterConfig(
|
|
2786
2878
|
passingScopeRaw,
|
|
@@ -2796,8 +2888,8 @@ function validate5(value) {
|
|
|
2796
2888
|
}
|
|
2797
2889
|
var testingConfigDescriptor = {
|
|
2798
2890
|
section: TESTING_SECTION,
|
|
2799
|
-
defaults:
|
|
2800
|
-
validate:
|
|
2891
|
+
defaults: defaults3,
|
|
2892
|
+
validate: validate6
|
|
2801
2893
|
};
|
|
2802
2894
|
|
|
2803
2895
|
// src/validation/literal/config.ts
|
|
@@ -2854,7 +2946,13 @@ var LITERAL_DEFAULTS = {
|
|
|
2854
2946
|
minStringLength: DEFAULT_MIN_STRING_LENGTH,
|
|
2855
2947
|
minNumberDigits: DEFAULT_MIN_NUMBER_DIGITS
|
|
2856
2948
|
};
|
|
2857
|
-
function
|
|
2949
|
+
function isStringArray(value) {
|
|
2950
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
2951
|
+
}
|
|
2952
|
+
function isNonNegativeInteger(value) {
|
|
2953
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2954
|
+
}
|
|
2955
|
+
function validate7(value) {
|
|
2858
2956
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2859
2957
|
return { ok: false, error: `${LITERAL_SECTION} section must be an object` };
|
|
2860
2958
|
}
|
|
@@ -2864,32 +2962,31 @@ function validate6(value) {
|
|
|
2864
2962
|
}
|
|
2865
2963
|
const presets = candidate["presets"];
|
|
2866
2964
|
if (presets !== void 0) {
|
|
2867
|
-
if (!
|
|
2965
|
+
if (!isStringArray(presets)) {
|
|
2868
2966
|
return { ok: false, error: `${LITERAL_SECTION}.presets must be an array of strings` };
|
|
2869
2967
|
}
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
}
|
|
2968
|
+
const unknownPreset = presets.find((id) => !PRESET_REGISTRY.has(id));
|
|
2969
|
+
if (unknownPreset !== void 0) {
|
|
2970
|
+
return { ok: false, error: `${LITERAL_SECTION}.presets: unrecognized preset "${unknownPreset}"` };
|
|
2874
2971
|
}
|
|
2875
2972
|
}
|
|
2876
2973
|
const include = candidate["include"];
|
|
2877
|
-
if (include !== void 0 &&
|
|
2974
|
+
if (include !== void 0 && !isStringArray(include)) {
|
|
2878
2975
|
return { ok: false, error: `${LITERAL_SECTION}.include must be an array of strings` };
|
|
2879
2976
|
}
|
|
2880
2977
|
const exclude = candidate["exclude"];
|
|
2881
|
-
if (exclude !== void 0 &&
|
|
2978
|
+
if (exclude !== void 0 && !isStringArray(exclude)) {
|
|
2882
2979
|
return { ok: false, error: `${LITERAL_SECTION}.exclude must be an array of strings` };
|
|
2883
2980
|
}
|
|
2884
2981
|
const minStringLength = candidate["minStringLength"] ?? LITERAL_DEFAULTS.minStringLength;
|
|
2885
|
-
if (
|
|
2982
|
+
if (!isNonNegativeInteger(minStringLength)) {
|
|
2886
2983
|
return {
|
|
2887
2984
|
ok: false,
|
|
2888
2985
|
error: `${LITERAL_SECTION}.minStringLength must be a non-negative integer`
|
|
2889
2986
|
};
|
|
2890
2987
|
}
|
|
2891
2988
|
const minNumberDigits = candidate["minNumberDigits"] ?? LITERAL_DEFAULTS.minNumberDigits;
|
|
2892
|
-
if (
|
|
2989
|
+
if (!isNonNegativeInteger(minNumberDigits)) {
|
|
2893
2990
|
return {
|
|
2894
2991
|
ok: false,
|
|
2895
2992
|
error: `${LITERAL_SECTION}.minNumberDigits must be a non-negative integer`
|
|
@@ -2909,7 +3006,7 @@ function validate6(value) {
|
|
|
2909
3006
|
var literalConfigDescriptor = {
|
|
2910
3007
|
section: LITERAL_SECTION,
|
|
2911
3008
|
defaults: LITERAL_DEFAULTS,
|
|
2912
|
-
validate:
|
|
3009
|
+
validate: validate7
|
|
2913
3010
|
};
|
|
2914
3011
|
|
|
2915
3012
|
// src/validation/config/descriptor.ts
|
|
@@ -2925,9 +3022,10 @@ var VALIDATION_PATH_TOOL_SUBSECTIONS = {
|
|
|
2925
3022
|
CIRCULAR: "circular",
|
|
2926
3023
|
KNIP: "knip",
|
|
2927
3024
|
MARKDOWN: "markdown",
|
|
2928
|
-
LITERAL: "literal"
|
|
3025
|
+
LITERAL: "literal",
|
|
3026
|
+
FORMATTING: "formatting"
|
|
2929
3027
|
};
|
|
2930
|
-
var
|
|
3028
|
+
var defaults4 = {
|
|
2931
3029
|
paths: {},
|
|
2932
3030
|
literal: {
|
|
2933
3031
|
enabled: true,
|
|
@@ -2967,7 +3065,7 @@ function validateLiteral(raw) {
|
|
|
2967
3065
|
};
|
|
2968
3066
|
}
|
|
2969
3067
|
const candidate = raw;
|
|
2970
|
-
const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ??
|
|
3068
|
+
const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ?? defaults4.literal.enabled;
|
|
2971
3069
|
if (typeof enabledRaw !== "boolean") {
|
|
2972
3070
|
return {
|
|
2973
3071
|
ok: false,
|
|
@@ -2992,7 +3090,7 @@ function validateKnip(raw) {
|
|
|
2992
3090
|
};
|
|
2993
3091
|
}
|
|
2994
3092
|
const candidate = raw;
|
|
2995
|
-
const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ??
|
|
3093
|
+
const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ?? defaults4.knip.enabled;
|
|
2996
3094
|
if (typeof enabledRaw !== "boolean") {
|
|
2997
3095
|
return {
|
|
2998
3096
|
ok: false,
|
|
@@ -3001,7 +3099,7 @@ function validateKnip(raw) {
|
|
|
3001
3099
|
}
|
|
3002
3100
|
return { ok: true, value: { enabled: enabledRaw } };
|
|
3003
3101
|
}
|
|
3004
|
-
function
|
|
3102
|
+
function validate8(value) {
|
|
3005
3103
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3006
3104
|
return { ok: false, error: `${VALIDATION_SECTION} section must be an object` };
|
|
3007
3105
|
}
|
|
@@ -3019,8 +3117,8 @@ function validate7(value) {
|
|
|
3019
3117
|
}
|
|
3020
3118
|
var validationConfigDescriptor = {
|
|
3021
3119
|
section: VALIDATION_SECTION,
|
|
3022
|
-
defaults:
|
|
3023
|
-
validate:
|
|
3120
|
+
defaults: defaults4,
|
|
3121
|
+
validate: validate8
|
|
3024
3122
|
};
|
|
3025
3123
|
|
|
3026
3124
|
// src/config/registry.ts
|
|
@@ -3030,7 +3128,8 @@ var productionRegistry = [
|
|
|
3030
3128
|
testingConfigDescriptor,
|
|
3031
3129
|
fileInclusionConfigDescriptor,
|
|
3032
3130
|
precommitConfigDescriptor,
|
|
3033
|
-
agentEnvironmentConfigDescriptor
|
|
3131
|
+
agentEnvironmentConfigDescriptor,
|
|
3132
|
+
diagnoseConfigDescriptor
|
|
3034
3133
|
];
|
|
3035
3134
|
|
|
3036
3135
|
// src/config/descriptor-digest.ts
|
|
@@ -3328,7 +3427,7 @@ function setNested(target, path7, value) {
|
|
|
3328
3427
|
cursor = fresh;
|
|
3329
3428
|
}
|
|
3330
3429
|
}
|
|
3331
|
-
cursor[path7
|
|
3430
|
+
cursor[path7.at(-1)] = value;
|
|
3332
3431
|
}
|
|
3333
3432
|
function validateParsedSections(filename, parsed) {
|
|
3334
3433
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
@@ -3339,9 +3438,6 @@ function validateParsedSections(filename, parsed) {
|
|
|
3339
3438
|
function isFileNotFound(error) {
|
|
3340
3439
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
3341
3440
|
}
|
|
3342
|
-
function toMessage(error) {
|
|
3343
|
-
return error instanceof Error ? error.message : String(error);
|
|
3344
|
-
}
|
|
3345
3441
|
|
|
3346
3442
|
// src/commands/config/defaults.ts
|
|
3347
3443
|
function defaultsCommand(options, deps) {
|
|
@@ -3576,12 +3672,6 @@ var CHECK_NAME = {
|
|
|
3576
3672
|
SESSION_STORE: "session-store",
|
|
3577
3673
|
MARKETPLACE_INSTALL: "marketplace-install"
|
|
3578
3674
|
};
|
|
3579
|
-
function isRecord3(value) {
|
|
3580
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3581
|
-
}
|
|
3582
|
-
function isNonEmptyString(value) {
|
|
3583
|
-
return typeof value === "string" && value.length > 0;
|
|
3584
|
-
}
|
|
3585
3675
|
function validateChecks(raw, available) {
|
|
3586
3676
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
3587
3677
|
return { ok: false, error: "manifest `checks` must be a non-empty array of check names" };
|
|
@@ -3595,12 +3685,6 @@ function validateChecks(raw, available) {
|
|
|
3595
3685
|
}
|
|
3596
3686
|
return { ok: true, value: raw };
|
|
3597
3687
|
}
|
|
3598
|
-
function validateMarketplace2(raw) {
|
|
3599
|
-
if (!isRecord3(raw) || !isNonEmptyString(raw.name) || !isNonEmptyString(raw.source)) {
|
|
3600
|
-
return { ok: false, error: "manifest `marketplace` must carry a non-empty `name` and `source`" };
|
|
3601
|
-
}
|
|
3602
|
-
return { ok: true, value: { name: raw.name, source: raw.source } };
|
|
3603
|
-
}
|
|
3604
3688
|
function parseManifest(rawJson, availableChecks) {
|
|
3605
3689
|
let parsed;
|
|
3606
3690
|
try {
|
|
@@ -3608,7 +3692,7 @@ function parseManifest(rawJson, availableChecks) {
|
|
|
3608
3692
|
} catch (error) {
|
|
3609
3693
|
return { ok: false, error: `manifest is not valid JSON: ${error.message}` };
|
|
3610
3694
|
}
|
|
3611
|
-
if (!
|
|
3695
|
+
if (!isRecord2(parsed)) {
|
|
3612
3696
|
return { ok: false, error: "manifest must be a JSON object" };
|
|
3613
3697
|
}
|
|
3614
3698
|
const checks = validateChecks(parsed.checks, new Set(availableChecks));
|
|
@@ -3621,9 +3705,9 @@ function parseManifest(rawJson, availableChecks) {
|
|
|
3621
3705
|
manifest.spxFloor = parsed.spx_floor;
|
|
3622
3706
|
}
|
|
3623
3707
|
if (checks.value.includes(CHECK_NAME.MARKETPLACE_INSTALL)) {
|
|
3624
|
-
const marketplace =
|
|
3708
|
+
const marketplace = validateMarketplaceIdentity(parsed.marketplace, "manifest `marketplace`");
|
|
3625
3709
|
if (!marketplace.ok) return marketplace;
|
|
3626
|
-
if (!
|
|
3710
|
+
if (!isNonEmptyStringArray(parsed.expected_plugins)) {
|
|
3627
3711
|
return { ok: false, error: "manifest selects `marketplace-install` but carries no `expected_plugins`" };
|
|
3628
3712
|
}
|
|
3629
3713
|
manifest.marketplace = marketplace.value;
|
|
@@ -3738,21 +3822,70 @@ function renderReport(report2, format2, options) {
|
|
|
3738
3822
|
return format2 === DIAGNOSE_FORMAT.JSON ? renderReportJson(report2) : renderReportText(report2, options);
|
|
3739
3823
|
}
|
|
3740
3824
|
|
|
3825
|
+
// src/domains/diagnose/resolve.ts
|
|
3826
|
+
var CHECK_NAMES = new Set(Object.values(CHECK_NAME));
|
|
3827
|
+
function isCheckName(value) {
|
|
3828
|
+
return CHECK_NAMES.has(value);
|
|
3829
|
+
}
|
|
3830
|
+
function resolveDiagnoseFacts(options) {
|
|
3831
|
+
if (options.manifest !== void 0) {
|
|
3832
|
+
return { ok: true, value: options.manifest };
|
|
3833
|
+
}
|
|
3834
|
+
const { config, availableChecks } = options;
|
|
3835
|
+
const configuredChecks = config.checks;
|
|
3836
|
+
let checks;
|
|
3837
|
+
if (configuredChecks === void 0) {
|
|
3838
|
+
checks = availableChecks;
|
|
3839
|
+
} else {
|
|
3840
|
+
const available = new Set(availableChecks);
|
|
3841
|
+
const unavailable = configuredChecks.filter((name) => !isCheckName(name) || !available.has(name));
|
|
3842
|
+
if (unavailable.length > 0) {
|
|
3843
|
+
return {
|
|
3844
|
+
ok: false,
|
|
3845
|
+
error: `diagnose config \`checks\` names checks not available in this build: ${unavailable.join(", ")}`
|
|
3846
|
+
};
|
|
3847
|
+
}
|
|
3848
|
+
checks = configuredChecks.filter(isCheckName);
|
|
3849
|
+
}
|
|
3850
|
+
return {
|
|
3851
|
+
ok: true,
|
|
3852
|
+
value: {
|
|
3853
|
+
checks,
|
|
3854
|
+
spxFloor: config.spxFloor,
|
|
3855
|
+
marketplace: config.marketplace,
|
|
3856
|
+
expectedPlugins: config.expectedPlugins
|
|
3857
|
+
}
|
|
3858
|
+
};
|
|
3859
|
+
}
|
|
3860
|
+
|
|
3741
3861
|
// src/commands/diagnose/index.ts
|
|
3742
|
-
async function
|
|
3862
|
+
async function resolveDiagnoseConfig(productDir) {
|
|
3863
|
+
const loaded = await resolveConfig(productDir, [diagnoseConfigDescriptor]);
|
|
3864
|
+
if (!loaded.ok) return loaded;
|
|
3865
|
+
return { ok: true, value: loaded.value[diagnoseConfigDescriptor.section] };
|
|
3866
|
+
}
|
|
3867
|
+
async function readManifest(fs8, manifestPath, availableChecks) {
|
|
3743
3868
|
let raw;
|
|
3744
3869
|
try {
|
|
3745
|
-
raw = await
|
|
3870
|
+
raw = await fs8.readFile(manifestPath);
|
|
3746
3871
|
} catch (error) {
|
|
3747
|
-
return {
|
|
3748
|
-
ok: false,
|
|
3749
|
-
error: `cannot read diagnose manifest at ${options.manifestPath}: ${error.message}`
|
|
3750
|
-
};
|
|
3872
|
+
return { ok: false, error: `cannot read diagnose manifest at ${manifestPath}: ${error.message}` };
|
|
3751
3873
|
}
|
|
3874
|
+
return parseManifest(raw, availableChecks);
|
|
3875
|
+
}
|
|
3876
|
+
async function diagnoseCommand(options) {
|
|
3752
3877
|
const availableChecks = Object.keys(options.registry);
|
|
3753
|
-
const manifest =
|
|
3754
|
-
if (!manifest.ok) return manifest;
|
|
3755
|
-
const
|
|
3878
|
+
const manifest = options.manifestPath === void 0 ? void 0 : await readManifest(options.fs, options.manifestPath, availableChecks);
|
|
3879
|
+
if (manifest !== void 0 && !manifest.ok) return manifest;
|
|
3880
|
+
const config = manifest === void 0 ? await resolveDiagnoseConfig(options.productDir) : { ok: true, value: {} };
|
|
3881
|
+
if (!config.ok) return config;
|
|
3882
|
+
const resolved = resolveDiagnoseFacts({
|
|
3883
|
+
manifest: manifest?.value,
|
|
3884
|
+
config: config.value,
|
|
3885
|
+
availableChecks
|
|
3886
|
+
});
|
|
3887
|
+
if (!resolved.ok) return resolved;
|
|
3888
|
+
const report2 = await runDiagnose(resolved.value, options.registry);
|
|
3756
3889
|
if (!report2.ok) return report2;
|
|
3757
3890
|
return {
|
|
3758
3891
|
ok: true,
|
|
@@ -4458,6 +4591,7 @@ function sessionStoreRunner(probe) {
|
|
|
4458
4591
|
// src/domains/diagnose/checks/spx-reachability.ts
|
|
4459
4592
|
var SPX_REACHABILITY_VERDICT = {
|
|
4460
4593
|
REACHABLE: "reachable",
|
|
4594
|
+
PRESENT: "present",
|
|
4461
4595
|
BELOW_FLOOR: "below-floor",
|
|
4462
4596
|
UNREACHABLE: "unreachable",
|
|
4463
4597
|
UNKNOWN: "unknown"
|
|
@@ -4508,9 +4642,10 @@ function meetsFloor(version2, floor) {
|
|
|
4508
4642
|
}
|
|
4509
4643
|
var REMEDIATION4 = {
|
|
4510
4644
|
[SPX_REACHABILITY_VERDICT.REACHABLE]: "spx is on PATH at or above the required floor; no action needed.",
|
|
4645
|
+
[SPX_REACHABILITY_VERDICT.PRESENT]: "spx is on PATH; no version floor is configured to compare against, so only presence is reported.",
|
|
4511
4646
|
[SPX_REACHABILITY_VERDICT.BELOW_FLOOR]: "Update spx to at least the required floor (pnpm add -g @outcomeeng/spx).",
|
|
4512
4647
|
[SPX_REACHABILITY_VERDICT.UNREACHABLE]: "Install spx and ensure it resolves on PATH (pnpm add -g @outcomeeng/spx).",
|
|
4513
|
-
[SPX_REACHABILITY_VERDICT.UNKNOWN]: "Re-run diagnose; if it persists, verify the manifest
|
|
4648
|
+
[SPX_REACHABILITY_VERDICT.UNKNOWN]: "Re-run diagnose; if it persists, verify the configured or manifest-supplied spx_floor is a valid semver, that spx is on PATH, and that spx --version reports a semver version."
|
|
4514
4649
|
};
|
|
4515
4650
|
function record4(verdict, bucket, reading, floor) {
|
|
4516
4651
|
return {
|
|
@@ -4526,12 +4661,15 @@ function record4(verdict, bucket, reading, floor) {
|
|
|
4526
4661
|
};
|
|
4527
4662
|
}
|
|
4528
4663
|
function classifySpxReachability(reading, floor) {
|
|
4529
|
-
if (reading.errored
|
|
4664
|
+
if (reading.errored) {
|
|
4530
4665
|
return record4(SPX_REACHABILITY_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading, floor);
|
|
4531
4666
|
}
|
|
4532
4667
|
if (reading.resolvedPath === null) {
|
|
4533
4668
|
return record4(SPX_REACHABILITY_VERDICT.UNREACHABLE, VERDICT_BUCKET.BROKEN, reading, floor);
|
|
4534
4669
|
}
|
|
4670
|
+
if (floor === void 0) {
|
|
4671
|
+
return record4(SPX_REACHABILITY_VERDICT.PRESENT, VERDICT_BUCKET.HEALTHY, reading, floor);
|
|
4672
|
+
}
|
|
4535
4673
|
if (reading.version === null) {
|
|
4536
4674
|
return record4(SPX_REACHABILITY_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading, floor);
|
|
4537
4675
|
}
|
|
@@ -4639,7 +4777,7 @@ var DIAGNOSE_CLI = {
|
|
|
4639
4777
|
COLOR_FLAG: "--color",
|
|
4640
4778
|
NO_COLOR_FLAG: "--no-color"
|
|
4641
4779
|
};
|
|
4642
|
-
var DIAGNOSE_DOMAIN_DESCRIPTION = "Run deterministic environment-diagnostics checks from a
|
|
4780
|
+
var DIAGNOSE_DOMAIN_DESCRIPTION = "Run deterministic environment-diagnostics checks, resolving facts from spx.config or a --manifest";
|
|
4643
4781
|
var DEFAULT_REGISTRY = {
|
|
4644
4782
|
[CHECK_NAME.SPX_REACHABILITY]: spxReachabilityRunner(defaultSpxReachabilityProbe),
|
|
4645
4783
|
[CHECK_NAME.SESSION_ENVIRONMENT]: sessionEnvironmentRunner(defaultSessionEnvironmentProbe),
|
|
@@ -4655,11 +4793,15 @@ var diagnoseDomain = {
|
|
|
4655
4793
|
name: DIAGNOSE_CLI.COMMAND,
|
|
4656
4794
|
description: DIAGNOSE_DOMAIN_DESCRIPTION,
|
|
4657
4795
|
register: (program2) => {
|
|
4658
|
-
program2.command(DIAGNOSE_CLI.COMMAND).description(DIAGNOSE_DOMAIN_DESCRIPTION).
|
|
4796
|
+
program2.command(DIAGNOSE_CLI.COMMAND).description(DIAGNOSE_DOMAIN_DESCRIPTION).option(
|
|
4797
|
+
`${DIAGNOSE_CLI.MANIFEST_FLAG} <path>`,
|
|
4798
|
+
"Path to a declarative diagnose manifest that fully instruments the diagnosis"
|
|
4799
|
+
).addOption(
|
|
4659
4800
|
new Option(`${DIAGNOSE_CLI.FORMAT_FLAG} <format>`, "Output format").choices([DIAGNOSE_FORMAT.TEXT, DIAGNOSE_FORMAT.JSON]).default(DIAGNOSE_FORMAT.TEXT)
|
|
4660
4801
|
).addOption(new Option(`${DIAGNOSE_CLI.COLOR_FLAG}`, "Force colored output")).addOption(new Option(`${DIAGNOSE_CLI.NO_COLOR_FLAG}`, "Disable colored output")).action(async (options) => {
|
|
4661
4802
|
const result = await diagnoseCommand({
|
|
4662
4803
|
manifestPath: options.manifest,
|
|
4804
|
+
productDir: process.cwd(),
|
|
4663
4805
|
format: options.format,
|
|
4664
4806
|
color: resolveColorChoice({
|
|
4665
4807
|
flag: options.color,
|
|
@@ -5159,6 +5301,84 @@ function createJournal(backend, identity) {
|
|
|
5159
5301
|
};
|
|
5160
5302
|
}
|
|
5161
5303
|
|
|
5304
|
+
// src/lib/github-snapshot-sink/gh-api-runner.ts
|
|
5305
|
+
import { execa as execa5 } from "execa";
|
|
5306
|
+
var GH_API = {
|
|
5307
|
+
executable: "gh"
|
|
5308
|
+
};
|
|
5309
|
+
var runGhApi = async (args, options) => {
|
|
5310
|
+
const result = await execa5(GH_API.executable, [...args], {
|
|
5311
|
+
...options?.input === void 0 ? {} : { input: options.input }
|
|
5312
|
+
});
|
|
5313
|
+
return { stdout: typeof result.stdout === "string" ? result.stdout : "" };
|
|
5314
|
+
};
|
|
5315
|
+
|
|
5316
|
+
// src/lib/github-snapshot-sink/pull-request-comment-client.ts
|
|
5317
|
+
var GITHUB_API_ERROR = {
|
|
5318
|
+
SURFACE_UNSUPPORTED: "the pull-request comment client supports only the pull-request comment surface"
|
|
5319
|
+
};
|
|
5320
|
+
var GITHUB_API = {
|
|
5321
|
+
apiCommand: "api",
|
|
5322
|
+
fieldFlag: "-f",
|
|
5323
|
+
methodFlag: "-X",
|
|
5324
|
+
paginateFlag: "--paginate",
|
|
5325
|
+
slurpFlag: "--slurp",
|
|
5326
|
+
postMethod: "POST",
|
|
5327
|
+
patchMethod: "PATCH",
|
|
5328
|
+
bodyField: "body"
|
|
5329
|
+
};
|
|
5330
|
+
function githubCommentMarkerTag(marker) {
|
|
5331
|
+
return `<!-- ${marker} -->`;
|
|
5332
|
+
}
|
|
5333
|
+
function issueCommentsPath(repository, pullNumber) {
|
|
5334
|
+
return `repos/${repository}/issues/${pullNumber}/comments`;
|
|
5335
|
+
}
|
|
5336
|
+
function issueCommentPath(repository, commentId) {
|
|
5337
|
+
return `repos/${repository}/issues/comments/${commentId}`;
|
|
5338
|
+
}
|
|
5339
|
+
function createGithubPullRequestCommentClient(options) {
|
|
5340
|
+
const { run } = options;
|
|
5341
|
+
return {
|
|
5342
|
+
async upsertPullRequestComment({ pullNumber, marker, body }) {
|
|
5343
|
+
const markedBody = `${body}
|
|
5344
|
+
${githubCommentMarkerTag(marker)}`;
|
|
5345
|
+
const listed = await run([
|
|
5346
|
+
GITHUB_API.apiCommand,
|
|
5347
|
+
issueCommentsPath(options.repository, pullNumber),
|
|
5348
|
+
GITHUB_API.paginateFlag,
|
|
5349
|
+
GITHUB_API.slurpFlag
|
|
5350
|
+
]);
|
|
5351
|
+
const comments = JSON.parse(listed.stdout).flat();
|
|
5352
|
+
const existing = comments.find((comment) => comment.body.includes(githubCommentMarkerTag(marker)));
|
|
5353
|
+
if (existing === void 0) {
|
|
5354
|
+
await run([
|
|
5355
|
+
GITHUB_API.apiCommand,
|
|
5356
|
+
issueCommentsPath(options.repository, pullNumber),
|
|
5357
|
+
GITHUB_API.methodFlag,
|
|
5358
|
+
GITHUB_API.postMethod,
|
|
5359
|
+
GITHUB_API.fieldFlag,
|
|
5360
|
+
`${GITHUB_API.bodyField}=${markedBody}`
|
|
5361
|
+
]);
|
|
5362
|
+
return;
|
|
5363
|
+
}
|
|
5364
|
+
await run([
|
|
5365
|
+
GITHUB_API.apiCommand,
|
|
5366
|
+
issueCommentPath(options.repository, existing.id),
|
|
5367
|
+
GITHUB_API.methodFlag,
|
|
5368
|
+
GITHUB_API.patchMethod,
|
|
5369
|
+
GITHUB_API.fieldFlag,
|
|
5370
|
+
`${GITHUB_API.bodyField}=${markedBody}`
|
|
5371
|
+
]);
|
|
5372
|
+
},
|
|
5373
|
+
uploadActionsArtifact() {
|
|
5374
|
+
return Promise.reject(new Error(GITHUB_API_ERROR.SURFACE_UNSUPPORTED));
|
|
5375
|
+
},
|
|
5376
|
+
saveActionsCache() {
|
|
5377
|
+
return Promise.reject(new Error(GITHUB_API_ERROR.SURFACE_UNSUPPORTED));
|
|
5378
|
+
}
|
|
5379
|
+
};
|
|
5380
|
+
}
|
|
5381
|
+
|
|
5162
5382
|
// src/lib/github-snapshot-sink/index.ts
|
|
5163
5383
|
var SNAPSHOT_SURFACE_KIND = {
|
|
5164
5384
|
PULL_REQUEST_COMMENT: "pull-request-comment",
|
|
@@ -5369,7 +5589,7 @@ async function appendJournalEvent(ref, input, sink, options = {}) {
|
|
|
5369
5589
|
try {
|
|
5370
5590
|
event = await bound.value.journal.append(input);
|
|
5371
5591
|
} catch (error) {
|
|
5372
|
-
return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.APPEND_FAILED}: ${
|
|
5592
|
+
return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.APPEND_FAILED}: ${toMessage(error)}` };
|
|
5373
5593
|
}
|
|
5374
5594
|
try {
|
|
5375
5595
|
await sink.emit(event);
|
|
@@ -5383,7 +5603,7 @@ async function readJournalEvents(ref, fromCursor, options = {}) {
|
|
|
5383
5603
|
try {
|
|
5384
5604
|
return { ok: true, value: await bound.value.journal.read(fromCursor) };
|
|
5385
5605
|
} catch (error) {
|
|
5386
|
-
return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.READ_FAILED}: ${
|
|
5606
|
+
return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.READ_FAILED}: ${toMessage(error)}` };
|
|
5387
5607
|
}
|
|
5388
5608
|
}
|
|
5389
5609
|
async function sealJournalRun(ref, options = {}) {
|
|
@@ -5393,7 +5613,7 @@ async function sealJournalRun(ref, options = {}) {
|
|
|
5393
5613
|
await bound.value.journal.seal();
|
|
5394
5614
|
return { ok: true, value: void 0 };
|
|
5395
5615
|
} catch (error) {
|
|
5396
|
-
return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.SEAL_FAILED}: ${
|
|
5616
|
+
return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.SEAL_FAILED}: ${toMessage(error)}` };
|
|
5397
5617
|
}
|
|
5398
5618
|
}
|
|
5399
5619
|
async function renderJournalRun(ref, projection, options = {}) {
|
|
@@ -5402,12 +5622,9 @@ async function renderJournalRun(ref, projection, options = {}) {
|
|
|
5402
5622
|
try {
|
|
5403
5623
|
return { ok: true, value: await bound.value.journal.render(projection) };
|
|
5404
5624
|
} catch (error) {
|
|
5405
|
-
return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.RENDER_FAILED}: ${
|
|
5625
|
+
return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.RENDER_FAILED}: ${toMessage(error)}` };
|
|
5406
5626
|
}
|
|
5407
5627
|
}
|
|
5408
|
-
function toMessage2(error) {
|
|
5409
|
-
return error instanceof Error ? error.message : String(error);
|
|
5410
|
-
}
|
|
5411
5628
|
|
|
5412
5629
|
// src/commands/journal/cli.ts
|
|
5413
5630
|
var JOURNAL_CLI_EXIT_CODE = {
|
|
@@ -5600,80 +5817,6 @@ async function journalRenderCommand(scope2, deps = {}) {
|
|
|
5600
5817
|
return okResult(JSON.stringify(rendered.value));
|
|
5601
5818
|
}
|
|
5602
5819
|
|
|
5603
|
-
// src/commands/journal/github-client.ts
|
|
5604
|
-
import { execa as execa5 } from "execa";
|
|
5605
|
-
var GITHUB_CLI_ERROR = {
|
|
5606
|
-
SURFACE_UNSUPPORTED: "the pull-request comment client supports only the pull-request comment surface"
|
|
5607
|
-
};
|
|
5608
|
-
var GITHUB_CLI = {
|
|
5609
|
-
executable: "gh",
|
|
5610
|
-
apiCommand: "api",
|
|
5611
|
-
fieldFlag: "-f",
|
|
5612
|
-
methodFlag: "-X",
|
|
5613
|
-
paginateFlag: "--paginate",
|
|
5614
|
-
slurpFlag: "--slurp",
|
|
5615
|
-
postMethod: "POST",
|
|
5616
|
-
patchMethod: "PATCH",
|
|
5617
|
-
bodyField: "body"
|
|
5618
|
-
};
|
|
5619
|
-
var defaultGhRunner = async (args, options) => {
|
|
5620
|
-
const result = await execa5(GITHUB_CLI.executable, [...args], {
|
|
5621
|
-
...options?.input === void 0 ? {} : { input: options.input }
|
|
5622
|
-
});
|
|
5623
|
-
return { stdout: typeof result.stdout === "string" ? result.stdout : "" };
|
|
5624
|
-
};
|
|
5625
|
-
function githubCommentMarkerTag(marker) {
|
|
5626
|
-
return `<!-- ${marker} -->`;
|
|
5627
|
-
}
|
|
5628
|
-
function issueCommentsPath(repository, pullNumber) {
|
|
5629
|
-
return `repos/${repository}/issues/${pullNumber}/comments`;
|
|
5630
|
-
}
|
|
5631
|
-
function issueCommentPath(repository, commentId) {
|
|
5632
|
-
return `repos/${repository}/issues/comments/${commentId}`;
|
|
5633
|
-
}
|
|
5634
|
-
function createGithubPrCommentClient(options) {
|
|
5635
|
-
const run = options.run ?? defaultGhRunner;
|
|
5636
|
-
return {
|
|
5637
|
-
async upsertPullRequestComment({ pullNumber, marker, body }) {
|
|
5638
|
-
const markedBody = `${body}
|
|
5639
|
-
${githubCommentMarkerTag(marker)}`;
|
|
5640
|
-
const listed = await run([
|
|
5641
|
-
GITHUB_CLI.apiCommand,
|
|
5642
|
-
issueCommentsPath(options.repository, pullNumber),
|
|
5643
|
-
GITHUB_CLI.paginateFlag,
|
|
5644
|
-
GITHUB_CLI.slurpFlag
|
|
5645
|
-
]);
|
|
5646
|
-
const comments = JSON.parse(listed.stdout).flat();
|
|
5647
|
-
const existing = comments.find((comment) => comment.body.includes(githubCommentMarkerTag(marker)));
|
|
5648
|
-
if (existing === void 0) {
|
|
5649
|
-
await run([
|
|
5650
|
-
GITHUB_CLI.apiCommand,
|
|
5651
|
-
issueCommentsPath(options.repository, pullNumber),
|
|
5652
|
-
GITHUB_CLI.methodFlag,
|
|
5653
|
-
GITHUB_CLI.postMethod,
|
|
5654
|
-
GITHUB_CLI.fieldFlag,
|
|
5655
|
-
`${GITHUB_CLI.bodyField}=${markedBody}`
|
|
5656
|
-
]);
|
|
5657
|
-
return;
|
|
5658
|
-
}
|
|
5659
|
-
await run([
|
|
5660
|
-
GITHUB_CLI.apiCommand,
|
|
5661
|
-
issueCommentPath(options.repository, existing.id),
|
|
5662
|
-
GITHUB_CLI.methodFlag,
|
|
5663
|
-
GITHUB_CLI.patchMethod,
|
|
5664
|
-
GITHUB_CLI.fieldFlag,
|
|
5665
|
-
`${GITHUB_CLI.bodyField}=${markedBody}`
|
|
5666
|
-
]);
|
|
5667
|
-
},
|
|
5668
|
-
uploadActionsArtifact() {
|
|
5669
|
-
return Promise.reject(new Error(GITHUB_CLI_ERROR.SURFACE_UNSUPPORTED));
|
|
5670
|
-
},
|
|
5671
|
-
saveActionsCache() {
|
|
5672
|
-
return Promise.reject(new Error(GITHUB_CLI_ERROR.SURFACE_UNSUPPORTED));
|
|
5673
|
-
}
|
|
5674
|
-
};
|
|
5675
|
-
}
|
|
5676
|
-
|
|
5677
5820
|
// src/lib/process-lifecycle/exit-codes.ts
|
|
5678
5821
|
var SIGINT_EXIT_CODE = 130;
|
|
5679
5822
|
var SIGTERM_EXIT_CODE = 143;
|
|
@@ -5913,7 +6056,7 @@ function streamBinding() {
|
|
|
5913
6056
|
const repository = process.env[JOURNAL_CLI_ENV.GITHUB_REPOSITORY] ?? "";
|
|
5914
6057
|
return {
|
|
5915
6058
|
localSink: stdoutStreamSink(),
|
|
5916
|
-
githubClient:
|
|
6059
|
+
githubClient: createGithubPullRequestCommentClient({ repository, run: runGhApi }),
|
|
5917
6060
|
githubRepository: repository
|
|
5918
6061
|
};
|
|
5919
6062
|
}
|
|
@@ -9954,8 +10097,9 @@ function createTestingDomain(deps = defaultTestingCliDependencies) {
|
|
|
9954
10097
|
}
|
|
9955
10098
|
var testingDomain = createTestingDomain();
|
|
9956
10099
|
|
|
9957
|
-
// src/commands/validation/
|
|
9958
|
-
import {
|
|
10100
|
+
// src/commands/validation/formatting.ts
|
|
10101
|
+
import { existsSync } from "fs";
|
|
10102
|
+
import { isAbsolute as isAbsolute3, join as join24, relative as relative3 } from "path";
|
|
9959
10103
|
|
|
9960
10104
|
// src/validation/config/path-filter.ts
|
|
9961
10105
|
import { isAbsolute as isAbsolute2, relative as relative2 } from "path";
|
|
@@ -10052,9 +10196,180 @@ function applyValidationPathFilterToScope(scopeConfig, filter) {
|
|
|
10052
10196
|
};
|
|
10053
10197
|
}
|
|
10054
10198
|
|
|
10199
|
+
// src/validation/steps/subprocess-output.ts
|
|
10200
|
+
var VALIDATION_SUBPROCESS_EVENTS = {
|
|
10201
|
+
CLOSE: "close",
|
|
10202
|
+
DATA: "data",
|
|
10203
|
+
DRAIN: "drain",
|
|
10204
|
+
ERROR: "error"
|
|
10205
|
+
};
|
|
10206
|
+
var defaultValidationSubprocessOutputStreams = {
|
|
10207
|
+
stdout: process.stdout,
|
|
10208
|
+
stderr: process.stderr
|
|
10209
|
+
};
|
|
10210
|
+
function forwardValidationSubprocessOutput(child, streams = defaultValidationSubprocessOutputStreams) {
|
|
10211
|
+
child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, (chunk) => {
|
|
10212
|
+
forwardChunkWithBackpressure(child.stdout, streams.stdout, chunk);
|
|
10213
|
+
});
|
|
10214
|
+
child.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, (chunk) => {
|
|
10215
|
+
forwardChunkWithBackpressure(child.stderr, streams.stderr, chunk);
|
|
10216
|
+
});
|
|
10217
|
+
}
|
|
10218
|
+
function forwardChunkWithBackpressure(source, stream, chunk) {
|
|
10219
|
+
const ready = stream.write(chunk);
|
|
10220
|
+
if (ready || stream.once === void 0 || source === null || source === void 0) {
|
|
10221
|
+
return;
|
|
10222
|
+
}
|
|
10223
|
+
source.pause();
|
|
10224
|
+
stream.once(VALIDATION_SUBPROCESS_EVENTS.DRAIN, () => {
|
|
10225
|
+
source.resume();
|
|
10226
|
+
});
|
|
10227
|
+
}
|
|
10228
|
+
|
|
10229
|
+
// src/validation/steps/formatting.ts
|
|
10230
|
+
var DPRINT_COMMAND = "dprint";
|
|
10231
|
+
var DPRINT_CHECK_SUBCOMMAND = "check";
|
|
10232
|
+
var DPRINT_CONFIG_FILENAME = "dprint.jsonc";
|
|
10233
|
+
var defaultFormattingProcessRunner = lifecycleProcessRunner;
|
|
10234
|
+
function buildDprintCheckArgs(options) {
|
|
10235
|
+
return [DPRINT_CHECK_SUBCOMMAND, ...options.files ?? []];
|
|
10236
|
+
}
|
|
10237
|
+
async function validateFormatting(context, runner = defaultFormattingProcessRunner) {
|
|
10238
|
+
const args = buildDprintCheckArgs({ files: context.files });
|
|
10239
|
+
return new Promise((resolve8) => {
|
|
10240
|
+
const child = spawnManagedSubprocess(runner, DPRINT_COMMAND, args, { cwd: context.projectRoot });
|
|
10241
|
+
const chunks = [];
|
|
10242
|
+
const capture = (chunk) => {
|
|
10243
|
+
chunks.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString());
|
|
10244
|
+
};
|
|
10245
|
+
child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
|
|
10246
|
+
child.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
|
|
10247
|
+
child.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
|
|
10248
|
+
resolve8({ success: code === 0, output: chunks.join("") });
|
|
10249
|
+
});
|
|
10250
|
+
child.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
|
|
10251
|
+
resolve8({ success: false, output: chunks.join(""), error: error.message });
|
|
10252
|
+
});
|
|
10253
|
+
});
|
|
10254
|
+
}
|
|
10255
|
+
|
|
10256
|
+
// src/commands/validation/messages.ts
|
|
10257
|
+
var VALIDATION_STAGE_DISPLAY_NAMES = {
|
|
10258
|
+
CIRCULAR: "dependency-cruiser",
|
|
10259
|
+
KNIP: "Knip",
|
|
10260
|
+
ESLINT: "ESLint",
|
|
10261
|
+
TYPESCRIPT: "TypeScript",
|
|
10262
|
+
MARKDOWN: "Markdown",
|
|
10263
|
+
LITERAL: "Literal",
|
|
10264
|
+
FORMATTING: "Formatting"
|
|
10265
|
+
};
|
|
10266
|
+
var VALIDATION_SKIP_LABELS = {
|
|
10267
|
+
VERB: "Skipping",
|
|
10268
|
+
CIRCULAR_REASON: "skip-circular",
|
|
10269
|
+
LITERAL_REASON: "skip-literal",
|
|
10270
|
+
DISABLED_BY_PREFIX: "disabled by",
|
|
10271
|
+
TYPESCRIPT_ABSENT_REASON: "TypeScript not detected in project",
|
|
10272
|
+
VALIDATION_PATHS_NO_TARGETS_REASON: "validation paths matched no files",
|
|
10273
|
+
MARKDOWN_NO_SCOPE_REASON: "no markdown files in --files scope",
|
|
10274
|
+
MARKDOWN_NO_DEFAULT_DIRECTORIES_REASON: "no spx/ or docs/ directories found"
|
|
10275
|
+
};
|
|
10276
|
+
var VALIDATION_COMMAND_OUTPUT = {
|
|
10277
|
+
CIRCULAR_FOUND: `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: circular dependencies found`,
|
|
10278
|
+
CIRCULAR_NONE_FOUND: `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: \u2713 No circular dependencies found`,
|
|
10279
|
+
KNIP_CONFIG_ERROR: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: \u2717 config error`,
|
|
10280
|
+
KNIP_DISABLED: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: skipped (${VALIDATION_SKIP_LABELS.DISABLED_BY_PREFIX} validation.knip.enabled)`,
|
|
10281
|
+
KNIP_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: \u2713 No unused code found`,
|
|
10282
|
+
KNIP_FAILURE: "Unused code found",
|
|
10283
|
+
ESLINT_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT}: \u2713 No errors found`,
|
|
10284
|
+
ESLINT_FAILURE: `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT} validation failed`,
|
|
10285
|
+
ESLINT_MISSING_CONFIG: "ESLint config not found: project has tsconfig.json but no eslint.config.{ts,js,mjs,cjs}",
|
|
10286
|
+
TYPESCRIPT_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT}: \u2713 No type errors`,
|
|
10287
|
+
TYPESCRIPT_FAILURE: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT} validation failed`,
|
|
10288
|
+
MARKDOWN_NO_ISSUES: `${VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN}: No issues found`,
|
|
10289
|
+
MARKDOWN_ERROR_SUMMARY_SUFFIX: "error(s) found",
|
|
10290
|
+
FORMATTING_NO_ISSUES: `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: \u2713 No formatting issues found`,
|
|
10291
|
+
FORMATTING_FAILURE_SUMMARY: `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: unformatted files found`
|
|
10292
|
+
};
|
|
10293
|
+
var CIRCULAR_SKIP_OUTPUT = `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: skipped (--${VALIDATION_SKIP_LABELS.CIRCULAR_REASON})`;
|
|
10294
|
+
var CIRCULAR_SKIP_JSON_OUTPUT = JSON.stringify({
|
|
10295
|
+
skipped: true,
|
|
10296
|
+
reason: VALIDATION_SKIP_LABELS.CIRCULAR_REASON
|
|
10297
|
+
});
|
|
10298
|
+
var LITERAL_SKIP_OUTPUT = `${VALIDATION_STAGE_DISPLAY_NAMES.LITERAL}: skipped (--${VALIDATION_SKIP_LABELS.LITERAL_REASON})`;
|
|
10299
|
+
var LITERAL_SKIP_JSON_OUTPUT = JSON.stringify({
|
|
10300
|
+
skipped: true,
|
|
10301
|
+
reason: VALIDATION_SKIP_LABELS.LITERAL_REASON
|
|
10302
|
+
});
|
|
10303
|
+
function formatTypeScriptAbsentSkipMessage(stageName) {
|
|
10304
|
+
return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.TYPESCRIPT_ABSENT_REASON})`;
|
|
10305
|
+
}
|
|
10306
|
+
function formatValidationPathsNoTargetsSkipMessage(stageName) {
|
|
10307
|
+
return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.VALIDATION_PATHS_NO_TARGETS_REASON})`;
|
|
10308
|
+
}
|
|
10309
|
+
|
|
10310
|
+
// src/commands/validation/formatting.ts
|
|
10311
|
+
var FORMATTING_COMMAND_OUTPUT = {
|
|
10312
|
+
NO_ISSUES: VALIDATION_COMMAND_OUTPUT.FORMATTING_NO_ISSUES,
|
|
10313
|
+
FAILURE_SUMMARY: VALIDATION_COMMAND_OUTPUT.FORMATTING_FAILURE_SUMMARY,
|
|
10314
|
+
EMPTY_SCOPE_REASON: "no files in scope",
|
|
10315
|
+
NO_CONFIG_SKIP_REASON: `no ${DPRINT_CONFIG_FILENAME} at product root`
|
|
10316
|
+
};
|
|
10317
|
+
var FORMATTING_CONFIG_ERROR_MESSAGE = `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: \u2717 config error`;
|
|
10318
|
+
async function formattingCommand(options) {
|
|
10319
|
+
const { cwd, files, quiet } = options;
|
|
10320
|
+
const startTime = Date.now();
|
|
10321
|
+
const loaded = await resolveConfig(cwd, [validationConfigDescriptor]);
|
|
10322
|
+
if (!loaded.ok) {
|
|
10323
|
+
return {
|
|
10324
|
+
exitCode: 1,
|
|
10325
|
+
output: `${FORMATTING_CONFIG_ERROR_MESSAGE} \u2014 ${loaded.error}`,
|
|
10326
|
+
durationMs: Date.now() - startTime
|
|
10327
|
+
};
|
|
10328
|
+
}
|
|
10329
|
+
const validationConfig = loaded.value[validationConfigDescriptor.section];
|
|
10330
|
+
if (!existsSync(join24(cwd, DPRINT_CONFIG_FILENAME))) {
|
|
10331
|
+
const output2 = quiet ? "" : `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: skipped (${FORMATTING_COMMAND_OUTPUT.NO_CONFIG_SKIP_REASON})`;
|
|
10332
|
+
return { exitCode: 0, output: output2, durationMs: Date.now() - startTime };
|
|
10333
|
+
}
|
|
10334
|
+
const pathFilter = validationPathFilterForTool(
|
|
10335
|
+
validationConfig.paths,
|
|
10336
|
+
VALIDATION_PATH_TOOL_SUBSECTIONS.FORMATTING
|
|
10337
|
+
);
|
|
10338
|
+
const hasExplicitScope = files !== void 0 && files.length > 0;
|
|
10339
|
+
const scopedFiles = hasExplicitScope ? files.map((filePath) => isAbsolute3(filePath) ? relative3(cwd, filePath) : filePath).filter((relativePath) => pathPassesValidationFilter(relativePath, pathFilter)) : void 0;
|
|
10340
|
+
if (hasExplicitScope && (scopedFiles === void 0 || scopedFiles.length === 0)) {
|
|
10341
|
+
const output2 = quiet ? "" : `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: skipped (${FORMATTING_COMMAND_OUTPUT.EMPTY_SCOPE_REASON})`;
|
|
10342
|
+
return { exitCode: 0, output: output2, durationMs: Date.now() - startTime };
|
|
10343
|
+
}
|
|
10344
|
+
const result = await validateFormatting({ projectRoot: cwd, files: scopedFiles });
|
|
10345
|
+
const durationMs = Date.now() - startTime;
|
|
10346
|
+
if (result.success) {
|
|
10347
|
+
return { exitCode: 0, output: quiet ? "" : FORMATTING_COMMAND_OUTPUT.NO_ISSUES, durationMs };
|
|
10348
|
+
}
|
|
10349
|
+
const detail = result.error ?? result.output;
|
|
10350
|
+
const output = [FORMATTING_COMMAND_OUTPUT.FAILURE_SUMMARY, detail].filter((line) => line.length > 0).join("\n");
|
|
10351
|
+
return { exitCode: 1, output, durationMs };
|
|
10352
|
+
}
|
|
10353
|
+
|
|
10354
|
+
// src/validation/languages/formatting.ts
|
|
10355
|
+
var FORMATTING_LANGUAGE_NAME = "formatting";
|
|
10356
|
+
var formattingValidationLanguage = {
|
|
10357
|
+
name: FORMATTING_LANGUAGE_NAME,
|
|
10358
|
+
stages: [
|
|
10359
|
+
{
|
|
10360
|
+
name: VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING,
|
|
10361
|
+
failsPipeline: true,
|
|
10362
|
+
run: (context) => formattingCommand({ cwd: context.cwd, files: context.files, quiet: context.quiet })
|
|
10363
|
+
}
|
|
10364
|
+
]
|
|
10365
|
+
};
|
|
10366
|
+
|
|
10367
|
+
// src/commands/validation/markdown.ts
|
|
10368
|
+
import { relative as relative4 } from "path";
|
|
10369
|
+
|
|
10055
10370
|
// src/validation/steps/markdown.ts
|
|
10056
|
-
import { existsSync, statSync } from "fs";
|
|
10057
|
-
import { basename as basename4, dirname as dirname8, join as
|
|
10371
|
+
import { existsSync as existsSync2, statSync } from "fs";
|
|
10372
|
+
import { basename as basename4, dirname as dirname8, join as join25, relative as pathRelative } from "path";
|
|
10058
10373
|
import { main as markdownlintMain } from "markdownlint-cli2";
|
|
10059
10374
|
import relativeLinksRule from "markdownlint-rule-relative-links";
|
|
10060
10375
|
var MARKDOWN_DEFAULT_DIRECTORY_NAMES = ["spx", "docs"];
|
|
@@ -10093,7 +10408,7 @@ function buildMarkdownlintConfig(directoryName) {
|
|
|
10093
10408
|
};
|
|
10094
10409
|
}
|
|
10095
10410
|
function getDefaultDirectories(projectRoot) {
|
|
10096
|
-
return MARKDOWN_DEFAULT_DIRECTORY_NAMES.map((name) =>
|
|
10411
|
+
return MARKDOWN_DEFAULT_DIRECTORY_NAMES.map((name) => join25(projectRoot, name)).filter((dir) => existsSync2(dir));
|
|
10097
10412
|
}
|
|
10098
10413
|
function resolveMarkdownValidationTarget(path7, deps = defaultMarkdownValidationTargetDeps) {
|
|
10099
10414
|
if (isExistingDirectory(path7, deps)) {
|
|
@@ -10176,7 +10491,7 @@ async function validateTarget(target, config, projectRoot, ignoreGlobs = []) {
|
|
|
10176
10491
|
if (parsed) {
|
|
10177
10492
|
errors.push({
|
|
10178
10493
|
...parsed,
|
|
10179
|
-
file:
|
|
10494
|
+
file: join25(directory, parsed.file)
|
|
10180
10495
|
});
|
|
10181
10496
|
}
|
|
10182
10497
|
}
|
|
@@ -10215,57 +10530,6 @@ function markdownlintConfigDirectoryName(directory, projectRoot) {
|
|
|
10215
10530
|
return basename4(directory);
|
|
10216
10531
|
}
|
|
10217
10532
|
|
|
10218
|
-
// src/commands/validation/messages.ts
|
|
10219
|
-
var VALIDATION_STAGE_DISPLAY_NAMES = {
|
|
10220
|
-
CIRCULAR: "dependency-cruiser",
|
|
10221
|
-
KNIP: "Knip",
|
|
10222
|
-
ESLINT: "ESLint",
|
|
10223
|
-
TYPESCRIPT: "TypeScript",
|
|
10224
|
-
MARKDOWN: "Markdown",
|
|
10225
|
-
LITERAL: "Literal"
|
|
10226
|
-
};
|
|
10227
|
-
var VALIDATION_SKIP_LABELS = {
|
|
10228
|
-
VERB: "Skipping",
|
|
10229
|
-
CIRCULAR_REASON: "skip-circular",
|
|
10230
|
-
LITERAL_REASON: "skip-literal",
|
|
10231
|
-
DISABLED_BY_PREFIX: "disabled by",
|
|
10232
|
-
TYPESCRIPT_ABSENT_REASON: "TypeScript not detected in project",
|
|
10233
|
-
VALIDATION_PATHS_NO_TARGETS_REASON: "validation paths matched no files",
|
|
10234
|
-
MARKDOWN_NO_SCOPE_REASON: "no markdown files in --files scope",
|
|
10235
|
-
MARKDOWN_NO_DEFAULT_DIRECTORIES_REASON: "no spx/ or docs/ directories found"
|
|
10236
|
-
};
|
|
10237
|
-
var VALIDATION_COMMAND_OUTPUT = {
|
|
10238
|
-
CIRCULAR_FOUND: `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: circular dependencies found`,
|
|
10239
|
-
CIRCULAR_NONE_FOUND: `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: \u2713 No circular dependencies found`,
|
|
10240
|
-
KNIP_CONFIG_ERROR: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: \u2717 config error`,
|
|
10241
|
-
KNIP_DISABLED: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: skipped (${VALIDATION_SKIP_LABELS.DISABLED_BY_PREFIX} validation.knip.enabled)`,
|
|
10242
|
-
KNIP_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: \u2713 No unused code found`,
|
|
10243
|
-
KNIP_FAILURE: "Unused code found",
|
|
10244
|
-
ESLINT_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT}: \u2713 No errors found`,
|
|
10245
|
-
ESLINT_FAILURE: `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT} validation failed`,
|
|
10246
|
-
ESLINT_MISSING_CONFIG: "ESLint config not found: project has tsconfig.json but no eslint.config.{ts,js,mjs,cjs}",
|
|
10247
|
-
TYPESCRIPT_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT}: \u2713 No type errors`,
|
|
10248
|
-
TYPESCRIPT_FAILURE: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT} validation failed`,
|
|
10249
|
-
MARKDOWN_NO_ISSUES: `${VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN}: No issues found`,
|
|
10250
|
-
MARKDOWN_ERROR_SUMMARY_SUFFIX: "error(s) found"
|
|
10251
|
-
};
|
|
10252
|
-
var CIRCULAR_SKIP_OUTPUT = `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: skipped (--${VALIDATION_SKIP_LABELS.CIRCULAR_REASON})`;
|
|
10253
|
-
var CIRCULAR_SKIP_JSON_OUTPUT = JSON.stringify({
|
|
10254
|
-
skipped: true,
|
|
10255
|
-
reason: VALIDATION_SKIP_LABELS.CIRCULAR_REASON
|
|
10256
|
-
});
|
|
10257
|
-
var LITERAL_SKIP_OUTPUT = `${VALIDATION_STAGE_DISPLAY_NAMES.LITERAL}: skipped (--${VALIDATION_SKIP_LABELS.LITERAL_REASON})`;
|
|
10258
|
-
var LITERAL_SKIP_JSON_OUTPUT = JSON.stringify({
|
|
10259
|
-
skipped: true,
|
|
10260
|
-
reason: VALIDATION_SKIP_LABELS.LITERAL_REASON
|
|
10261
|
-
});
|
|
10262
|
-
function formatTypeScriptAbsentSkipMessage(stageName) {
|
|
10263
|
-
return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.TYPESCRIPT_ABSENT_REASON})`;
|
|
10264
|
-
}
|
|
10265
|
-
function formatValidationPathsNoTargetsSkipMessage(stageName) {
|
|
10266
|
-
return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.VALIDATION_PATHS_NO_TARGETS_REASON})`;
|
|
10267
|
-
}
|
|
10268
|
-
|
|
10269
10533
|
// src/commands/validation/markdown.ts
|
|
10270
10534
|
var MARKDOWN_COMMAND_OUTPUT = {
|
|
10271
10535
|
ERROR_SUMMARY_SUFFIX: VALIDATION_COMMAND_OUTPUT.MARKDOWN_ERROR_SUMMARY_SUFFIX,
|
|
@@ -10295,7 +10559,7 @@ async function markdownCommand(options) {
|
|
|
10295
10559
|
path: path7
|
|
10296
10560
|
}));
|
|
10297
10561
|
const targets = unfilteredTargets.filter(
|
|
10298
|
-
(target) => pathPassesValidationFilter(
|
|
10562
|
+
(target) => pathPassesValidationFilter(relative4(cwd, target.path), pathFilter)
|
|
10299
10563
|
);
|
|
10300
10564
|
const skippedTargets = targetResolutions === void 0 ? [] : targetResolutions.map((resolution) => resolution.skipped).filter((skipped) => skipped !== void 0);
|
|
10301
10565
|
const skippedOutput = quiet ? [] : skippedTargets.map(formatSkippedFileScope);
|
|
@@ -11204,8 +11468,8 @@ var ParseErrorCode;
|
|
|
11204
11468
|
})(ParseErrorCode || (ParseErrorCode = {}));
|
|
11205
11469
|
|
|
11206
11470
|
// src/validation/config/scope.ts
|
|
11207
|
-
import { existsSync as
|
|
11208
|
-
import { isAbsolute as
|
|
11471
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "fs";
|
|
11472
|
+
import { isAbsolute as isAbsolute4, join as join26, relative as relative5, resolve as resolve6 } from "path";
|
|
11209
11473
|
var TSCONFIG_FILES = {
|
|
11210
11474
|
full: "tsconfig.json",
|
|
11211
11475
|
production: "tsconfig.production.json"
|
|
@@ -11234,7 +11498,7 @@ var TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS = [
|
|
|
11234
11498
|
var TEMPORARY_TSCONFIG_PARENT_SEGMENTS = ["node_modules", ".cache", "spx"];
|
|
11235
11499
|
var defaultScopeDeps = {
|
|
11236
11500
|
readFileSync: readFileSync3,
|
|
11237
|
-
existsSync:
|
|
11501
|
+
existsSync: existsSync3,
|
|
11238
11502
|
readdirSync
|
|
11239
11503
|
};
|
|
11240
11504
|
var EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND = {
|
|
@@ -11242,7 +11506,7 @@ var EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND = {
|
|
|
11242
11506
|
FILE: "file"
|
|
11243
11507
|
};
|
|
11244
11508
|
function resolveProjectPath(projectRoot, path7) {
|
|
11245
|
-
return
|
|
11509
|
+
return isAbsolute4(path7) ? path7 : join26(projectRoot, path7);
|
|
11246
11510
|
}
|
|
11247
11511
|
function parseTypeScriptConfig(configPath, deps = defaultScopeDeps) {
|
|
11248
11512
|
try {
|
|
@@ -11281,12 +11545,12 @@ function hasTypeScriptFilesRecursive(dirPath, maxDepth = 2, deps = defaultScopeD
|
|
|
11281
11545
|
try {
|
|
11282
11546
|
const items = deps.readdirSync(dirPath, { withFileTypes: true });
|
|
11283
11547
|
const hasDirectTsFiles = items.some(
|
|
11284
|
-
(item) => item.isFile() && pathHasTypeScriptSourceExtension(item.name) && pathPassesTypeScriptFileDiscoveryExcludes(
|
|
11548
|
+
(item) => item.isFile() && pathHasTypeScriptSourceExtension(item.name) && pathPassesTypeScriptFileDiscoveryExcludes(join26(dirPath, item.name), options)
|
|
11285
11549
|
);
|
|
11286
11550
|
if (hasDirectTsFiles) return true;
|
|
11287
11551
|
const subdirs = items.filter((item) => item.isDirectory() && !item.name.startsWith("."));
|
|
11288
11552
|
for (const subdir of subdirs.slice(0, 5)) {
|
|
11289
|
-
if (hasTypeScriptFilesRecursive(
|
|
11553
|
+
if (hasTypeScriptFilesRecursive(join26(dirPath, subdir.name), maxDepth - 1, deps, options)) {
|
|
11290
11554
|
return true;
|
|
11291
11555
|
}
|
|
11292
11556
|
}
|
|
@@ -11323,7 +11587,7 @@ function directoryContributesTypeScriptScope(dir, config, projectRoot, deps) {
|
|
|
11323
11587
|
return false;
|
|
11324
11588
|
}
|
|
11325
11589
|
try {
|
|
11326
|
-
return hasTypeScriptFilesRecursive(
|
|
11590
|
+
return hasTypeScriptFilesRecursive(join26(projectRoot, dir), 2, deps, {
|
|
11327
11591
|
excludePatterns: config.exclude,
|
|
11328
11592
|
projectRoot
|
|
11329
11593
|
});
|
|
@@ -11552,13 +11816,13 @@ function normalizeActiveIncludePattern(pattern, projectRoot, deps) {
|
|
|
11552
11816
|
function filterActiveIncludePatterns(patterns, excludePatterns, projectRoot, deps) {
|
|
11553
11817
|
return patterns.map((pattern) => normalizeActiveIncludePattern(pattern, projectRoot, deps)).filter((pattern) => typeScriptScopePatternTargetsTypeScriptSource(pattern)).filter((pattern) => {
|
|
11554
11818
|
const topLevelDir = getLiteralTopLevelPatternDirectory(pattern);
|
|
11555
|
-
return topLevelDir === null || deps.existsSync(
|
|
11819
|
+
return topLevelDir === null || deps.existsSync(join26(projectRoot, topLevelDir));
|
|
11556
11820
|
}).filter((pattern) => pathPassesValidationFilter(pattern, { exclude: excludePatterns }));
|
|
11557
11821
|
}
|
|
11558
11822
|
function getValidationDirectories(scope2, projectRoot, deps = defaultScopeDeps) {
|
|
11559
11823
|
const config = resolveTypeScriptConfig(scope2, projectRoot, deps);
|
|
11560
11824
|
const configDirectories = getTopLevelDirectoriesWithTypeScript(config, projectRoot, deps);
|
|
11561
|
-
const existingDirectories = configDirectories.filter((dir) => deps.existsSync(
|
|
11825
|
+
const existingDirectories = configDirectories.filter((dir) => deps.existsSync(join26(projectRoot, dir)));
|
|
11562
11826
|
return existingDirectories;
|
|
11563
11827
|
}
|
|
11564
11828
|
function getTypeScriptScope(scope2, projectRoot, deps = defaultScopeDeps) {
|
|
@@ -11579,14 +11843,14 @@ function pathPassesTypeScriptScope(path7, scopeConfig) {
|
|
|
11579
11843
|
return included && !excluded;
|
|
11580
11844
|
}
|
|
11581
11845
|
function pathStaysInsideTypeScriptScopeRoot(projectRoot, path7) {
|
|
11582
|
-
const resolvedPath =
|
|
11583
|
-
const relativePath =
|
|
11846
|
+
const resolvedPath = isAbsolute4(path7) ? resolve6(path7) : resolve6(projectRoot, path7);
|
|
11847
|
+
const relativePath = relative5(projectRoot, resolvedPath);
|
|
11584
11848
|
const segments = normalizeTypeScriptScopePath(relativePath).split(PATH_SEGMENT_SEPARATOR3);
|
|
11585
|
-
return relativePath.length === 0 || !segments.includes("..") && !
|
|
11849
|
+
return relativePath.length === 0 || !segments.includes("..") && !isAbsolute4(relativePath);
|
|
11586
11850
|
}
|
|
11587
11851
|
function toProjectRelativeTypeScriptScopePath(projectRoot, path7) {
|
|
11588
|
-
const resolvedPath =
|
|
11589
|
-
const relativePath =
|
|
11852
|
+
const resolvedPath = isAbsolute4(path7) ? resolve6(path7) : resolve6(projectRoot, path7);
|
|
11853
|
+
const relativePath = relative5(projectRoot, resolvedPath);
|
|
11590
11854
|
return relativePath.length === 0 ? TYPESCRIPT_SCOPE_PROJECT_ROOT : normalizeTypeScriptScopePath(relativePath);
|
|
11591
11855
|
}
|
|
11592
11856
|
function toExplicitTypeScriptScopeTarget(projectRoot, originalPath, deps = defaultScopeDeps) {
|
|
@@ -11625,10 +11889,10 @@ function explicitTypeScriptScopeTargetPassesScope(target, scopeConfig) {
|
|
|
11625
11889
|
if (typeScriptSourcePatterns.length > 0) {
|
|
11626
11890
|
return typeScriptSourcePatterns.some((pattern) => typeScriptScopePatternIntersectsDirectory(pattern, target.path));
|
|
11627
11891
|
}
|
|
11628
|
-
return pathPassesTypeScriptScope(
|
|
11892
|
+
return pathPassesTypeScriptScope(join26(target.path, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME), scopeConfig);
|
|
11629
11893
|
}
|
|
11630
11894
|
function directoryPassesTypeScriptExcludes(directory, scopeConfig) {
|
|
11631
|
-
const probePath =
|
|
11895
|
+
const probePath = join26(directory, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME);
|
|
11632
11896
|
return !scopeConfig.excludePatterns.some(
|
|
11633
11897
|
(pattern) => typeScriptScopePatternCoversDirectorySourceSet(pattern, directory) || pathMatchesTypeScriptPattern(probePath, pattern)
|
|
11634
11898
|
);
|
|
@@ -11819,10 +12083,10 @@ function resolvedModulePath(resolvedPath) {
|
|
|
11819
12083
|
return resolvedPath;
|
|
11820
12084
|
}
|
|
11821
12085
|
}
|
|
11822
|
-
function nearestPackageRoot(filePath,
|
|
12086
|
+
function nearestPackageRoot(filePath, existsSync8) {
|
|
11823
12087
|
let currentDirectory = path6.dirname(filePath);
|
|
11824
12088
|
while (true) {
|
|
11825
|
-
if (
|
|
12089
|
+
if (existsSync8(path6.join(currentDirectory, PACKAGE_MANIFEST_FILENAME))) {
|
|
11826
12090
|
return currentDirectory;
|
|
11827
12091
|
}
|
|
11828
12092
|
const parentDirectory = path6.dirname(currentDirectory);
|
|
@@ -11832,9 +12096,9 @@ function nearestPackageRoot(filePath, existsSync7) {
|
|
|
11832
12096
|
currentDirectory = parentDirectory;
|
|
11833
12097
|
}
|
|
11834
12098
|
}
|
|
11835
|
-
function bundledToolPath(resolvedPath,
|
|
12099
|
+
function bundledToolPath(resolvedPath, existsSync8) {
|
|
11836
12100
|
const bundledFilePath = resolvedModulePath(resolvedPath);
|
|
11837
|
-
return nearestPackageRoot(bundledFilePath,
|
|
12101
|
+
return nearestPackageRoot(bundledFilePath, existsSync8) ?? path6.dirname(bundledFilePath);
|
|
11838
12102
|
}
|
|
11839
12103
|
async function discoverTool(tool, options = {}) {
|
|
11840
12104
|
const { projectRoot = process.cwd(), deps = defaultToolDiscoveryDeps } = options;
|
|
@@ -11891,7 +12155,7 @@ import {
|
|
|
11891
12155
|
cruise as dependencyCruiser
|
|
11892
12156
|
} from "dependency-cruiser";
|
|
11893
12157
|
import extractTypeScriptConfig from "dependency-cruiser/config-utl/extract-ts-config";
|
|
11894
|
-
import { join as
|
|
12158
|
+
import { join as join27 } from "path";
|
|
11895
12159
|
var DEPENDENCY_CRUISER_MODULE_SYSTEMS = ["es6", "cjs"];
|
|
11896
12160
|
var DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_GLOB_SUFFIXES = [...TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS];
|
|
11897
12161
|
var DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_PATTERN = String.raw`(?<!\.d)\.(?:[cm]?ts|tsx)$`;
|
|
@@ -12131,7 +12395,7 @@ async function validateCircularDependencies(scope2, typescriptScope, projectRoot
|
|
|
12131
12395
|
if (analyzeSourcePatterns.length === 0) {
|
|
12132
12396
|
return { success: true };
|
|
12133
12397
|
}
|
|
12134
|
-
const tsConfigFile =
|
|
12398
|
+
const tsConfigFile = join27(projectRoot, TSCONFIG_FILES[scope2]);
|
|
12135
12399
|
const result = await deps.dependencyCruiser(
|
|
12136
12400
|
analyzeSourcePatterns,
|
|
12137
12401
|
buildDependencyCruiserOptions(typescriptScope, projectRoot, tsConfigFile),
|
|
@@ -12251,9 +12515,9 @@ async function circularCommand(options, deps = defaultCircularCommandDeps) {
|
|
|
12251
12515
|
}
|
|
12252
12516
|
|
|
12253
12517
|
// src/validation/steps/knip.ts
|
|
12254
|
-
import { existsSync as
|
|
12518
|
+
import { existsSync as existsSync4 } from "fs";
|
|
12255
12519
|
import { mkdir as mkdir5, mkdtemp as mkdtemp2, rm, writeFile as writeFile3 } from "fs/promises";
|
|
12256
|
-
import { isAbsolute as
|
|
12520
|
+
import { isAbsolute as isAbsolute5, join as join28 } from "path";
|
|
12257
12521
|
var defaultKnipProcessRunner = lifecycleProcessRunner;
|
|
12258
12522
|
var KNIP_COMMAND_TOKENS = {
|
|
12259
12523
|
COMMAND: "knip",
|
|
@@ -12262,7 +12526,7 @@ var KNIP_COMMAND_TOKENS = {
|
|
|
12262
12526
|
USE_TSCONFIG_FILES_FLAG: "--use-tsconfig-files"
|
|
12263
12527
|
};
|
|
12264
12528
|
var defaultKnipDeps = {
|
|
12265
|
-
existsSync:
|
|
12529
|
+
existsSync: existsSync4,
|
|
12266
12530
|
mkdir: mkdir5,
|
|
12267
12531
|
mkdtemp: mkdtemp2,
|
|
12268
12532
|
rm,
|
|
@@ -12286,7 +12550,7 @@ async function validateKnip2(context, runner = defaultKnipProcessRunner, deps =
|
|
|
12286
12550
|
}
|
|
12287
12551
|
async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
|
|
12288
12552
|
const scopedTsconfig = typescriptScope.filteredByValidationPaths ? await createScopedKnipTsconfig(projectRoot, typescriptScope, deps) : void 0;
|
|
12289
|
-
const localBin =
|
|
12553
|
+
const localBin = join28(projectRoot, "node_modules", ".bin", "knip");
|
|
12290
12554
|
const binary = deps.existsSync(localBin) ? localBin : KNIP_COMMAND_TOKENS.NPX_COMMAND;
|
|
12291
12555
|
const baseArgs = scopedTsconfig === void 0 ? [] : [
|
|
12292
12556
|
KNIP_COMMAND_TOKENS.USE_TSCONFIG_FILES_FLAG,
|
|
@@ -12341,11 +12605,11 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
|
|
|
12341
12605
|
});
|
|
12342
12606
|
}
|
|
12343
12607
|
async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
|
|
12344
|
-
const tempParentDir =
|
|
12608
|
+
const tempParentDir = join28(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
|
|
12345
12609
|
await deps.mkdir(tempParentDir, { recursive: true });
|
|
12346
|
-
const tempDir = await deps.mkdtemp(
|
|
12347
|
-
const configPath =
|
|
12348
|
-
const toProjectPathPattern = (pattern) =>
|
|
12610
|
+
const tempDir = await deps.mkdtemp(join28(tempParentDir, "validate-knip-"));
|
|
12611
|
+
const configPath = join28(tempDir, TSCONFIG_FILES.full);
|
|
12612
|
+
const toProjectPathPattern = (pattern) => isAbsolute5(pattern) ? pattern : join28(projectRoot, pattern);
|
|
12349
12613
|
const project = [
|
|
12350
12614
|
...typescriptScope.directories.flatMap(
|
|
12351
12615
|
(directory) => TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS.map((pattern) => `${directory}/${pattern}`)
|
|
@@ -12353,7 +12617,7 @@ async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
|
|
|
12353
12617
|
...typescriptScope.filePatterns
|
|
12354
12618
|
];
|
|
12355
12619
|
const config = {
|
|
12356
|
-
extends:
|
|
12620
|
+
extends: join28(projectRoot, TSCONFIG_FILES.full),
|
|
12357
12621
|
include: project.map(toProjectPathPattern),
|
|
12358
12622
|
exclude: typescriptScope.excludePatterns.map(toProjectPathPattern)
|
|
12359
12623
|
};
|
|
@@ -12419,13 +12683,13 @@ async function knipCommand(options, deps = defaultKnipCommandDeps) {
|
|
|
12419
12683
|
}
|
|
12420
12684
|
|
|
12421
12685
|
// src/validation/steps/eslint.ts
|
|
12422
|
-
import { existsSync as
|
|
12423
|
-
import { join as
|
|
12686
|
+
import { existsSync as existsSync6 } from "fs";
|
|
12687
|
+
import { join as join30 } from "path";
|
|
12424
12688
|
|
|
12425
12689
|
// src/validation/lint-policy.ts
|
|
12426
12690
|
import { execFileSync } from "child_process";
|
|
12427
|
-
import { existsSync as
|
|
12428
|
-
import { join as
|
|
12691
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
|
|
12692
|
+
import { join as join29 } from "path";
|
|
12429
12693
|
|
|
12430
12694
|
// src/validation/lint-policy-constants.ts
|
|
12431
12695
|
var LINT_POLICY_MANIFESTS = {
|
|
@@ -12471,19 +12735,19 @@ var BASE_BRANCH_REFS = [LINT_POLICY_BASE_REFS.REMOTE_MAIN, LINT_POLICY_BASE_REFS
|
|
|
12471
12735
|
function isSpecTreeNodePath(entry) {
|
|
12472
12736
|
return NODE_SUFFIXES.some((suffix) => entry.endsWith(suffix));
|
|
12473
12737
|
}
|
|
12474
|
-
function
|
|
12738
|
+
function readManifest2(productDir, file, key) {
|
|
12475
12739
|
return parseLintPolicyManifest(
|
|
12476
|
-
readFileSync4(
|
|
12740
|
+
readFileSync4(join29(productDir, file), "utf-8"),
|
|
12477
12741
|
file,
|
|
12478
12742
|
key
|
|
12479
12743
|
);
|
|
12480
12744
|
}
|
|
12481
12745
|
function manifestExists(productDir, file) {
|
|
12482
|
-
return
|
|
12746
|
+
return existsSync5(join29(productDir, file));
|
|
12483
12747
|
}
|
|
12484
12748
|
function findDeprecatedSpecNodePath(productDir) {
|
|
12485
12749
|
function visit2(relativeDirectory) {
|
|
12486
|
-
const absoluteDirectory =
|
|
12750
|
+
const absoluteDirectory = join29(productDir, relativeDirectory);
|
|
12487
12751
|
for (const entry of readdirSync2(absoluteDirectory, { withFileTypes: true })) {
|
|
12488
12752
|
if (!entry.isDirectory()) {
|
|
12489
12753
|
continue;
|
|
@@ -12499,8 +12763,8 @@ function findDeprecatedSpecNodePath(productDir) {
|
|
|
12499
12763
|
}
|
|
12500
12764
|
return void 0;
|
|
12501
12765
|
}
|
|
12502
|
-
const specTreeRootPath =
|
|
12503
|
-
if (!
|
|
12766
|
+
const specTreeRootPath = join29(productDir, SPEC_TREE_ROOT);
|
|
12767
|
+
if (!existsSync5(specTreeRootPath)) {
|
|
12504
12768
|
return void 0;
|
|
12505
12769
|
}
|
|
12506
12770
|
return visit2(SPEC_TREE_ROOT);
|
|
@@ -12525,8 +12789,8 @@ function assertManifestEntries(productDir, file, entries, suffixPredicate, suffi
|
|
|
12525
12789
|
if (!suffixPredicate(entry)) {
|
|
12526
12790
|
throw new Error(`${file} entry must ${suffixDescription}: ${entry}`);
|
|
12527
12791
|
}
|
|
12528
|
-
const absoluteEntry =
|
|
12529
|
-
if (!
|
|
12792
|
+
const absoluteEntry = join29(productDir, entry);
|
|
12793
|
+
if (!existsSync5(absoluteEntry) || !statSync2(absoluteEntry).isDirectory()) {
|
|
12530
12794
|
throw new Error(`${file} entry does not exist as a directory: ${entry}`);
|
|
12531
12795
|
}
|
|
12532
12796
|
}
|
|
@@ -12658,11 +12922,11 @@ function validateLintPolicy(productDir) {
|
|
|
12658
12922
|
}
|
|
12659
12923
|
validateTestLintDebtNodeManifest(
|
|
12660
12924
|
productDir,
|
|
12661
|
-
|
|
12925
|
+
readManifest2(productDir, TEST_LINT_DEBT_NODE_MANIFEST_FILE, TEST_LINT_DEBT_NODE_MANIFEST_KEY)
|
|
12662
12926
|
);
|
|
12663
12927
|
validateTestOwnedConstantDebtNodeManifest(
|
|
12664
12928
|
productDir,
|
|
12665
|
-
|
|
12929
|
+
readManifest2(
|
|
12666
12930
|
productDir,
|
|
12667
12931
|
TEST_OWNED_CONSTANT_DEBT_NODE_MANIFEST_FILE,
|
|
12668
12932
|
TEST_OWNED_CONSTANT_DEBT_NODE_MANIFEST_KEY
|
|
@@ -12674,36 +12938,6 @@ function validateLintPolicy(productDir) {
|
|
|
12674
12938
|
}
|
|
12675
12939
|
}
|
|
12676
12940
|
|
|
12677
|
-
// src/validation/steps/subprocess-output.ts
|
|
12678
|
-
var VALIDATION_SUBPROCESS_EVENTS = {
|
|
12679
|
-
CLOSE: "close",
|
|
12680
|
-
DATA: "data",
|
|
12681
|
-
DRAIN: "drain",
|
|
12682
|
-
ERROR: "error"
|
|
12683
|
-
};
|
|
12684
|
-
var defaultValidationSubprocessOutputStreams = {
|
|
12685
|
-
stdout: process.stdout,
|
|
12686
|
-
stderr: process.stderr
|
|
12687
|
-
};
|
|
12688
|
-
function forwardValidationSubprocessOutput(child, streams = defaultValidationSubprocessOutputStreams) {
|
|
12689
|
-
child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, (chunk) => {
|
|
12690
|
-
forwardChunkWithBackpressure(child.stdout, streams.stdout, chunk);
|
|
12691
|
-
});
|
|
12692
|
-
child.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, (chunk) => {
|
|
12693
|
-
forwardChunkWithBackpressure(child.stderr, streams.stderr, chunk);
|
|
12694
|
-
});
|
|
12695
|
-
}
|
|
12696
|
-
function forwardChunkWithBackpressure(source, stream, chunk) {
|
|
12697
|
-
const ready = stream.write(chunk);
|
|
12698
|
-
if (ready || stream.once === void 0 || source === null || source === void 0) {
|
|
12699
|
-
return;
|
|
12700
|
-
}
|
|
12701
|
-
source.pause();
|
|
12702
|
-
stream.once(VALIDATION_SUBPROCESS_EVENTS.DRAIN, () => {
|
|
12703
|
-
source.resume();
|
|
12704
|
-
});
|
|
12705
|
-
}
|
|
12706
|
-
|
|
12707
12941
|
// src/validation/steps/eslint.ts
|
|
12708
12942
|
var defaultEslintProcessRunner = lifecycleProcessRunner;
|
|
12709
12943
|
var DEFAULT_ESLINT_CONFIG_FILE = "eslint.config.ts";
|
|
@@ -12771,8 +13005,8 @@ async function validateESLint(context, runner = defaultEslintProcessRunner, outp
|
|
|
12771
13005
|
scopeConfig: context.scopeConfig
|
|
12772
13006
|
});
|
|
12773
13007
|
return new Promise((resolve8) => {
|
|
12774
|
-
const localBin =
|
|
12775
|
-
const binary =
|
|
13008
|
+
const localBin = join30(projectRoot, ...ESLINT_LOCAL_BIN_SEGMENTS);
|
|
13009
|
+
const binary = existsSync6(localBin) ? localBin : "npx";
|
|
12776
13010
|
const spawnArgs = binary === "npx" ? eslintArgs : eslintArgs.slice(1);
|
|
12777
13011
|
const eslintProcess = spawnManagedSubprocess(runner, binary, spawnArgs, {
|
|
12778
13012
|
cwd: projectRoot
|
|
@@ -12873,7 +13107,7 @@ async function lintCommand(options) {
|
|
|
12873
13107
|
|
|
12874
13108
|
// src/validation/literal/index.ts
|
|
12875
13109
|
import { readFile as readFile9 } from "fs/promises";
|
|
12876
|
-
import { isAbsolute as
|
|
13110
|
+
import { isAbsolute as isAbsolute6, relative as relative7, resolve as resolve7 } from "path";
|
|
12877
13111
|
|
|
12878
13112
|
// src/lib/file-inclusion/predicates/ignore-source.ts
|
|
12879
13113
|
var IGNORE_SOURCE_LAYER = "ignore-source";
|
|
@@ -12908,7 +13142,7 @@ var ignoreSourceLayer = makeLayer(
|
|
|
12908
13142
|
|
|
12909
13143
|
// src/lib/file-inclusion/pipeline.ts
|
|
12910
13144
|
import { readdir as readdir7 } from "fs/promises";
|
|
12911
|
-
import { join as
|
|
13145
|
+
import { join as join31, relative as relative6, sep as sep2 } from "path";
|
|
12912
13146
|
var EXPLICIT_OVERRIDE_LAYER = "explicit-override";
|
|
12913
13147
|
function isNodeError3(err) {
|
|
12914
13148
|
return err instanceof Error && "code" in err;
|
|
@@ -12926,11 +13160,11 @@ async function collectPaths(absoluteDir, projectRoot, result, artifactDirs) {
|
|
|
12926
13160
|
for (const entry of dirEntries) {
|
|
12927
13161
|
if (entry.isDirectory()) {
|
|
12928
13162
|
if (artifactDirs.has(entry.name)) continue;
|
|
12929
|
-
const absolutePath =
|
|
13163
|
+
const absolutePath = join31(absoluteDir, entry.name);
|
|
12930
13164
|
await collectPaths(absolutePath, projectRoot, result, artifactDirs);
|
|
12931
13165
|
} else if (entry.isFile()) {
|
|
12932
|
-
const absolutePath =
|
|
12933
|
-
const rel =
|
|
13166
|
+
const absolutePath = join31(absoluteDir, entry.name);
|
|
13167
|
+
const rel = relative6(projectRoot, absolutePath);
|
|
12934
13168
|
result.push(sep2 === "/" ? rel : rel.split(sep2).join("/"));
|
|
12935
13169
|
}
|
|
12936
13170
|
}
|
|
@@ -13336,8 +13570,8 @@ async function validateLiteralReuse(input) {
|
|
|
13336
13570
|
const config = input.config ?? literalConfigDescriptor.defaults;
|
|
13337
13571
|
const request = input.files ? {
|
|
13338
13572
|
explicit: input.files.map((f) => {
|
|
13339
|
-
const abs =
|
|
13340
|
-
return
|
|
13573
|
+
const abs = isAbsolute6(f) ? f : resolve7(input.productDir, f);
|
|
13574
|
+
return relative7(input.productDir, abs).split(/[\\/]/g).join("/");
|
|
13341
13575
|
})
|
|
13342
13576
|
} : { walkRoot: input.productDir };
|
|
13343
13577
|
const scope2 = await runPipeline(
|
|
@@ -13358,7 +13592,7 @@ async function validateLiteralReuse(input) {
|
|
|
13358
13592
|
const testOccurrencesByFile = /* @__PURE__ */ new Map();
|
|
13359
13593
|
const indexedOccurrencesByFile = /* @__PURE__ */ new Map();
|
|
13360
13594
|
for (const abs of candidateFiles) {
|
|
13361
|
-
const rel =
|
|
13595
|
+
const rel = relative7(input.productDir, abs).split(/[\\/]/g).join("/");
|
|
13362
13596
|
const content = await readSafe(abs);
|
|
13363
13597
|
if (content === null) continue;
|
|
13364
13598
|
const occurrences = collectLiterals(content, rel, collectOptions);
|
|
@@ -13596,23 +13830,23 @@ function formatLoc(loc) {
|
|
|
13596
13830
|
}
|
|
13597
13831
|
|
|
13598
13832
|
// src/validation/steps/typescript.ts
|
|
13599
|
-
import { existsSync as
|
|
13833
|
+
import { existsSync as existsSync7, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
13600
13834
|
import { mkdtemp as mkdtemp3 } from "fs/promises";
|
|
13601
|
-
import { isAbsolute as
|
|
13835
|
+
import { isAbsolute as isAbsolute7, join as join32 } from "path";
|
|
13602
13836
|
var defaultTypeScriptProcessRunner = lifecycleProcessRunner;
|
|
13603
13837
|
var defaultTypeScriptDeps = {
|
|
13604
13838
|
mkdtemp: mkdtemp3,
|
|
13605
13839
|
mkdirSync,
|
|
13606
13840
|
writeFileSync,
|
|
13607
13841
|
rmSync,
|
|
13608
|
-
existsSync:
|
|
13842
|
+
existsSync: existsSync7
|
|
13609
13843
|
};
|
|
13610
13844
|
var TEMPORARY_TSCONFIG_COMPILER_OPTIONS = { noEmit: true };
|
|
13611
13845
|
var TEMPORARY_TSCONFIG_DIR_PREFIX = "validate-ts-";
|
|
13612
13846
|
async function createTemporaryTsconfigDir(projectRoot, deps) {
|
|
13613
|
-
const parent =
|
|
13847
|
+
const parent = join32(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
|
|
13614
13848
|
deps.mkdirSync(parent, { recursive: true });
|
|
13615
|
-
return deps.mkdtemp(
|
|
13849
|
+
return deps.mkdtemp(join32(parent, TEMPORARY_TSCONFIG_DIR_PREFIX));
|
|
13616
13850
|
}
|
|
13617
13851
|
function buildTypeScriptArgs(context) {
|
|
13618
13852
|
const { scope: scope2, configFile } = context;
|
|
@@ -13620,11 +13854,11 @@ function buildTypeScriptArgs(context) {
|
|
|
13620
13854
|
}
|
|
13621
13855
|
async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = defaultTypeScriptDeps) {
|
|
13622
13856
|
const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
|
|
13623
|
-
const configPath =
|
|
13857
|
+
const configPath = join32(tempDir, "tsconfig.json");
|
|
13624
13858
|
const baseConfigFile = TSCONFIG_FILES[scope2];
|
|
13625
|
-
const absoluteFiles = files.map((file) =>
|
|
13859
|
+
const absoluteFiles = files.map((file) => isAbsolute7(file) ? file : join32(projectRoot, file));
|
|
13626
13860
|
const tempConfig = {
|
|
13627
|
-
extends:
|
|
13861
|
+
extends: join32(projectRoot, baseConfigFile),
|
|
13628
13862
|
files: absoluteFiles,
|
|
13629
13863
|
include: [],
|
|
13630
13864
|
exclude: [],
|
|
@@ -13641,11 +13875,11 @@ async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = def
|
|
|
13641
13875
|
}
|
|
13642
13876
|
async function createScopeFilteredTsconfig(scope2, projectRoot, scopeConfig, deps = defaultTypeScriptDeps) {
|
|
13643
13877
|
const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
|
|
13644
|
-
const configPath =
|
|
13878
|
+
const configPath = join32(tempDir, "tsconfig.json");
|
|
13645
13879
|
const baseConfigFile = TSCONFIG_FILES[scope2];
|
|
13646
|
-
const toProjectPathPattern = (pattern) =>
|
|
13880
|
+
const toProjectPathPattern = (pattern) => isAbsolute7(pattern) ? pattern : join32(projectRoot, pattern);
|
|
13647
13881
|
const tempConfig = {
|
|
13648
|
-
extends:
|
|
13882
|
+
extends: join32(projectRoot, baseConfigFile),
|
|
13649
13883
|
include: scopeConfig.filePatterns.map(toProjectPathPattern),
|
|
13650
13884
|
exclude: scopeConfig.excludePatterns.map(toProjectPathPattern),
|
|
13651
13885
|
compilerOptions: TEMPORARY_TSCONFIG_COMPILER_OPTIONS
|
|
@@ -13673,7 +13907,7 @@ async function validateTypeScript(context, options = {}) {
|
|
|
13673
13907
|
const { configPath, cleanup } = await createFileSpecificTsconfig(scope2, files, projectRoot, deps);
|
|
13674
13908
|
try {
|
|
13675
13909
|
return await new Promise((resolve8) => {
|
|
13676
|
-
const tscBin =
|
|
13910
|
+
const tscBin = join32(projectRoot, "node_modules", ".bin", "tsc");
|
|
13677
13911
|
const tscBinary = deps.existsSync(tscBin) ? tscBin : "npx";
|
|
13678
13912
|
const tscArgs2 = tscBinary === "npx" ? ["tsc", "--project", configPath] : ["--project", configPath];
|
|
13679
13913
|
const tscProcess = spawnManagedSubprocess(runner, tscBinary, tscArgs2, {
|
|
@@ -13706,7 +13940,7 @@ async function validateTypeScript(context, options = {}) {
|
|
|
13706
13940
|
return { success: true, skipped: true };
|
|
13707
13941
|
}
|
|
13708
13942
|
const { configPath, cleanup } = await createScopeFilteredTsconfig(scope2, projectRoot, scopeConfig, deps);
|
|
13709
|
-
const tscBin =
|
|
13943
|
+
const tscBin = join32(projectRoot, "node_modules", ".bin", "tsc");
|
|
13710
13944
|
tool = deps.existsSync(tscBin) ? tscBin : "npx";
|
|
13711
13945
|
tscArgs = tool === "npx" ? ["tsc", "--project", configPath] : ["--project", configPath];
|
|
13712
13946
|
return new Promise((resolve8) => {
|
|
@@ -13728,7 +13962,7 @@ async function validateTypeScript(context, options = {}) {
|
|
|
13728
13962
|
});
|
|
13729
13963
|
});
|
|
13730
13964
|
} else {
|
|
13731
|
-
const tscBin =
|
|
13965
|
+
const tscBin = join32(projectRoot, "node_modules", ".bin", "tsc");
|
|
13732
13966
|
tool = deps.existsSync(tscBin) ? tscBin : "npx";
|
|
13733
13967
|
const rawArgs = buildTypeScriptArgs({ scope: scope2, configFile });
|
|
13734
13968
|
tscArgs = tool === "npx" ? rawArgs : rawArgs.slice(1);
|
|
@@ -13905,11 +14139,12 @@ var typescriptValidationLanguage = {
|
|
|
13905
14139
|
|
|
13906
14140
|
// src/validation/registry.ts
|
|
13907
14141
|
var validationRegistry = {
|
|
13908
|
-
languages: [typescriptValidationLanguage, markdownValidationLanguage]
|
|
14142
|
+
languages: [typescriptValidationLanguage, markdownValidationLanguage, formattingValidationLanguage]
|
|
13909
14143
|
};
|
|
13910
|
-
|
|
13911
|
-
(language) => language.stages
|
|
13912
|
-
|
|
14144
|
+
function composeValidationPipelineStages(languages) {
|
|
14145
|
+
return languages.flatMap((language) => language.stages);
|
|
14146
|
+
}
|
|
14147
|
+
var validationPipelineStages = composeValidationPipelineStages(validationRegistry.languages);
|
|
13913
14148
|
var VALIDATION_PIPELINE_TOTAL_STEPS = validationPipelineStages.length;
|
|
13914
14149
|
|
|
13915
14150
|
// src/commands/validation/format.ts
|
|
@@ -13972,7 +14207,7 @@ async function allCommand(options) {
|
|
|
13972
14207
|
// src/validation/literal/allowlist-existing.ts
|
|
13973
14208
|
import { randomBytes } from "crypto";
|
|
13974
14209
|
import { rename as rename4, writeFile as writeFile4 } from "fs/promises";
|
|
13975
|
-
import { dirname as dirname9, join as
|
|
14210
|
+
import { dirname as dirname9, join as join33 } from "path";
|
|
13976
14211
|
var EXIT_OK = 0;
|
|
13977
14212
|
var EXIT_ERROR = 1;
|
|
13978
14213
|
var INCLUDE_FIELD = "include";
|
|
@@ -13992,7 +14227,7 @@ var productionWriter = {
|
|
|
13992
14227
|
async write(filePath, content) {
|
|
13993
14228
|
const dir = dirname9(filePath);
|
|
13994
14229
|
const random = randomBytes(RANDOM_TOKEN_BYTES).toString("hex");
|
|
13995
|
-
const tmpPath =
|
|
14230
|
+
const tmpPath = join33(dir, `${TEMP_FILE_PREFIX}${random}${TEMP_FILE_SUFFIX}`);
|
|
13996
14231
|
await writeFile4(tmpPath, content, "utf8");
|
|
13997
14232
|
await rename4(tmpPath, filePath);
|
|
13998
14233
|
}
|
|
@@ -14098,6 +14333,10 @@ var validationCliDefinition = {
|
|
|
14098
14333
|
alias: "md",
|
|
14099
14334
|
description: "Validate markdown link integrity and structure"
|
|
14100
14335
|
},
|
|
14336
|
+
format: {
|
|
14337
|
+
commandName: "format",
|
|
14338
|
+
description: "Check code formatting with dprint"
|
|
14339
|
+
},
|
|
14101
14340
|
all: {
|
|
14102
14341
|
commandName: "all",
|
|
14103
14342
|
description: "Run all validations"
|
|
@@ -14278,6 +14517,16 @@ function registerValidationCommands(validationCmd) {
|
|
|
14278
14517
|
process.exit(result.exitCode);
|
|
14279
14518
|
});
|
|
14280
14519
|
addCommonOptions(markdownCmd);
|
|
14520
|
+
const formatCmd = addValidationSubcommand(validationCmd, subcommands.format).action(async (options) => {
|
|
14521
|
+
const result = await formattingCommand({
|
|
14522
|
+
cwd: process.cwd(),
|
|
14523
|
+
files: options.files,
|
|
14524
|
+
quiet: options.quiet
|
|
14525
|
+
});
|
|
14526
|
+
if (result.output) console.log(result.output);
|
|
14527
|
+
process.exit(result.exitCode);
|
|
14528
|
+
});
|
|
14529
|
+
addCommonOptions(formatCmd);
|
|
14281
14530
|
const allCmd = addValidationSubcommand(validationCmd, subcommands.all).option("--fix", "Auto-fix ESLint issues").option(allValidationCliOptions.skipCircular.flag, allValidationCliOptions.skipCircular.description).option(allValidationCliOptions.skipLiteral.flag, allValidationCliOptions.skipLiteral.description).action(async (options) => {
|
|
14282
14531
|
const result = await allCommand({
|
|
14283
14532
|
cwd: process.cwd(),
|