@clossys/writer 0.3.2
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 +196 -0
- package/LICENSE +21 -0
- package/README.md +593 -0
- package/dist/addressability.d.ts +245 -0
- package/dist/addressability.d.ts.map +1 -0
- package/dist/addressability.js +567 -0
- package/dist/addressability.js.map +1 -0
- package/dist/checker.d.ts +159 -0
- package/dist/checker.d.ts.map +1 -0
- package/dist/checker.js +177 -0
- package/dist/checker.js.map +1 -0
- package/dist/cli.d.ts +149 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1049 -0
- package/dist/cli.js.map +1 -0
- package/dist/copy-gate.d.ts +107 -0
- package/dist/copy-gate.d.ts.map +1 -0
- package/dist/copy-gate.js +132 -0
- package/dist/copy-gate.js.map +1 -0
- package/dist/fingerprint.d.ts +61 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +64 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/locale-coverage.d.ts +227 -0
- package/dist/locale-coverage.d.ts.map +1 -0
- package/dist/locale-coverage.js +414 -0
- package/dist/locale-coverage.js.map +1 -0
- package/dist/passage.d.ts +294 -0
- package/dist/passage.d.ts.map +1 -0
- package/dist/passage.js +463 -0
- package/dist/passage.js.map +1 -0
- package/dist/path-exclusions.d.ts +114 -0
- package/dist/path-exclusions.d.ts.map +1 -0
- package/dist/path-exclusions.js +213 -0
- package/dist/path-exclusions.js.map +1 -0
- package/dist/registry.d.ts +61 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +64 -0
- package/dist/registry.js.map +1 -0
- package/dist/resolve.d.ts +33 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +133 -0
- package/dist/resolve.js.map +1 -0
- package/dist/scan.d.ts +457 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +1698 -0
- package/dist/scan.js.map +1 -0
- package/dist/schema.d.ts +81 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +482 -0
- package/dist/schema.js.map +1 -0
- package/dist/types.d.ts +268 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +40 -0
- package/dist/types.js.map +1 -0
- package/dist/voice/checker.d.ts +186 -0
- package/dist/voice/checker.d.ts.map +1 -0
- package/dist/voice/checker.js +483 -0
- package/dist/voice/checker.js.map +1 -0
- package/dist/voice/derivation-coverage.d.ts +168 -0
- package/dist/voice/derivation-coverage.d.ts.map +1 -0
- package/dist/voice/derivation-coverage.js +181 -0
- package/dist/voice/derivation-coverage.js.map +1 -0
- package/dist/voice/fields.d.ts +107 -0
- package/dist/voice/fields.d.ts.map +1 -0
- package/dist/voice/fields.js +144 -0
- package/dist/voice/fields.js.map +1 -0
- package/dist/voice/index.d.ts +19 -0
- package/dist/voice/index.d.ts.map +1 -0
- package/dist/voice/index.js +24 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/internal/parse-template.d.ts +27 -0
- package/dist/voice/internal/parse-template.d.ts.map +1 -0
- package/dist/voice/internal/parse-template.js +111 -0
- package/dist/voice/internal/parse-template.js.map +1 -0
- package/dist/voice/internal/pattern-safety.d.ts +111 -0
- package/dist/voice/internal/pattern-safety.d.ts.map +1 -0
- package/dist/voice/internal/pattern-safety.js +278 -0
- package/dist/voice/internal/pattern-safety.js.map +1 -0
- package/dist/voice/schema.d.ts +65 -0
- package/dist/voice/schema.d.ts.map +1 -0
- package/dist/voice/schema.js +535 -0
- package/dist/voice/schema.js.map +1 -0
- package/dist/voice/types.d.ts +282 -0
- package/dist/voice/types.d.ts.map +1 -0
- package/dist/voice/types.js +37 -0
- package/dist/voice/types.js.map +1 -0
- package/package.json +72 -0
- package/src/addressability.ts +664 -0
- package/src/checker.ts +293 -0
- package/src/cli.ts +1166 -0
- package/src/copy-gate.ts +197 -0
- package/src/fingerprint.ts +66 -0
- package/src/index.ts +163 -0
- package/src/locale-coverage.ts +512 -0
- package/src/passage.ts +644 -0
- package/src/path-exclusions.ts +265 -0
- package/src/registry.ts +103 -0
- package/src/resolve.ts +172 -0
- package/src/scan.ts +1959 -0
- package/src/schema.ts +528 -0
- package/src/types.ts +301 -0
- package/src/voice/checker.ts +590 -0
- package/src/voice/derivation-coverage.ts +217 -0
- package/src/voice/fields.ts +160 -0
- package/src/voice/index.ts +55 -0
- package/src/voice/internal/parse-template.ts +114 -0
- package/src/voice/internal/pattern-safety.ts +336 -0
- package/src/voice/schema.ts +585 -0
- package/src/voice/types.ts +329 -0
- package/templates/voice-record.template.jsonc +135 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `checkCopyRecord` — runs every entry in a `CopyRecord` through
|
|
3
|
+
* `@clossys/writer/voice`'s own `checkCopy`, against a single
|
|
4
|
+
* `VoiceRecord`, and reports the result. This is the one file in this
|
|
5
|
+
* package that imports `@clossys/writer/voice`: `types.ts` and
|
|
6
|
+
* `schema.ts` are pure, dependency-free data/validation, exactly like
|
|
7
|
+
* `voice`'s own `types.ts`/`schema.ts`; this file is where the two
|
|
8
|
+
* packages' machinery actually meet.
|
|
9
|
+
*
|
|
10
|
+
* FIVE AGENTS FINISHED A REPAIR ROUND ON THIS REPOSITORY AN HOUR BEFORE
|
|
11
|
+
* THIS FILE WAS WRITTEN, AND EVERY ONE OF THEM SHIPPED A FIRST DRAFT THAT
|
|
12
|
+
* REPRODUCED THE EXACT DEFECT IT WAS FIXING. The recurring shape was "a
|
|
13
|
+
* check that passes while asserting nothing" — `checkCopyRecord` IS a
|
|
14
|
+
* check, so this file is written against that failure mode directly, not
|
|
15
|
+
* as an afterthought:
|
|
16
|
+
*
|
|
17
|
+
* 1. NO SILENT NARROWING. If an entry is not run through `checkCopy`
|
|
18
|
+
* for any reason, it is recorded in `report.skipped`, by id — never
|
|
19
|
+
* folded silently into a "0 findings" result that looks identical
|
|
20
|
+
* to "everything ran and was clean". `report.checkedCount` and
|
|
21
|
+
* `report.skippedCount` are always populated (not just inferable
|
|
22
|
+
* from array lengths), so a caller reading only those two numbers,
|
|
23
|
+
* without inspecting either array, still cannot mistake one for the
|
|
24
|
+
* other.
|
|
25
|
+
* 2. FAILS CLOSED. An invalid `CopyRecord`, an invalid `VoiceRecord`,
|
|
26
|
+
* or a `CopyRecord` with zero entries are each treated as a
|
|
27
|
+
* FAILURE — `report.complete` is `false` and `report.findings`
|
|
28
|
+
* names the reason — never as a clean pass with nothing to check.
|
|
29
|
+
* `checkCopyRecord` re-validates BOTH its arguments itself
|
|
30
|
+
* (`validateCopyRecordShape` and, imported from `voice`,
|
|
31
|
+
* `validateVoiceRecordShape`), even though both are typed inputs a
|
|
32
|
+
* TypeScript caller "should" have already validated — because a
|
|
33
|
+
* plain-JS caller, or a value that merely satisfies the type at
|
|
34
|
+
* compile time without actually conforming at runtime, is exactly
|
|
35
|
+
* the case a check that "passes while asserting nothing" would fail
|
|
36
|
+
* to catch. This is a deliberate difference from `voice`'s own
|
|
37
|
+
* `checkCopy`, which does NOT self-validate its `VoiceRecord`
|
|
38
|
+
* argument (see that function's own doc comment) — this package
|
|
39
|
+
* chooses the stricter behavior for its own entry point because it
|
|
40
|
+
* is the thing this whole package's design brief was written to
|
|
41
|
+
* distrust in itself.
|
|
42
|
+
* 3. `report.complete` MEANS "did this run check everything it could
|
|
43
|
+
* have", never "did everything pass" — the same non-negotiable
|
|
44
|
+
* distinction `voice`'s own `VoiceCheckReport.complete` draws. A
|
|
45
|
+
* `CopyRecord` with real voice violations in it can still be
|
|
46
|
+
* `complete: true` (every entry got checked; some of them failed);
|
|
47
|
+
* a `CopyRecord` this function refused to check at all is always
|
|
48
|
+
* `complete: false`, no matter how "clean" an empty `findings`
|
|
49
|
+
* array might otherwise look.
|
|
50
|
+
*
|
|
51
|
+
* Pure, no I/O: `checkCopyRecord` never reads a file — that is
|
|
52
|
+
* `registry.ts`'s job — and never resolves a `factRef` against anything,
|
|
53
|
+
* for the same reason `voice`'s `checkCopy` does not: see
|
|
54
|
+
* `@clossys/writer/voice`'s README, "The `factRef` seam".
|
|
55
|
+
*/
|
|
56
|
+
import { type VoiceCheckReport, type VoiceCheckWaiver, type VoiceFinding, type VoiceRecord } from "./voice/index.js";
|
|
57
|
+
import type { CopyEntryId, CopyRecord } from "./types.js";
|
|
58
|
+
/** One entry `checkCopyRecord` did NOT run through `voice`'s `checkCopy` this run, and why. */
|
|
59
|
+
export interface CopyEntrySkip {
|
|
60
|
+
entryId: CopyEntryId;
|
|
61
|
+
/**
|
|
62
|
+
* Human-readable reason. In practice always because the `CopyRecord` or
|
|
63
|
+
* `VoiceRecord` given to this call failed its own shape validation —
|
|
64
|
+
* see `report.findings` for the specific violation(s). An entry that
|
|
65
|
+
* passes `validateCopyRecordShape` (id well-formed and unique, `text`
|
|
66
|
+
* and `context` non-empty, every declared placeholder present) is
|
|
67
|
+
* always run, never skipped for a reason internal to the entry itself;
|
|
68
|
+
* `voice`'s own `checkCopy` has its own, separately-reported notion of
|
|
69
|
+
* an incomplete run (see `CopyEntryCheckResult.report.skipped`) for a
|
|
70
|
+
* dimension it could not evaluate against a structurally valid entry.
|
|
71
|
+
*/
|
|
72
|
+
reason: string;
|
|
73
|
+
}
|
|
74
|
+
/** One entry `checkCopyRecord` DID run through `voice`'s `checkCopy`, and the resulting report. */
|
|
75
|
+
export interface CopyEntryCheckResult {
|
|
76
|
+
entryId: CopyEntryId;
|
|
77
|
+
/**
|
|
78
|
+
* `voice`'s own `checkCopy` report for this entry's `text`, unmodified.
|
|
79
|
+
* Its own `.complete`/`.skipped` describe whether `checkCopy` itself
|
|
80
|
+
* could evaluate every dimension against this ONE entry — a separate
|
|
81
|
+
* question from whether `checkCopyRecord` ran this entry at all.
|
|
82
|
+
*/
|
|
83
|
+
report: VoiceCheckReport;
|
|
84
|
+
}
|
|
85
|
+
/** One `VoiceFinding` from one entry's `checkCopy` run, flattened into `report.findings` and tagged with the entry it came from. */
|
|
86
|
+
export interface CopyRecordFinding extends VoiceFinding {
|
|
87
|
+
/** The entry this finding is about, or `undefined` for a record-level finding not specific to any one entry (an invalid `CopyRecord`/`VoiceRecord`, or zero entries). */
|
|
88
|
+
entryId?: CopyEntryId;
|
|
89
|
+
}
|
|
90
|
+
/** A `CopyRecordFinding` that was waived, with the waiver that covered it — mirrors `voice`'s `WaivedVoiceFinding`. */
|
|
91
|
+
export interface CopyRecordWaivedFinding extends CopyRecordFinding {
|
|
92
|
+
waiver: VoiceCheckWaiver;
|
|
93
|
+
}
|
|
94
|
+
export interface CopyRecordCheckOptions {
|
|
95
|
+
/**
|
|
96
|
+
* Waivers applied uniformly to EVERY entry's `checkCopy` call — the
|
|
97
|
+
* same `VoiceCheckWaiver[]` shape `voice`'s own `checkCopy` accepts.
|
|
98
|
+
* There is no per-entry waiver targeting in this version: a waiver
|
|
99
|
+
* scoped to one entry's finding still matches the same `rule`+`path`
|
|
100
|
+
* pair in every other entry's report too. A caller that genuinely needs
|
|
101
|
+
* per-entry waiver scoping can call `voice`'s `checkCopy` directly,
|
|
102
|
+
* per entry, with its own waiver list — this package's own `checker.ts`
|
|
103
|
+
* does exactly that under the hood.
|
|
104
|
+
*/
|
|
105
|
+
waivers?: VoiceCheckWaiver[];
|
|
106
|
+
}
|
|
107
|
+
export interface CopyRecordCheckReport {
|
|
108
|
+
recordId: string;
|
|
109
|
+
/** Every entry actually run through `checkCopy`, with its own `VoiceCheckReport`. */
|
|
110
|
+
checked: CopyEntryCheckResult[];
|
|
111
|
+
/** Every entry NOT run, by id, and why. Empty does not by itself mean "nothing was skipped for a bad reason" — see `report.complete`, which is `false` whenever this run could not be trusted enough to check every entry, including the cases where this is legitimately `[]` (zero entries; see `checkedCount`). */
|
|
112
|
+
skipped: CopyEntrySkip[];
|
|
113
|
+
/** `checked.length`, restated as its own field so a caller does not have to inspect the array to get this number — see this file's top-of-file "no silent narrowing" note. */
|
|
114
|
+
checkedCount: number;
|
|
115
|
+
/** `skipped.length`, restated as its own field for the same reason as `checkedCount`. */
|
|
116
|
+
skippedCount: number;
|
|
117
|
+
/**
|
|
118
|
+
* Every non-waived finding this run produced, flattened across every
|
|
119
|
+
* checked entry AND any record-level problem (an invalid `CopyRecord`,
|
|
120
|
+
* an invalid `VoiceRecord`, or zero entries), each entry-sourced
|
|
121
|
+
* finding tagged with the `entryId` it came from. This is the one field
|
|
122
|
+
* a caller checking "is anything wrong" should read — it is populated
|
|
123
|
+
* in every failure case this function knows about, never left empty to
|
|
124
|
+
* imply a clean run when the run in fact could not be trusted (see
|
|
125
|
+
* `.complete`).
|
|
126
|
+
*/
|
|
127
|
+
findings: CopyRecordFinding[];
|
|
128
|
+
/** Every finding covered by a waiver, across every checked entry, each with the waiver that covered it — mirrors `voice`'s `VoiceCheckReport.waived`. */
|
|
129
|
+
waived: CopyRecordWaivedFinding[];
|
|
130
|
+
/**
|
|
131
|
+
* `true` exactly when: the given `CopyRecord` was structurally valid,
|
|
132
|
+
* the given `VoiceRecord` was structurally valid, the record had at
|
|
133
|
+
* least one entry, and every entry was actually run (`skipped` is
|
|
134
|
+
* empty). `false` in every other case. Independent of `findings` —
|
|
135
|
+
* see this file's top-of-file note #3. Read this field, not an empty
|
|
136
|
+
* `findings` array, to decide whether a run can be trusted as "checked
|
|
137
|
+
* everything, found nothing".
|
|
138
|
+
*/
|
|
139
|
+
complete: boolean;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Checks every entry in `record` against `voiceRecord`, via `voice`'s own
|
|
143
|
+
* `checkCopy`. See this file's top-of-file doc comment for the fail-
|
|
144
|
+
* closed behavior on an invalid `record`, an invalid `voiceRecord`, and a
|
|
145
|
+
* `record` with zero entries.
|
|
146
|
+
*
|
|
147
|
+
* Throws a plain `Error` (not a finding) for caller-input problems that
|
|
148
|
+
* are not this function's job to interpret as data: `record` or
|
|
149
|
+
* `voiceRecord` not an object at all. This mirrors `voice`'s own
|
|
150
|
+
* `checkCopy`, which treats a malformed call the same way — a hard
|
|
151
|
+
* upfront rejection, not a soft finding that could be mistaken for
|
|
152
|
+
* something the checker discovered about the record itself. A `record`
|
|
153
|
+
* or `voiceRecord` that IS an object but fails shape validation is,
|
|
154
|
+
* unlike a non-object, a real finding (`report.findings`), not a thrown
|
|
155
|
+
* error — see note #2 above for why this function validates both itself
|
|
156
|
+
* rather than trusting its own type signature.
|
|
157
|
+
*/
|
|
158
|
+
export declare function checkCopyRecord(record: CopyRecord, voiceRecord: VoiceRecord, options?: CopyRecordCheckOptions): CopyRecordCheckReport;
|
|
159
|
+
//# sourceMappingURL=checker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE1D,+FAA+F;AAC/F,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;OAUG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,mGAAmG;AACnG,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,WAAW,CAAC;IACrB;;;;;OAKG;IACH,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,oIAAoI;AACpI,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,yKAAyK;IACzK,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED,uHAAuH;AACvH,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,sTAAsT;IACtT,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,8KAA8K;IAC9K,YAAY,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;;OASG;IACH,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,yJAAyJ;IACzJ,MAAM,EAAE,uBAAuB,EAAE,CAAC;IAClC;;;;;;;;OAQG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAyCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,sBAA2B,GACnC,qBAAqB,CAuEvB"}
|
package/dist/checker.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `checkCopyRecord` — runs every entry in a `CopyRecord` through
|
|
3
|
+
* `@clossys/writer/voice`'s own `checkCopy`, against a single
|
|
4
|
+
* `VoiceRecord`, and reports the result. This is the one file in this
|
|
5
|
+
* package that imports `@clossys/writer/voice`: `types.ts` and
|
|
6
|
+
* `schema.ts` are pure, dependency-free data/validation, exactly like
|
|
7
|
+
* `voice`'s own `types.ts`/`schema.ts`; this file is where the two
|
|
8
|
+
* packages' machinery actually meet.
|
|
9
|
+
*
|
|
10
|
+
* FIVE AGENTS FINISHED A REPAIR ROUND ON THIS REPOSITORY AN HOUR BEFORE
|
|
11
|
+
* THIS FILE WAS WRITTEN, AND EVERY ONE OF THEM SHIPPED A FIRST DRAFT THAT
|
|
12
|
+
* REPRODUCED THE EXACT DEFECT IT WAS FIXING. The recurring shape was "a
|
|
13
|
+
* check that passes while asserting nothing" — `checkCopyRecord` IS a
|
|
14
|
+
* check, so this file is written against that failure mode directly, not
|
|
15
|
+
* as an afterthought:
|
|
16
|
+
*
|
|
17
|
+
* 1. NO SILENT NARROWING. If an entry is not run through `checkCopy`
|
|
18
|
+
* for any reason, it is recorded in `report.skipped`, by id — never
|
|
19
|
+
* folded silently into a "0 findings" result that looks identical
|
|
20
|
+
* to "everything ran and was clean". `report.checkedCount` and
|
|
21
|
+
* `report.skippedCount` are always populated (not just inferable
|
|
22
|
+
* from array lengths), so a caller reading only those two numbers,
|
|
23
|
+
* without inspecting either array, still cannot mistake one for the
|
|
24
|
+
* other.
|
|
25
|
+
* 2. FAILS CLOSED. An invalid `CopyRecord`, an invalid `VoiceRecord`,
|
|
26
|
+
* or a `CopyRecord` with zero entries are each treated as a
|
|
27
|
+
* FAILURE — `report.complete` is `false` and `report.findings`
|
|
28
|
+
* names the reason — never as a clean pass with nothing to check.
|
|
29
|
+
* `checkCopyRecord` re-validates BOTH its arguments itself
|
|
30
|
+
* (`validateCopyRecordShape` and, imported from `voice`,
|
|
31
|
+
* `validateVoiceRecordShape`), even though both are typed inputs a
|
|
32
|
+
* TypeScript caller "should" have already validated — because a
|
|
33
|
+
* plain-JS caller, or a value that merely satisfies the type at
|
|
34
|
+
* compile time without actually conforming at runtime, is exactly
|
|
35
|
+
* the case a check that "passes while asserting nothing" would fail
|
|
36
|
+
* to catch. This is a deliberate difference from `voice`'s own
|
|
37
|
+
* `checkCopy`, which does NOT self-validate its `VoiceRecord`
|
|
38
|
+
* argument (see that function's own doc comment) — this package
|
|
39
|
+
* chooses the stricter behavior for its own entry point because it
|
|
40
|
+
* is the thing this whole package's design brief was written to
|
|
41
|
+
* distrust in itself.
|
|
42
|
+
* 3. `report.complete` MEANS "did this run check everything it could
|
|
43
|
+
* have", never "did everything pass" — the same non-negotiable
|
|
44
|
+
* distinction `voice`'s own `VoiceCheckReport.complete` draws. A
|
|
45
|
+
* `CopyRecord` with real voice violations in it can still be
|
|
46
|
+
* `complete: true` (every entry got checked; some of them failed);
|
|
47
|
+
* a `CopyRecord` this function refused to check at all is always
|
|
48
|
+
* `complete: false`, no matter how "clean" an empty `findings`
|
|
49
|
+
* array might otherwise look.
|
|
50
|
+
*
|
|
51
|
+
* Pure, no I/O: `checkCopyRecord` never reads a file — that is
|
|
52
|
+
* `registry.ts`'s job — and never resolves a `factRef` against anything,
|
|
53
|
+
* for the same reason `voice`'s `checkCopy` does not: see
|
|
54
|
+
* `@clossys/writer/voice`'s README, "The `factRef` seam".
|
|
55
|
+
*/
|
|
56
|
+
import { checkCopy, validateVoiceRecordShape, } from "./voice/index.js";
|
|
57
|
+
import { validateCopyRecordShape } from "./schema.js";
|
|
58
|
+
function recordLevelFailure(recordId, findings, skipped) {
|
|
59
|
+
return {
|
|
60
|
+
recordId,
|
|
61
|
+
checked: [],
|
|
62
|
+
skipped,
|
|
63
|
+
checkedCount: 0,
|
|
64
|
+
skippedCount: skipped.length,
|
|
65
|
+
findings,
|
|
66
|
+
waived: [],
|
|
67
|
+
complete: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Best-effort per-entry skip attribution for a `CopyRecord` whose own
|
|
72
|
+
* shape is invalid. `record.entries` is typed as `CopyEntry[]`, but the
|
|
73
|
+
* whole point of the branch that calls this is that the type cannot be
|
|
74
|
+
* trusted at runtime — so this reads defensively, using whatever id it
|
|
75
|
+
* can find and falling back to a positional label (`"$3"`, meaning index
|
|
76
|
+
* 3) for an entry too malformed to have a usable id at all. Never throws.
|
|
77
|
+
*/
|
|
78
|
+
function bestEffortSkipList(entriesValue, reason) {
|
|
79
|
+
if (!Array.isArray(entriesValue))
|
|
80
|
+
return [];
|
|
81
|
+
return entriesValue.map((entry, i) => {
|
|
82
|
+
const hasUsableId = typeof entry === "object" &&
|
|
83
|
+
entry !== null &&
|
|
84
|
+
!Array.isArray(entry) &&
|
|
85
|
+
typeof entry.id === "string" &&
|
|
86
|
+
entry.id.length > 0;
|
|
87
|
+
const entryId = hasUsableId ? entry.id : `$${i}`;
|
|
88
|
+
return { entryId, reason };
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Checks every entry in `record` against `voiceRecord`, via `voice`'s own
|
|
93
|
+
* `checkCopy`. See this file's top-of-file doc comment for the fail-
|
|
94
|
+
* closed behavior on an invalid `record`, an invalid `voiceRecord`, and a
|
|
95
|
+
* `record` with zero entries.
|
|
96
|
+
*
|
|
97
|
+
* Throws a plain `Error` (not a finding) for caller-input problems that
|
|
98
|
+
* are not this function's job to interpret as data: `record` or
|
|
99
|
+
* `voiceRecord` not an object at all. This mirrors `voice`'s own
|
|
100
|
+
* `checkCopy`, which treats a malformed call the same way — a hard
|
|
101
|
+
* upfront rejection, not a soft finding that could be mistaken for
|
|
102
|
+
* something the checker discovered about the record itself. A `record`
|
|
103
|
+
* or `voiceRecord` that IS an object but fails shape validation is,
|
|
104
|
+
* unlike a non-object, a real finding (`report.findings`), not a thrown
|
|
105
|
+
* error — see note #2 above for why this function validates both itself
|
|
106
|
+
* rather than trusting its own type signature.
|
|
107
|
+
*/
|
|
108
|
+
export function checkCopyRecord(record, voiceRecord, options = {}) {
|
|
109
|
+
if (record === null || typeof record !== "object") {
|
|
110
|
+
throw new TypeError("checkCopyRecord: record must be a CopyRecord object");
|
|
111
|
+
}
|
|
112
|
+
if (voiceRecord === null || typeof voiceRecord !== "object") {
|
|
113
|
+
throw new TypeError("checkCopyRecord: voiceRecord must be a VoiceRecord object");
|
|
114
|
+
}
|
|
115
|
+
const recordIdForReport = isNonEmptyStringField(record, "id") ? record.id : "(unknown)";
|
|
116
|
+
// --- fail closed: the CopyRecord itself must be well-formed --------------
|
|
117
|
+
const recordShapeFindings = validateCopyRecordShape(record);
|
|
118
|
+
if (recordShapeFindings.length > 0) {
|
|
119
|
+
const findings = recordShapeFindings.map((f) => ({ ...f }));
|
|
120
|
+
const entriesValue = record.entries;
|
|
121
|
+
return recordLevelFailure(recordIdForReport, findings, bestEffortSkipList(entriesValue, "record-shape-invalid"));
|
|
122
|
+
}
|
|
123
|
+
// --- fail closed: the VoiceRecord itself must be well-formed --------------
|
|
124
|
+
// Unlike voice's own checkCopy, this function does not trust its typed
|
|
125
|
+
// input — see this file's top-of-file note #2 for why.
|
|
126
|
+
const voiceShapeFindings = validateVoiceRecordShape(voiceRecord);
|
|
127
|
+
if (voiceShapeFindings.length > 0) {
|
|
128
|
+
const findings = voiceShapeFindings.map((f) => ({ ...f }));
|
|
129
|
+
// record is now known well-formed (previous branch would have returned
|
|
130
|
+
// otherwise), so every entry has a real, unique id to attribute a skip to.
|
|
131
|
+
const skipped = record.entries.map((entry) => ({
|
|
132
|
+
entryId: entry.id,
|
|
133
|
+
reason: "voice-record-invalid",
|
|
134
|
+
}));
|
|
135
|
+
return recordLevelFailure(recordIdForReport, findings, skipped);
|
|
136
|
+
}
|
|
137
|
+
// --- fail closed: zero entries is nothing to check, not a clean pass -----
|
|
138
|
+
if (record.entries.length === 0) {
|
|
139
|
+
const findings = [
|
|
140
|
+
{
|
|
141
|
+
rule: "record:no-entries",
|
|
142
|
+
severity: "error",
|
|
143
|
+
message: `CopyRecord "${recordIdForReport}" has zero entries — there is nothing for checkCopyRecord to check.`,
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
return recordLevelFailure(recordIdForReport, findings, []);
|
|
147
|
+
}
|
|
148
|
+
// --- the real work: run every entry through voice's checkCopy ------------
|
|
149
|
+
const checked = [];
|
|
150
|
+
const findings = [];
|
|
151
|
+
const waived = [];
|
|
152
|
+
for (const entry of record.entries) {
|
|
153
|
+
const entryReport = checkCopy(voiceRecord, entry.text, { waivers: options.waivers });
|
|
154
|
+
checked.push({ entryId: entry.id, report: entryReport });
|
|
155
|
+
for (const finding of entryReport.findings) {
|
|
156
|
+
findings.push({ ...finding, entryId: entry.id });
|
|
157
|
+
}
|
|
158
|
+
for (const w of entryReport.waived) {
|
|
159
|
+
waived.push({ ...w, entryId: entry.id });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
recordId: recordIdForReport,
|
|
164
|
+
checked,
|
|
165
|
+
skipped: [],
|
|
166
|
+
checkedCount: checked.length,
|
|
167
|
+
skippedCount: 0,
|
|
168
|
+
findings,
|
|
169
|
+
waived,
|
|
170
|
+
complete: true,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function isNonEmptyStringField(value, key) {
|
|
174
|
+
const v = value[key];
|
|
175
|
+
return typeof v === "string" && v.length > 0;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `writer-check` — the CLI for `checkCopyTraceability` — AND, in this same
|
|
4
|
+
* file and under this SAME `writer-check` bin, a subcommand
|
|
5
|
+
* (`writer-check addressability`, dispatched on `argv[0] === "addressability"`
|
|
6
|
+
* before any of the existing argument parsing below runs — see the bottom
|
|
7
|
+
* of this file), `checkAddressability`: is user-facing prose resolved from
|
|
8
|
+
* the copy registry by id, rather than typed inline? A stricter, DIFFERENT
|
|
9
|
+
* question from traceability — see `addressability.ts`'s top doc comment
|
|
10
|
+
* for the full split. Deliberately NOT merged into the default command's
|
|
11
|
+
* own exit code: traceability's own fixtures/tests are built entirely
|
|
12
|
+
* around bare literal assignments (a registered string, an unregistered
|
|
13
|
+
* one, a `copy-gate:ignore` marker, a `copy:<id>` citation) — exactly the
|
|
14
|
+
* shapes addressability treats as either a violation (an attribute/
|
|
15
|
+
* text-node literal) or an unclassifiable position (a bare assignment,
|
|
16
|
+
* position 3 — see `addressability.ts`), by design and on purpose.
|
|
17
|
+
* Combining the two into one number would make the default command's own
|
|
18
|
+
* "clean pass" fixtures structurally unwritable (a literal a
|
|
19
|
+
* registered-text match needs to exist for traceability to prove anything
|
|
20
|
+
* is exactly a literal addressability cannot confirm is safe) rather than
|
|
21
|
+
* genuinely complementary. A SEPARATE `bin` entry pointing at this same
|
|
22
|
+
* compiled file was rejected for the identical reason `@example/ui`
|
|
23
|
+
* never does that: this repository's own root `package.json` invokes every
|
|
24
|
+
* gate by its COMPILED PATH (`node packages/ui/dist/tokens/contrast-cli.js`,
|
|
25
|
+
* `node packages/controller/dist/cli.js`, ...), never by installed `bin`
|
|
26
|
+
* name — a second `bin` entry pointing at the same file is invisible to
|
|
27
|
+
* that invocation style, and worse, silently falls through to the OTHER
|
|
28
|
+
* command rather than erroring. An explicit `argv[0]` subcommand is
|
|
29
|
+
* reachable however the file is invoked, by path or by name. Both
|
|
30
|
+
* commands are still presentation-only wrappers over this package's pure
|
|
31
|
+
* gates, both still print every skip/exclusion count unconditionally, and
|
|
32
|
+
* both still use the identical three-state exit-code contract described
|
|
33
|
+
* below — same shape, same file, same bin, two subcommands, two numbers,
|
|
34
|
+
* matching `@example/strategy`'s `strategy-facts-check` shape
|
|
35
|
+
* closely, deliberately: same argument order (the thing being checked
|
|
36
|
+
* FOR, then the thing being checked, where applicable), same `--help`,
|
|
37
|
+
* same `CliInputError` split between "bad arguments" and "ran, found
|
|
38
|
+
* something wrong".
|
|
39
|
+
*
|
|
40
|
+
* A second subcommand, `voice-derivation-coverage`, wires
|
|
41
|
+
* `checkVoiceDerivationCoverage` (`./voice/derivation-coverage.ts`) in with
|
|
42
|
+
* the identical shape: parse argv, load two JSON files, run the pure check,
|
|
43
|
+
* print a report, pick an exit code from the same 0/1/2 contract — see
|
|
44
|
+
* `runVoiceDerivationCoverage`'s own doc comment below. `main()` dispatches
|
|
45
|
+
* to it only when `argv[0]` is exactly `"voice-derivation-coverage"`; any
|
|
46
|
+
* other first argument (including every existing caller's real
|
|
47
|
+
* `record-file` path) falls through to the original, unchanged behavior
|
|
48
|
+
* above — this addition is purely additive to the argv contract.
|
|
49
|
+
*
|
|
50
|
+
* A third subcommand, `locale-coverage`, wires `checkLocaleCoverage`
|
|
51
|
+
* (`./locale-coverage.ts`) in with the identical shape again: parse argv,
|
|
52
|
+
* load a JSON registries file, run the pure check, print a report, pick an
|
|
53
|
+
* exit code — see `runLocaleCoverage`'s own doc comment below.
|
|
54
|
+
* `checkLocaleCoverage` already defines its own caller contract for turning
|
|
55
|
+
* a `LocaleCoverageReport` into this package's usual three-state exit code
|
|
56
|
+
* (see `LocaleCoverageReport.complete`'s own doc comment in
|
|
57
|
+
* `locale-coverage.ts`); `runLocaleCoverage` applies that mapping exactly,
|
|
58
|
+
* rather than inventing a second one. `main()` dispatches to it only when
|
|
59
|
+
* `argv[0]` is exactly `"locale-coverage"` — checked alongside
|
|
60
|
+
* `"voice-derivation-coverage"`, before either falls through to the
|
|
61
|
+
* original `record-file` handling — so this, too, is purely additive to the
|
|
62
|
+
* argv contract.
|
|
63
|
+
*
|
|
64
|
+
* A FOURTH subcommand, `passages` (`writer-check passages <registry-file>`),
|
|
65
|
+
* wires `checkPassageComposition` (`./passage.ts`) in — the passage-layer
|
|
66
|
+
* COMPOSITION gate: does every passage in a `PassageRecord` reference only
|
|
67
|
+
* registered entries and glossary terms, never an inlined literal string
|
|
68
|
+
* or another passage's own internals? Dispatched EXACTLY the way
|
|
69
|
+
* `addressability` is (see below): a fully separate top-level branch at
|
|
70
|
+
* the very bottom of this file, its own exported `mainPassagesCheck`, its
|
|
71
|
+
* own `runPassagesCheck` wrapper — never folded through `main()` at all,
|
|
72
|
+
* for the identical reason addressability is not: this gate's own ternary
|
|
73
|
+
* precedence ("a real violation wins over an incomplete picture" — see
|
|
74
|
+
* `passage.ts`'s own top doc comment, "THE TERNARY") is independent of
|
|
75
|
+
* every other subcommand's exit-code contract, and keeping it a fully
|
|
76
|
+
* separate dispatch path keeps that independence visible in this file's
|
|
77
|
+
* own structure, not just in a comment.
|
|
78
|
+
*
|
|
79
|
+
* Exit codes — a contract a consumer's CI depends on, matching this
|
|
80
|
+
* repository's `foundry-check` convention (`@example/gates`):
|
|
81
|
+
*
|
|
82
|
+
* 0 — ran cleanly: the copy record loaded, at least one file was
|
|
83
|
+
* actually scanned (successfully tokenized — see `scan.ts`'s
|
|
84
|
+
* `ScanResult.filesScanned`), zero findings.
|
|
85
|
+
* 1 — ran cleanly, at least one finding (an unregistered copy candidate,
|
|
86
|
+
* or a citation to a copy id that does not exist).
|
|
87
|
+
* 2 — could not run: bad input, the copy record missing/unreadable/
|
|
88
|
+
* invalid, the scan directory does not exist, the walk matched zero
|
|
89
|
+
* files, ANY matched file failed to tokenize (see `scan.parseFailures`
|
|
90
|
+
* — a file that could not be parsed was never examined for copy, and
|
|
91
|
+
* one such file among a hundred clean ones is still incomplete
|
|
92
|
+
* coverage, not a clean scan), an unreadable directory
|
|
93
|
+
* during the walk, an unexpected exception, a malformed
|
|
94
|
+
* `ScanOptions.pathExclusions` entry (see below), OR — as of the JSX
|
|
95
|
+
* text-node scanning added for issue #37 — at least one
|
|
96
|
+
* `ScanResult.unchecked` entry: a JSX construct the scanner
|
|
97
|
+
* recognized but could not reliably classify. That last case lands
|
|
98
|
+
* here, not as a `1`-severity finding, deliberately: `unchecked`
|
|
99
|
+
* means a REGION of an otherwise-matched, otherwise-parseable file
|
|
100
|
+
* was never actually examined for copy — the exact same shape of
|
|
101
|
+
* problem `parseFailures`/`filesScanned === 0` already are, just at
|
|
102
|
+
* finer grain (part of one file, rather than a whole file), so it
|
|
103
|
+
* gets the same answer they do. Every real finding is still printed
|
|
104
|
+
* first (see `printGateReport`) — `unchecked` does not suppress
|
|
105
|
+
* what WAS learned, it just refuses to let that partial picture
|
|
106
|
+
* read as "clean" the way a bare exit `0` would. Kept strictly
|
|
107
|
+
* distinct from 1 — a gate that reports "clean" after failing to
|
|
108
|
+
* run, OR after only partially running, is worse than no gate at
|
|
109
|
+
* all. This is the explicit third state this gate is built around:
|
|
110
|
+
* "could not check" must never be reported as a pass.
|
|
111
|
+
*
|
|
112
|
+
* A malformed `pathExclusions` entry (see `scan.ts`/`path-exclusions.ts`)
|
|
113
|
+
* gets the same `2` treatment as `unchecked`, for the identical reason: an
|
|
114
|
+
* exclusion list this run cannot trust means this run cannot say which
|
|
115
|
+
* files were correctly in or out of scope, which is "could not run", not "a
|
|
116
|
+
* clean scan that happened to find nothing". This CLI does not currently
|
|
117
|
+
* accept `pathExclusions` from argv — see `main()`'s own comment — so this
|
|
118
|
+
* only fires for a caller driving `scanCopySourceTree` directly with a
|
|
119
|
+
* malformed list and inspecting `ScanResult` themselves; it is handled here
|
|
120
|
+
* anyway so the accounting/exit-code contract stays correct for a future
|
|
121
|
+
* CLI flag without a second pass over this logic.
|
|
122
|
+
*/
|
|
123
|
+
/** Exported for `cli.test.ts` — anything wrong with the arguments themselves always maps to exit code 2, never 1. */
|
|
124
|
+
export declare class CliInputError extends Error {
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Exported (unlike a typical CLI `main`) so `cli.test.ts` can exercise the
|
|
128
|
+
* whole argv-to-exit-code contract directly, against real `mkdtemp` temp
|
|
129
|
+
* directories, without spawning a subprocess for every case. Takes `argv`
|
|
130
|
+
* as a parameter rather than reading `process.argv` itself for exactly
|
|
131
|
+
* that reason — `run()` below is the only caller that reads the real
|
|
132
|
+
* `process.argv`.
|
|
133
|
+
*/
|
|
134
|
+
export declare function main(argv: string[]): number;
|
|
135
|
+
/**
|
|
136
|
+
* Exported (unlike a typical CLI `main`) for the identical reason `main`
|
|
137
|
+
* and `mainAddressabilityCheck` above are — `cli.test.ts` exercises the
|
|
138
|
+
* whole argv-to-exit-code contract directly against real fixture files,
|
|
139
|
+
* without spawning a subprocess.
|
|
140
|
+
*/
|
|
141
|
+
export declare function mainPassagesCheck(argv: string[]): number;
|
|
142
|
+
/**
|
|
143
|
+
* Exported (unlike a typical CLI `main`) for the identical reason `main`
|
|
144
|
+
* above is — `cli.test.ts` exercises the whole argv-to-exit-code contract
|
|
145
|
+
* directly against real `mkdtemp` directories, without spawning a
|
|
146
|
+
* subprocess.
|
|
147
|
+
*/
|
|
148
|
+
export declare function mainAddressabilityCheck(argv: string[]): number;
|
|
149
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwHG;AAuEH,qHAAqH;AACrH,qBAAa,aAAc,SAAQ,KAAK;CAAG;AA2f3C;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAyH3C;AAsGD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAqCxD;AAkFD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CA6B9D"}
|