@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.
Files changed (115) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/LICENSE +21 -0
  3. package/README.md +593 -0
  4. package/dist/addressability.d.ts +245 -0
  5. package/dist/addressability.d.ts.map +1 -0
  6. package/dist/addressability.js +567 -0
  7. package/dist/addressability.js.map +1 -0
  8. package/dist/checker.d.ts +159 -0
  9. package/dist/checker.d.ts.map +1 -0
  10. package/dist/checker.js +177 -0
  11. package/dist/checker.js.map +1 -0
  12. package/dist/cli.d.ts +149 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +1049 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/copy-gate.d.ts +107 -0
  17. package/dist/copy-gate.d.ts.map +1 -0
  18. package/dist/copy-gate.js +132 -0
  19. package/dist/copy-gate.js.map +1 -0
  20. package/dist/fingerprint.d.ts +61 -0
  21. package/dist/fingerprint.d.ts.map +1 -0
  22. package/dist/fingerprint.js +64 -0
  23. package/dist/fingerprint.js.map +1 -0
  24. package/dist/index.d.ts +47 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +63 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/locale-coverage.d.ts +227 -0
  29. package/dist/locale-coverage.d.ts.map +1 -0
  30. package/dist/locale-coverage.js +414 -0
  31. package/dist/locale-coverage.js.map +1 -0
  32. package/dist/passage.d.ts +294 -0
  33. package/dist/passage.d.ts.map +1 -0
  34. package/dist/passage.js +463 -0
  35. package/dist/passage.js.map +1 -0
  36. package/dist/path-exclusions.d.ts +114 -0
  37. package/dist/path-exclusions.d.ts.map +1 -0
  38. package/dist/path-exclusions.js +213 -0
  39. package/dist/path-exclusions.js.map +1 -0
  40. package/dist/registry.d.ts +61 -0
  41. package/dist/registry.d.ts.map +1 -0
  42. package/dist/registry.js +64 -0
  43. package/dist/registry.js.map +1 -0
  44. package/dist/resolve.d.ts +33 -0
  45. package/dist/resolve.d.ts.map +1 -0
  46. package/dist/resolve.js +133 -0
  47. package/dist/resolve.js.map +1 -0
  48. package/dist/scan.d.ts +457 -0
  49. package/dist/scan.d.ts.map +1 -0
  50. package/dist/scan.js +1698 -0
  51. package/dist/scan.js.map +1 -0
  52. package/dist/schema.d.ts +81 -0
  53. package/dist/schema.d.ts.map +1 -0
  54. package/dist/schema.js +482 -0
  55. package/dist/schema.js.map +1 -0
  56. package/dist/types.d.ts +268 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +40 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/voice/checker.d.ts +186 -0
  61. package/dist/voice/checker.d.ts.map +1 -0
  62. package/dist/voice/checker.js +483 -0
  63. package/dist/voice/checker.js.map +1 -0
  64. package/dist/voice/derivation-coverage.d.ts +168 -0
  65. package/dist/voice/derivation-coverage.d.ts.map +1 -0
  66. package/dist/voice/derivation-coverage.js +181 -0
  67. package/dist/voice/derivation-coverage.js.map +1 -0
  68. package/dist/voice/fields.d.ts +107 -0
  69. package/dist/voice/fields.d.ts.map +1 -0
  70. package/dist/voice/fields.js +144 -0
  71. package/dist/voice/fields.js.map +1 -0
  72. package/dist/voice/index.d.ts +19 -0
  73. package/dist/voice/index.d.ts.map +1 -0
  74. package/dist/voice/index.js +24 -0
  75. package/dist/voice/index.js.map +1 -0
  76. package/dist/voice/internal/parse-template.d.ts +27 -0
  77. package/dist/voice/internal/parse-template.d.ts.map +1 -0
  78. package/dist/voice/internal/parse-template.js +111 -0
  79. package/dist/voice/internal/parse-template.js.map +1 -0
  80. package/dist/voice/internal/pattern-safety.d.ts +111 -0
  81. package/dist/voice/internal/pattern-safety.d.ts.map +1 -0
  82. package/dist/voice/internal/pattern-safety.js +278 -0
  83. package/dist/voice/internal/pattern-safety.js.map +1 -0
  84. package/dist/voice/schema.d.ts +65 -0
  85. package/dist/voice/schema.d.ts.map +1 -0
  86. package/dist/voice/schema.js +535 -0
  87. package/dist/voice/schema.js.map +1 -0
  88. package/dist/voice/types.d.ts +282 -0
  89. package/dist/voice/types.d.ts.map +1 -0
  90. package/dist/voice/types.js +37 -0
  91. package/dist/voice/types.js.map +1 -0
  92. package/package.json +72 -0
  93. package/src/addressability.ts +664 -0
  94. package/src/checker.ts +293 -0
  95. package/src/cli.ts +1166 -0
  96. package/src/copy-gate.ts +197 -0
  97. package/src/fingerprint.ts +66 -0
  98. package/src/index.ts +163 -0
  99. package/src/locale-coverage.ts +512 -0
  100. package/src/passage.ts +644 -0
  101. package/src/path-exclusions.ts +265 -0
  102. package/src/registry.ts +103 -0
  103. package/src/resolve.ts +172 -0
  104. package/src/scan.ts +1959 -0
  105. package/src/schema.ts +528 -0
  106. package/src/types.ts +301 -0
  107. package/src/voice/checker.ts +590 -0
  108. package/src/voice/derivation-coverage.ts +217 -0
  109. package/src/voice/fields.ts +160 -0
  110. package/src/voice/index.ts +55 -0
  111. package/src/voice/internal/parse-template.ts +114 -0
  112. package/src/voice/internal/pattern-safety.ts +336 -0
  113. package/src/voice/schema.ts +585 -0
  114. package/src/voice/types.ts +329 -0
  115. package/templates/voice-record.template.jsonc +135 -0
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Path exclusions for `scan.ts`'s SCANNING SURFACE — `scanCopySourceTree`'s
3
+ * directory walk — never to be confused with `scan.ts`'s existing
4
+ * `ExclusionReason`/`ExcludedLiteral`, a DIFFERENT feature this file does
5
+ * not touch. Read `scan.ts`'s own top doc comment ("THE BOUNDARY") before
6
+ * this one: that mechanism decides, for a LITERAL already found inside a
7
+ * file that IS being scanned, whether that one literal is real copy or
8
+ * something structural (an import specifier, a CSS class, an aria label,
9
+ * ...) — a per-LITERAL classification made from the few dozen characters of
10
+ * context immediately around it. This file answers a completely different
11
+ * question, at a completely different granularity: should this FILE be
12
+ * looked at for copy candidates AT ALL, before a single character of it is
13
+ * ever tokenized? That is a per-FILE scope decision, made once, from the
14
+ * file's own path alone — extending `ExclusionReason` to cover it would be
15
+ * a coincidence of both being named "exclusion", not a shared mechanism;
16
+ * this is why it is a new, separate type (`PathExclusion`) and a new,
17
+ * separate `ScanResult` field (`excludedFiles`), not a tenth
18
+ * `ExclusionReason`.
19
+ *
20
+ * THE MOTIVATING CASE ("mention vs. use"): a style guide or README that
21
+ * documents this voice's own banned terms — "never say X, say Y instead" —
22
+ * necessarily CONTAINS the literal banned text, as a MENTION, not a USE.
23
+ * Scanned like any other file, that mention looks identical to real,
24
+ * unregistered product copy to `checkCopyTraceability` (`copy-gate.ts`):
25
+ * the traceability gate has no notion of "this file's job is to talk about
26
+ * copy, not ship it". A `PathExclusion` on that one file's path is the fix
27
+ * — the file is never tokenized at all, so it can never produce a
28
+ * candidate, `unregistered-copy` finding, or citation. See this file's
29
+ * README section, "Path exclusions vs. per-literal exclusions", for the
30
+ * fuller argument, including why this stays scoped to `scan.ts`'s existing
31
+ * scanning surface (copy TRACEABILITY) rather than inventing a new
32
+ * directory-scanning feature for VOICE/glossary rules, which this package
33
+ * does not otherwise have — a consumer building their own "walk a
34
+ * directory, then run each file's text through `checkCopy`" tool gets the
35
+ * identical mention-vs-use protection by reusing THIS mechanism (call
36
+ * `scanCopySourceTree` with `pathExclusions`, then feed its candidates'
37
+ * text through `checkCopy` themselves) rather than this package growing a
38
+ * second, redundant exclusion list.
39
+ *
40
+ * FAILS CLOSED, mirroring `checker.ts`'s `VoiceCheckWaiver` handling (the
41
+ * established precedent for "a caller-supplied list of exceptions" in this
42
+ * package's ecosystem) almost exactly:
43
+ *
44
+ * - A MALFORMED entry (not an object, `path`/`reason` missing or not a
45
+ * non-empty string, or a `path` this file's own small pattern language
46
+ * cannot parse — see `classifyPathExclusionPattern`) is never applied:
47
+ * it exempts NOTHING, and is reported as its own `"error"`-severity
48
+ * `PathExclusionFinding` — the exact "a malformed exclusion entry is a
49
+ * finding, never a silent exemption" requirement. This mirrors
50
+ * `"waiver:invalid"`.
51
+ * - An exclusion that never matched a single scanned file is dead
52
+ * configuration — reported as its own `"warning"`-severity
53
+ * `PathExclusionFinding`, mirroring `"waiver:unused"`, so a stale
54
+ * exclusion (the file it named was renamed or deleted) cannot silently
55
+ * sit in a config forever looking like it is still doing something.
56
+ */
57
+ function isPlainObject(value) {
58
+ return typeof value === "object" && value !== null && !Array.isArray(value);
59
+ }
60
+ function isNonEmptyString(value) {
61
+ return typeof value === "string" && value.trim().length > 0;
62
+ }
63
+ /**
64
+ * Classifies `pattern` into one of this file's three supported shapes, or
65
+ * `undefined` if it matches none of them — the entire "glob" grammar this
66
+ * package supports, deliberately small (no glob library, per this
67
+ * repository's zero-runtime-dependency rule):
68
+ *
69
+ * 1. `"exact"` — no `*` anywhere. Matches only the identical repo-relative
70
+ * path, e.g. `"docs/style-guide.ts"`.
71
+ * 2. `"subtree"` — ends in exactly `"/**"`, with no other `*` anywhere
72
+ * else in the pattern. Matches every file under that directory,
73
+ * recursively, e.g. `"docs/**"` matches `"docs/style-guide.ts"` AND
74
+ * `"docs/internal/glossary.ts"`.
75
+ * 3. `"segment-wildcard"` — exactly one `*` (not `**`), confined to the
76
+ * pattern's FINAL `/`-separated segment, e.g. `"docs/*.ts"`. Every
77
+ * segment before the last must match exactly; the last segment's `*`
78
+ * matches any run of characters (including none) within that one
79
+ * segment only — it does not cross a `/`.
80
+ *
81
+ * A `*`/`**` anywhere else (mid-path, more than one `*` in the final
82
+ * segment, a `**` not forming the whole-pattern `"/**"` suffix, an empty
83
+ * pattern) is rejected — `undefined` — by design: this is a fixed-scope
84
+ * exclusion mechanism for known files/directories, not a general glob
85
+ * engine, and a caller who needs more expressive matching should use
86
+ * `"subtree"` on a containing directory instead.
87
+ */
88
+ function classifyPathExclusionPattern(pattern) {
89
+ if (pattern.length === 0)
90
+ return undefined;
91
+ if (!pattern.includes("*"))
92
+ return "exact";
93
+ if (pattern.endsWith("/**") && pattern.indexOf("*") === pattern.length - 2) {
94
+ // The only "*" characters are the trailing "**" pair, and nothing
95
+ // precedes them but a "/" — reject a bare "**" with no directory prefix
96
+ // (nothing meaningful to scope to) and reject any other "*" earlier in
97
+ // the string (already true here: `indexOf` found the FIRST "*" at the
98
+ // trailing pair's position, so there is no earlier one).
99
+ const prefix = pattern.slice(0, -3);
100
+ return prefix.length > 0 && !prefix.includes("*") ? "subtree" : undefined;
101
+ }
102
+ const lastSlash = pattern.lastIndexOf("/");
103
+ const lastSegment = lastSlash === -1 ? pattern : pattern.slice(lastSlash + 1);
104
+ const priorSegments = lastSlash === -1 ? "" : pattern.slice(0, lastSlash);
105
+ const starCount = (lastSegment.match(/\*/g) ?? []).length;
106
+ if (starCount === 1 && !lastSegment.includes("**") && !priorSegments.includes("*")) {
107
+ return "segment-wildcard";
108
+ }
109
+ return undefined;
110
+ }
111
+ /**
112
+ * Matches `relPath` (already `/`-joined, repo-relative — the same shape
113
+ * `scanCopySourceTree` produces for every `CopyCandidate.file`) against one
114
+ * already-classified exclusion. Never called with a pattern that failed
115
+ * `classifyPathExclusionPattern` — `validatePathExclusions` filters those
116
+ * out before anything reaches here, so this function does not re-validate.
117
+ */
118
+ export function matchesPathExclusion(relPath, exclusion) {
119
+ switch (exclusion.kind) {
120
+ case "exact":
121
+ return relPath === exclusion.path;
122
+ case "subtree": {
123
+ const prefix = exclusion.path.slice(0, -2); // "docs/**" -> "docs/"
124
+ return relPath.startsWith(prefix);
125
+ }
126
+ case "segment-wildcard": {
127
+ const lastSlash = exclusion.path.lastIndexOf("/");
128
+ const priorSegments = lastSlash === -1 ? "" : exclusion.path.slice(0, lastSlash + 1);
129
+ const lastSegmentPattern = lastSlash === -1 ? exclusion.path : exclusion.path.slice(lastSlash + 1);
130
+ if (!relPath.startsWith(priorSegments))
131
+ return false;
132
+ const relRest = relPath.slice(priorSegments.length);
133
+ if (relRest.includes("/"))
134
+ return false; // the wildcard segment never crosses a "/"
135
+ const [before, after] = lastSegmentPattern.split("*");
136
+ return relRest.startsWith(before) && relRest.endsWith(after) && relRest.length >= before.length + after.length;
137
+ }
138
+ default: {
139
+ const unhandled = exclusion.kind;
140
+ throw new Error(`matchesPathExclusion: unknown pattern kind ${JSON.stringify(unhandled)}`);
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * Validates `value` (`ScanOptions.pathExclusions`, or any raw candidate list
146
+ * — this is called on a TYPED `PathExclusion[]` from a TypeScript caller,
147
+ * but re-validates every field anyway, the same defense-in-depth this
148
+ * repository's other schema functions apply to already-typed input: see
149
+ * `@clossys/writer/voice`'s `schema.ts` header comment for why a
150
+ * plain-JS caller, or a value that merely satisfies the type at compile
151
+ * time without conforming at runtime, is exactly the case this guards
152
+ * against). Never throws.
153
+ */
154
+ export function validatePathExclusions(value) {
155
+ if (value === undefined)
156
+ return { valid: [], findings: [] };
157
+ if (!Array.isArray(value)) {
158
+ return {
159
+ valid: [],
160
+ findings: [
161
+ {
162
+ rule: "path-exclusion-invalid",
163
+ severity: "error",
164
+ message: `pathExclusions must be an array when present, got ${JSON.stringify(value)}.`,
165
+ },
166
+ ],
167
+ };
168
+ }
169
+ const valid = [];
170
+ const findings = [];
171
+ value.forEach((raw, i) => {
172
+ if (!isPlainObject(raw)) {
173
+ findings.push({
174
+ rule: "path-exclusion-invalid",
175
+ severity: "error",
176
+ message: `pathExclusions[${i}] must be an object with "path" and "reason", got ${JSON.stringify(raw)}.`,
177
+ });
178
+ return;
179
+ }
180
+ const path = raw.path;
181
+ const reason = raw.reason;
182
+ if (!isNonEmptyString(path)) {
183
+ findings.push({
184
+ rule: "path-exclusion-invalid",
185
+ severity: "error",
186
+ message: `pathExclusions[${i}].path must be a non-empty string, got ${JSON.stringify(path)}.`,
187
+ });
188
+ return;
189
+ }
190
+ if (!isNonEmptyString(reason)) {
191
+ findings.push({
192
+ rule: "path-exclusion-invalid",
193
+ severity: "error",
194
+ message: `pathExclusions[${i}].reason must be a non-empty string, got ${JSON.stringify(reason)}. An exclusion with no stated reason is not auditable.`,
195
+ path,
196
+ });
197
+ return;
198
+ }
199
+ const kind = classifyPathExclusionPattern(path);
200
+ if (kind === undefined) {
201
+ findings.push({
202
+ rule: "path-exclusion-invalid",
203
+ severity: "error",
204
+ message: `pathExclusions[${i}].path "${path}" is not a supported pattern — supported shapes are an exact path, a "dir/**" subtree, or a single "*" confined to the final path segment. See path-exclusions.ts's classifyPathExclusionPattern doc comment.`,
205
+ path,
206
+ });
207
+ return;
208
+ }
209
+ valid.push({ path, reason, kind });
210
+ });
211
+ return { valid, findings };
212
+ }
213
+ //# sourceMappingURL=path-exclusions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-exclusions.js","sourceRoot":"","sources":["../src/path-exclusions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * `readCopyRecord` — the one place in this package that touches a
3
+ * filesystem. Everything in `schema.ts` is pure shape; this function is
4
+ * what turns a consumer's real copy registry file into validated data, in
5
+ * the same gather-don't-judge spirit `@example/strategy`'s
6
+ * `readStrategy` uses for a `strategy/` directory: read what's there,
7
+ * validate it, and record — never throw on — anything that could not be
8
+ * turned into usable data. Judgement about what to DO with an unreadable
9
+ * or invalid registry (fail a build, warn, block a release) belongs to
10
+ * the caller, not to this function.
11
+ *
12
+ * Deliberately narrower than `readStrategy`: a `CopyRecord` is a single
13
+ * JSON file, not a directory of several independently-optional files, so
14
+ * there is exactly one thing that can go wrong and exactly one place it
15
+ * can go wrong at.
16
+ */
17
+ import type { CopyRecord } from "./types.js";
18
+ /**
19
+ * Why `path` did not become a usable `CopyRecord`. `"unreadable"` is a
20
+ * real I/O failure — the file does not exist, a permission was denied, or
21
+ * it vanished mid-read; unknown content could be hiding behind any of
22
+ * those, which is why they are not distinguished further here.
23
+ * `"unparseable"` means the bytes were read but are not valid JSON.
24
+ * `"invalid-schema"` means the JSON parsed but does not satisfy
25
+ * `CopyRecord`'s schema (a missing required field, a wrong type, a
26
+ * duplicate id, a declared placeholder missing from its entry's text) —
27
+ * see `schema.ts`'s `validateCopyRecordShape` for the full list of checks
28
+ * that can produce this.
29
+ */
30
+ export type CopyRegistryReadIssueReason = "unreadable" | "unparseable" | "invalid-schema";
31
+ export interface CopyRegistryReadIssue {
32
+ reason: CopyRegistryReadIssueReason;
33
+ /** Human-readable detail: the I/O error message, the JSON parse error, or `parseCopyRecord`'s full issue list. */
34
+ detail: string;
35
+ }
36
+ export interface CopyRegistryReadResult {
37
+ /** The path this result was read from, exactly as given — echoed back for a caller building its own error message. */
38
+ path: string;
39
+ /** The parsed, defaulted `CopyRecord`, or `undefined` if `issues` is non-empty. */
40
+ record?: CopyRecord;
41
+ /** Empty means `path` was read, parsed, and validated cleanly. Non-empty means `record` is `undefined` — see `CopyRegistryReadIssueReason`. */
42
+ issues: CopyRegistryReadIssue[];
43
+ /**
44
+ * `true` exactly when `issues` is empty and `record` is populated.
45
+ * Named to match `@example/strategy`'s `StrategyBundle.complete`
46
+ * and `@clossys/writer/voice`'s `VoiceCheckReport.complete` —
47
+ * "everything this function could have checked, it did, and it found
48
+ * nothing wrong" is the same one-boolean-read contract across all
49
+ * three, on purpose.
50
+ */
51
+ complete: boolean;
52
+ }
53
+ /**
54
+ * Reads and validates the `CopyRecord` at `path`. Never throws: every
55
+ * failure — an unreadable file, invalid JSON, or a schema violation — is
56
+ * recorded into the returned `CopyRegistryReadResult.issues` and
57
+ * reflected in `.complete`, the same discipline `readStrategy` holds to
58
+ * for a strategy directory.
59
+ */
60
+ export declare function readCopyRecord(path: string): CopyRegistryReadResult;
61
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,2BAA2B,GAAG,YAAY,GAAG,aAAa,GAAG,gBAAgB,CAAC;AAE1F,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,2BAA2B,CAAC;IACpC,kHAAkH;IAClH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,sHAAsH;IACtH,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,+IAA+I;IAC/I,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC;;;;;;;OAOG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,sBAAsB,CAoCnE"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * `readCopyRecord` — the one place in this package that touches a
3
+ * filesystem. Everything in `schema.ts` is pure shape; this function is
4
+ * what turns a consumer's real copy registry file into validated data, in
5
+ * the same gather-don't-judge spirit `@example/strategy`'s
6
+ * `readStrategy` uses for a `strategy/` directory: read what's there,
7
+ * validate it, and record — never throw on — anything that could not be
8
+ * turned into usable data. Judgement about what to DO with an unreadable
9
+ * or invalid registry (fail a build, warn, block a release) belongs to
10
+ * the caller, not to this function.
11
+ *
12
+ * Deliberately narrower than `readStrategy`: a `CopyRecord` is a single
13
+ * JSON file, not a directory of several independently-optional files, so
14
+ * there is exactly one thing that can go wrong and exactly one place it
15
+ * can go wrong at.
16
+ */
17
+ import { readFileSync } from "node:fs";
18
+ import { parseCopyRecord } from "./schema.js";
19
+ /**
20
+ * Reads and validates the `CopyRecord` at `path`. Never throws: every
21
+ * failure — an unreadable file, invalid JSON, or a schema violation — is
22
+ * recorded into the returned `CopyRegistryReadResult.issues` and
23
+ * reflected in `.complete`, the same discipline `readStrategy` holds to
24
+ * for a strategy directory.
25
+ */
26
+ export function readCopyRecord(path) {
27
+ let raw;
28
+ try {
29
+ raw = readFileSync(path, "utf8");
30
+ }
31
+ catch (error) {
32
+ return {
33
+ path,
34
+ issues: [{ reason: "unreadable", detail: error instanceof Error ? error.message : String(error) }],
35
+ complete: false,
36
+ };
37
+ }
38
+ let parsed;
39
+ try {
40
+ parsed = JSON.parse(raw);
41
+ }
42
+ catch (error) {
43
+ return {
44
+ path,
45
+ issues: [{ reason: "unparseable", detail: error instanceof Error ? error.message : String(error) }],
46
+ complete: false,
47
+ };
48
+ }
49
+ try {
50
+ const record = parseCopyRecord(parsed);
51
+ return { path, record, issues: [], complete: true };
52
+ }
53
+ catch (error) {
54
+ // parseCopyRecord throws a single Error whose message already lists
55
+ // every validateCopyRecordShape finding, one per line — reused as-is
56
+ // rather than re-deriving the same detail a second way.
57
+ return {
58
+ path,
59
+ issues: [{ reason: "invalid-schema", detail: error instanceof Error ? error.message : String(error) }],
60
+ complete: false,
61
+ };
62
+ }
63
+ }
64
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAyC9C;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,IAAI;YACJ,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAClG,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,IAAI;YACJ,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACnG,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QACvC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACtD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,oEAAoE;QACpE,qEAAqE;QACrE,wDAAwD;QACxD,OAAO;YACL,IAAI;YACJ,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACtG,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Strict, deterministic `CopyRef` resolution for rendered surfaces.
3
+ *
4
+ * This is intentionally separate from the source scanner's `CopyRecord`
5
+ * support. A scanner can compare a local sentence against an unversioned
6
+ * record; publishing a page needs stronger evidence: a locale, revision,
7
+ * source provenance, and an approved entry. The result exposes every one of
8
+ * those inputs so `@example/surface` can place them in its output
9
+ * manifest without knowing how a consumer stores copy.
10
+ */
11
+ import type { CopyRef, CopyRegistry, CopyResolution, CopyResolver } from "./types.js";
12
+ export type CopyResolveIssueReason = "invalid-registry" | "invalid-ref" | "locale-mismatch" | "unknown-copy-id" | "copy-not-approved" | "missing-placeholder-value" | "unexpected-placeholder-value";
13
+ export interface CopyResolveIssue {
14
+ reason: CopyResolveIssueReason;
15
+ message: string;
16
+ id?: string;
17
+ placeholder?: string;
18
+ }
19
+ export interface CopyResolveResult {
20
+ resolution?: CopyResolution;
21
+ issues: CopyResolveIssue[];
22
+ complete: boolean;
23
+ }
24
+ /**
25
+ * Resolves a `CopyRef` against one locale-specific registry. Any absence,
26
+ * lifecycle failure, locale mismatch, or placeholder mismatch produces no
27
+ * text and a non-complete result. Consumers may log `issues`; renderers must
28
+ * treat a missing `resolution` as an unresolved required input.
29
+ */
30
+ export declare function resolveCopyRef(registry: CopyRegistry | unknown, ref: CopyRef | unknown): CopyResolveResult;
31
+ /** Creates the narrow resolver callback for presentation code. */
32
+ export declare function createCopyResolver(registry: CopyRegistry | unknown): CopyResolver;
33
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,cAAc,EACd,YAAY,EAEb,MAAM,YAAY,CAAC;AAGpB,MAAM,MAAM,sBAAsB,GAC9B,kBAAkB,GAClB,aAAa,GACb,iBAAiB,GACjB,iBAAiB,GACjB,mBAAmB,GACnB,2BAA2B,GAC3B,8BAA8B,CAAC;AAEnC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAkBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,iBAAiB,CAsG1G;AAED,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,GAAG,YAAY,CAEjF"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Strict, deterministic `CopyRef` resolution for rendered surfaces.
3
+ *
4
+ * This is intentionally separate from the source scanner's `CopyRecord`
5
+ * support. A scanner can compare a local sentence against an unversioned
6
+ * record; publishing a page needs stronger evidence: a locale, revision,
7
+ * source provenance, and an approved entry. The result exposes every one of
8
+ * those inputs so `@example/surface` can place them in its output
9
+ * manifest without knowing how a consumer stores copy.
10
+ */
11
+ import { validateCopyRegistryShape } from "./schema.js";
12
+ function isNonEmptyString(value) {
13
+ return typeof value === "string" && value.trim().length > 0;
14
+ }
15
+ function isPlainObject(value) {
16
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17
+ }
18
+ function isCopyValue(value) {
19
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
20
+ }
21
+ function interpolate(text, values) {
22
+ return text.replace(/\{([^{}]+)\}/g, (_match, name) => String(values[name]));
23
+ }
24
+ /**
25
+ * Resolves a `CopyRef` against one locale-specific registry. Any absence,
26
+ * lifecycle failure, locale mismatch, or placeholder mismatch produces no
27
+ * text and a non-complete result. Consumers may log `issues`; renderers must
28
+ * treat a missing `resolution` as an unresolved required input.
29
+ */
30
+ export function resolveCopyRef(registry, ref) {
31
+ const registryFindings = validateCopyRegistryShape(registry);
32
+ if (registryFindings.length > 0) {
33
+ return {
34
+ issues: [{ reason: "invalid-registry", message: "CopyRegistry must pass validateCopyRegistryShape before it can resolve CopyRefs." }],
35
+ complete: false,
36
+ };
37
+ }
38
+ // Safe after the validator's complete structural check. The public
39
+ // signature still accepts unknown because JavaScript callers can bypass
40
+ // TypeScript and resolution must fail closed rather than throw.
41
+ const validRegistry = registry;
42
+ if (!isPlainObject(ref) || !isNonEmptyString(ref.id) || (ref.locale !== undefined && !isNonEmptyString(ref.locale)) || (ref.values !== undefined && !isPlainObject(ref.values))) {
43
+ return {
44
+ issues: [{ reason: "invalid-ref", message: "CopyRef.id must be a non-empty string." }],
45
+ complete: false,
46
+ };
47
+ }
48
+ const validRef = ref;
49
+ if (validRef.locale !== undefined && validRef.locale !== validRegistry.locale) {
50
+ return {
51
+ issues: [
52
+ {
53
+ reason: "locale-mismatch",
54
+ id: validRef.id,
55
+ message: `CopyRef "${validRef.id}" requests locale "${validRef.locale}", but this registry provides "${validRegistry.locale}".`,
56
+ },
57
+ ],
58
+ complete: false,
59
+ };
60
+ }
61
+ const entry = validRegistry.entries.find((candidate) => candidate.id === validRef.id);
62
+ if (!entry) {
63
+ return {
64
+ issues: [
65
+ { reason: "unknown-copy-id", id: validRef.id, message: `CopyRef "${validRef.id}" is not present in registry "${validRegistry.id}".` },
66
+ ],
67
+ complete: false,
68
+ };
69
+ }
70
+ if (entry.status !== "approved") {
71
+ return {
72
+ issues: [
73
+ {
74
+ reason: "copy-not-approved",
75
+ id: validRef.id,
76
+ message: `CopyRef "${validRef.id}" is ${entry.status} and cannot be rendered until it is approved.`,
77
+ },
78
+ ],
79
+ complete: false,
80
+ };
81
+ }
82
+ const values = validRef.values ?? {};
83
+ const issues = [];
84
+ const declared = new Set(entry.placeholders ?? []);
85
+ for (const placeholder of declared) {
86
+ if (!Object.hasOwn(values, placeholder)) {
87
+ issues.push({
88
+ reason: "missing-placeholder-value",
89
+ id: validRef.id,
90
+ placeholder,
91
+ message: `CopyRef "${validRef.id}" is missing a value for required placeholder "${placeholder}".`,
92
+ });
93
+ }
94
+ else if (!isCopyValue(values[placeholder])) {
95
+ issues.push({
96
+ reason: "missing-placeholder-value",
97
+ id: validRef.id,
98
+ placeholder,
99
+ message: `CopyRef "${validRef.id}" has an invalid value for placeholder "${placeholder}".`,
100
+ });
101
+ }
102
+ }
103
+ for (const name of Object.keys(values)) {
104
+ if (!declared.has(name)) {
105
+ issues.push({
106
+ reason: "unexpected-placeholder-value",
107
+ id: validRef.id,
108
+ placeholder: name,
109
+ message: `CopyRef "${validRef.id}" supplies "${name}", which is not declared by that copy entry.`,
110
+ });
111
+ }
112
+ }
113
+ if (issues.length > 0)
114
+ return { issues, complete: false };
115
+ return {
116
+ resolution: {
117
+ ref: validRef,
118
+ text: interpolate(entry.text, values),
119
+ recordId: validRegistry.id,
120
+ revision: validRegistry.revision,
121
+ locale: validRegistry.locale,
122
+ source: validRegistry.source,
123
+ entryId: entry.id,
124
+ },
125
+ issues: [],
126
+ complete: true,
127
+ };
128
+ }
129
+ /** Creates the narrow resolver callback for presentation code. */
130
+ export function createCopyResolver(registry) {
131
+ return (ref) => resolveCopyRef(registry, ref).resolution;
132
+ }
133
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}