@rudra-js/attested 0.6.0 → 0.7.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"hidden.d.ts","sourceRoot":"","sources":["../src/hidden.ts"],"names":[],"mappings":"AAmBA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C"}
1
+ {"version":3,"file":"hidden.d.ts","sourceRoot":"","sources":["../src/hidden.ts"],"names":[],"mappings":"AAMA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C"}
package/dist/hidden.js CHANGED
@@ -1,17 +1,5 @@
1
- // One definition for both layers. It lived in two files, and that is why widening it
2
- // for the quantity layer left the wording layer open twice running.
3
- /**
4
- * Characters that render as nothing: format characters, default-ignorable code points
5
- * and the controls. Cf and Default_Ignorable each hold characters the other does not.
6
- */
7
1
  const INVISIBLE = /[\p{Cf}\p{Default_Ignorable_Code_Point}\p{Cc}]/gu;
8
- /** The controls that do take room. Deleting these would read `Only 2\n3 left` as 23. */
9
2
  const SPACING = /[\t\n\v\f\r\u0085]/;
10
- /**
11
- * Marks that hang on the character before them instead of taking a column of their own,
12
- * so `4<U+0305>9` reads as 49. Spacing marks (Mc) are not here: a Devanagari matra is
13
- * a visible gap between two digits and separates them for a reader.
14
- */
15
3
  const MARKS = /[\p{Mn}\p{Me}]/gu;
16
4
  export function stripInvisible(text) {
17
5
  return text.replace(INVISIBLE, (char) => (SPACING.test(char) ? char : ''));
@@ -1 +1 @@
1
- {"version":3,"file":"hidden.js","sourceRoot":"","sources":["../src/hidden.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,oEAAoE;AAEpE;;;GAGG;AACH,MAAM,SAAS,GAAG,kDAAkD,CAAC;AAErE,wFAAwF;AACxF,MAAM,OAAO,GAAG,oBAAoB,CAAC;AAErC;;;;GAIG;AACH,MAAM,KAAK,GAAG,kBAAkB,CAAC;AAEjC,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC"}
1
+ {"version":3,"file":"hidden.js","sourceRoot":"","sources":["../src/hidden.ts"],"names":[],"mappings":"AAAA,MAAM,SAAS,GAAG,kDAAkD,CAAC;AAErE,MAAM,OAAO,GAAG,oBAAoB,CAAC;AAErC,MAAM,KAAK,GAAG,kBAAkB,CAAC;AAEjC,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,MAAM,EACN,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- // @rudra-js/attested — the guarantee that a model cannot lie about a product.
2
1
  export { verify, verifyFields, } from './verify.js';
3
2
  export { BANNED_PHRASES } from './phrases.js';
4
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAE9E,OAAO,EACL,MAAM,EACN,YAAY,GAQb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,YAAY,GAQb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
@@ -1,12 +1,8 @@
1
1
  export interface Numeral {
2
- /** The run exactly as it appears in the text. */
3
2
  token: string;
4
- /** Every reading of that run, canonical. Empty when the layer cannot read the run. */
5
3
  forms: string[];
6
- /** `digits` is a run this layer reads. The other two it cannot read at all. */
7
4
  kind: 'digits' | 'magnitude' | 'other-numeral';
8
5
  }
9
6
  export declare function numeralsIn(text: string): Numeral[];
10
- /** Every reading of every numeral the host stands behind. */
11
7
  export declare function supportedValues(values: readonly (string | number | bigint)[]): Set<string>;
12
8
  //# sourceMappingURL=numerals.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"numerals.d.ts","sourceRoot":"","sources":["../src/numerals.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,OAAO;IACtB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,+EAA+E;IAC/E,IAAI,EAAE,QAAQ,GAAG,WAAW,GAAG,eAAe,CAAC;CAChD;AAoJD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAIlD;AAmDD,6DAA6D;AAC7D,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAQ1F"}
1
+ {"version":3,"file":"numerals.d.ts","sourceRoot":"","sources":["../src/numerals.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,EAAE,MAAM,EAAE,CAAC;IAEhB,IAAI,EAAE,QAAQ,GAAG,WAAW,GAAG,eAAe,CAAC;CAChD;AAoID,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAIlD;AAoCD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAQ1F"}
package/dist/numerals.js CHANGED
@@ -1,17 +1,10 @@
1
- // Everything here is string work. No float ever holds a value, so a twenty-digit
2
- // order number compares exactly.
3
1
  import { stripInvisible, stripMarks } from './hidden.js';
4
- /** A dot or a comma. Either mark is a decimal point in one locale and grouping in another. */
5
2
  const AMBIGUOUS = '.,';
6
- /** The Arabic decimal separator, which is never grouping. */
7
3
  const DECIMAL_ONLY = '\u066b';
8
- /** Marks that are only ever grouping: Arabic thousands, the space family, the apostrophe. */
9
4
  const GROUPING_ONLY = '\u066c\u0020\u00a0\u2007\u2008\u2009\u202f\u0027\u2019';
10
5
  const SEPARATORS = AMBIGUOUS + DECIMAL_ONLY + GROUPING_ONLY;
11
6
  const RUN = `\\p{Nd}+(?:[${SEPARATORS}]\\p{Nd}+)*`;
12
- /** Marks that multiply the digits in front of them: 万 億 兆, and the k/M/B of English copy. */
13
7
  const MAGNITUDE = '(?:[十百千万萬億亿兆]|[kKMB](?!\\p{L}))';
14
- /** Numeric characters that are not decimal digits: ½ ² ② Ⅲ. */
15
8
  const OTHER_NUMERAL = '[\\p{No}\\p{Nl}]+';
16
9
  const SCAN = new RegExp(`${RUN}${MAGNITUDE}|${RUN}|${OTHER_NUMERAL}`, 'gu');
17
10
  const LOOSE = new RegExp(`[${GROUPING_ONLY}]`, 'u');
@@ -19,12 +12,6 @@ const IS_DIGIT = /^\p{Nd}$/u;
19
12
  const STARTS_DIGIT = /^\p{Nd}/u;
20
13
  const ENDS_DIGIT = /\p{Nd}$/u;
21
14
  const zeros = new Map();
22
- /**
23
- * Unicode writes decimal digits in complete sets of ten, and adjacent sets abut, so
24
- * the start of a maximal run of digit code points is a zero and the offset into that
25
- * run, modulo ten, is the value. Walking back without that modulo reads every digit
26
- * of the second and later sets in the maths block as a nine.
27
- */
28
15
  function digitValue(char) {
29
16
  const code = char.codePointAt(0) ?? 0;
30
17
  let zero = zeros.get(code);
@@ -43,7 +30,6 @@ function toAscii(token) {
43
30
  }
44
31
  return out;
45
32
  }
46
- /** Strips the zeros that carry no value, so 39.00 and 39 are one string. */
47
33
  function canonical(whole, fraction) {
48
34
  let start = 0;
49
35
  while (start < whole.length - 1 && whole[start] === '0')
@@ -55,7 +41,6 @@ function canonical(whole, fraction) {
55
41
  const tail = fraction.slice(0, end);
56
42
  return tail.length === 0 ? head : `${head}.${tail}`;
57
43
  }
58
- /** Whether these runs read as one grouped whole number: 12,345,678 or 1,29,999. */
59
44
  function isGrouping(groups) {
60
45
  if (groups.length === 1)
61
46
  return true;
@@ -73,12 +58,10 @@ function isGrouping(groups) {
73
58
  const lead = (groups[0] ?? '').length;
74
59
  if (three)
75
60
  return lead >= 1 && lead <= 3;
76
- // 1,29,999 — the Indian grouping, two digits at a time above the last three.
77
61
  if (two)
78
62
  return lead >= 1 && lead <= 2;
79
63
  return false;
80
64
  }
81
- /** Whether every mark is the same one, and every one of them can be grouping. */
82
65
  function allGrouping(separators) {
83
66
  for (const separator of separators) {
84
67
  if (separator !== separators[0])
@@ -88,7 +71,6 @@ function allGrouping(separators) {
88
71
  }
89
72
  return true;
90
73
  }
91
- /** `1,299` is 1299 in London and in Berlin. `1234,567` is a decimal in both. */
92
74
  function formsOf(token) {
93
75
  const ascii = toAscii(token);
94
76
  const groups = [];
@@ -114,8 +96,6 @@ function formsOf(token) {
114
96
  const leading = separators.slice(0, -1);
115
97
  const point = separators[separators.length - 1] ?? '';
116
98
  const fraction = groups[groups.length - 1] ?? '';
117
- // One dot or comma with exactly three digits behind it is grouping wherever it is
118
- // written, so `4.800` is four thousand eight hundred and never the rating 4.8.
119
99
  const groupingWins = forms.length > 0 && leading.length === 0 && fraction.length === 3;
120
100
  if (!groupingWins &&
121
101
  !GROUPING_ONLY.includes(point) &&
@@ -140,7 +120,6 @@ function collect(found, token) {
140
120
  found.push({ token, forms, kind: 'digits' });
141
121
  return;
142
122
  }
143
- // `8 10 12` is three numbers, not one badly grouped one.
144
123
  if (LOOSE.test(token)) {
145
124
  for (const part of token.split(LOOSE)) {
146
125
  if (part.length > 0)
@@ -148,9 +127,6 @@ function collect(found, token) {
148
127
  }
149
128
  return;
150
129
  }
151
- // No locale reads `24.12.2026` as a number, so only a fact written the same way
152
- // stands behind it. The raw run carries a mark, and a reading never does, so it
153
- // can never be mistaken for one.
154
130
  found.push({ token, forms: [toAscii(token)], kind: 'digits' });
155
131
  }
156
132
  export function numeralsIn(text) {
@@ -159,24 +135,13 @@ export function numeralsIn(text) {
159
135
  collect(found, match[0]);
160
136
  return found;
161
137
  }
162
- /** A whole fact in exponent notation and nothing else, so expanding it swallows no prose. */
163
138
  const BARE_EXPONENT = /^[+-]?\d+(?:\.\d+)?[eE][+-]?\d+$/;
164
- /**
165
- * The widest digit run a fact may lay out to. Every finite JS number fits — 5e-324 is
166
- * the longest, at 326 characters — and nothing a shop sells is wider than that.
167
- */
168
139
  const DIGIT_LIMIT = 1000;
169
- /**
170
- * `String(1e21)` is `1e+21`, which reads as the two numerals 1 and 21. Lay the same digits
171
- * out in place. Empty past the limit: `1e2000000000` is twelve characters and no product
172
- * fact, and laying it out builds a run long enough to take the process down with it.
173
- */
174
140
  function positional(written) {
175
141
  const marker = written.search(/[eE]/);
176
142
  if (marker < 0)
177
143
  return written;
178
144
  const power = Number(written.slice(marker + 1));
179
- // The sign never reaches a numeral, so dropping it here mints no digit the host lacks.
180
145
  const body = written.slice(0, marker).replace(/^[+-]/, '');
181
146
  const point = body.indexOf('.');
182
147
  const digits = point < 0 ? body : body.slice(0, point) + body.slice(point + 1);
@@ -193,11 +158,6 @@ function positional(written) {
193
158
  }
194
159
  return `${digits.slice(0, place)}.${digits.slice(place)}`;
195
160
  }
196
- /**
197
- * A string fact is read as the host typed it, except when the whole of it is a number
198
- * in exponent notation: `String`, `JSON.stringify`, a CSV export and an API that writes
199
- * 64-bit values as strings all produce that, and none of them is the host choosing digits.
200
- */
201
161
  function digitsOf(value) {
202
162
  if (typeof value === 'number')
203
163
  return positional(String(value));
@@ -206,7 +166,6 @@ function digitsOf(value) {
206
166
  const trimmed = value.trim();
207
167
  return BARE_EXPONENT.test(trimmed) ? positional(trimmed) : value;
208
168
  }
209
- /** Every reading of every numeral the host stands behind. */
210
169
  export function supportedValues(values) {
211
170
  const supported = new Set();
212
171
  for (const value of values) {
@@ -1 +1 @@
1
- {"version":3,"file":"numerals.js","sourceRoot":"","sources":["../src/numerals.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"numerals.js","sourceRoot":"","sources":["../src/numerals.ts"],"names":[],"mappings":"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"}
package/dist/phrases.d.ts CHANGED
@@ -1,28 +1,14 @@
1
- /** Claims with no value behind them. Stored in the form `normalisePhrasing` produces. */
2
1
  export declare const BANNED_PHRASES: readonly string[];
3
- /** One shape for text and phrases, so a hyphen or a stray mark cannot hide a claim. */
4
2
  export declare function normalisePhrasing(text: string): string;
5
3
  export interface Span {
6
4
  start: number;
7
- /** The last character of the match, not one past it. */
8
5
  end: number;
9
6
  }
10
7
  export interface Indexed {
11
- /** The text as it was handed in, which is what spans index and the word gap reads. */
12
8
  text: string;
13
- /** The same characters with the gaps dropped, which is what matching runs against. */
14
9
  haystack: string;
15
- /** Where each character of `haystack` really sat in `text`. */
16
10
  spots: number[];
17
11
  }
18
- /**
19
- * Build this once and read every phrase off it. The text must already be normalised.
20
- *
21
- * Matching runs with every space dropped, because 送料 無料 is 送料無料 to anyone and a
22
- * space is free. `spots` carries each character back to where it really sat, so the
23
- * word-gap guard still reads the neighbours the shopper sees.
24
- */
25
12
  export declare function indexPhrasing(text: string): Indexed;
26
- /** Spans index the text the index was built from. The phrase must already be normalised. */
27
13
  export declare function spansIn(indexed: Indexed, phrase: string): Span[];
28
14
  //# sourceMappingURL=phrases.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"phrases.d.ts","sourceRoot":"","sources":["../src/phrases.ts"],"names":[],"mappings":"AAEA,yFAAyF;AACzF,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAkF3C,CAAC;AA0CF,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAetD;AAQD,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,OAAO;IACtB,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYnD;AAED,4FAA4F;AAG5F,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,CA2BhE"}
1
+ {"version":3,"file":"phrases.d.ts","sourceRoot":"","sources":["../src/phrases.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAkF3C,CAAC;AAoCF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWtD;AAQD,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IAEd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IAEb,QAAQ,EAAE,MAAM,CAAC;IAEjB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYnD;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,CA2BhE"}
package/dist/phrases.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { stripInvisible, stripMarks } from './hidden.js';
2
- /** Claims with no value behind them. Stored in the form `normalisePhrasing` produces. */
3
2
  export const BANNED_PHRASES = [
4
3
  'cheap',
5
4
  'cheaper',
@@ -83,11 +82,6 @@ export const BANNED_PHRASES = [
83
82
  'customer favorite',
84
83
  'money back',
85
84
  ];
86
- /**
87
- * Letters that render as a Latin letter but are not one. Folded so a Cyrillic о in
88
- * "in stоck" cannot walk a claim past its own entry on the list. Both the text and
89
- * the phrase are folded, so a genuine Cyrillic phrase still matches itself.
90
- */
91
85
  const CONFUSABLES = {
92
86
  а: 'a',
93
87
  в: 'b',
@@ -119,18 +113,12 @@ const CONFUSABLES = {
119
113
  χ: 'x',
120
114
  ϲ: 'c',
121
115
  };
122
- /** A run of whitespace holding one of these is a break the shopper sees. */
123
116
  const LINE_BREAK = /[\n\v\f\r\u0085\u2028\u2029]/;
124
- /** One shape for text and phrases, so a hyphen or a stray mark cannot hide a claim. */
125
117
  export function normalisePhrasing(text) {
126
- // Marks come off after composition, so an accented e keeps its accent while an
127
- // overline, which composes with nothing, does not hide `free` from its own entry.
128
118
  const lowered = stripMarks(stripInvisible(text).normalize('NFKC').toLowerCase().normalize('NFC'));
129
119
  let folded = '';
130
120
  for (const char of lowered)
131
121
  folded += CONFUSABLES[char] ?? char;
132
- // A line break survives as itself. Matching reads straight through it, and only
133
- // the allowance in verify.ts cares, because it will not forgive across a break.
134
122
  return folded
135
123
  .replace(/[\u2018\u2019]/g, "'")
136
124
  .replace(/[-_\u2010-\u2015]+/g, ' ')
@@ -141,19 +129,11 @@ const ASCII_WORD = /[a-z0-9]/;
141
129
  function isGlued(char) {
142
130
  return char !== undefined && ASCII_WORD.test(char);
143
131
  }
144
- /**
145
- * Build this once and read every phrase off it. The text must already be normalised.
146
- *
147
- * Matching runs with every space dropped, because 送料 無料 is 送料無料 to anyone and a
148
- * space is free. `spots` carries each character back to where it really sat, so the
149
- * word-gap guard still reads the neighbours the shopper sees.
150
- */
151
132
  export function indexPhrasing(text) {
152
133
  const tight = [];
153
134
  const spots = [];
154
135
  for (let index = 0; index < text.length; index += 1) {
155
136
  const char = text[index] ?? '';
156
- // A line break reads like a space here, so a claim split over two lines is caught.
157
137
  if (char === ' ' || char === '\n')
158
138
  continue;
159
139
  tight.push(char);
@@ -161,9 +141,6 @@ export function indexPhrasing(text) {
161
141
  }
162
142
  return { text, haystack: tight.join(''), spots };
163
143
  }
164
- /** Spans index the text the index was built from. The phrase must already be normalised. */
165
- // The word-gap rule only guards an ASCII edge, so `cheap` misses `cheapskate`
166
- // while a Japanese phrase — which has no word gaps to find — still matches.
167
144
  export function spansIn(indexed, phrase) {
168
145
  const { text, haystack, spots } = indexed;
169
146
  const spans = [];
@@ -180,7 +157,6 @@ export function spansIn(indexed, phrase) {
180
157
  const start = spots[at] ?? 0;
181
158
  const end = spots[at + needle.length - 1] ?? 0;
182
159
  const next = text[end + 1];
183
- // A trailing s is a plural, not a different word: `discount` catches `discounts`.
184
160
  const plural = next === 's' && !isGlued(text[end + 2]);
185
161
  const before = guardStart && isGlued(text[start - 1]);
186
162
  const after = guardEnd && isGlued(next) && !plural;
@@ -1 +1 @@
1
- {"version":3,"file":"phrases.js","sourceRoot":"","sources":["../src/phrases.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"phrases.js","sourceRoot":"","sources":["../src/phrases.ts"],"names":[],"mappings":"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"}
package/dist/verify.d.ts CHANGED
@@ -1,35 +1,23 @@
1
1
  export type Layer = 'quantity' | 'wording';
2
2
  export interface Finding {
3
3
  layer: Layer;
4
- /** The numeral as the text wrote it, or the phrase as the denylist holds it. */
5
4
  token: string;
6
- /** Audit evidence. Names the layer and the token. */
7
5
  reason: string;
8
6
  }
9
7
  export interface LayerReport {
10
8
  supported: boolean;
11
- /**
12
- * What this layer's verdict is worth. `proof` means every numeral in the text is
13
- * a value the host supplied — not that the sentence around it is true.
14
- */
15
9
  strength: 'proof' | 'best-effort';
16
- /** Numerals found, or phrases screened against. Zero means nothing was checked. */
17
10
  checked: number;
18
- /** One per distinct token that failed. */
19
11
  findings: Finding[];
20
12
  }
21
13
  export interface VerifyResult {
22
- /** True when both layers are. */
23
14
  supported: boolean;
24
15
  quantity: LayerReport;
25
16
  wording: LayerReport;
26
17
  }
27
18
  export interface Facts {
28
- /** In any shape they are written: `39`, `'$1,299.00'`, `'4,8'`. Every numeral in one counts. */
29
19
  values: readonly (string | number | bigint)[];
30
- /** Claims to ban on top of the built-in English list, for the host's own language. */
31
20
  bannedPhrases?: readonly string[];
32
- /** Wording the shop stands behind. A banned claim inside one of these is not reported. */
33
21
  allowedPhrases?: readonly string[];
34
22
  }
35
23
  export interface FieldResult {
@@ -37,14 +25,10 @@ export interface FieldResult {
37
25
  result: VerifyResult;
38
26
  }
39
27
  export interface BatchResult {
40
- /** True when every field is, and when the fields read as one carry no banned claim. */
41
28
  supported: boolean;
42
29
  fields: FieldResult[];
43
- /** The wording layer over every field joined, so a phrase split across two fields still reads. */
44
30
  acrossFields: LayerReport;
45
31
  }
46
- /** Whether one piece of model-written text stands on the facts the host supplied. */
47
32
  export declare function verify(text: string, facts: Facts): VerifyResult;
48
- /** The same check over every field of one product, without repeating the facts. */
49
33
  export declare function verifyFields(fields: Record<string, string>, facts: Facts): BatchResult;
50
34
  //# sourceMappingURL=verify.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,KAAK,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3C,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,KAAK,CAAC;IACb,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,QAAQ,EAAE,OAAO,GAAG,aAAa,CAAC;IAClC,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,iCAAiC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,WAAW,CAAC;IACtB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,KAAK;IACpB,gGAAgG;IAChG,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC9C,sFAAsF;IACtF,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,0FAA0F;IAC1F,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,kGAAkG;IAClG,YAAY,EAAE,WAAW,CAAC;CAC3B;AA4ID,qFAAqF;AACrF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,YAAY,CAE/D;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,WAAW,CAqBtF"}
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,KAAK,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3C,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,KAAK,CAAC;IAEb,KAAK,EAAE,MAAM,CAAC;IAEd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAC;IAEnB,QAAQ,EAAE,OAAO,GAAG,aAAa,CAAC;IAElC,OAAO,EAAE,MAAM,CAAC;IAEhB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,WAAW,CAAC;IACtB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAE9C,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAElC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,WAAW,EAAE,CAAC;IAEtB,YAAY,EAAE,WAAW,CAAC;CAC3B;AAkID,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,YAAY,CAE/D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,WAAW,CAmBtF"}
package/dist/verify.js CHANGED
@@ -1,11 +1,5 @@
1
1
  import { numeralsIn, supportedValues } from './numerals.js';
2
2
  import { BANNED_PHRASES, indexPhrasing, normalisePhrasing, spansIn } from './phrases.js';
3
- /**
4
- * One reading per fact list, keyed on the list itself, because core hands `verify` the
5
- * same array again for every field it screens. Held against a copy of what the list
6
- * carried: `readonly` on `Facts.values` is a compile-time view of one reference, and a
7
- * host holding the array can write to it between two calls.
8
- */
9
3
  const readings = new WeakMap();
10
4
  function unchanged(held, values) {
11
5
  if (held.length !== values.length)
@@ -18,7 +12,6 @@ function unchanged(held, values) {
18
12
  }
19
13
  function valuesOf(facts) {
20
14
  const values = facts.values;
21
- // A caller outside TypeScript can pass something with no identity to key on.
22
15
  if (!Array.isArray(values))
23
16
  return supportedValues(values);
24
17
  const read = readings.get(values);
@@ -90,16 +83,12 @@ function checkWording(text, phrases, allowed) {
90
83
  const spans = [];
91
84
  for (const phrase of allowed) {
92
85
  for (const span of spansIn(indexed, phrase)) {
93
- // The denylist reads through a break and the allowance does not, so both rules
94
- // err toward reporting. A model that puts the negation in one block and the
95
- // claim in another has written two things the shopper reads apart.
96
86
  if (!normalised.slice(span.start, span.end + 1).includes('\n'))
97
87
  spans.push(span);
98
88
  }
99
89
  }
100
90
  const findings = [];
101
91
  for (const phrase of phrases) {
102
- // One hit inside allowed wording is forgiven; the same phrase elsewhere is not.
103
92
  let caught = false;
104
93
  for (const hit of spansIn(indexed, phrase)) {
105
94
  if (!standsBehind(spans, hit))
@@ -125,11 +114,9 @@ function check(text, supported, phrases, allowed) {
125
114
  const wording = checkWording(text, phrases, allowed);
126
115
  return { supported: quantity.supported && wording.supported, quantity, wording };
127
116
  }
128
- /** Whether one piece of model-written text stands on the facts the host supplied. */
129
117
  export function verify(text, facts) {
130
118
  return check(text, valuesOf(facts), phrasesOf(facts), allowedOf(facts));
131
119
  }
132
- /** The same check over every field of one product, without repeating the facts. */
133
120
  export function verifyFields(fields, facts) {
134
121
  const supported = valuesOf(facts);
135
122
  const phrases = phrasesOf(facts);
@@ -145,8 +132,6 @@ export function verifyFields(fields, facts) {
145
132
  results.push({ field, result });
146
133
  written.push(text);
147
134
  }
148
- // A card renders its fields next to each other, so `Free` and `delivery on every
149
- // order` are one sentence to a shopper even though neither field carries one.
150
135
  const acrossFields = checkWording(written.join(' '), phrases, allowed);
151
136
  return { supported: allSupported && acrossFields.supported, fields: results, acrossFields };
152
137
  }
@@ -1 +1 @@
1
- {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rudra-js/attested",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Checks model-written product copy against the facts a shop stands behind",
5
5
  "keywords": [
6
6
  "llm",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "license": "MIT",
14
14
  "author": "Clive Dsouza",
15
- "homepage": "https://github.com/clivedsouza1010/rudra-js/tree/main/packages/attested#readme",
15
+ "homepage": "https://rudrajs.com",
16
16
  "bugs": {
17
17
  "url": "https://github.com/clivedsouza1010/rudra-js/issues"
18
18
  },
package/src/hidden.ts CHANGED
@@ -1,20 +1,7 @@
1
- // One definition for both layers. It lived in two files, and that is why widening it
2
- // for the quantity layer left the wording layer open twice running.
3
-
4
- /**
5
- * Characters that render as nothing: format characters, default-ignorable code points
6
- * and the controls. Cf and Default_Ignorable each hold characters the other does not.
7
- */
8
1
  const INVISIBLE = /[\p{Cf}\p{Default_Ignorable_Code_Point}\p{Cc}]/gu;
9
2
 
10
- /** The controls that do take room. Deleting these would read `Only 2\n3 left` as 23. */
11
3
  const SPACING = /[\t\n\v\f\r\u0085]/;
12
4
 
13
- /**
14
- * Marks that hang on the character before them instead of taking a column of their own,
15
- * so `4<U+0305>9` reads as 49. Spacing marks (Mc) are not here: a Devanagari matra is
16
- * a visible gap between two digits and separates them for a reader.
17
- */
18
5
  const MARKS = /[\p{Mn}\p{Me}]/gu;
19
6
 
20
7
  export function stripInvisible(text: string): string {
package/src/index.ts CHANGED
@@ -1,5 +1,3 @@
1
- // @rudra-js/attested — the guarantee that a model cannot lie about a product.
2
-
3
1
  export {
4
2
  verify,
5
3
  verifyFields,
package/src/numerals.ts CHANGED
@@ -1,21 +1,17 @@
1
- // Everything here is string work. No float ever holds a value, so a twenty-digit
2
- // order number compares exactly.
3
-
4
1
  import { stripInvisible, stripMarks } from './hidden.js';
5
2
 
6
- /** A dot or a comma. Either mark is a decimal point in one locale and grouping in another. */
7
3
  const AMBIGUOUS = '.,';
8
- /** The Arabic decimal separator, which is never grouping. */
4
+
9
5
  const DECIMAL_ONLY = '\u066b';
10
- /** Marks that are only ever grouping: Arabic thousands, the space family, the apostrophe. */
6
+
11
7
  const GROUPING_ONLY = '\u066c\u0020\u00a0\u2007\u2008\u2009\u202f\u0027\u2019';
12
8
 
13
9
  const SEPARATORS = AMBIGUOUS + DECIMAL_ONLY + GROUPING_ONLY;
14
10
 
15
11
  const RUN = `\\p{Nd}+(?:[${SEPARATORS}]\\p{Nd}+)*`;
16
- /** Marks that multiply the digits in front of them: 万 億 兆, and the k/M/B of English copy. */
12
+
17
13
  const MAGNITUDE = '(?:[十百千万萬億亿兆]|[kKMB](?!\\p{L}))';
18
- /** Numeric characters that are not decimal digits: ½ ² ② Ⅲ. */
14
+
19
15
  const OTHER_NUMERAL = '[\\p{No}\\p{Nl}]+';
20
16
 
21
17
  const SCAN = new RegExp(`${RUN}${MAGNITUDE}|${RUN}|${OTHER_NUMERAL}`, 'gu');
@@ -26,22 +22,15 @@ const STARTS_DIGIT = /^\p{Nd}/u;
26
22
  const ENDS_DIGIT = /\p{Nd}$/u;
27
23
 
28
24
  export interface Numeral {
29
- /** The run exactly as it appears in the text. */
30
25
  token: string;
31
- /** Every reading of that run, canonical. Empty when the layer cannot read the run. */
26
+
32
27
  forms: string[];
33
- /** `digits` is a run this layer reads. The other two it cannot read at all. */
28
+
34
29
  kind: 'digits' | 'magnitude' | 'other-numeral';
35
30
  }
36
31
 
37
32
  const zeros = new Map<number, number>();
38
33
 
39
- /**
40
- * Unicode writes decimal digits in complete sets of ten, and adjacent sets abut, so
41
- * the start of a maximal run of digit code points is a zero and the offset into that
42
- * run, modulo ten, is the value. Walking back without that modulo reads every digit
43
- * of the second and later sets in the maths block as a nine.
44
- */
45
34
  function digitValue(char: string): number {
46
35
  const code = char.codePointAt(0) ?? 0;
47
36
 
@@ -63,7 +52,6 @@ function toAscii(token: string): string {
63
52
  return out;
64
53
  }
65
54
 
66
- /** Strips the zeros that carry no value, so 39.00 and 39 are one string. */
67
55
  function canonical(whole: string, fraction: string): string {
68
56
  let start = 0;
69
57
  while (start < whole.length - 1 && whole[start] === '0') start += 1;
@@ -76,7 +64,6 @@ function canonical(whole: string, fraction: string): string {
76
64
  return tail.length === 0 ? head : `${head}.${tail}`;
77
65
  }
78
66
 
79
- /** Whether these runs read as one grouped whole number: 12,345,678 or 1,29,999. */
80
67
  function isGrouping(groups: string[]): boolean {
81
68
  if (groups.length === 1) return true;
82
69
  if ((groups[groups.length - 1] ?? '').length !== 3) return false;
@@ -91,12 +78,11 @@ function isGrouping(groups: string[]): boolean {
91
78
 
92
79
  const lead = (groups[0] ?? '').length;
93
80
  if (three) return lead >= 1 && lead <= 3;
94
- // 1,29,999 — the Indian grouping, two digits at a time above the last three.
81
+
95
82
  if (two) return lead >= 1 && lead <= 2;
96
83
  return false;
97
84
  }
98
85
 
99
- /** Whether every mark is the same one, and every one of them can be grouping. */
100
86
  function allGrouping(separators: string[]): boolean {
101
87
  for (const separator of separators) {
102
88
  if (separator !== separators[0]) return false;
@@ -105,7 +91,6 @@ function allGrouping(separators: string[]): boolean {
105
91
  return true;
106
92
  }
107
93
 
108
- /** `1,299` is 1299 in London and in Berlin. `1234,567` is a decimal in both. */
109
94
  function formsOf(token: string): string[] {
110
95
  const ascii = toAscii(token);
111
96
 
@@ -133,8 +118,6 @@ function formsOf(token: string): string[] {
133
118
  const point = separators[separators.length - 1] ?? '';
134
119
  const fraction = groups[groups.length - 1] ?? '';
135
120
 
136
- // One dot or comma with exactly three digits behind it is grouping wherever it is
137
- // written, so `4.800` is four thousand eight hundred and never the rating 4.8.
138
121
  const groupingWins = forms.length > 0 && leading.length === 0 && fraction.length === 3;
139
122
 
140
123
  if (
@@ -166,7 +149,6 @@ function collect(found: Numeral[], token: string): void {
166
149
  return;
167
150
  }
168
151
 
169
- // `8 10 12` is three numbers, not one badly grouped one.
170
152
  if (LOOSE.test(token)) {
171
153
  for (const part of token.split(LOOSE)) {
172
154
  if (part.length > 0) collect(found, part);
@@ -174,9 +156,6 @@ function collect(found: Numeral[], token: string): void {
174
156
  return;
175
157
  }
176
158
 
177
- // No locale reads `24.12.2026` as a number, so only a fact written the same way
178
- // stands behind it. The raw run carries a mark, and a reading never does, so it
179
- // can never be mistaken for one.
180
159
  found.push({ token, forms: [toAscii(token)], kind: 'digits' });
181
160
  }
182
161
 
@@ -186,26 +165,16 @@ export function numeralsIn(text: string): Numeral[] {
186
165
  return found;
187
166
  }
188
167
 
189
- /** A whole fact in exponent notation and nothing else, so expanding it swallows no prose. */
190
168
  const BARE_EXPONENT = /^[+-]?\d+(?:\.\d+)?[eE][+-]?\d+$/;
191
169
 
192
- /**
193
- * The widest digit run a fact may lay out to. Every finite JS number fits — 5e-324 is
194
- * the longest, at 326 characters — and nothing a shop sells is wider than that.
195
- */
196
170
  const DIGIT_LIMIT = 1000;
197
171
 
198
- /**
199
- * `String(1e21)` is `1e+21`, which reads as the two numerals 1 and 21. Lay the same digits
200
- * out in place. Empty past the limit: `1e2000000000` is twelve characters and no product
201
- * fact, and laying it out builds a run long enough to take the process down with it.
202
- */
203
172
  function positional(written: string): string {
204
173
  const marker = written.search(/[eE]/);
205
174
  if (marker < 0) return written;
206
175
 
207
176
  const power = Number(written.slice(marker + 1));
208
- // The sign never reaches a numeral, so dropping it here mints no digit the host lacks.
177
+
209
178
  const body = written.slice(0, marker).replace(/^[+-]/, '');
210
179
  const point = body.indexOf('.');
211
180
  const digits = point < 0 ? body : body.slice(0, point) + body.slice(point + 1);
@@ -222,11 +191,6 @@ function positional(written: string): string {
222
191
  return `${digits.slice(0, place)}.${digits.slice(place)}`;
223
192
  }
224
193
 
225
- /**
226
- * A string fact is read as the host typed it, except when the whole of it is a number
227
- * in exponent notation: `String`, `JSON.stringify`, a CSV export and an API that writes
228
- * 64-bit values as strings all produce that, and none of them is the host choosing digits.
229
- */
230
194
  function digitsOf(value: string | number | bigint): string {
231
195
  if (typeof value === 'number') return positional(String(value));
232
196
  if (typeof value !== 'string') return String(value);
@@ -235,7 +199,6 @@ function digitsOf(value: string | number | bigint): string {
235
199
  return BARE_EXPONENT.test(trimmed) ? positional(trimmed) : value;
236
200
  }
237
201
 
238
- /** Every reading of every numeral the host stands behind. */
239
202
  export function supportedValues(values: readonly (string | number | bigint)[]): Set<string> {
240
203
  const supported = new Set<string>();
241
204
  for (const value of values) {
package/src/phrases.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { stripInvisible, stripMarks } from './hidden.js';
2
2
 
3
- /** Claims with no value behind them. Stored in the form `normalisePhrasing` produces. */
4
3
  export const BANNED_PHRASES: readonly string[] = [
5
4
  'cheap',
6
5
  'cheaper',
@@ -85,11 +84,6 @@ export const BANNED_PHRASES: readonly string[] = [
85
84
  'money back',
86
85
  ];
87
86
 
88
- /**
89
- * Letters that render as a Latin letter but are not one. Folded so a Cyrillic о in
90
- * "in stоck" cannot walk a claim past its own entry on the list. Both the text and
91
- * the phrase are folded, so a genuine Cyrillic phrase still matches itself.
92
- */
93
87
  const CONFUSABLES: Record<string, string> = {
94
88
  а: 'a',
95
89
  в: 'b',
@@ -122,20 +116,14 @@ const CONFUSABLES: Record<string, string> = {
122
116
  ϲ: 'c',
123
117
  };
124
118
 
125
- /** A run of whitespace holding one of these is a break the shopper sees. */
126
119
  const LINE_BREAK = /[\n\v\f\r\u0085\u2028\u2029]/;
127
120
 
128
- /** One shape for text and phrases, so a hyphen or a stray mark cannot hide a claim. */
129
121
  export function normalisePhrasing(text: string): string {
130
- // Marks come off after composition, so an accented e keeps its accent while an
131
- // overline, which composes with nothing, does not hide `free` from its own entry.
132
122
  const lowered = stripMarks(stripInvisible(text).normalize('NFKC').toLowerCase().normalize('NFC'));
133
123
 
134
124
  let folded = '';
135
125
  for (const char of lowered) folded += CONFUSABLES[char] ?? char;
136
126
 
137
- // A line break survives as itself. Matching reads straight through it, and only
138
- // the allowance in verify.ts cares, because it will not forgive across a break.
139
127
  return folded
140
128
  .replace(/[\u2018\u2019]/g, "'")
141
129
  .replace(/[-_\u2010-\u2015]+/g, ' ')
@@ -151,32 +139,24 @@ function isGlued(char: string | undefined): boolean {
151
139
 
152
140
  export interface Span {
153
141
  start: number;
154
- /** The last character of the match, not one past it. */
142
+
155
143
  end: number;
156
144
  }
157
145
 
158
146
  export interface Indexed {
159
- /** The text as it was handed in, which is what spans index and the word gap reads. */
160
147
  text: string;
161
- /** The same characters with the gaps dropped, which is what matching runs against. */
148
+
162
149
  haystack: string;
163
- /** Where each character of `haystack` really sat in `text`. */
150
+
164
151
  spots: number[];
165
152
  }
166
153
 
167
- /**
168
- * Build this once and read every phrase off it. The text must already be normalised.
169
- *
170
- * Matching runs with every space dropped, because 送料 無料 is 送料無料 to anyone and a
171
- * space is free. `spots` carries each character back to where it really sat, so the
172
- * word-gap guard still reads the neighbours the shopper sees.
173
- */
174
154
  export function indexPhrasing(text: string): Indexed {
175
155
  const tight: string[] = [];
176
156
  const spots: number[] = [];
177
157
  for (let index = 0; index < text.length; index += 1) {
178
158
  const char = text[index] ?? '';
179
- // A line break reads like a space here, so a claim split over two lines is caught.
159
+
180
160
  if (char === ' ' || char === '\n') continue;
181
161
  tight.push(char);
182
162
  spots.push(index);
@@ -185,9 +165,6 @@ export function indexPhrasing(text: string): Indexed {
185
165
  return { text, haystack: tight.join(''), spots };
186
166
  }
187
167
 
188
- /** Spans index the text the index was built from. The phrase must already be normalised. */
189
- // The word-gap rule only guards an ASCII edge, so `cheap` misses `cheapskate`
190
- // while a Japanese phrase — which has no word gaps to find — still matches.
191
168
  export function spansIn(indexed: Indexed, phrase: string): Span[] {
192
169
  const { text, haystack, spots } = indexed;
193
170
 
@@ -206,7 +183,7 @@ export function spansIn(indexed: Indexed, phrase: string): Span[] {
206
183
  const start = spots[at] ?? 0;
207
184
  const end = spots[at + needle.length - 1] ?? 0;
208
185
  const next = text[end + 1];
209
- // A trailing s is a plural, not a different word: `discount` catches `discounts`.
186
+
210
187
  const plural = next === 's' && !isGlued(text[end + 2]);
211
188
 
212
189
  const before = guardStart && isGlued(text[start - 1]);
package/src/verify.ts CHANGED
@@ -1,44 +1,37 @@
1
1
  import { numeralsIn, supportedValues, type Numeral } from './numerals.js';
2
2
  import { BANNED_PHRASES, indexPhrasing, normalisePhrasing, spansIn, type Span } from './phrases.js';
3
3
 
4
- // Two layers, reported apart, because only one of them is a proof.
5
-
6
4
  export type Layer = 'quantity' | 'wording';
7
5
 
8
6
  export interface Finding {
9
7
  layer: Layer;
10
- /** The numeral as the text wrote it, or the phrase as the denylist holds it. */
8
+
11
9
  token: string;
12
- /** Audit evidence. Names the layer and the token. */
10
+
13
11
  reason: string;
14
12
  }
15
13
 
16
14
  export interface LayerReport {
17
15
  supported: boolean;
18
- /**
19
- * What this layer's verdict is worth. `proof` means every numeral in the text is
20
- * a value the host supplied — not that the sentence around it is true.
21
- */
16
+
22
17
  strength: 'proof' | 'best-effort';
23
- /** Numerals found, or phrases screened against. Zero means nothing was checked. */
18
+
24
19
  checked: number;
25
- /** One per distinct token that failed. */
20
+
26
21
  findings: Finding[];
27
22
  }
28
23
 
29
24
  export interface VerifyResult {
30
- /** True when both layers are. */
31
25
  supported: boolean;
32
26
  quantity: LayerReport;
33
27
  wording: LayerReport;
34
28
  }
35
29
 
36
30
  export interface Facts {
37
- /** In any shape they are written: `39`, `'$1,299.00'`, `'4,8'`. Every numeral in one counts. */
38
31
  values: readonly (string | number | bigint)[];
39
- /** Claims to ban on top of the built-in English list, for the host's own language. */
32
+
40
33
  bannedPhrases?: readonly string[];
41
- /** Wording the shop stands behind. A banned claim inside one of these is not reported. */
34
+
42
35
  allowedPhrases?: readonly string[];
43
36
  }
44
37
 
@@ -48,19 +41,12 @@ export interface FieldResult {
48
41
  }
49
42
 
50
43
  export interface BatchResult {
51
- /** True when every field is, and when the fields read as one carry no banned claim. */
52
44
  supported: boolean;
53
45
  fields: FieldResult[];
54
- /** The wording layer over every field joined, so a phrase split across two fields still reads. */
46
+
55
47
  acrossFields: LayerReport;
56
48
  }
57
49
 
58
- /**
59
- * One reading per fact list, keyed on the list itself, because core hands `verify` the
60
- * same array again for every field it screens. Held against a copy of what the list
61
- * carried: `readonly` on `Facts.values` is a compile-time view of one reference, and a
62
- * host holding the array can write to it between two calls.
63
- */
64
50
  const readings = new WeakMap<object, { held: readonly unknown[]; supported: Set<string> }>();
65
51
 
66
52
  function unchanged(held: readonly unknown[], values: readonly unknown[]): boolean {
@@ -73,7 +59,7 @@ function unchanged(held: readonly unknown[], values: readonly unknown[]): boolea
73
59
 
74
60
  function valuesOf(facts: Facts): Set<string> {
75
61
  const values = facts.values;
76
- // A caller outside TypeScript can pass something with no identity to key on.
62
+
77
63
  if (!Array.isArray(values)) return supportedValues(values);
78
64
 
79
65
  const read = readings.get(values);
@@ -151,16 +137,12 @@ function checkWording(text: string, phrases: string[], allowed: string[]): Layer
151
137
  const spans: Span[] = [];
152
138
  for (const phrase of allowed) {
153
139
  for (const span of spansIn(indexed, phrase)) {
154
- // The denylist reads through a break and the allowance does not, so both rules
155
- // err toward reporting. A model that puts the negation in one block and the
156
- // claim in another has written two things the shopper reads apart.
157
140
  if (!normalised.slice(span.start, span.end + 1).includes('\n')) spans.push(span);
158
141
  }
159
142
  }
160
143
 
161
144
  const findings: Finding[] = [];
162
145
  for (const phrase of phrases) {
163
- // One hit inside allowed wording is forgiven; the same phrase elsewhere is not.
164
146
  let caught = false;
165
147
  for (const hit of spansIn(indexed, phrase)) {
166
148
  if (!standsBehind(spans, hit)) caught = true;
@@ -193,12 +175,10 @@ function check(
193
175
  return { supported: quantity.supported && wording.supported, quantity, wording };
194
176
  }
195
177
 
196
- /** Whether one piece of model-written text stands on the facts the host supplied. */
197
178
  export function verify(text: string, facts: Facts): VerifyResult {
198
179
  return check(text, valuesOf(facts), phrasesOf(facts), allowedOf(facts));
199
180
  }
200
181
 
201
- /** The same check over every field of one product, without repeating the facts. */
202
182
  export function verifyFields(fields: Record<string, string>, facts: Facts): BatchResult {
203
183
  const supported = valuesOf(facts);
204
184
  const phrases = phrasesOf(facts);
@@ -215,8 +195,6 @@ export function verifyFields(fields: Record<string, string>, facts: Facts): Batc
215
195
  written.push(text);
216
196
  }
217
197
 
218
- // A card renders its fields next to each other, so `Free` and `delivery on every
219
- // order` are one sentence to a shopper even though neither field carries one.
220
198
  const acrossFields = checkWording(written.join(' '), phrases, allowed);
221
199
 
222
200
  return { supported: allSupported && acrossFields.supported, fields: results, acrossFields };