@archwall/core 0.1.0 → 0.2.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,82 @@
1
+ # @archwall/core
2
+
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - c512a8a: Fix unusable published packages. `0.1.0` and `0.2.0` should not be installed.
8
+
9
+ Both earlier releases went to npm with manifests that were never rewritten for publishing:
10
+
11
+ - `exports` still pointed at `./src/*.ts`, which no tarball contains — `files` is `["dist"]`. Any
12
+ import of any package threw `ERR_MODULE_NOT_FOUND`.
13
+ - Internal dependencies were published as `"@archwall/core": "workspace:^"`, so installing anything
14
+ with an internal dependency failed with `EUNSUPPORTEDPROTOCOL`.
15
+
16
+ Both fields come from Yarn features — `publishConfig.exports` and the `workspace:` protocol — that
17
+ Yarn substitutes when it packs. Releases ran through `changeset publish`, which shells out to
18
+ `npm publish`; npm treats `publishConfig` as npm config, warns `Unknown publishConfig config
19
+ "exports"`, and drops it. The pack smoke test never caught it because it packs with Yarn, so it was
20
+ checking a tarball no release ever uploaded.
21
+
22
+ Releases now pack with Yarn and upload the finished tarball with `npm publish`, which keeps the
23
+ correct manifest and keeps OIDC trusted publishing (Yarn's own publisher has no token exchange).
24
+ `verify:pack` gained two guards: no `workspace:` range in a publishable manifest, and every
25
+ advertised entrypoint must exist in the tarball — including for ESM-only packages, which the CJS
26
+ load check skips.
27
+
28
+ `@archwall/test-utils` is published for the first time in this release.
29
+
30
+ ## 0.2.0
31
+
32
+ ### Minor Changes
33
+
34
+ - 1321ebb: Contract corrections. These are the changes that are cheap now and a
35
+ major version later, so they are made together while the packages are still 0.x.
36
+
37
+ **Type-only edges are a fact, not a producer's private decision.** `Edge` gains an open
38
+ `attributes` bag, with `typeOnly` as its first well-known member and a `type-only-edges`
39
+ capability gating it. The CLI previously _deleted_ type-only edges during its scan, which made
40
+ it silently disagree with every bundler adapter about what the graph contained, with no way for
41
+ a user to get them back. It now labels them, and whether an erased import counts as a dependency
42
+ is answered by configuration — a new `dropTypeOnlyEdges()` transform — rather than by whichever
43
+ producer happened to build the graph. `kind` keeps answering exactly one question, so a
44
+ re-export that is also type-only is finally expressible.
45
+
46
+ Edges that dedupe onto one another now _merge_ their attributes by intersection rather than
47
+ first-wins. `import type { A } from "./x"` beside `import { b } from "./x"` is one dependency
48
+ and it is not type-only; union semantics there would let a "type-only may cross this boundary"
49
+ rule wave through a real violation.
50
+
51
+ **The CLI no longer passes green over files it never read.** It lexes JavaScript and TypeScript
52
+ only, so `.vue`, `.svelte`, and `.astro` files inside the project boundary produced no module, no
53
+ edge, and no warning — a green CI run over a codebase half of which was invisible. A new
54
+ `unscannable-files` diagnostic names the count, the extensions, and a sample of paths, gated by
55
+ `failOnDiagnostics.unscannableFiles` (default off, so nobody's CI breaks on upgrade).
56
+ **Breaking:** `buildGraphFromFilesystem` now returns `{ graph, diagnostics }` rather than a
57
+ `ProjectGraph`.
58
+
59
+ **Rules receive a `GraphView`, not a `GraphQuery`.** The read surface a rule may rely on is now
60
+ an interface; `GraphQuery` is its sole implementation and has moved to `@archwall/core/internal`.
61
+ Naming the class froze the implementation into the public contract, made test doubles impossible,
62
+ and would have turned any future interned or columnar store into a breaking change.
63
+ **Breaking:** import `GraphQuery` from `@archwall/core/internal` if you were constructing one —
64
+ or, if you were doing so to test a rule, use `@archwall/test-utils` instead.
65
+
66
+ **`@archwall/test-utils` is now published.** There is no rule ecosystem without a supported way
67
+ to test a rule, and `ProjectGraph` is opaque by design, so building a fixture graph by hand is
68
+ not something a third party could otherwise do. `runRule` drives the real engine, so scoping,
69
+ option validation, and message rendering behave in a test exactly as they do in a build.
70
+
71
+ **Reserved seams, declared before they are honoured.** `analyze()` takes an optional third
72
+ parameter (`signal` is honoured between rules; `previous` is accepted and ignored, reserving the
73
+ name for incremental reuse). `ProjectGraph` carries a `revision`. `AnalysisResult` always carries
74
+ a `suppressed` list, `UserConfig` accepts `baseline`, and a `baseline-stale` diagnostic gate
75
+ exists — all inert for now. Each of these lives on a type that is about to be frozen, and adding
76
+ them after the freeze would cost a major apiece.
77
+
78
+ ## 0.1.0
79
+
80
+ ### Minor Changes
81
+
82
+ - 4415522: Initial release.
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_prepare = require("./prepare-C1FfL8Qd.cjs");
2
+ const require_prepare = require("./prepare-CEaZxLPI.cjs");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_prepare.__toESM(node_path, 1);
5
5
  let picomatch = require("picomatch");
@@ -329,8 +329,7 @@ function renderMessage(template, data) {
329
329
  * Fingerprint scheme version. Bump when the algorithm changes so that a stale baseline
330
330
  * ERRORS instead of silently mismatching every entry.
331
331
  *
332
- * `aw3` is the first scheme over canonical module ids
333
- * (docs/adr/0012-canonical-module-identity.md). Before it, a violation about `react` hashed the
332
+ * `aw3` is the first scheme over canonical module ids. Before it, a violation about `react` hashed the
334
333
  * host's own id — a resolved `node_modules` path under the CLI, the bare specifier under esbuild
335
334
  * — so the same finding fingerprinted differently under two bundlers.
336
335
  */
@@ -684,6 +683,14 @@ const DIAGNOSTIC_GATES = {
684
683
  deprecated: {
685
684
  codes: ["rule-deprecated"],
686
685
  default: false
686
+ },
687
+ unscannableFiles: {
688
+ codes: ["unscannable-files"],
689
+ default: false
690
+ },
691
+ baselineStale: {
692
+ codes: ["baseline-stale"],
693
+ default: false
687
694
  }
688
695
  };
689
696
  const GATE_KEYS = Object.keys(DIAGNOSTIC_GATES);
@@ -703,7 +710,9 @@ function resolveFailOnDiagnostics(user) {
703
710
  emptyScope: gate("emptyScope"),
704
711
  invalidOptions: gate("invalidOptions"),
705
712
  invalidConfig: gate("invalidConfig"),
706
- deprecated: gate("deprecated")
713
+ deprecated: gate("deprecated"),
714
+ unscannableFiles: gate("unscannableFiles"),
715
+ baselineStale: gate("baselineStale")
707
716
  };
708
717
  }
709
718
  /** The diagnostic codes that should fail a run, given the resolved gates. */
@@ -907,6 +916,7 @@ function resolveConfig(user, opts) {
907
916
  const repoRoot = node_path.resolve(cwd, user.repoRoot ?? ".");
908
917
  return {
909
918
  repoRoot,
919
+ baseline: user.baseline !== void 0 ? node_path.resolve(repoRoot, user.baseline) : null,
910
920
  sourceRoot: node_path.resolve(repoRoot, user.sourceRoot ?? "."),
911
921
  include: user.include ?? [...DEFAULT_INCLUDE],
912
922
  exclude: [...user.excludeDefaults === false ? [] : DEFAULT_EXCLUDE, ...user.exclude ?? []],
@@ -957,8 +967,9 @@ function defineTransform(transform) {
957
967
  *
958
968
  * Pure — no I/O, no reporter calls; reporters are driven by the run edge (integration-kit).
959
969
  */
960
- async function analyze(graph, config) {
970
+ async function analyze(graph, config, options = {}) {
961
971
  const started = performance.now();
972
+ options.signal?.throwIfAborted();
962
973
  require_prepare.assertIrCompatible(graph.irVersion);
963
974
  const diagnostics = [...config.diagnostics];
964
975
  const effective = new Set(graph.host.capabilities);
@@ -1127,9 +1138,10 @@ async function analyze(graph, config) {
1127
1138
  });
1128
1139
  }
1129
1140
  const active = runs.filter((r) => !r.halted);
1130
- dispatchVisitors(active, diagnostics, scopeKeyOf);
1141
+ dispatchVisitors(active, diagnostics, scopeKeyOf, options.signal);
1131
1142
  for (const run of active) {
1132
1143
  if (run.halted || run.resolved.rule.check === void 0) continue;
1144
+ options.signal?.throwIfAborted();
1133
1145
  const startedRule = performance.now();
1134
1146
  try {
1135
1147
  await run.resolved.rule.check(run.ctx);
@@ -1146,6 +1158,7 @@ async function analyze(graph, config) {
1146
1158
  diagnostics.push(...auditClassification(classified));
1147
1159
  return {
1148
1160
  violations: kept.sort(compareViolations),
1161
+ suppressed: [],
1149
1162
  diagnostics,
1150
1163
  rules: runs.map((r) => r.info),
1151
1164
  repoRoot: config.repoRoot,
@@ -1170,7 +1183,7 @@ async function analyze(graph, config) {
1170
1183
  * slice, so a rule that throws stops and is marked failed while the other thirty-nine keep
1171
1184
  * their results — without paying for exception handling on every edge.
1172
1185
  */
1173
- function dispatchVisitors(runs, diagnostics, scopeKeyOf) {
1186
+ function dispatchVisitors(runs, diagnostics, scopeKeyOf, signal) {
1174
1187
  const edgeBuckets = /* @__PURE__ */ new Map();
1175
1188
  const moduleBuckets = /* @__PURE__ */ new Map();
1176
1189
  for (const run of runs) {
@@ -1223,6 +1236,7 @@ function dispatchVisitors(runs, diagnostics, scopeKeyOf) {
1223
1236
  const items = bucket.slice();
1224
1237
  for (const { run, visit } of bucket.members) {
1225
1238
  if (run.halted) continue;
1239
+ signal?.throwIfAborted();
1226
1240
  const startedRule = performance.now();
1227
1241
  try {
1228
1242
  for (const item of items) visit(item, run.ctx);
@@ -1347,12 +1361,42 @@ function dropSelfEdges() {
1347
1361
  });
1348
1362
  }
1349
1363
  //#endregion
1364
+ //#region src/transforms/drop-type-only-edges.ts
1365
+ /**
1366
+ * Removes edges the host marked `attributes.typeOnly`.
1367
+ *
1368
+ * This is the *policy* half of type-only support, deliberately separated from the *fact* half.
1369
+ * Producers report what the code says; whether an erased import counts as a dependency is a
1370
+ * question about the user's architecture, and different answers are legitimately right:
1371
+ *
1372
+ * - A layering rule usually SHOULD see type-only edges — `domain` importing an
1373
+ * `infrastructure` type still couples the two at design time, which is the thing layering
1374
+ * exists to prevent.
1375
+ * - A cycle rule usually should NOT — a type-only cycle costs nothing at runtime and
1376
+ * `no-cycles` flagging one is the most common false positive in this whole category of tool.
1377
+ *
1378
+ * So it is off by default and opted into per config, rather than being baked into a producer.
1379
+ * Before this existed the CLI simply never emitted type-only edges, which made that choice for
1380
+ * everyone, made the CLI disagree with every bundler adapter, and left no way to get the
1381
+ * edges back.
1382
+ *
1383
+ * Only meaningful when the host declares `type-only-edges`; against a host that erased type
1384
+ * imports before ArchWall saw them, there is nothing here to remove and this is a no-op.
1385
+ */
1386
+ function dropTypeOnlyEdges() {
1387
+ return defineTransform({
1388
+ name: "drop-type-only-edges",
1389
+ transform(graph) {
1390
+ graph.removeEdges((e) => e.attributes?.typeOnly === true);
1391
+ }
1392
+ });
1393
+ }
1394
+ //#endregion
1350
1395
  exports.ArchWallError = require_prepare.ArchWallError;
1351
1396
  exports.BUILTIN_REPORTER_NAMES = BUILTIN_REPORTER_NAMES;
1352
1397
  exports.DIAGNOSTIC_GATES = DIAGNOSTIC_GATES;
1353
1398
  exports.FINGERPRINT_SCHEME = FINGERPRINT_SCHEME;
1354
1399
  exports.FIRST_PARTY_KINDS = require_prepare.FIRST_PARTY_KINDS;
1355
- exports.GraphQuery = require_prepare.GraphQuery;
1356
1400
  exports.IR_VERSION = require_prepare.IR_VERSION;
1357
1401
  exports.IrVersionMismatchError = require_prepare.IrVersionMismatchError;
1358
1402
  exports.MODULE_ID_SCHEMES = require_prepare.MODULE_ID_SCHEMES;
@@ -1374,6 +1418,7 @@ exports.defineRule = defineRule;
1374
1418
  exports.defineTransform = defineTransform;
1375
1419
  exports.displayModuleId = require_prepare.displayModuleId;
1376
1420
  exports.dropSelfEdges = dropSelfEdges;
1421
+ exports.dropTypeOnlyEdges = dropTypeOnlyEdges;
1377
1422
  exports.failingDiagnosticCodes = failingDiagnosticCodes;
1378
1423
  exports.fingerprintOf = fingerprintOf;
1379
1424
  exports.formatViolation = formatViolation;