@issuegraph/editor 0.1.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/LICENSE +201 -0
- package/README.md +238 -0
- package/dist/audit/findings.d.ts +225 -0
- package/dist/audit/findings.d.ts.map +1 -0
- package/dist/audit/findings.js +348 -0
- package/dist/audit/findings.js.map +1 -0
- package/dist/audit/styles.d.ts +37 -0
- package/dist/audit/styles.d.ts.map +1 -0
- package/dist/audit/styles.js +72 -0
- package/dist/audit/styles.js.map +1 -0
- package/dist/audit/surface.d.ts +155 -0
- package/dist/audit/surface.d.ts.map +1 -0
- package/dist/audit/surface.js +202 -0
- package/dist/audit/surface.js.map +1 -0
- package/dist/create/draft.d.ts +119 -0
- package/dist/create/draft.d.ts.map +1 -0
- package/dist/create/draft.js +114 -0
- package/dist/create/draft.js.map +1 -0
- package/dist/create/keys.d.ts +225 -0
- package/dist/create/keys.d.ts.map +1 -0
- package/dist/create/keys.js +228 -0
- package/dist/create/keys.js.map +1 -0
- package/dist/create/placement.d.ts +69 -0
- package/dist/create/placement.d.ts.map +1 -0
- package/dist/create/placement.js +76 -0
- package/dist/create/placement.js.map +1 -0
- package/dist/firstpass/batch.d.ts +190 -0
- package/dist/firstpass/batch.d.ts.map +1 -0
- package/dist/firstpass/batch.js +143 -0
- package/dist/firstpass/batch.js.map +1 -0
- package/dist/firstpass/candidates.d.ts +128 -0
- package/dist/firstpass/candidates.d.ts.map +1 -0
- package/dist/firstpass/candidates.js +39 -0
- package/dist/firstpass/candidates.js.map +1 -0
- package/dist/firstpass/keys.d.ts +90 -0
- package/dist/firstpass/keys.d.ts.map +1 -0
- package/dist/firstpass/keys.js +112 -0
- package/dist/firstpass/keys.js.map +1 -0
- package/dist/firstpass/queue.d.ts +183 -0
- package/dist/firstpass/queue.d.ts.map +1 -0
- package/dist/firstpass/queue.js +159 -0
- package/dist/firstpass/queue.js.map +1 -0
- package/dist/firstpass/render.d.ts +100 -0
- package/dist/firstpass/render.d.ts.map +1 -0
- package/dist/firstpass/render.js +216 -0
- package/dist/firstpass/render.js.map +1 -0
- package/dist/firstpass/styles.d.ts +36 -0
- package/dist/firstpass/styles.d.ts.map +1 -0
- package/dist/firstpass/styles.js +148 -0
- package/dist/firstpass/styles.js.map +1 -0
- package/dist/firstpass/view.d.ts +82 -0
- package/dist/firstpass/view.d.ts.map +1 -0
- package/dist/firstpass/view.js +60 -0
- package/dist/firstpass/view.js.map +1 -0
- package/dist/firstpass/words.d.ts +105 -0
- package/dist/firstpass/words.d.ts.map +1 -0
- package/dist/firstpass/words.js +28 -0
- package/dist/firstpass/words.js.map +1 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +147 -0
- package/dist/index.js.map +1 -0
- package/dist/overlay/grammar.d.ts +259 -0
- package/dist/overlay/grammar.d.ts.map +1 -0
- package/dist/overlay/grammar.js +223 -0
- package/dist/overlay/grammar.js.map +1 -0
- package/dist/overlay/render.d.ts +125 -0
- package/dist/overlay/render.d.ts.map +1 -0
- package/dist/overlay/render.js +439 -0
- package/dist/overlay/render.js.map +1 -0
- package/dist/overlay/styles.d.ts +41 -0
- package/dist/overlay/styles.d.ts.map +1 -0
- package/dist/overlay/styles.js +124 -0
- package/dist/overlay/styles.js.map +1 -0
- package/dist/picker/render.d.ts +80 -0
- package/dist/picker/render.d.ts.map +1 -0
- package/dist/picker/render.js +112 -0
- package/dist/picker/render.js.map +1 -0
- package/dist/picker/styles.d.ts +26 -0
- package/dist/picker/styles.d.ts.map +1 -0
- package/dist/picker/styles.js +116 -0
- package/dist/picker/styles.js.map +1 -0
- package/dist/picker/view.d.ts +135 -0
- package/dist/picker/view.d.ts.map +1 -0
- package/dist/picker/view.js +117 -0
- package/dist/picker/view.js.map +1 -0
- package/dist/picker/words.d.ts +53 -0
- package/dist/picker/words.d.ts.map +1 -0
- package/dist/picker/words.js +28 -0
- package/dist/picker/words.js.map +1 -0
- package/dist/reevaluate/render.d.ts +77 -0
- package/dist/reevaluate/render.d.ts.map +1 -0
- package/dist/reevaluate/render.js +211 -0
- package/dist/reevaluate/render.js.map +1 -0
- package/dist/reevaluate/styles.d.ts +24 -0
- package/dist/reevaluate/styles.d.ts.map +1 -0
- package/dist/reevaluate/styles.js +182 -0
- package/dist/reevaluate/styles.js.map +1 -0
- package/dist/reevaluate/view.d.ts +102 -0
- package/dist/reevaluate/view.d.ts.map +1 -0
- package/dist/reevaluate/view.js +111 -0
- package/dist/reevaluate/view.js.map +1 -0
- package/dist/reevaluate/words.d.ts +54 -0
- package/dist/reevaluate/words.d.ts.map +1 -0
- package/dist/reevaluate/words.js +29 -0
- package/dist/reevaluate/words.js.map +1 -0
- package/dist/scale/commands.d.ts +60 -0
- package/dist/scale/commands.d.ts.map +1 -0
- package/dist/scale/commands.js +49 -0
- package/dist/scale/commands.js.map +1 -0
- package/dist/scale/ladder.d.ts +141 -0
- package/dist/scale/ladder.d.ts.map +1 -0
- package/dist/scale/ladder.js +243 -0
- package/dist/scale/ladder.js.map +1 -0
- package/dist/scale/render.d.ts +85 -0
- package/dist/scale/render.d.ts.map +1 -0
- package/dist/scale/render.js +194 -0
- package/dist/scale/render.js.map +1 -0
- package/dist/scale/styles.d.ts +21 -0
- package/dist/scale/styles.d.ts.map +1 -0
- package/dist/scale/styles.js +105 -0
- package/dist/scale/styles.js.map +1 -0
- package/dist/workspace/inspector.d.ts +117 -0
- package/dist/workspace/inspector.d.ts.map +1 -0
- package/dist/workspace/inspector.js +140 -0
- package/dist/workspace/inspector.js.map +1 -0
- package/dist/workspace/rail.d.ts +122 -0
- package/dist/workspace/rail.d.ts.map +1 -0
- package/dist/workspace/rail.js +216 -0
- package/dist/workspace/rail.js.map +1 -0
- package/dist/workspace/render.d.ts +139 -0
- package/dist/workspace/render.d.ts.map +1 -0
- package/dist/workspace/render.js +366 -0
- package/dist/workspace/render.js.map +1 -0
- package/dist/workspace/selection.d.ts +92 -0
- package/dist/workspace/selection.d.ts.map +1 -0
- package/dist/workspace/selection.js +83 -0
- package/dist/workspace/selection.js.map +1 -0
- package/dist/workspace/styles.d.ts +39 -0
- package/dist/workspace/styles.d.ts.map +1 -0
- package/dist/workspace/styles.js +313 -0
- package/dist/workspace/styles.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The audit: four findings about an encoding, as a pure detector over one
|
|
3
|
+
* document.
|
|
4
|
+
*
|
|
5
|
+
* Design §17d names exactly four, and they are not variations on one theme —
|
|
6
|
+
* each says something different about how much of the encoding can be trusted:
|
|
7
|
+
*
|
|
8
|
+
* cycle nothing in the component can EVER be ready
|
|
9
|
+
* stale blocker bookkeeping; a closed blocker already satisfies readiness
|
|
10
|
+
* dead duplicate ref the issue is out of the order and nothing tracks its work
|
|
11
|
+
* encoding refused its edges are incomplete, and it looks merely unencoded
|
|
12
|
+
*
|
|
13
|
+
* THE FOURTH IS THE ONE THAT IS EASY TO DROP, and it is the reason this module
|
|
14
|
+
* takes more than a document. It is not a relationship finding — it is the
|
|
15
|
+
* ABSENCE of readable relationships, and §17e is explicit that most issues
|
|
16
|
+
* legitimately have none. Without it, an issue whose declaration the reader
|
|
17
|
+
* refused is indistinguishable from an issue that declares nothing, which is an
|
|
18
|
+
* absence rendered as a value in the one place somebody is auditing for
|
|
19
|
+
* encoding accuracy. A parsed document cannot carry it: a refusal is a fact
|
|
20
|
+
* about the raw body, and by the time a document exists the body is gone. So
|
|
21
|
+
* the host states it, from the reader's own answer.
|
|
22
|
+
*
|
|
23
|
+
* NOTHING HERE FIXES ANYTHING. Every finding is a judgment call — a stale
|
|
24
|
+
* blocker may be deliberate history — so the detector reports and the surface
|
|
25
|
+
* navigates. `auto-fix` appears nowhere in this package, deliberately.
|
|
26
|
+
*
|
|
27
|
+
* @see https://github.com/autnmy/issuegraph/blob/main/SPEC.md
|
|
28
|
+
*/
|
|
29
|
+
import type { GraphDocument, IssueRef } from '@issuegraph/store';
|
|
30
|
+
/**
|
|
31
|
+
* The four classes, in the order §17d states them — which is also the order
|
|
32
|
+
* findings are reported in, so a list is stable between runs.
|
|
33
|
+
*/
|
|
34
|
+
export declare const AUDIT_CLASSES: readonly ["cycle", "stale-blocker", "dead-duplicate-ref", "encoding-refused"];
|
|
35
|
+
export type AuditClass = (typeof AUDIT_CLASSES)[number];
|
|
36
|
+
/**
|
|
37
|
+
* How much a finding costs, in the design's own words rather than a number.
|
|
38
|
+
*
|
|
39
|
+
* Named rather than ranked because the four are not points on one scale: a
|
|
40
|
+
* cycle stops the work, a dead duplicate ref hides work that looks handled, and
|
|
41
|
+
* those are different harms. {@link AUDIT_CLASS_SPECS} carries the one ordering
|
|
42
|
+
* anything needs — which finding wins a row that has several.
|
|
43
|
+
*/
|
|
44
|
+
export type AuditSeverity =
|
|
45
|
+
/** The only finding that stops work outright: no member can ever be ready. */
|
|
46
|
+
'blocks-work'
|
|
47
|
+
/** The issue is excluded from the order while nothing tracks its work. */
|
|
48
|
+
| 'dangerous'
|
|
49
|
+
/** Closed blockers satisfy readiness; clearing is bookkeeping. */
|
|
50
|
+
| 'misleading'
|
|
51
|
+
/** Until it parses the issue has no edges at all. */
|
|
52
|
+
| 'blocks-own-edges';
|
|
53
|
+
/** What is true of a class, for every finding in it. */
|
|
54
|
+
export interface AuditClassSpec {
|
|
55
|
+
readonly severity: AuditSeverity;
|
|
56
|
+
/**
|
|
57
|
+
* Which finding speaks for a row carrying several. Higher wins.
|
|
58
|
+
*
|
|
59
|
+
* A ROW ORDERING, NOT A SEVERITY ORDERING. `severity` stays a name because
|
|
60
|
+
* the four harms are not commensurable; this number exists only because one
|
|
61
|
+
* issue can carry two findings and the left-bar has one row to say it on.
|
|
62
|
+
*/
|
|
63
|
+
readonly weight: number;
|
|
64
|
+
/**
|
|
65
|
+
* Whether the finding may be kept as deliberate history.
|
|
66
|
+
*
|
|
67
|
+
* TRUE ON `stale-blocker` AND NOTHING ELSE (§17d). A closed blocker is often
|
|
68
|
+
* a record of what a piece of work waited for, and clearing it destroys that;
|
|
69
|
+
* none of the other three is ever something to keep. Stated as data on the
|
|
70
|
+
* class so the affordance cannot be offered by a render site that decides for
|
|
71
|
+
* itself.
|
|
72
|
+
*/
|
|
73
|
+
readonly keepAsHistory: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The class table. A total record, so adding a class to {@link AUDIT_CLASSES}
|
|
77
|
+
* is a compile error until this states what it costs — which is what stops a
|
|
78
|
+
* fifth finding arriving with its severity chosen wherever it is first drawn.
|
|
79
|
+
*/
|
|
80
|
+
export declare const AUDIT_CLASS_SPECS: Readonly<{
|
|
81
|
+
readonly cycle: Readonly<{
|
|
82
|
+
severity: "blocks-work";
|
|
83
|
+
weight: 3;
|
|
84
|
+
keepAsHistory: false;
|
|
85
|
+
}>;
|
|
86
|
+
readonly 'dead-duplicate-ref': Readonly<{
|
|
87
|
+
severity: "dangerous";
|
|
88
|
+
weight: 2;
|
|
89
|
+
keepAsHistory: false;
|
|
90
|
+
}>;
|
|
91
|
+
readonly 'encoding-refused': Readonly<{
|
|
92
|
+
severity: "blocks-own-edges";
|
|
93
|
+
weight: 1;
|
|
94
|
+
keepAsHistory: false;
|
|
95
|
+
}>;
|
|
96
|
+
readonly 'stale-blocker': Readonly<{
|
|
97
|
+
severity: "misleading";
|
|
98
|
+
weight: 0;
|
|
99
|
+
keepAsHistory: true;
|
|
100
|
+
}>;
|
|
101
|
+
}>;
|
|
102
|
+
/** One finding. Severity travels ON it, never looked up at the render site. */
|
|
103
|
+
/**
|
|
104
|
+
* One finding.
|
|
105
|
+
*
|
|
106
|
+
* `severity` and `keepAsHistory` are FIELDS, because the design asks for them
|
|
107
|
+
* to be — *"severity is data on the finding, not a colour chosen at the render
|
|
108
|
+
* site"* — but they are never a caller's to choose. They are read off the class
|
|
109
|
+
* table where a finding is built, so the table is the single source and a
|
|
110
|
+
* finding whose severity disagrees with its class cannot be constructed.
|
|
111
|
+
*
|
|
112
|
+
* NOTHING ELSE CONSTRUCTS ONE. Every finding in existence comes from this
|
|
113
|
+
* module's own builder, which reads both fields off the table — so there is no
|
|
114
|
+
* disagreement to refuse, from TypeScript or anywhere else. An earlier revision
|
|
115
|
+
* accepted findings from a caller and tried to enforce the relationship in the
|
|
116
|
+
* TYPE instead; that needs either a cast, which this repository forbids, or a
|
|
117
|
+
* four-row builder table, and it still could not reach a value deserialized
|
|
118
|
+
* from a wire. Removing the boundary was cheaper than either.
|
|
119
|
+
*/
|
|
120
|
+
export interface AuditFinding {
|
|
121
|
+
readonly kind: AuditClass;
|
|
122
|
+
readonly severity: AuditSeverity;
|
|
123
|
+
readonly keepAsHistory: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Every issue the finding is about: a SET, sorted, with no ref twice. A cycle
|
|
126
|
+
* names its whole component; the other three name one issue, or the two ends
|
|
127
|
+
* of the edge that produced them.
|
|
128
|
+
*
|
|
129
|
+
* Both properties are established where a finding is built, not merely
|
|
130
|
+
* expected — the row grammar counts an entry per member, so a repeated ref
|
|
131
|
+
* would report one finding as two.
|
|
132
|
+
*/
|
|
133
|
+
readonly members: readonly IssueRef[];
|
|
134
|
+
/** What the reader can be told, in one sentence. Never parsed, never a code. */
|
|
135
|
+
readonly detail: string;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* An issue whose declaration the reader refused.
|
|
139
|
+
*
|
|
140
|
+
* The `diagnostic` is the reader's own words when the host has them — this
|
|
141
|
+
* package never produces one, because it never sees a body.
|
|
142
|
+
*/
|
|
143
|
+
export interface EncodingRefusal {
|
|
144
|
+
readonly ref: IssueRef;
|
|
145
|
+
readonly diagnostic?: string | undefined;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The two answers the audit needs from a reader, and does not compute.
|
|
149
|
+
*
|
|
150
|
+
* THE PORT, AND THE WHOLE REASON THIS MODULE WALKS NOTHING. Both fields come
|
|
151
|
+
* straight off `buildModel(...)` in `@issuegraph/reader`: `cycles` is
|
|
152
|
+
* `Model.cycles`, `duplicateCanonical` is `Model.duplicateCanonical`. A second
|
|
153
|
+
* implementation of either here would be the mirror whose input space drifts,
|
|
154
|
+
* and both would drift in specific, checkable ways rather than hypothetical
|
|
155
|
+
* ones — see each field.
|
|
156
|
+
*
|
|
157
|
+
* IT IS THE READER'S ANSWER SPECIFICALLY, NOT `@issuegraph/derive`'s
|
|
158
|
+
* `wouldCycleOnBlockedBy`, AND THAT DISTINCTION IS THE WHOLE POINT. That
|
|
159
|
+
* function is a PRE-WRITE GUARD, and its documented divergences all lean the
|
|
160
|
+
* fail-safe way for a write that is about to happen: it spans closed nodes, and
|
|
161
|
+
* it does NOT exempt a together unit's internal `blocked-by` edges. Refusing
|
|
162
|
+
* too much is the recoverable direction before a write — a human can decline
|
|
163
|
+
* the refusal — and it is exactly the wrong direction for a statement about
|
|
164
|
+
* what a backlog IS. §6.6 says so in as many words: internal edges *"stay
|
|
165
|
+
* advisory... they would make every group carrying its own ordering read as
|
|
166
|
+
* stuck"*. Interpreting the write guard as an edge-on-cycle test therefore
|
|
167
|
+
* reports a `blocks-work` finding for every ordinary together group that
|
|
168
|
+
* carries its own ordering.
|
|
169
|
+
*
|
|
170
|
+
* REQUIRED, never defaulted. Both classes that rest on this are the ones that
|
|
171
|
+
* matter most — the finding that stops work, and the finding that hides work —
|
|
172
|
+
* so a host with no reader must not quietly receive a thinner audit and read it
|
|
173
|
+
* as a complete one. That is the same absence-rendered-as-a-value the fourth
|
|
174
|
+
* class exists to refuse, arriving through this module's own front door.
|
|
175
|
+
*/
|
|
176
|
+
export interface AuditGraph {
|
|
177
|
+
/**
|
|
178
|
+
* `Model.cycles` — the `blocked-by` cycles among OPEN nodes (§6.6), each as
|
|
179
|
+
* sorted keys, contracted over schedulable units with internal edges dropped.
|
|
180
|
+
*
|
|
181
|
+
* Every rule in that sentence is one this module would otherwise have to
|
|
182
|
+
* restate, and §6.6 calls a reader that skips the contraction non-conforming.
|
|
183
|
+
*/
|
|
184
|
+
readonly cycles: readonly (readonly IssueRef[])[];
|
|
185
|
+
/**
|
|
186
|
+
* `Model.duplicateCanonical` — the TRANSITIVE §4.3.3 canonical for a ref, or
|
|
187
|
+
* `null` when the ref is already canonical.
|
|
188
|
+
*
|
|
189
|
+
* Transitive is the load-bearing word. With `a duplicate-of b`, `b
|
|
190
|
+
* duplicate-of c` and `c` closed, the reader excludes BOTH `a` and `b` from
|
|
191
|
+
* the order — so both are dead references, and a test against each edge's
|
|
192
|
+
* immediate target sees only `b`, because `b` itself is open. The same
|
|
193
|
+
* resolution applies to a `blocked-by` naming a duplicate, which §4.3.3 reads
|
|
194
|
+
* as naming its canonical.
|
|
195
|
+
*/
|
|
196
|
+
readonly duplicateCanonical: (ref: IssueRef) => IssueRef | null;
|
|
197
|
+
}
|
|
198
|
+
export interface AuditInput {
|
|
199
|
+
readonly document: GraphDocument;
|
|
200
|
+
/**
|
|
201
|
+
* See {@link AuditGraph}. Required, and stated in the STORE's own ref
|
|
202
|
+
* spelling — the host builds the model, so the host owns the translation
|
|
203
|
+
* between a store reference (opaque, never parsed) and a model key
|
|
204
|
+
* (normalised against a home repo, §4.2). That is the right side of the seam
|
|
205
|
+
* for it: the host is the only party holding both.
|
|
206
|
+
*/
|
|
207
|
+
readonly graph: AuditGraph;
|
|
208
|
+
/**
|
|
209
|
+
* Refs the reader refused, from the host's own parse. Absent means the host
|
|
210
|
+
* has nothing to report, which is not the same claim as "every issue parsed"
|
|
211
|
+
* — but it is the only claim a caller who omits it has made, so the class
|
|
212
|
+
* simply yields no findings.
|
|
213
|
+
*/
|
|
214
|
+
readonly encodingRefused?: readonly EncodingRefusal[] | undefined;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Every finding in one document, in {@link AUDIT_CLASSES} order.
|
|
218
|
+
*
|
|
219
|
+
* Pure and total: it reads the document it was given, asks the probe, and
|
|
220
|
+
* touches nothing else. A malformed or partial document yields fewer findings
|
|
221
|
+
* rather than an exception — an audit that throws on the data it is auditing is
|
|
222
|
+
* worse than one that reports what it can see.
|
|
223
|
+
*/
|
|
224
|
+
export declare function auditDocument(input: AuditInput): readonly AuditFinding[];
|
|
225
|
+
//# sourceMappingURL=findings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings.d.ts","sourceRoot":"","sources":["../../src/audit/findings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAc,MAAM,mBAAmB,CAAC;AAE7E;;;GAGG;AACH,eAAO,MAAM,aAAa,+EAKf,CAAC;AAEZ,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa;AACvB,8EAA8E;AAC5E,aAAa;AACf,0EAA0E;GACxE,WAAW;AACb,kEAAkE;GAChE,YAAY;AACd,qDAAqD;GACnD,kBAAkB,CAAC;AAEvB,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;EASoC,CAAC;AAGnE,+EAA+E;AAC/E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IACtC,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,QAAQ,EAAE,CAAC,EAAE,CAAC;IAClD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,kBAAkB,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,QAAQ,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,eAAe,EAAE,GAAG,SAAS,CAAC;CACnE;AAsZD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,YAAY,EAAE,CAUxE"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The audit: four findings about an encoding, as a pure detector over one
|
|
3
|
+
* document.
|
|
4
|
+
*
|
|
5
|
+
* Design §17d names exactly four, and they are not variations on one theme —
|
|
6
|
+
* each says something different about how much of the encoding can be trusted:
|
|
7
|
+
*
|
|
8
|
+
* cycle nothing in the component can EVER be ready
|
|
9
|
+
* stale blocker bookkeeping; a closed blocker already satisfies readiness
|
|
10
|
+
* dead duplicate ref the issue is out of the order and nothing tracks its work
|
|
11
|
+
* encoding refused its edges are incomplete, and it looks merely unencoded
|
|
12
|
+
*
|
|
13
|
+
* THE FOURTH IS THE ONE THAT IS EASY TO DROP, and it is the reason this module
|
|
14
|
+
* takes more than a document. It is not a relationship finding — it is the
|
|
15
|
+
* ABSENCE of readable relationships, and §17e is explicit that most issues
|
|
16
|
+
* legitimately have none. Without it, an issue whose declaration the reader
|
|
17
|
+
* refused is indistinguishable from an issue that declares nothing, which is an
|
|
18
|
+
* absence rendered as a value in the one place somebody is auditing for
|
|
19
|
+
* encoding accuracy. A parsed document cannot carry it: a refusal is a fact
|
|
20
|
+
* about the raw body, and by the time a document exists the body is gone. So
|
|
21
|
+
* the host states it, from the reader's own answer.
|
|
22
|
+
*
|
|
23
|
+
* NOTHING HERE FIXES ANYTHING. Every finding is a judgment call — a stale
|
|
24
|
+
* blocker may be deliberate history — so the detector reports and the surface
|
|
25
|
+
* navigates. `auto-fix` appears nowhere in this package, deliberately.
|
|
26
|
+
*
|
|
27
|
+
* @see https://github.com/autnmy/issuegraph/blob/main/SPEC.md
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* The four classes, in the order §17d states them — which is also the order
|
|
31
|
+
* findings are reported in, so a list is stable between runs.
|
|
32
|
+
*/
|
|
33
|
+
export const AUDIT_CLASSES = Object.freeze([
|
|
34
|
+
'cycle',
|
|
35
|
+
'stale-blocker',
|
|
36
|
+
'dead-duplicate-ref',
|
|
37
|
+
'encoding-refused',
|
|
38
|
+
]);
|
|
39
|
+
/**
|
|
40
|
+
* The class table. A total record, so adding a class to {@link AUDIT_CLASSES}
|
|
41
|
+
* is a compile error until this states what it costs — which is what stops a
|
|
42
|
+
* fifth finding arriving with its severity chosen wherever it is first drawn.
|
|
43
|
+
*/
|
|
44
|
+
export const AUDIT_CLASS_SPECS = Object.freeze({
|
|
45
|
+
cycle: Object.freeze({ severity: 'blocks-work', weight: 3, keepAsHistory: false }),
|
|
46
|
+
'dead-duplicate-ref': Object.freeze({ severity: 'dangerous', weight: 2, keepAsHistory: false }),
|
|
47
|
+
'encoding-refused': Object.freeze({
|
|
48
|
+
severity: 'blocks-own-edges',
|
|
49
|
+
weight: 1,
|
|
50
|
+
keepAsHistory: false,
|
|
51
|
+
}),
|
|
52
|
+
'stale-blocker': Object.freeze({ severity: 'misleading', weight: 0, keepAsHistory: true }),
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Every ref the document carries, whatever its state.
|
|
56
|
+
*
|
|
57
|
+
* IT STANDS IN FOR THE READER'S `referenceable` SET, which is what decides
|
|
58
|
+
* whether a reference resolves through a `duplicate-of` at all. That set holds
|
|
59
|
+
* FULL nodes only — a declarer-only node is added to the key map and
|
|
60
|
+
* explicitly "does not become referenceable" — because a weak node may add
|
|
61
|
+
* constraints and may never satisfy one. A host builds both this document and
|
|
62
|
+
* the model from the same issues, so the refs it carries are exactly the full
|
|
63
|
+
* nodes; anything else it knows about arrives as a declarer.
|
|
64
|
+
*
|
|
65
|
+
* WHERE THE PROXY IS WRONG IT IS WRONG SAFELY: it can only ever be NARROWER
|
|
66
|
+
* than `referenceable`, so a target it does not carry goes unresolved and the
|
|
67
|
+
* finding is withheld. That is the same direction the stale-blocker class
|
|
68
|
+
* already takes for an absent declarer, and the opposite of the failure it
|
|
69
|
+
* replaces.
|
|
70
|
+
*/
|
|
71
|
+
function carriedRefs(document) {
|
|
72
|
+
return new Set(document.issues.map((issue) => issue.ref));
|
|
73
|
+
}
|
|
74
|
+
/** The refs the document holds as OPEN issues. Absence here means UNKNOWN. */
|
|
75
|
+
function openRefs(document) {
|
|
76
|
+
const open = new Set();
|
|
77
|
+
for (const issue of document.issues) {
|
|
78
|
+
if (issue.state === 'open')
|
|
79
|
+
open.add(issue.ref);
|
|
80
|
+
}
|
|
81
|
+
return open;
|
|
82
|
+
}
|
|
83
|
+
/** Whether the document holds this ref as a CLOSED issue. */
|
|
84
|
+
function closedRefs(document) {
|
|
85
|
+
const closed = new Set();
|
|
86
|
+
for (const issue of document.issues) {
|
|
87
|
+
if (issue.state === 'closed')
|
|
88
|
+
closed.add(issue.ref);
|
|
89
|
+
}
|
|
90
|
+
return closed;
|
|
91
|
+
}
|
|
92
|
+
/** Lexicographic on the sorted member lists, so a finding list is stable. */
|
|
93
|
+
function compareMembers(a, b) {
|
|
94
|
+
for (let index = 0; index < Math.min(a.length, b.length); index += 1) {
|
|
95
|
+
const left = a[index];
|
|
96
|
+
const right = b[index];
|
|
97
|
+
if (left !== right)
|
|
98
|
+
return left < right ? -1 : 1;
|
|
99
|
+
}
|
|
100
|
+
return a.length - b.length;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A finding with nothing left aliased to anyone else's object.
|
|
104
|
+
*
|
|
105
|
+
* BOTH LEVELS, AND THAT IS THE WHOLE POINT. `Object.freeze` is shallow, and
|
|
106
|
+
* `readonly AuditFinding[]` accepts a MUTABLE array of MUTABLE objects — so
|
|
107
|
+
* freezing the outer value leaves both the finding and its `members` writable
|
|
108
|
+
* through the caller's own reference, and a later push changes what a consumer
|
|
109
|
+
* already rendered. Fixing one level at a time just moves the aliasing down,
|
|
110
|
+
* and `members` is the last level there is: every other field is a primitive.
|
|
111
|
+
*
|
|
112
|
+
* Exported so {@link ../surface.ts auditOverlay} normalizes what it is HANDED
|
|
113
|
+
* by the same rule this module applies to what it BUILDS. Two spellings of
|
|
114
|
+
* "settled" is how the two come to disagree.
|
|
115
|
+
*/
|
|
116
|
+
/**
|
|
117
|
+
* A finding, with every invariant established by construction.
|
|
118
|
+
*
|
|
119
|
+
* THERE IS NO LONGER A BOUNDARY HERE TO DEFEND, AND THAT IS THE CHANGE. This
|
|
120
|
+
* function used to be reachable with a caller's own object, so it grew a
|
|
121
|
+
* validator: a class lookup that a prototype key could satisfy, a members list
|
|
122
|
+
* that might not be an array, a `severity` that might disagree with `kind`, a
|
|
123
|
+
* members list carrying a ref twice, then one carrying none. Six review rounds,
|
|
124
|
+
* each a different way for a value TypeScript never checked to be wrong, and
|
|
125
|
+
* two claims of closure that were premature because the surface is not
|
|
126
|
+
* enumerable.
|
|
127
|
+
*
|
|
128
|
+
* So the boundary was removed rather than hardened a seventh time:
|
|
129
|
+
* {@link ../surface.ts auditOverlay} now derives its findings by running the
|
|
130
|
+
* audit instead of accepting a list, and every finding in existence comes from
|
|
131
|
+
* here. What were runtime defences are invariants again:
|
|
132
|
+
*
|
|
133
|
+
* kind a literal from AUDIT_CLASSES, at four call sites
|
|
134
|
+
* severity read from the table, never passed in
|
|
135
|
+
* keepAsHistory read from the table, never passed in
|
|
136
|
+
* members a sorted set, non-empty
|
|
137
|
+
* detail built here
|
|
138
|
+
*
|
|
139
|
+
* `members` is still normalized rather than assumed, because the detectors
|
|
140
|
+
* build it from document data — a self-blocking edge really does hand this
|
|
141
|
+
* `['a', 'a']`. Non-empty is asserted rather than filtered: a finding naming
|
|
142
|
+
* nobody would add to the header count while giving a reader nothing to open,
|
|
143
|
+
* and no detector can produce one, so silence would hide a defect rather than
|
|
144
|
+
* tolerate an input.
|
|
145
|
+
*/
|
|
146
|
+
function finding(kind, members, detail) {
|
|
147
|
+
const spec = AUDIT_CLASS_SPECS[kind];
|
|
148
|
+
// A SORTED SET. Sorting is what makes two runs over one document produce
|
|
149
|
+
// equal findings; deduplication matters because the row grammar counts one
|
|
150
|
+
// entry per member, and a self-blocking edge hands this the same ref twice.
|
|
151
|
+
const named = Object.freeze([...new Set(members)].sort());
|
|
152
|
+
if (named.length === 0)
|
|
153
|
+
throw new Error(`unreachable: a ${kind} finding names no issues`);
|
|
154
|
+
return Object.freeze({
|
|
155
|
+
kind,
|
|
156
|
+
severity: spec.severity,
|
|
157
|
+
keepAsHistory: spec.keepAsHistory,
|
|
158
|
+
members: named,
|
|
159
|
+
detail,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function edgesOfKind(document, kind) {
|
|
163
|
+
return document.edges.filter((edge) => edge.kind === kind);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The two classes that read this ask about an edge; the other two do not, and
|
|
167
|
+
* deliberately keep none of it. §6.6 already restricts cycles to open nodes,
|
|
168
|
+
* and an encoding refusal is a fact about a DECLARATION — which the model reads
|
|
169
|
+
* from closed nodes too, so a closed issue's unreadable `duplicate-of` still
|
|
170
|
+
* misroutes live edges.
|
|
171
|
+
*/
|
|
172
|
+
function verdictFor(edge, closed, open, carried, refused, graph,
|
|
173
|
+
// WHICH END'S CANONICAL DECIDES THE ANSWER. A stale blocker asks where the
|
|
174
|
+
// TARGET's work went; a dead duplicate ref asks where the DECLARER's did.
|
|
175
|
+
resolveFrom) {
|
|
176
|
+
// THE TWO ENDS RESOLVE UNDER DIFFERENT RULES, because the reader applies its
|
|
177
|
+
// referenceability gate in exactly one of the two places:
|
|
178
|
+
//
|
|
179
|
+
// - A RELATIONSHIP TARGET is canonicalized only when it is REFERENCEABLE.
|
|
180
|
+
// `duplicateCanonical` answers for any key the model holds, the
|
|
181
|
+
// declarer-only tier included, but a weak node is not referenceable — it
|
|
182
|
+
// may add constraints and may never satisfy one. Asking unconditionally
|
|
183
|
+
// turned an unresolvable blocker, which the reader treats as BLOCKING,
|
|
184
|
+
// into a discharged one.
|
|
185
|
+
// - A DECLARER'S OWN CHAIN carries no such gate. `duplicateCanonicalOf`
|
|
186
|
+
// starts from the key map, which holds weak nodes too, and the gate inside
|
|
187
|
+
// it applies to the NEXT hop. So an absent declarer's chain resolves, and
|
|
188
|
+
// gating it discarded the transitive answer the dead-duplicate class is
|
|
189
|
+
// built on — the very case that class deliberately reports on an unknown
|
|
190
|
+
// declarer.
|
|
191
|
+
const effective = resolveFrom === 'to'
|
|
192
|
+
? ((carried.has(edge.to) ? graph.duplicateCanonical(edge.to) : null) ?? edge.to)
|
|
193
|
+
: (graph.duplicateCanonical(edge.from) ?? edge.to);
|
|
194
|
+
return {
|
|
195
|
+
effective,
|
|
196
|
+
effectiveClosed: closed.has(effective),
|
|
197
|
+
// THE TARGET SIDE ONLY, and the scope is load-bearing rather than
|
|
198
|
+
// incidental. What a refusal costs is knowledge of where the edge POINTS:
|
|
199
|
+
// the unreadable declaration is the one that might have redirected the
|
|
200
|
+
// chain. The DECLARER's own refusal says nothing about whether the issue on
|
|
201
|
+
// the other end is closed, so suppressing on it would hide a true finding —
|
|
202
|
+
// measured, by this package's own all-four-classes fixture, which went to
|
|
203
|
+
// three the moment that leg was added.
|
|
204
|
+
knowable: !refused.has(edge.to) && !refused.has(effective),
|
|
205
|
+
declarerLive: !closed.has(edge.from),
|
|
206
|
+
declarerOpen: open.has(edge.from),
|
|
207
|
+
declarerEdgesRead: graph.duplicateCanonical(edge.from) === null,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The reader's stuck groups, one finding each.
|
|
212
|
+
*
|
|
213
|
+
* A PASS-THROUGH, AND DELIBERATELY SO. §6.6's answer already carries every rule
|
|
214
|
+
* that makes it correct — open nodes only, contracted over schedulable units,
|
|
215
|
+
* internal `blocked-by` edges dropped as advisory — and each of those is a rule
|
|
216
|
+
* this module would otherwise restate and eventually disagree with. What is
|
|
217
|
+
* left here is presentation: sorting, and the sentence.
|
|
218
|
+
*
|
|
219
|
+
* An empty group is skipped rather than drawn: a finding naming nobody would
|
|
220
|
+
* add to the header count while giving a reader nothing to navigate to.
|
|
221
|
+
*/
|
|
222
|
+
function cycleFindings(graph) {
|
|
223
|
+
return graph.cycles
|
|
224
|
+
.filter((members) => members.length > 0)
|
|
225
|
+
.map((members) => [...members].sort())
|
|
226
|
+
.sort((a, b) => compareMembers(a, b))
|
|
227
|
+
.map((members) => finding('cycle', members, `${members.join(' · ')} form a blocked-by cycle; no member can ever become ready`));
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* A `blocked-by` whose EFFECTIVE target is closed.
|
|
231
|
+
*
|
|
232
|
+
* EFFECTIVE, because §4.3.3 makes an edge naming a duplicate name its canonical
|
|
233
|
+
* instead — so a blocker that is itself a duplicate of a closed issue is a
|
|
234
|
+
* stale blocker, and testing the immediate target alone misses it.
|
|
235
|
+
*
|
|
236
|
+
* "LONG-closed" IS NOT AVAILABLE HERE and the shortfall is stated rather than
|
|
237
|
+
* approximated: a document carries no timestamp, so how long ago a blocker
|
|
238
|
+
* closed is not a fact this layer holds. Every closed blocker is reported
|
|
239
|
+
* instead — the safe direction for a finding whose whole severity is
|
|
240
|
+
* `misleading`, and the one a host can narrow with a date it does have.
|
|
241
|
+
*
|
|
242
|
+
* A TARGET THE DOCUMENT DOES NOT HOLD IS NOT REPORTED. It is unknown, not
|
|
243
|
+
* closed, and reporting it would state as bookkeeping what is really a paging
|
|
244
|
+
* boundary.
|
|
245
|
+
*/
|
|
246
|
+
function staleBlockerFindings(document, graph, refused) {
|
|
247
|
+
const closed = closedRefs(document);
|
|
248
|
+
const open = openRefs(document);
|
|
249
|
+
const carried = carriedRefs(document);
|
|
250
|
+
const findings = [];
|
|
251
|
+
for (const edge of edgesOfKind(document, 'blocked-by')) {
|
|
252
|
+
const verdict = verdictFor(edge, closed, open, carried, refused, graph, 'to');
|
|
253
|
+
// PROVABLY OPEN, not merely not-closed — see `declarerOpen`.
|
|
254
|
+
if (!verdict.declarerOpen || !verdict.knowable || !verdict.effectiveClosed)
|
|
255
|
+
continue;
|
|
256
|
+
// A blocked-by written on a DUPLICATE is not read by the model at all, so
|
|
257
|
+
// it holds nothing and there is no readiness for a closed target to have
|
|
258
|
+
// satisfied. `dead-duplicate-ref` deliberately does not consult this — see
|
|
259
|
+
// the field.
|
|
260
|
+
if (!verdict.declarerEdgesRead)
|
|
261
|
+
continue;
|
|
262
|
+
const effective = verdict.effective;
|
|
263
|
+
const via = effective === edge.to ? '' : ` (via ${edge.to}, which duplicates it)`;
|
|
264
|
+
findings.push(finding('stale-blocker', [edge.from, edge.to].sort(), `${edge.from} is blocked-by ${effective}${via}, which is closed; readiness is already satisfied`));
|
|
265
|
+
}
|
|
266
|
+
return findings.sort((a, b) => compareMembers(a.members, b.members));
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* A `duplicate-of` whose TRANSITIVE canonical is closed.
|
|
270
|
+
*
|
|
271
|
+
* The dangerous one: §4.3.3 excludes a duplicate from the order entirely, so
|
|
272
|
+
* with the canonical closed the work is tracked by nothing at all while the
|
|
273
|
+
* backlog reads as though it were handled.
|
|
274
|
+
*
|
|
275
|
+
* TRANSITIVE IS WHAT MAKES IT COMPLETE. In a chain — `a duplicate-of b`, `b
|
|
276
|
+
* duplicate-of c`, `c` closed — the reader excludes both `a` and `b`, so both
|
|
277
|
+
* references are dead. Testing each edge's immediate target reports `b` and
|
|
278
|
+
* misses `a`, because `b` is open: the miss is silent, and it is a miss in the
|
|
279
|
+
* one class whose whole point is work that looks handled and is not.
|
|
280
|
+
*
|
|
281
|
+
* The fallback to the immediate target covers a chain the model could not
|
|
282
|
+
* resolve, where `duplicateCanonical` answers `null`. Reporting on what the
|
|
283
|
+
* edge itself names is the fail-safe direction for a `dangerous` class.
|
|
284
|
+
*/
|
|
285
|
+
function deadDuplicateFindings(document, graph, refused) {
|
|
286
|
+
const closed = closedRefs(document);
|
|
287
|
+
const open = openRefs(document);
|
|
288
|
+
const carried = carriedRefs(document);
|
|
289
|
+
const findings = [];
|
|
290
|
+
for (const edge of edgesOfKind(document, 'duplicate-of')) {
|
|
291
|
+
const verdict = verdictFor(edge, closed, open, carried, refused, graph, 'from');
|
|
292
|
+
// NOT provably closed, which deliberately admits an unknown declarer — see
|
|
293
|
+
// `declarerLive`, and the asymmetry with the stale-blocker case above.
|
|
294
|
+
if (!verdict.declarerLive || !verdict.knowable || !verdict.effectiveClosed)
|
|
295
|
+
continue;
|
|
296
|
+
const canonical = verdict.effective;
|
|
297
|
+
const via = canonical === edge.to ? '' : ` (through ${edge.to})`;
|
|
298
|
+
findings.push(finding('dead-duplicate-ref', [edge.from, edge.to].sort(), `${edge.from} is duplicate-of ${canonical}${via}, which is closed; its work is excluded from the order and tracked nowhere`));
|
|
299
|
+
}
|
|
300
|
+
return findings.sort((a, b) => compareMembers(a.members, b.members));
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* The refusals the host reported.
|
|
304
|
+
*
|
|
305
|
+
* NOT FILTERED TO THE DOCUMENT. A refused declaration is a fact the host
|
|
306
|
+
* asserted about an issue it read, and dropping the ones the document does not
|
|
307
|
+
* carry would silently discard findings on exactly the issues a paging boundary
|
|
308
|
+
* has not reached yet. The row overlay simply has no row to mark for those,
|
|
309
|
+
* which is a presentation consequence rather than a reason to lose the finding.
|
|
310
|
+
*
|
|
311
|
+
* Deduplicated by ref, first occurrence winning — the model's own rule for a
|
|
312
|
+
* repeated key, kept here so a host that reports one issue twice does not get
|
|
313
|
+
* two identical findings.
|
|
314
|
+
*/
|
|
315
|
+
function encodingRefusedFindings(refusals) {
|
|
316
|
+
const seen = new Set();
|
|
317
|
+
const findings = [];
|
|
318
|
+
for (const refusal of refusals) {
|
|
319
|
+
if (seen.has(refusal.ref))
|
|
320
|
+
continue;
|
|
321
|
+
seen.add(refusal.ref);
|
|
322
|
+
const because = refusal.diagnostic === undefined || refusal.diagnostic === ''
|
|
323
|
+
? ''
|
|
324
|
+
: `: ${refusal.diagnostic}`;
|
|
325
|
+
findings.push(finding('encoding-refused', [refusal.ref], `${refusal.ref} declares relationships the reader refused${because}; the edges it declares are incomplete and cannot be trusted until it parses`));
|
|
326
|
+
}
|
|
327
|
+
return findings.sort((a, b) => compareMembers(a.members, b.members));
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Every finding in one document, in {@link AUDIT_CLASSES} order.
|
|
331
|
+
*
|
|
332
|
+
* Pure and total: it reads the document it was given, asks the probe, and
|
|
333
|
+
* touches nothing else. A malformed or partial document yields fewer findings
|
|
334
|
+
* rather than an exception — an audit that throws on the data it is auditing is
|
|
335
|
+
* worse than one that reports what it can see.
|
|
336
|
+
*/
|
|
337
|
+
export function auditDocument(input) {
|
|
338
|
+
const { document, graph } = input;
|
|
339
|
+
const refusals = input.encodingRefused ?? [];
|
|
340
|
+
const refused = new Set(refusals.map((refusal) => refusal.ref));
|
|
341
|
+
return Object.freeze([
|
|
342
|
+
...cycleFindings(graph),
|
|
343
|
+
...staleBlockerFindings(document, graph, refused),
|
|
344
|
+
...deadDuplicateFindings(document, graph, refused),
|
|
345
|
+
...encodingRefusedFindings(refusals),
|
|
346
|
+
]);
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=findings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings.js","sourceRoot":"","sources":["../../src/audit/findings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The audit's structural stylesheet.
|
|
3
|
+
*
|
|
4
|
+
* Same contract as the viewer's and the ladder's, and for the same reason: it
|
|
5
|
+
* carries layout, weight and state, never a value. Every colour and length is a
|
|
6
|
+
* `var(--ig-…)` the host's theme already resolves, so a second theme keeps
|
|
7
|
+
* working here. `styles.test.ts` scans these bytes for a literal and for a
|
|
8
|
+
* token the theme does not define.
|
|
9
|
+
*
|
|
10
|
+
* NO TOKENS OF ITS OWN, WHICH IS A REVERSAL WORTH RECORDING. An earlier draft
|
|
11
|
+
* declared `--ig-audit-bar` and shipped an `auditThemeCss()` to default it. The
|
|
12
|
+
* failure mode that killed it is the one this package's other stylesheet test
|
|
13
|
+
* exists to catch: a host that installs the viewer's theme and forgets a second
|
|
14
|
+
* one gets a `var()` resolving to nothing, and the bar silently does not draw —
|
|
15
|
+
* "the failure that looks like a styling bug for weeks". Reading the palette's
|
|
16
|
+
* own gold has no such state, and a host who wants a different attention colour
|
|
17
|
+
* still has one: target the attribute in their own CSS, which needs no API from
|
|
18
|
+
* us. Fewer exports is also the safer direction for a package that can add one
|
|
19
|
+
* later and never take one back.
|
|
20
|
+
*
|
|
21
|
+
* THE BAR IS AN INSET BOX SHADOW rather than a border, because a border changes
|
|
22
|
+
* a row's box and every affected row would shift by its width the moment a
|
|
23
|
+
* finding appeared. §17d asks for a count that never moves; a rail that jumps
|
|
24
|
+
* would be the same broken promise one element over.
|
|
25
|
+
*
|
|
26
|
+
* SCOPING, THE ONE PLACE THIS DEPARTS FROM ITS SIBLINGS. They scope every
|
|
27
|
+
* selector under an `.ig-` class because they own the elements they draw. The
|
|
28
|
+
* bar does not: it lands on a row the VIEWER rendered, and adding a class there
|
|
29
|
+
* means rewriting a `class` attribute this layer has no business touching. So
|
|
30
|
+
* it is scoped by {@link AUDIT_SEVERITY_ATTRIBUTE} instead — this package's own
|
|
31
|
+
* namespaced name, which bounds it to elements a host stamped on purpose.
|
|
32
|
+
*
|
|
33
|
+
* There is no transition, no animation and no `@keyframes` here, and there is a
|
|
34
|
+
* test that says so.
|
|
35
|
+
*/
|
|
36
|
+
export declare const auditStylesheet = "\n.ig-audit {\n align-items: center;\n display: inline-flex;\n font-family: var(--ig-font-ui);\n font-size: var(--ig-font-size-small);\n gap: var(--ig-space-tight);\n}\n\n.ig-audit-toggle {\n align-items: center;\n background: none;\n border: 0;\n color: var(--ig-text-muted);\n cursor: pointer;\n display: inline-flex;\n font: inherit;\n gap: var(--ig-space-tight);\n padding: 0;\n}\n\n.ig-audit-toggle[aria-pressed='true'] {\n color: var(--ig-text);\n}\n\n.ig-audit-count {\n color: var(--ig-text);\n font-family: var(--ig-font-mono);\n font-variant-numeric: tabular-nums;\n}\n\n[data-ig-audit] {\n box-shadow: inset var(--ig-stroke) 0 0 0 var(--ig-edge-serialize-with);\n}\n";
|
|
37
|
+
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/audit/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,eAAO,MAAM,eAAe,6rBAkC3B,CAAC"}
|