@descent-vtt/spec-graph 0.11.1 → 0.12.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/CHANGELOG.md CHANGED
@@ -11,6 +11,38 @@ the family's
11
11
  [versioning policy](https://github.com/DescentVTT/spec-core/blob/main/docs/adr/0009-versions-before-1-0.md)
12
12
  records.
13
13
 
14
+ ## 0.12.0
15
+
16
+ Names every JavaScript object answers to - `constructor`, `__proto__` - are
17
+ read as the unknown names they are in seven places, three of which could
18
+ leave a run that passed refusing or finding something now.
19
+
20
+ ### Changed
21
+
22
+ - A node type named `constructor` is refused as any unknown type is: a
23
+ query or a project rule written with it matched nothing in silence, and is
24
+ now a usage error, exit 2. Upgrading: write `document`, `item` or `*`.
25
+ - A table column headed `Constructor` declares no relation, where it made
26
+ the table a register and each cell an edge of no kind. Upgrading: a table
27
+ that was a register only by that column needs a `Status` column or a
28
+ relation the README lists.
29
+ - A `@spec-edge` whose `kind` names no relation is a `references` edge, and
30
+ `--verbose` lists it as a parse problem; any other word drew an edge of a
31
+ kind no rule knows, and one to its own document ended the check with exit
32
+ 2 (ADR-0014). Upgrading: write one of the eight relations.
33
+
34
+ ### Fixed
35
+
36
+ - A front-matter key, a `state=` word or a directory named `constructor` or
37
+ `__proto__` no longer ends a query or a check on a TypeError: the key is
38
+ found where it was written, the state is ignored, and a document under
39
+ such a directory keeps its own id.
40
+
41
+ ### Documentation
42
+
43
+ - The README's SARIF example pins the actions the workflows run now, and a
44
+ test holds it to them.
45
+
14
46
  ## 0.11.1
15
47
 
16
48
  Documentation and robustness: the README's SARIF example is pinned as CI
package/README.md CHANGED
@@ -552,6 +552,8 @@ Each takes these attributes. Any other is a parse problem, listed by
552
552
 
553
553
  - `reason`, `note`, `owner` and a node's `kind` are for whoever reads the
554
554
  source; spec-graph reads nothing from them.
555
+ - An edge's `kind` is one of the relations a selector steps along. Any other
556
+ word is a parse problem too, and the edge is read as `references`.
555
557
  - `aliases` splits at commas, semicolons and spaces.
556
558
  - A value is quoted with either quote mark, or bare when it holds no space or
557
559
  quote, and an attribute with no value is a flag.
@@ -1061,16 +1063,16 @@ permissions:
1061
1063
  contents: read
1062
1064
  security-events: write
1063
1065
  steps:
1064
- - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
1066
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
1065
1067
  with:
1066
1068
  persist-credentials: false
1067
- - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
1069
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
1068
1070
  with:
1069
1071
  node-version: '24'
1070
1072
  - run: npm ci
1071
1073
  - run: npx spec-graph --format sarif > spec-graph.sarif
1072
1074
  continue-on-error: true
1073
- - uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
1075
+ - uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
1074
1076
  with:
1075
1077
  sarif_file: spec-graph.sarif
1076
1078
  ```
@@ -1287,9 +1289,9 @@ tools share, whose code is copied into `src/vendor/` and checked by hash rather
1287
1289
  than installed. So the whole package is auditable in an afternoon — and nothing
1288
1290
  in the pipeline can take longer than its input.
1289
1291
 
1290
- **Verified, not just covered.** 1,886 tests; 99.4% statement and 99.8% line
1292
+ **Verified, not just covered.** 1,918 tests; 99.4% statement and 99.8% line
1291
1293
  coverage. Coverage says a line ran, so the suite is also held to a mutation
1292
- score - 96.31% over 8,385 mutants, measured by CI's full sweep, and a build
1294
+ score - 96.38% over 8,539 mutants, measured by CI's full sweep, and a build
1293
1295
  fails under 93 - because a vocabulary entry or a boundary
1294
1296
  condition can be weakened by an ordinary-looking refactor without a single test
1295
1297
  going red. [ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md) is straight about what
package/dist/extract.d.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import { type DocumentIdentity } from './identity.js';
18
18
  import { type ScannedDocument } from './markdown.js';
19
- import type { DocumentNode, EdgeKind, EdgeOrigin, ItemNode, MisreadKey, ParseProblem, SourceRef } from './types.js';
19
+ import { type DocumentNode, type EdgeKind, type EdgeOrigin, type ItemNode, type MisreadKey, type ParseProblem, type SourceRef } from './types.js';
20
20
  import { type YamlEntry } from './yaml.js';
21
21
  /**
22
22
  * A relation that has been read but not yet resolved to a target node.
package/dist/extract.js CHANGED
@@ -21,6 +21,7 @@ import { anchorsOf, isMarkdownLine, isOnlyComment, referenceLinks, scanMarkdown,
21
21
  import { findSpecificationRegions, isRegisterTable, opensHeading, regionAt, } from './sections.js';
22
22
  import { resolveItemState } from './state.js';
23
23
  import { refOf } from './source.js';
24
+ import { EDGE_KINDS, } from './types.js';
24
25
  import { readEntries, toRecord, valuesOf } from './yaml.js';
25
26
  /* -------------------------------------------------------------------------- */
26
27
  /* Vocabulary */
@@ -513,6 +514,13 @@ export function extractDocument(input) {
513
514
  at: at(directive.start, directive.end),
514
515
  });
515
516
  }
517
+ const kind = directive.name === 'spec-edge' ? attr(directive, 'kind') : null;
518
+ if (kind !== null && !isEdgeKind(kind.value)) {
519
+ problems.push({
520
+ message: `unknown relation "${kind.value}" on @spec-edge, read as references - expected one of: ${EDGE_KINDS.join(', ')}`,
521
+ at: at(kind.start, kind.end),
522
+ });
523
+ }
516
524
  }
517
525
  const { entries, problems: unread } = readEntries(scanned);
518
526
  for (const problem of unread) {
@@ -981,6 +989,17 @@ function summarise(text) {
981
989
  function isClaimed(claimed, offset) {
982
990
  return claimed.some((range) => offset >= range.start && offset < range.end);
983
991
  }
992
+ /**
993
+ * Whether a word a `@spec-edge` gives as its kind is a relation.
994
+ *
995
+ * Asked of the list, as a selector's relation is: the word was once taken as
996
+ * it stood, so `kind="blocks"` made an edge of a kind no rule knows, a
997
+ * document with such an edge to itself ended the run, and `kind="constructor"`
998
+ * did not, since the table of kinds is an object and answers to that name.
999
+ */
1000
+ function isEdgeKind(word) {
1001
+ return EDGE_KINDS.includes(word);
1002
+ }
984
1003
  function extractReferences(context) {
985
1004
  const { scanned, directives, document, documentAt, items, byKey, status, claimed, file, index } = context;
986
1005
  // This list and the one `withoutOverlaps` builds from it are only ever read
@@ -1035,11 +1054,14 @@ function extractReferences(context) {
1035
1054
  for (const directive of directives) {
1036
1055
  if (directive.name !== 'spec-edge')
1037
1056
  continue;
1038
- const kind = attr(directive, 'kind')?.value;
1057
+ const written = attr(directive, 'kind')?.value;
1039
1058
  const to = attr(directive, 'to');
1040
1059
  const from = attr(directive, 'from');
1041
- if (!kind)
1060
+ if (!written)
1042
1061
  continue;
1062
+ // A word that names no relation is still a link to somewhere, as a cell
1063
+ // under a column that names none is: a citation, and no more than one.
1064
+ const kind = isEdgeKind(written) ? written : 'references';
1043
1065
  const owner = ownerOf(items, directive.start) ?? documentAt(directive.start).id;
1044
1066
  if (to) {
1045
1067
  out.push({
@@ -1048,7 +1070,7 @@ function extractReferences(context) {
1048
1070
  target: to.value,
1049
1071
  origin: 'directive',
1050
1072
  declaredAt: at(to.start, to.end),
1051
- raw: `@spec-edge kind="${kind}" to="${to.value}"`,
1073
+ raw: `@spec-edge kind="${written}" to="${to.value}"`,
1052
1074
  inverted: false,
1053
1075
  opportunistic: false,
1054
1076
  });
@@ -1060,7 +1082,7 @@ function extractReferences(context) {
1060
1082
  target: from.value,
1061
1083
  origin: 'directive',
1062
1084
  declaredAt: at(from.start, from.end),
1063
- raw: `@spec-edge kind="${kind}" from="${from.value}"`,
1085
+ raw: `@spec-edge kind="${written}" from="${from.value}"`,
1064
1086
  inverted: true,
1065
1087
  opportunistic: false,
1066
1088
  });