@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,24 @@
1
+ /**
2
+ * @clossys/writer/voice — public entry point. See README.md for the full
3
+ * picture: the machinery-vs-values split this package mirrors from
4
+ * @example/ui/tokens, exactly what `checkCopy` does and does not
5
+ * attempt, and the `factRef` seam into a separate `strategy` package this
6
+ * package deliberately does not import from.
7
+ */
8
+ export { FORMALITY_LEVELS, GLOSSARY_STATUSES, VOICE_SEVERITIES } from "./types.js";
9
+ export { VOICE_FIELDS, TEMPLATE_PLACEHOLDER } from "./fields.js";
10
+ export { validateVoiceRecordShape, parseVoiceRecord } from "./schema.js";
11
+ export { checkCopy, auditClaimsRegister, isCiBlockingSeverity } from "./checker.js";
12
+ // The other half of the seam `@example/strategy`'s
13
+ // `checkBrandCoverage` documents but cannot itself close (it does not, and
14
+ // cannot, import this package) — see `derivation-coverage.ts`'s top-of-file
15
+ // doc comment for the full account of the gap this closes.
16
+ export { checkVoiceDerivationCoverage } from "./derivation-coverage.js";
17
+ // The regex-safety gate itself — exported so a consumer can validate a
18
+ // pattern (e.g. in an editor/CI step, before it ever reaches a VoiceRecord)
19
+ // using the exact same logic schema.ts/checker.ts already run internally,
20
+ // rather than reimplementing or second-guessing it. See
21
+ // `internal/pattern-safety.ts`'s top doc comment for the full, documented
22
+ // regex-safety position this package takes.
23
+ export { checkPatternSafety } from "./internal/pattern-safety.js";
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/voice/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAiBnF,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGjE,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzE,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAUpF,mDAAmD;AACnD,2EAA2E;AAC3E,4EAA4E;AAC5E,2DAA2D;AAC3D,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAGxE,uEAAuE;AACvE,4EAA4E;AAC5E,0EAA0E;AAC1E,wDAAwD;AACxD,0EAA0E;AAC1E,4CAA4C;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A minimal, test-only JSONC reader for `templates/voice-record.template.jsonc`
3
+ * — NOT part of this package's public API (nothing under `src/internal/` is
4
+ * reachable from `src/index.ts`, and `package.json#exports` never points
5
+ * here). It exists solely so `src/field-coverage.test.ts` can parse the
6
+ * template as data and check it against `src/fields.ts`'s `VOICE_FIELDS`,
7
+ * without pulling in a real JSONC parser as a dependency this package would
8
+ * otherwise have zero reason to carry — the same call `@example/ui/tokens`
9
+ * makes for `src/internal/parse-css.ts`.
10
+ */
11
+ /** Parses `text` as JSONC (JSON plus `//` and `/* *\/` comments). Throws the underlying `JSON.parse` error, with position information, if the stripped result still isn't valid JSON. */
12
+ export declare function parseTemplate(text: string): unknown;
13
+ /**
14
+ * Flattens `value` into the set of dot-paths `src/fields.ts`'s `VOICE_FIELDS`
15
+ * uses, matching its own "a leaf, not a tree" rule: a plain object's keys
16
+ * are walked recursively, but an ARRAY is recorded as a single path (its
17
+ * own) and never descended into — `glossary` and `claims` are each one
18
+ * path, not `glossary.0.term`, `glossary.1.term`, ..., because an array's
19
+ * LENGTH and CONTENTS are exactly the bindable data a consumer supplies, not
20
+ * more structure for this function to enumerate. See `fields.ts`'s header
21
+ * comment, "A leaf, not a tree", for the full reasoning — this function is
22
+ * that rule's mechanical mirror image, the same relationship
23
+ * `findAllCustomPropertyNames` in `@example/ui/tokens`' own
24
+ * `internal/parse-css.ts` has to that package's `brand-template.css`.
25
+ */
26
+ export declare function extractFieldPaths(value: unknown, prefix?: string): Set<string>;
27
+ //# sourceMappingURL=parse-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-template.d.ts","sourceRoot":"","sources":["../../../src/voice/internal/parse-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA2DH,yLAAyL;AACzL,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CA2B1E"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * A minimal, test-only JSONC reader for `templates/voice-record.template.jsonc`
3
+ * — NOT part of this package's public API (nothing under `src/internal/` is
4
+ * reachable from `src/index.ts`, and `package.json#exports` never points
5
+ * here). It exists solely so `src/field-coverage.test.ts` can parse the
6
+ * template as data and check it against `src/fields.ts`'s `VOICE_FIELDS`,
7
+ * without pulling in a real JSONC parser as a dependency this package would
8
+ * otherwise have zero reason to carry — the same call `@example/ui/tokens`
9
+ * makes for `src/internal/parse-css.ts`.
10
+ */
11
+ /**
12
+ * Strips `//` line comments and `/* *\/` block comments from `text`,
13
+ * WITHOUT touching either kind of sequence when it appears inside a JSON
14
+ * string literal (`"a // not a comment"`, `"a /* not one either *\/"`) —
15
+ * the one thing a naive regex-replace over the whole file gets wrong. Walks
16
+ * the text one character at a time, tracking exactly one piece of state
17
+ * (whether the cursor is inside a string, honoring `\"` escapes) so a `//`
18
+ * or `/*` that is really just part of a quoted value is copied through
19
+ * unchanged rather than eaten.
20
+ */
21
+ function stripJsonComments(text) {
22
+ let out = "";
23
+ let inString = false;
24
+ for (let i = 0; i < text.length; i++) {
25
+ const ch = text[i];
26
+ const next = text[i + 1];
27
+ if (inString) {
28
+ out += ch;
29
+ if (ch === "\\") {
30
+ // Copy the escaped character too, unexamined — this is what keeps
31
+ // `\"` from prematurely ending the string and `\\` from being
32
+ // mistaken for the start of a second escape.
33
+ if (i + 1 < text.length) {
34
+ out += text[i + 1];
35
+ i++;
36
+ }
37
+ continue;
38
+ }
39
+ if (ch === '"')
40
+ inString = false;
41
+ continue;
42
+ }
43
+ if (ch === '"') {
44
+ inString = true;
45
+ out += ch;
46
+ continue;
47
+ }
48
+ if (ch === "/" && next === "/") {
49
+ while (i < text.length && text[i] !== "\n")
50
+ i++;
51
+ out += "\n"; // preserve line numbering for anyone debugging a parse error
52
+ continue;
53
+ }
54
+ if (ch === "/" && next === "*") {
55
+ i += 2;
56
+ while (i < text.length && !(text[i] === "*" && text[i + 1] === "/"))
57
+ i++;
58
+ i++; // land on the trailing '/'
59
+ continue;
60
+ }
61
+ out += ch;
62
+ }
63
+ return out;
64
+ }
65
+ /** Parses `text` as JSONC (JSON plus `//` and `/* *\/` comments). Throws the underlying `JSON.parse` error, with position information, if the stripped result still isn't valid JSON. */
66
+ export function parseTemplate(text) {
67
+ return JSON.parse(stripJsonComments(text));
68
+ }
69
+ /**
70
+ * Flattens `value` into the set of dot-paths `src/fields.ts`'s `VOICE_FIELDS`
71
+ * uses, matching its own "a leaf, not a tree" rule: a plain object's keys
72
+ * are walked recursively, but an ARRAY is recorded as a single path (its
73
+ * own) and never descended into — `glossary` and `claims` are each one
74
+ * path, not `glossary.0.term`, `glossary.1.term`, ..., because an array's
75
+ * LENGTH and CONTENTS are exactly the bindable data a consumer supplies, not
76
+ * more structure for this function to enumerate. See `fields.ts`'s header
77
+ * comment, "A leaf, not a tree", for the full reasoning — this function is
78
+ * that rule's mechanical mirror image, the same relationship
79
+ * `findAllCustomPropertyNames` in `@example/ui/tokens`' own
80
+ * `internal/parse-css.ts` has to that package's `brand-template.css`.
81
+ */
82
+ export function extractFieldPaths(value, prefix = "") {
83
+ const paths = new Set();
84
+ if (Array.isArray(value)) {
85
+ if (prefix)
86
+ paths.add(prefix);
87
+ return paths;
88
+ }
89
+ if (value !== null && typeof value === "object") {
90
+ for (const [key, nestedValue] of Object.entries(value)) {
91
+ const path = prefix ? `${prefix}.${key}` : key;
92
+ const nestedPaths = extractFieldPaths(nestedValue, path);
93
+ if (nestedPaths.size === 0) {
94
+ // A leaf with no nested paths of its own (a string, number, boolean,
95
+ // null, or an empty object) is still a real field — record it directly.
96
+ paths.add(path);
97
+ }
98
+ else {
99
+ for (const nestedPath of nestedPaths)
100
+ paths.add(nestedPath);
101
+ }
102
+ }
103
+ return paths;
104
+ }
105
+ // A scalar reached with no prefix (extractFieldPaths called on a bare
106
+ // string/number/etc. at the root) has nothing to name — nothing to add.
107
+ if (prefix)
108
+ paths.add(prefix);
109
+ return paths;
110
+ }
111
+ //# sourceMappingURL=parse-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-template.js","sourceRoot":"","sources":["../../../src/voice/internal/parse-template.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * The regex-safety gate for `PatternRule.pattern` (see `../types.ts`) —
3
+ * shared by `../schema.ts` (registration-time validation, when a
4
+ * `VoiceRecord` is parsed from raw data) and `../checker.ts`
5
+ * (defense-in-depth at check time, since `checkCopy` deliberately does not
6
+ * trust its `VoiceRecord` argument — see that file's own doc comment).
7
+ *
8
+ * THE POSITION THIS PACKAGE TAKES ON REGEX SAFETY, STATED ONCE, IN FULL:
9
+ * bound the pattern at REGISTRATION TIME, never at run time. There is no
10
+ * runtime timeout, no worker thread, no execution sandbox anywhere in this
11
+ * package — Node has no synchronous way to cancel a regex mid-match, and a
12
+ * background-thread-plus-timeout scheme is real infrastructure this
13
+ * package's zero-dependency, synchronous, pure-function design (see
14
+ * `../types.ts`'s header comment) has no room for. Instead, `checkPatternSafety`
15
+ * below performs a STATIC analysis of the pattern's SOURCE TEXT and refuses
16
+ * to compile — reports as a finding, never silently drops (see `../schema.ts`
17
+ * and `../checker.ts`) — any pattern this analysis cannot bound:
18
+ *
19
+ * 1. Disallowed flags. Only `i` (case-insensitive), `u` (unicode), and `s`
20
+ * (dotAll) are accepted. `g`/`y` (global/sticky) are rejected because
21
+ * this package always does its own counting pass (see
22
+ * `countPatternMatches`) — accepting a caller's own `g`/`y` would only
23
+ * invite `lastIndex`-statefulness bugs for zero benefit. `m`
24
+ * (multiline) is rejected too: this package matches a pattern against a
25
+ * single piece of copy, never multi-line source text where `^`/`$`
26
+ * anchors are meant to bind per-line — accepting `m` would silently
27
+ * change what `^`/`$` mean in exactly the case most likely to surprise
28
+ * an author.
29
+ * 2. Pattern length. `source.length > MAX_PATTERN_SOURCE_LENGTH` (200) is
30
+ * rejected outright — a voice rule is a short, auditable phrase or
31
+ * character ban, never a novel-length expression, and a hard length
32
+ * cap bounds every other check's own work too.
33
+ * 3. Backreferences (`\1`-`\9`, `\k<name>`). A well-known, independent
34
+ * source of catastrophic backtracking, and one a voice/glossary rule
35
+ * has essentially no legitimate use for — rejected outright, no
36
+ * attempt to bound them.
37
+ * 4. Oversized bounded repetition (`{m,n}` or `{m}` whose effective upper
38
+ * bound exceeds `MAX_QUANTIFIER_BOUND`, 50). A large-but-bounded repeat
39
+ * count is still a real cost multiplier, especially combined with
40
+ * other repeats elsewhere in the same pattern.
41
+ * 5. NESTED QUANTIFIERS — the classic `(a+)+` / `(a*)*` / `(.*)+` shape
42
+ * that causes real exponential-time catastrophic backtracking.
43
+ * Detected via a hand-rolled structural scan (`analyzePatternSource`
44
+ * below): any quantifier (bounded or unbounded — `*`, `+`, `{m,}`, or
45
+ * `{m,n}`) applied to a GROUP whose own top-level content already
46
+ * contains an UNBOUNDED quantifier (`*`, `+`, or `{m,}` with no upper
47
+ * bound) is rejected. Deliberately conservative in the direction of
48
+ * REJECTING more than strictly necessary: a false positive (a pattern
49
+ * that would actually have run fine) is merely annoying to its author;
50
+ * a false negative here is a hung scanner, which this package will
51
+ * never accept as the trade.
52
+ *
53
+ * WHAT THIS DOES NOT CATCH — stated honestly, not silently:
54
+ *
55
+ * - Overlapping-alternation blowup (`(a|a)+`, `(a|ab)*`) that involves no
56
+ * nested quantifier at all. A real, known ReDoS shape this static scan
57
+ * does not detect — a full fix requires real regex-AST analysis (what
58
+ * tools like `safe-regex` do), which this package's zero-dependency,
59
+ * hand-rolled-scanner design does not attempt. Bounded in practice by
60
+ * the length cap (rule 2) and the fact that this package only ever
61
+ * matches a pattern against one short piece of product/marketing copy,
62
+ * never arbitrary-length adversarial input — but this is a mitigation,
63
+ * not a guarantee.
64
+ * - Aggregate cost across many pattern rules evaluated against the same
65
+ * `copy` string. Each pattern is bounded individually; the total cost of
66
+ * a large `patterns` list is a volume concern for the record's author,
67
+ * not something this gate polices.
68
+ *
69
+ * This is a real, bounded, DOCUMENTED static gate — not a general ReDoS
70
+ * detector, and this file does not claim to be one.
71
+ */
72
+ /** Regex flags this package will construct a `RegExp` with. Anything else is rejected — see this file's top doc comment, point 1. */
73
+ export declare const PATTERN_ALLOWED_FLAGS: ReadonlySet<string>;
74
+ /** Hard cap on `pattern.source.length` — see this file's top doc comment, point 2. */
75
+ export declare const MAX_PATTERN_SOURCE_LENGTH = 200;
76
+ /** Hard cap on a bounded quantifier's effective upper bound (`{m,n}`/`{m}`) — see this file's top doc comment, point 4. */
77
+ export declare const MAX_QUANTIFIER_BOUND = 50;
78
+ export type PatternSafetyIssue = "pattern-source-shape" | "pattern-flags-shape" | "pattern-too-long" | "pattern-backreference" | "pattern-quantifier-bound-too-large" | "pattern-nested-quantifier" | "pattern-does-not-compile";
79
+ export interface PatternSafetyOk {
80
+ ok: true;
81
+ regex: RegExp;
82
+ }
83
+ export interface PatternSafetyFailure {
84
+ ok: false;
85
+ issue: PatternSafetyIssue;
86
+ detail: string;
87
+ }
88
+ export type PatternSafetyResult = PatternSafetyOk | PatternSafetyFailure;
89
+ /**
90
+ * The full registration-time (and defense-in-depth check-time) safety gate
91
+ * for one `VoicePattern`. `pattern` is `unknown`, not the `{ source, flags
92
+ * }` shape it will usually already be — this is called from both
93
+ * `../schema.ts` (validating raw, untrusted JSON, where `pattern` may be
94
+ * anything) and `../checker.ts` (re-checking a typed `VoiceRecord` this
95
+ * package's own `checkCopy` does not otherwise trust — see that file's doc
96
+ * comment; a hostile or malformed in-memory record could still hand this a
97
+ * non-object). Never throws, for any input.
98
+ */
99
+ export declare function checkPatternSafety(pattern: unknown): PatternSafetyResult;
100
+ /**
101
+ * Runs an ALREADY-SAFE (passed `checkPatternSafety`) regex against `copy`,
102
+ * counting non-overlapping matches — the pattern-rule analog of
103
+ * `../checker.ts`'s own `countMatches`, except this never wraps the source
104
+ * in `\b...\b`: a pattern rule's whole point is that the caller's regex IS
105
+ * the expression, unmodified. Always adds "g" for counting, regardless of
106
+ * what flags were validated — "g" is never an allowed caller-supplied flag
107
+ * (see `checkPatternSafety`), so this is the one place it is ever added,
108
+ * and only for this internal counting pass.
109
+ */
110
+ export declare function countPatternMatches(copy: string, regex: RegExp): number;
111
+ //# sourceMappingURL=pattern-safety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-safety.d.ts","sourceRoot":"","sources":["../../../src/voice/internal/pattern-safety.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAEH,qIAAqI;AACrI,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,MAAM,CAA4B,CAAC;AAEnF,sFAAsF;AACtF,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,2HAA2H;AAC3H,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,MAAM,kBAAkB,GAC1B,sBAAsB,GACtB,qBAAqB,GACrB,kBAAkB,GAClB,uBAAuB,GACvB,oCAAoC,GACpC,2BAA2B,GAC3B,0BAA0B,CAAC;AAE/B,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,kBAAkB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAyKzE;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAuCxE;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvE"}
@@ -0,0 +1,278 @@
1
+ /**
2
+ * The regex-safety gate for `PatternRule.pattern` (see `../types.ts`) —
3
+ * shared by `../schema.ts` (registration-time validation, when a
4
+ * `VoiceRecord` is parsed from raw data) and `../checker.ts`
5
+ * (defense-in-depth at check time, since `checkCopy` deliberately does not
6
+ * trust its `VoiceRecord` argument — see that file's own doc comment).
7
+ *
8
+ * THE POSITION THIS PACKAGE TAKES ON REGEX SAFETY, STATED ONCE, IN FULL:
9
+ * bound the pattern at REGISTRATION TIME, never at run time. There is no
10
+ * runtime timeout, no worker thread, no execution sandbox anywhere in this
11
+ * package — Node has no synchronous way to cancel a regex mid-match, and a
12
+ * background-thread-plus-timeout scheme is real infrastructure this
13
+ * package's zero-dependency, synchronous, pure-function design (see
14
+ * `../types.ts`'s header comment) has no room for. Instead, `checkPatternSafety`
15
+ * below performs a STATIC analysis of the pattern's SOURCE TEXT and refuses
16
+ * to compile — reports as a finding, never silently drops (see `../schema.ts`
17
+ * and `../checker.ts`) — any pattern this analysis cannot bound:
18
+ *
19
+ * 1. Disallowed flags. Only `i` (case-insensitive), `u` (unicode), and `s`
20
+ * (dotAll) are accepted. `g`/`y` (global/sticky) are rejected because
21
+ * this package always does its own counting pass (see
22
+ * `countPatternMatches`) — accepting a caller's own `g`/`y` would only
23
+ * invite `lastIndex`-statefulness bugs for zero benefit. `m`
24
+ * (multiline) is rejected too: this package matches a pattern against a
25
+ * single piece of copy, never multi-line source text where `^`/`$`
26
+ * anchors are meant to bind per-line — accepting `m` would silently
27
+ * change what `^`/`$` mean in exactly the case most likely to surprise
28
+ * an author.
29
+ * 2. Pattern length. `source.length > MAX_PATTERN_SOURCE_LENGTH` (200) is
30
+ * rejected outright — a voice rule is a short, auditable phrase or
31
+ * character ban, never a novel-length expression, and a hard length
32
+ * cap bounds every other check's own work too.
33
+ * 3. Backreferences (`\1`-`\9`, `\k<name>`). A well-known, independent
34
+ * source of catastrophic backtracking, and one a voice/glossary rule
35
+ * has essentially no legitimate use for — rejected outright, no
36
+ * attempt to bound them.
37
+ * 4. Oversized bounded repetition (`{m,n}` or `{m}` whose effective upper
38
+ * bound exceeds `MAX_QUANTIFIER_BOUND`, 50). A large-but-bounded repeat
39
+ * count is still a real cost multiplier, especially combined with
40
+ * other repeats elsewhere in the same pattern.
41
+ * 5. NESTED QUANTIFIERS — the classic `(a+)+` / `(a*)*` / `(.*)+` shape
42
+ * that causes real exponential-time catastrophic backtracking.
43
+ * Detected via a hand-rolled structural scan (`analyzePatternSource`
44
+ * below): any quantifier (bounded or unbounded — `*`, `+`, `{m,}`, or
45
+ * `{m,n}`) applied to a GROUP whose own top-level content already
46
+ * contains an UNBOUNDED quantifier (`*`, `+`, or `{m,}` with no upper
47
+ * bound) is rejected. Deliberately conservative in the direction of
48
+ * REJECTING more than strictly necessary: a false positive (a pattern
49
+ * that would actually have run fine) is merely annoying to its author;
50
+ * a false negative here is a hung scanner, which this package will
51
+ * never accept as the trade.
52
+ *
53
+ * WHAT THIS DOES NOT CATCH — stated honestly, not silently:
54
+ *
55
+ * - Overlapping-alternation blowup (`(a|a)+`, `(a|ab)*`) that involves no
56
+ * nested quantifier at all. A real, known ReDoS shape this static scan
57
+ * does not detect — a full fix requires real regex-AST analysis (what
58
+ * tools like `safe-regex` do), which this package's zero-dependency,
59
+ * hand-rolled-scanner design does not attempt. Bounded in practice by
60
+ * the length cap (rule 2) and the fact that this package only ever
61
+ * matches a pattern against one short piece of product/marketing copy,
62
+ * never arbitrary-length adversarial input — but this is a mitigation,
63
+ * not a guarantee.
64
+ * - Aggregate cost across many pattern rules evaluated against the same
65
+ * `copy` string. Each pattern is bounded individually; the total cost of
66
+ * a large `patterns` list is a volume concern for the record's author,
67
+ * not something this gate polices.
68
+ *
69
+ * This is a real, bounded, DOCUMENTED static gate — not a general ReDoS
70
+ * detector, and this file does not claim to be one.
71
+ */
72
+ /** Regex flags this package will construct a `RegExp` with. Anything else is rejected — see this file's top doc comment, point 1. */
73
+ export const PATTERN_ALLOWED_FLAGS = new Set(["i", "u", "s"]);
74
+ /** Hard cap on `pattern.source.length` — see this file's top doc comment, point 2. */
75
+ export const MAX_PATTERN_SOURCE_LENGTH = 200;
76
+ /** Hard cap on a bounded quantifier's effective upper bound (`{m,n}`/`{m}`) — see this file's top doc comment, point 4. */
77
+ export const MAX_QUANTIFIER_BOUND = 50;
78
+ function fail(issue, detail) {
79
+ return { ok: false, issue, detail };
80
+ }
81
+ /**
82
+ * Structural scan for backreferences and nested/oversized quantifiers — see
83
+ * this file's top doc comment for exactly what is and is not caught. Walks
84
+ * `source` one character at a time, tracking character-class state (`[...]`)
85
+ * and a group-nesting stack, exactly the same bounded, hand-rolled style
86
+ * `../../scan.ts`'s own tokenizer uses for JS/JSX (see that file's top doc
87
+ * comment) — no regex-of-regexes, no parser library. Returns the FIRST
88
+ * issue found, or `undefined` if the source passes this scan (compilation
89
+ * itself is checked separately, by the caller, via `new RegExp`).
90
+ */
91
+ function analyzePatternSource(source) {
92
+ const n = source.length;
93
+ let i = 0;
94
+ let inClass = false;
95
+ const stack = [{ hasUnboundedQuantifier: false }];
96
+ // The frame belonging to the group that was JUST closed by the immediately
97
+ // preceding ")" — cleared by every other kind of token, since only a
98
+ // quantifier reading this value RIGHT AFTER a ")" means "applies to that
99
+ // group".
100
+ let lastClosedGroupFrame;
101
+ const applyUnboundedQuantifier = () => {
102
+ if (lastClosedGroupFrame?.hasUnboundedQuantifier) {
103
+ return fail("pattern-nested-quantifier", `a quantifier at offset ${i} applies to a group that itself contains an unbounded quantifier — this is the classic catastrophic-backtracking shape (e.g. "(a+)+"); rejected without attempting to run it`);
104
+ }
105
+ const top = stack[stack.length - 1];
106
+ if (top)
107
+ top.hasUnboundedQuantifier = true;
108
+ return undefined;
109
+ };
110
+ while (i < n) {
111
+ const c = source[i];
112
+ if (inClass) {
113
+ if (c === "\\") {
114
+ i += 2;
115
+ }
116
+ else if (c === "]") {
117
+ inClass = false;
118
+ i++;
119
+ }
120
+ else {
121
+ i++;
122
+ }
123
+ lastClosedGroupFrame = undefined;
124
+ continue;
125
+ }
126
+ if (c === "\\") {
127
+ const next = source[i + 1];
128
+ if (next !== undefined && next >= "1" && next <= "9") {
129
+ return fail("pattern-backreference", `backreference "\\${next}" at offset ${i} — a known independent source of catastrophic backtracking, rejected outright`);
130
+ }
131
+ if (next === "k" && source[i + 2] === "<") {
132
+ return fail("pattern-backreference", `named backreference "\\k<...>" at offset ${i} — a known independent source of catastrophic backtracking, rejected outright`);
133
+ }
134
+ i += 2;
135
+ lastClosedGroupFrame = undefined;
136
+ continue;
137
+ }
138
+ if (c === "[") {
139
+ inClass = true;
140
+ i++;
141
+ lastClosedGroupFrame = undefined;
142
+ continue;
143
+ }
144
+ if (c === "(") {
145
+ stack.push({ hasUnboundedQuantifier: false });
146
+ i++;
147
+ lastClosedGroupFrame = undefined;
148
+ continue;
149
+ }
150
+ if (c === ")") {
151
+ const closed = stack.pop();
152
+ if (!closed)
153
+ return fail("pattern-does-not-compile", `unmatched ")" at offset ${i}`);
154
+ i++;
155
+ lastClosedGroupFrame = closed;
156
+ continue;
157
+ }
158
+ if (c === "*" || c === "+") {
159
+ const issue = applyUnboundedQuantifier();
160
+ if (issue)
161
+ return issue;
162
+ i++;
163
+ if (source[i] === "?")
164
+ i++; // lazy modifier — still unbounded, does not change the analysis
165
+ lastClosedGroupFrame = undefined;
166
+ continue;
167
+ }
168
+ if (c === "?") {
169
+ // A bounded (0-1) quantifier on the previous atom/group. Never marks
170
+ // the enclosing frame as unbounded, and a group with an inner
171
+ // unbounded quantifier wrapped as "(...)?" (applied at most once) is
172
+ // NOT the nested-quantifier shape this scan exists to catch.
173
+ i++;
174
+ lastClosedGroupFrame = undefined;
175
+ continue;
176
+ }
177
+ if (c === "{") {
178
+ const close = source.indexOf("}", i);
179
+ const body = close === -1 ? undefined : source.slice(i + 1, close);
180
+ const match = body === undefined ? null : /^(\d+)(,(\d*))?$/.exec(body);
181
+ if (!match) {
182
+ // Not valid `{n}`/`{n,}`/`{n,m}` quantifier syntax — JS treats a
183
+ // lone "{" that doesn't form one of these as a literal character,
184
+ // not an error, so this scan does the same.
185
+ i++;
186
+ lastClosedGroupFrame = undefined;
187
+ continue;
188
+ }
189
+ const hasComma = match[2] !== undefined;
190
+ const upperText = match[3];
191
+ const unbounded = hasComma && (upperText === undefined || upperText === "");
192
+ if (unbounded) {
193
+ const issue = applyUnboundedQuantifier();
194
+ if (issue)
195
+ return issue;
196
+ }
197
+ else {
198
+ const upper = hasComma ? Number(upperText) : Number(match[1]);
199
+ if (upper > MAX_QUANTIFIER_BOUND) {
200
+ return fail("pattern-quantifier-bound-too-large", `quantifier "{${body}}" at offset ${i} has an upper bound of ${upper}, exceeding the maximum of ${MAX_QUANTIFIER_BOUND}`);
201
+ }
202
+ if (lastClosedGroupFrame?.hasUnboundedQuantifier) {
203
+ return fail("pattern-nested-quantifier", `quantifier "{${body}}" at offset ${i} applies to a group that itself contains an unbounded quantifier`);
204
+ }
205
+ }
206
+ i = close + 1;
207
+ if (source[i] === "?")
208
+ i++;
209
+ lastClosedGroupFrame = undefined;
210
+ continue;
211
+ }
212
+ i++;
213
+ lastClosedGroupFrame = undefined;
214
+ }
215
+ if (stack.length !== 1) {
216
+ return fail("pattern-does-not-compile", `unbalanced "(" — ${stack.length - 1} group(s) never closed`);
217
+ }
218
+ return undefined;
219
+ }
220
+ /**
221
+ * The full registration-time (and defense-in-depth check-time) safety gate
222
+ * for one `VoicePattern`. `pattern` is `unknown`, not the `{ source, flags
223
+ * }` shape it will usually already be — this is called from both
224
+ * `../schema.ts` (validating raw, untrusted JSON, where `pattern` may be
225
+ * anything) and `../checker.ts` (re-checking a typed `VoiceRecord` this
226
+ * package's own `checkCopy` does not otherwise trust — see that file's doc
227
+ * comment; a hostile or malformed in-memory record could still hand this a
228
+ * non-object). Never throws, for any input.
229
+ */
230
+ export function checkPatternSafety(pattern) {
231
+ if (typeof pattern !== "object" || pattern === null || Array.isArray(pattern)) {
232
+ return fail("pattern-source-shape", `pattern must be an object with a "source" string, got ${JSON.stringify(pattern)}`);
233
+ }
234
+ const source = pattern.source;
235
+ if (typeof source !== "string" || source.length === 0) {
236
+ return fail("pattern-source-shape", `pattern.source must be a non-empty string, got ${JSON.stringify(source)}`);
237
+ }
238
+ if (source.length > MAX_PATTERN_SOURCE_LENGTH) {
239
+ return fail("pattern-too-long", `pattern.source is ${source.length} character(s), exceeding the maximum of ${MAX_PATTERN_SOURCE_LENGTH}`);
240
+ }
241
+ const flags = pattern.flags;
242
+ if (flags !== undefined) {
243
+ if (typeof flags !== "string") {
244
+ return fail("pattern-flags-shape", `pattern.flags must be a string when present, got ${JSON.stringify(flags)}`);
245
+ }
246
+ for (const flag of flags) {
247
+ if (!PATTERN_ALLOWED_FLAGS.has(flag)) {
248
+ return fail("pattern-flags-shape", `pattern.flags "${flags}" contains "${flag}", which is not one of the allowed flags (${[...PATTERN_ALLOWED_FLAGS].join(", ")}) — see this file's top doc comment for why "g"/"y"/"m" are rejected`);
249
+ }
250
+ }
251
+ }
252
+ const structuralIssue = analyzePatternSource(source);
253
+ if (structuralIssue)
254
+ return structuralIssue;
255
+ try {
256
+ const regex = new RegExp(source, flags ?? "");
257
+ return { ok: true, regex };
258
+ }
259
+ catch (error) {
260
+ return fail("pattern-does-not-compile", error instanceof Error ? error.message : String(error));
261
+ }
262
+ }
263
+ /**
264
+ * Runs an ALREADY-SAFE (passed `checkPatternSafety`) regex against `copy`,
265
+ * counting non-overlapping matches — the pattern-rule analog of
266
+ * `../checker.ts`'s own `countMatches`, except this never wraps the source
267
+ * in `\b...\b`: a pattern rule's whole point is that the caller's regex IS
268
+ * the expression, unmodified. Always adds "g" for counting, regardless of
269
+ * what flags were validated — "g" is never an allowed caller-supplied flag
270
+ * (see `checkPatternSafety`), so this is the one place it is ever added,
271
+ * and only for this internal counting pass.
272
+ */
273
+ export function countPatternMatches(copy, regex) {
274
+ const counting = new RegExp(regex.source, regex.flags.includes("g") ? regex.flags : `${regex.flags}g`);
275
+ const matches = copy.match(counting);
276
+ return matches ? matches.length : 0;
277
+ }
278
+ //# sourceMappingURL=pattern-safety.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-safety.js","sourceRoot":"","sources":["../../../src/voice/internal/pattern-safety.ts"],"names":[],"mappings":"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"}