@ecoma-io/archkeep 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/analysis/csharp.mjs +3 -1
- package/src/analysis/dotnet/csproj.mjs +5 -1
- package/src/analysis/dotnet/namespaces.mjs +1 -0
- package/src/analysis/go.mjs +6 -0
- package/src/analysis/java.mjs +2 -0
- package/src/analysis/jvm/gradle.mjs +3 -1
- package/src/analysis/jvm/maven.mjs +6 -1
- package/src/analysis/jvm/packages.mjs +1 -0
- package/src/analysis/jvm/resolve.mjs +4 -2
- package/src/analysis/kotlin.mjs +2 -0
- package/src/analysis/markdown.mjs +1 -0
- package/src/analysis/python.mjs +8 -0
- package/src/analysis/rust.mjs +5 -1
- package/src/analysis/source-util.mjs +1 -1
- package/src/analysis/typescript.mjs +2 -0
- package/src/architecture-intent/model.mjs +11 -7
- package/src/architecture-intent/selectors.mjs +2 -1
- package/src/commands/change-intent.mjs +10 -9
- package/src/commands/change.mjs +86 -23
- package/src/commands/check.mjs +11 -9
- package/src/commands/completeness.mjs +7 -6
- package/src/commands/coverage-acceptance.mjs +46 -0
- package/src/commands/custom-rules.mjs +1 -0
- package/src/commands/delta-classify.mjs +3 -0
- package/src/commands/delta-snapshot.mjs +27 -5
- package/src/commands/delta.mjs +73 -39
- package/src/commands/diff.mjs +3 -9
- package/src/commands/drift.mjs +1 -1
- package/src/commands/evaluation-primitives.mjs +4 -4
- package/src/commands/evolution.mjs +2 -0
- package/src/commands/explain.mjs +2 -0
- package/src/commands/graph.mjs +39 -17
- package/src/commands/history.mjs +36 -0
- package/src/commands/plan-context-command.mjs +4 -1
- package/src/commands/policy.mjs +5 -2
- package/src/commands/scenario-evaluation.mjs +1 -1
- package/src/commands/snapshot-meta.mjs +33 -0
- package/src/commands/trajectory.mjs +2 -1
- package/src/config.mjs +1 -1
- package/src/custom-rules/host.mjs +3 -3
- package/src/custom-rules/values.mjs +1 -4
- package/src/eslint-config.mjs +1 -0
- package/src/fixtures/evolution-lifecycle/workspace.mjs +15 -4
- package/src/go-work.mjs +1 -1
- package/src/governance/adr-registry.mjs +4 -1
- package/src/governance/debt-ledger.mjs +1 -1
- package/src/governance/decision-fitness.mjs +2 -0
- package/src/governance/decision-graph.mjs +1 -0
- package/src/governance/discovery-proposal.mjs +8 -2
- package/src/governance/evolution-event.mjs +42 -0
- package/src/governance/fitness-registry.mjs +16 -1
- package/src/governance/preset-fingerprints.json +14 -14
- package/src/governance/profile-registry.mjs +22 -3
- package/src/governance/provenance-record.mjs +4 -1
- package/src/governance/reconcile-score.mjs +4 -0
- package/src/governance/row-schema.mjs +1 -0
- package/src/governance/verdict.mjs +37 -4
- package/src/governance/waiver.mjs +1 -0
- package/src/intent/intent-manifest.json +18 -12
- package/src/intent/mask-non-code.mjs +1 -0
- package/src/lsp/diagnostics.mjs +3 -2
- package/src/lsp/protocol.mjs +2 -1
- package/src/lsp/server.mjs +3 -0
- package/src/lsp/workspace-index.mjs +3 -1
- package/src/providers/native/differential.fixtures.mjs +29 -11
- package/src/providers/native/index.mjs +2 -1
- package/src/providers/native/model.mjs +4 -0
- package/src/report/envelope-shape.mjs +2 -0
- package/src/report/json.mjs +4 -2
- package/src/report/sarif.mjs +21 -8
- package/src/report/snapshot-text.mjs +3 -3
- package/src/report/text.mjs +10 -2
- package/src/rules/match.mjs +7 -5
- package/src/rules/specifiers.mjs +2 -0
- package/src/rules/tags.mjs +3 -2
- package/src/rules/topology.mjs +6 -1
- package/src/values.mjs +11 -0
- package/src/workspace.mjs +1 -0
package/src/rules/tags.mjs
CHANGED
|
@@ -29,7 +29,7 @@ import { tagMatches } from "./match.mjs";
|
|
|
29
29
|
import { getPath, pathExists } from "./reachability.mjs";
|
|
30
30
|
|
|
31
31
|
/** `"a", "b"` — how upstream renders a tag list inside a message. */
|
|
32
|
-
|
|
32
|
+
function stringifyTags(tags) {
|
|
33
33
|
return tags.map((t) => `"${t}"`).join(", ");
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -55,7 +55,7 @@ export function hasTag(project, tag) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/** True when the project carries NONE of these tags — upstream's spelling. */
|
|
58
|
-
|
|
58
|
+
function hasNoneOfTheseTags(project, tags) {
|
|
59
59
|
return tags.filter((tag) => hasTag(project, tag)).length === 0;
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -165,6 +165,7 @@ export function orphanedNotDependOnTags(depConstraints, graph) {
|
|
|
165
165
|
* are what the message prints so a reader can see the hop that did it.
|
|
166
166
|
*/
|
|
167
167
|
export function findDependenciesWithTags(targetProject, tags, graph, reach) {
|
|
168
|
+
// used by its own test
|
|
168
169
|
const reachable = Object.keys(graph.nodes)
|
|
169
170
|
.sort()
|
|
170
171
|
.filter(
|
package/src/rules/topology.mjs
CHANGED
|
@@ -57,6 +57,7 @@ export function appIsMFERemote(project) {
|
|
|
57
57
|
* — the common case, and the one that keeps `noSelfCircularDependencies` armed.
|
|
58
58
|
*/
|
|
59
59
|
export function entryPointOf(file, projectRoot, entryPoints) {
|
|
60
|
+
// used by its own test
|
|
60
61
|
if (!entryPoints || entryPoints.length === 0) return undefined;
|
|
61
62
|
const fileEntryPoint = entryPoints.find((entry) => entry.file === file);
|
|
62
63
|
if (fileEntryPoint) return fileEntryPoint.file;
|
|
@@ -88,7 +89,7 @@ export function entryPointOf(file, projectRoot, entryPoints) {
|
|
|
88
89
|
* standing in for upstream's module resolution — the resolver already ran; this
|
|
89
90
|
* layer never runs a second one.
|
|
90
91
|
*/
|
|
91
|
-
|
|
92
|
+
function secondaryEntryPointPath(resolvedFile, targetProject) {
|
|
92
93
|
if (!resolvedFile) return undefined;
|
|
93
94
|
return entryPointOf(resolvedFile, targetProject.data.root, targetProject.data?.entryPoints);
|
|
94
95
|
}
|
|
@@ -128,6 +129,7 @@ export function belongsToDifferentEntryPoint(resolvedFile, sourceFile, sourcePro
|
|
|
128
129
|
* @returns {string[]|null}
|
|
129
130
|
*/
|
|
130
131
|
export function findDynamicImportPath(graph, sourceProjectName, targetProjectName, visited = []) {
|
|
132
|
+
// used by its own test
|
|
131
133
|
if (visited.indexOf(sourceProjectName) > -1) return null;
|
|
132
134
|
for (const dependency of graph.dependencies?.[sourceProjectName] ?? []) {
|
|
133
135
|
if (dependency.type !== DYNAMIC) continue;
|
|
@@ -152,6 +154,7 @@ export function findDynamicImportPath(graph, sourceProjectName, targetProjectNam
|
|
|
152
154
|
* fired, and then the verdict would name evidence for a route nobody walked.
|
|
153
155
|
*/
|
|
154
156
|
export function hasDynamicImport(graph, sourceProjectName, targetProjectName) {
|
|
157
|
+
// used by its own test
|
|
155
158
|
return findDynamicImportPath(graph, sourceProjectName, targetProjectName) !== null;
|
|
156
159
|
}
|
|
157
160
|
|
|
@@ -209,6 +212,7 @@ export function createFileDependencyIndex(edges) {
|
|
|
209
212
|
|
|
210
213
|
/** The files carrying each hop of a path — one entry per consecutive pair. */
|
|
211
214
|
export function findFilesInCircularPath(fileIndex, circularPath) {
|
|
215
|
+
// used by its own test
|
|
212
216
|
const chain = [];
|
|
213
217
|
for (let i = 0; i < circularPath.length - 1; i++) {
|
|
214
218
|
chain.push(fileIndex.any.get(edgeKey(circularPath[i].name, circularPath[i + 1].name)) ?? []);
|
|
@@ -218,6 +222,7 @@ export function findFilesInCircularPath(fileIndex, circularPath) {
|
|
|
218
222
|
|
|
219
223
|
/** The files that lazy-load `target` from inside `source`. */
|
|
220
224
|
export function findFilesWithDynamicImports(fileIndex, sourceProjectName, targetProjectName) {
|
|
225
|
+
// used by its own test
|
|
221
226
|
return fileIndex.dynamic.get(edgeKey(sourceProjectName, targetProjectName)) ?? [];
|
|
222
227
|
}
|
|
223
228
|
|
package/src/values.mjs
CHANGED
|
@@ -36,6 +36,17 @@ export const isPlainObject = (value) =>
|
|
|
36
36
|
export const isStringArray = (value) =>
|
|
37
37
|
Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Whether a value is a string a reader could actually act on — non-empty
|
|
41
|
+
* after trimming. The verdict contract's reason fields (I3/I4) refuse
|
|
42
|
+
* everything else: `""` and `" "` are byte-present but semantically
|
|
43
|
+
* absent, and a non-string reason would ship a `typeof` artifact where the
|
|
44
|
+
* reader was promised a sentence.
|
|
45
|
+
*
|
|
46
|
+
* @type {(value: unknown) => value is string}
|
|
47
|
+
*/
|
|
48
|
+
export const isNonEmptyString = (value) => typeof value === "string" && value.trim() !== "";
|
|
49
|
+
|
|
39
50
|
/**
|
|
40
51
|
* A value's type, for an error message that shows what was actually there.
|
|
41
52
|
*
|
package/src/workspace.mjs
CHANGED
|
@@ -721,6 +721,7 @@ const DOTLESS_FURNITURE = Object.freeze(
|
|
|
721
721
|
* @returns {boolean}
|
|
722
722
|
*/
|
|
723
723
|
export function exemptFromUnsupportedLanguage(sourceFile) {
|
|
724
|
+
// used by its own test
|
|
724
725
|
const base = sourceFile.slice(sourceFile.lastIndexOf("/") + 1);
|
|
725
726
|
// Dotfiles are editor and tool state (`.gitignore`, `.env`, `.npmrc`).
|
|
726
727
|
if (base.startsWith(".")) return true;
|