@ecoma-io/archkeep 0.13.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.
Files changed (131) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +262 -0
  3. package/cli.mjs +2792 -0
  4. package/index.mjs +85 -0
  5. package/lsp.mjs +81 -0
  6. package/nx.mjs +24 -0
  7. package/package.json +81 -0
  8. package/presets/clean-architecture.json +78 -0
  9. package/presets/ddd-bounded-contexts.json +88 -0
  10. package/presets/hexagonal.json +68 -0
  11. package/presets/layered.json +92 -0
  12. package/presets/modular-monolith.json +85 -0
  13. package/presets/vertical-slice.json +68 -0
  14. package/src/analysis/analyze.mjs +218 -0
  15. package/src/analysis/contract.md +259 -0
  16. package/src/analysis/go.mjs +414 -0
  17. package/src/analysis/manifest-util.mjs +68 -0
  18. package/src/analysis/python.mjs +1266 -0
  19. package/src/analysis/registry.mjs +74 -0
  20. package/src/analysis/rust.mjs +674 -0
  21. package/src/analysis/source-util.mjs +230 -0
  22. package/src/analysis/typescript.mjs +1034 -0
  23. package/src/analysis/vue.mjs +156 -0
  24. package/src/architecture-intent/intent-fingerprint.mjs +29 -0
  25. package/src/architecture-intent/judge.mjs +539 -0
  26. package/src/architecture-intent/model.mjs +703 -0
  27. package/src/architecture-intent/selectors.mjs +170 -0
  28. package/src/canonical.mjs +48 -0
  29. package/src/commands/README.md +266 -0
  30. package/src/commands/adr.mjs +248 -0
  31. package/src/commands/check.mjs +989 -0
  32. package/src/commands/context-command.mjs +212 -0
  33. package/src/commands/context.mjs +790 -0
  34. package/src/commands/custom-rules.mjs +428 -0
  35. package/src/commands/debt.mjs +218 -0
  36. package/src/commands/diff.mjs +523 -0
  37. package/src/commands/discover.mjs +159 -0
  38. package/src/commands/drift.mjs +473 -0
  39. package/src/commands/edge-constraints.mjs +355 -0
  40. package/src/commands/explain.mjs +359 -0
  41. package/src/commands/fitness.mjs +226 -0
  42. package/src/commands/graph.mjs +297 -0
  43. package/src/commands/health.mjs +213 -0
  44. package/src/commands/history.mjs +614 -0
  45. package/src/commands/impact.mjs +226 -0
  46. package/src/commands/plan-context-command.mjs +496 -0
  47. package/src/commands/policy.mjs +138 -0
  48. package/src/commands/provenance-command.mjs +352 -0
  49. package/src/commands/provenance.mjs +159 -0
  50. package/src/commands/reconcile.mjs +219 -0
  51. package/src/commands/report.mjs +553 -0
  52. package/src/commands/snapshot-meta.mjs +107 -0
  53. package/src/commands/waivers.mjs +240 -0
  54. package/src/config.mjs +1308 -0
  55. package/src/containment.mjs +234 -0
  56. package/src/custom-rules/evidence.mjs +340 -0
  57. package/src/custom-rules/host.mjs +1023 -0
  58. package/src/custom-rules/values.mjs +43 -0
  59. package/src/entry-point.mjs +55 -0
  60. package/src/errors.mjs +36 -0
  61. package/src/eslint-config.mjs +542 -0
  62. package/src/go-work.mjs +394 -0
  63. package/src/governance/adr-registry.mjs +539 -0
  64. package/src/governance/clock.mjs +69 -0
  65. package/src/governance/debt-ledger.mjs +274 -0
  66. package/src/governance/discovery-proposal.mjs +423 -0
  67. package/src/governance/fitness-registry.mjs +504 -0
  68. package/src/governance/fitness-rules.mjs +668 -0
  69. package/src/governance/metrics.mjs +392 -0
  70. package/src/governance/preset-fingerprints.json +16 -0
  71. package/src/governance/profile-registry.mjs +366 -0
  72. package/src/governance/provenance-record.mjs +177 -0
  73. package/src/governance/reconcile-candidates.mjs +301 -0
  74. package/src/governance/reconcile-score.mjs +503 -0
  75. package/src/governance/row-schema.mjs +208 -0
  76. package/src/governance/verdict.mjs +127 -0
  77. package/src/governance/waiver.mjs +105 -0
  78. package/src/graph/create-dependencies.mjs +96 -0
  79. package/src/intent/intent-manifest.json +347 -0
  80. package/src/intent/mask-non-code.mjs +640 -0
  81. package/src/lsp/boundary-config.mjs +225 -0
  82. package/src/lsp/diagnose.mjs +202 -0
  83. package/src/lsp/diagnostics.mjs +241 -0
  84. package/src/lsp/protocol.mjs +215 -0
  85. package/src/lsp/server.mjs +922 -0
  86. package/src/lsp/workspace-index.mjs +891 -0
  87. package/src/nx-json.mjs +95 -0
  88. package/src/options.mjs +611 -0
  89. package/src/process.mjs +91 -0
  90. package/src/providers/moon.mjs +733 -0
  91. package/src/providers/native/README.md +204 -0
  92. package/src/providers/native/coverage.mjs +74 -0
  93. package/src/providers/native/differential.fixtures.mjs +1277 -0
  94. package/src/providers/native/discover.mjs +431 -0
  95. package/src/providers/native/graph.mjs +234 -0
  96. package/src/providers/native/index.mjs +152 -0
  97. package/src/providers/native/model.mjs +755 -0
  98. package/src/providers/nx.mjs +178 -0
  99. package/src/report/README.md +89 -0
  100. package/src/report/adr-text.mjs +129 -0
  101. package/src/report/context-text.mjs +109 -0
  102. package/src/report/debt-text.mjs +105 -0
  103. package/src/report/diff-text.mjs +219 -0
  104. package/src/report/discover-text.mjs +186 -0
  105. package/src/report/drift-text.mjs +194 -0
  106. package/src/report/envelope-shape.mjs +161 -0
  107. package/src/report/evidence.mjs +157 -0
  108. package/src/report/explain-text.mjs +159 -0
  109. package/src/report/graph-text.mjs +116 -0
  110. package/src/report/health-text.mjs +123 -0
  111. package/src/report/history-text.mjs +204 -0
  112. package/src/report/impact-text.mjs +128 -0
  113. package/src/report/json.mjs +173 -0
  114. package/src/report/plan-context-text.mjs +159 -0
  115. package/src/report/provenance-text.mjs +78 -0
  116. package/src/report/reconcile-text.mjs +159 -0
  117. package/src/report/report-text.mjs +264 -0
  118. package/src/report/sarif.mjs +953 -0
  119. package/src/report/text.mjs +823 -0
  120. package/src/report/waivers-text.mjs +100 -0
  121. package/src/rules/README.md +123 -0
  122. package/src/rules/index.mjs +962 -0
  123. package/src/rules/match.mjs +1708 -0
  124. package/src/rules/messages.mjs +73 -0
  125. package/src/rules/reachability.mjs +224 -0
  126. package/src/rules/specifiers.mjs +300 -0
  127. package/src/rules/tags.mjs +238 -0
  128. package/src/rules/topology.mjs +333 -0
  129. package/src/tsconfig-paths.mjs +237 -0
  130. package/src/verdict.mjs +145 -0
  131. package/src/workspace.mjs +580 -0
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The three questions every refusal in this layer asks of a value: is it a
3
+ * plain object, is it a string with something in it, and — for the sentence
4
+ * that refuses it — what was it actually.
5
+ *
6
+ * **They live here because the first two copies had already drifted.**
7
+ * `./evidence.mjs` and `./host.mjs` each grew their own `describeValue`, and
8
+ * by the time anyone compared them they rendered the same value two ways:
9
+ * `string "x"` in one, `string ("x")` in the other. Neither was wrong, which
10
+ * is exactly the problem — two refusals about the same shape read as though
11
+ * they came from two different tools, and a reader holding a load failure
12
+ * beside a bundle failure has no way to tell that the difference means
13
+ * nothing. The BigInt guard is the other half of the warning: it was written
14
+ * into both copies by hand, in one sitting, and the next fix would have landed
15
+ * in only one of them.
16
+ *
17
+ * The rendering is fixed here and nowhere else: `an array of N`, `null`,
18
+ * `undefined`, and otherwise the value's type followed by its JSON.
19
+ */
20
+
21
+ /** @type {(value: unknown) => value is Record<string, any>} */
22
+ export const isPlainObject = (value) =>
23
+ value !== null && typeof value === "object" && !Array.isArray(value);
24
+
25
+ /** @type {(value: unknown) => boolean} */
26
+ export const isNonEmptyString = (value) => typeof value === "string" && value.trim() !== "";
27
+
28
+ /**
29
+ * A value's type, for a refusal that shows what was actually there.
30
+ *
31
+ * @param {unknown} value
32
+ * @returns {string}
33
+ */
34
+ export function describeValue(value) {
35
+ if (Array.isArray(value)) return `an array of ${value.length}`;
36
+ if (value === null) return "null";
37
+ if (value === undefined) return "undefined";
38
+ // A BigInt is the one value `JSON.stringify` THROWS on rather than skipping,
39
+ // and this runs only on a refusal path — a formatter that threw would
40
+ // replace the named reason with a TypeError about something else entirely.
41
+ if (typeof value === "bigint") return `bigint ${value}`;
42
+ return `${typeof value} ${JSON.stringify(value) ?? String(value)}`;
43
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * "Was this module run as a program, or imported?" — the one question both
3
+ * executables ask, and the one they were both getting wrong once installed.
4
+ *
5
+ * The idiom this replaces compares `process.argv[1]` to `import.meta.url` as
6
+ * URLs. Those two strings are produced differently: Node resolves symlinks
7
+ * before it records a module's URL, and records `argv[1]` exactly as the caller
8
+ * spelled it. Run the file by a path that goes through a symlink and the
9
+ * comparison is false — the module loads, defines everything, runs nothing, and
10
+ * the process exits 0.
11
+ *
12
+ * That is not a hypothetical spelling. pnpm's layout IS a symlink:
13
+ * `node_modules/@scope/pkg` points into `node_modules/.pnpm/…`, so an installed
14
+ * consumer launching `node node_modules/@scope/pkg/lsp.mjs` hits it every time.
15
+ * Measured on a real `pnpm pack` + install: the language server answered an
16
+ * `initialize` frame with no bytes on stdout, none on stderr, and exit 0. The
17
+ * generated bin shim happens to `exec` the resolved path, which is why the CLI
18
+ * looked fine through `pnpm exec` while the editor path — Claude Code launches
19
+ * `${CLAUDE_PLUGIN_ROOT}/lsp.mjs` by path, with no shim in between — did not.
20
+ *
21
+ * Exit 0 and silence is the exact failure this project is built to refuse (a
22
+ * server that published nothing is read as "checked, clean"), so the comparison
23
+ * is made on real paths, where both spellings of the same file agree.
24
+ */
25
+ import { realpathSync } from "node:fs";
26
+ import { fileURLToPath } from "node:url";
27
+
28
+ /**
29
+ * Resolves a path through symlinks, falling back to the path itself.
30
+ *
31
+ * A path that cannot be resolved — deleted between spawn and this call, or a
32
+ * dangling link — is compared verbatim rather than throwing. Getting the answer
33
+ * wrong there means an executable does not run; throwing means it crashes with
34
+ * a stack trace naming a file the caller did not ask about.
35
+ */
36
+ function realOrGiven(path) {
37
+ try {
38
+ return realpathSync(path);
39
+ } catch {
40
+ return path;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * True when `moduleUrl` names the file Node was told to run.
46
+ *
47
+ * @param {string} moduleUrl `import.meta.url` of the calling module.
48
+ * @param {string | undefined} [argv1] The invoked path; defaults to `process.argv[1]`,
49
+ * which is absent when Node is run with `-e` or as a REPL — imported, not run.
50
+ * @returns {boolean}
51
+ */
52
+ export function isProgramEntry(moduleUrl, argv1 = process.argv[1]) {
53
+ if (!argv1) return false;
54
+ return realOrGiven(argv1) === realOrGiven(fileURLToPath(moduleUrl));
55
+ }
package/src/errors.mjs ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The typed error `cli.mjs`'s exit-code classification keys on.
3
+ *
4
+ * Which caught errors mean "the caller's argument is wrong" (exit 2) and
5
+ * which mean "the run could not look" (exit 3) used to be decided by testing
6
+ * `error.message` against a regex at every catch site in `../cli.mjs`. Prose
7
+ * is not a contract: rewording a message flipped an exit code with nothing
8
+ * failing anywhere — the stderr text still named the mistake, so only the
9
+ * verdict moved, and no gate watched that line. The throw sites now carry
10
+ * the decision as a class and the catch sites test `instanceof`, so a
11
+ * message can be reworded without moving an exit code.
12
+ *
13
+ * `UsageError` covers exactly the refusals those regexes matched: a path
14
+ * outside the workspace or matching no tracked file (`./workspace.mjs`'s
15
+ * `selectFiles`), an unknown project name (`./commands/impact.mjs`'s
16
+ * `computeImpact`, `./commands/context-command.mjs`'s
17
+ * `collectProjectContext`), and a malformed `file:line:column` site string
18
+ * (`./commands/explain.mjs`'s `parseSite`). They are one mistake in four
19
+ * spellings — retyping the argument is the fix, which is what separates
20
+ * them from every other failure a run can hit; a run that never reached a
21
+ * verdict throws a plain `Error` and stays exit 3, so "could not look" is
22
+ * never mistaken for "looked and found the caller at fault"
23
+ * (`../AGENTS.md`, check's four exit codes).
24
+ *
25
+ * One class, nothing else exported: a second class needs a catch site that
26
+ * treats two of these mistakes differently, and none does.
27
+ */
28
+ export class UsageError extends Error {
29
+ /**
30
+ * @param {string} message Printed verbatim on stderr; tests pin it.
31
+ */
32
+ constructor(message) {
33
+ super(message);
34
+ this.name = "UsageError";
35
+ }
36
+ }