@rudra-js/core 0.7.3 → 0.7.5

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 (55) hide show
  1. package/README.md +113 -22
  2. package/dist/claim-screening.d.ts +3 -3
  3. package/dist/claim-screening.d.ts.map +1 -1
  4. package/dist/claim-screening.js +37 -102
  5. package/dist/claim-screening.js.map +1 -1
  6. package/dist/component-generator.d.ts.map +1 -1
  7. package/dist/component-generator.js +161 -176
  8. package/dist/component-generator.js.map +1 -1
  9. package/dist/component-spec.d.ts +1 -0
  10. package/dist/component-spec.d.ts.map +1 -1
  11. package/dist/component-spec.js +3 -0
  12. package/dist/component-spec.js.map +1 -1
  13. package/dist/fallback-component.d.ts +1 -1
  14. package/dist/fallback-component.d.ts.map +1 -1
  15. package/dist/fallback-component.js +9 -17
  16. package/dist/fallback-component.js.map +1 -1
  17. package/dist/fit-to-shopper.d.ts.map +1 -1
  18. package/dist/fit-to-shopper.js +19 -19
  19. package/dist/fit-to-shopper.js.map +1 -1
  20. package/dist/model-prompt.d.ts.map +1 -1
  21. package/dist/model-prompt.js +56 -39
  22. package/dist/model-prompt.js.map +1 -1
  23. package/dist/product-selection.d.ts.map +1 -1
  24. package/dist/product-selection.js +15 -31
  25. package/dist/product-selection.js.map +1 -1
  26. package/dist/provider.d.ts.map +1 -1
  27. package/dist/provider.js.map +1 -1
  28. package/dist/reconciliation.d.ts +3 -3
  29. package/dist/reconciliation.d.ts.map +1 -1
  30. package/dist/reconciliation.js +133 -171
  31. package/dist/reconciliation.js.map +1 -1
  32. package/dist/signal-digest.d.ts +1 -1
  33. package/dist/signal-digest.d.ts.map +1 -1
  34. package/dist/signal-digest.js +51 -52
  35. package/dist/signal-digest.js.map +1 -1
  36. package/dist/spec-cache.d.ts +0 -4
  37. package/dist/spec-cache.d.ts.map +1 -1
  38. package/dist/spec-cache.js +15 -35
  39. package/dist/spec-cache.js.map +1 -1
  40. package/dist/tracking-input.d.ts.map +1 -1
  41. package/dist/tracking-input.js +16 -19
  42. package/dist/tracking-input.js.map +1 -1
  43. package/package.json +3 -3
  44. package/src/claim-screening.ts +41 -125
  45. package/src/component-generator.ts +223 -220
  46. package/src/component-spec.ts +4 -15
  47. package/src/fallback-component.ts +11 -20
  48. package/src/fit-to-shopper.ts +19 -21
  49. package/src/model-prompt.ts +55 -54
  50. package/src/product-selection.ts +21 -50
  51. package/src/provider.ts +0 -4
  52. package/src/reconciliation.ts +159 -268
  53. package/src/signal-digest.ts +50 -79
  54. package/src/spec-cache.ts +15 -48
  55. package/src/tracking-input.ts +21 -36
package/README.md CHANGED
@@ -16,7 +16,7 @@ package _is_ zod schemas, and an app on any zod 4 shares one copy with it. An
16
16
  app on zod 3 gets its own copy alongside ours, and the two don't mix: build on
17
17
  the exported schemas with their own methods (`productSchema.array()`) rather
18
18
  than your zod's (`z.array(productSchema)`), and read `error.issues` rather than
19
- checking `instanceof ZodError`. Whichever copy core ends up with writes the tool
19
+ checking `instanceof ZodError`. `@rudra-js/anthropic`'s own zod writes the tool
20
20
  schema we send the model, so its wording shifts a little between releases:
21
21
  before 4.5, a nullable field is written as `anyOf` rather than a type array. It
22
22
  means the same thing either way.
@@ -176,15 +176,6 @@ Which strings, exactly:
176
176
  - A `reason` or a `badge` sits under a named product, so it's read against that
177
177
  product's own tags and category. Every other field reads all the candidates'
178
178
  pooled.
179
- - A tag or category that is nothing but a number in exponent notation, and whose
180
- exponent runs past a thousand, is dropped here. `1e2000000000` is twelve
181
- characters that lay out into a run long enough to end the process, and
182
- `@rudra-js/attested` is a peer dependency, so the copy you have installed may
183
- be one that still tries. `@rudra-js/attested` draws its own line in the same
184
- place but measures the laid-out run rather than the exponent, so a handful of
185
- tags near the margin — `1e1000`, `1.5e1000`, `9999e998` — get past this rule
186
- and then stand behind nothing on the other side of it. Nothing that wide is a
187
- product fact either way.
188
179
 
189
180
  Two caveats worth saying out loud, because they're the shape of the check rather
190
181
  than bugs in it. Pooled means pooled: one product's `40 litre` tag stands behind
@@ -246,13 +237,14 @@ list, and not an `Accept-Language` header.
246
237
 
247
238
  ### Cohorts
248
239
 
249
- By default one generated component is shared between shoppers who look alike, and
250
- each shopper's own products are filled in per request. A cohort is the shopper's
251
- segment, the surface and slot, the locale, the item count, whether they're a
252
- first-time visitor, the category being browsed, and the category they lean
253
- towards. Everything that makes a person an individual stays out of it: who they
254
- are, what they liked, viewed or searched for. That's what lets many page views
255
- reuse one call.
240
+ By default one generated component is shared between shoppers who look alike,
241
+ and each shopper's own products are filled in per request. A cohort is the
242
+ shopper's segment, the surface and slot, the locale, the item count, whether
243
+ they have any history, the category being browsed, and the category they lean
244
+ towards. Who they are, what they searched for and the products they liked or
245
+ viewed stay out of it. For the shared cohort we work out only two things from
246
+ their own data: the category they lean towards and whether they have any
247
+ history. That's what lets many page views reuse one call.
256
248
 
257
249
  The candidate list is part of the cohort too, since the model is shown those
258
250
  products and writes about them. In most shops candidates come from the page, so
@@ -405,7 +397,8 @@ The two generation modes send different things. Cohort is the default.
405
397
  - the category being browsed (`context.currentCategory`)
406
398
  - the name of the category the shopper leans towards most. Just the name, the
407
399
  score stays behind
408
- - whether this shopper has no history at all
400
+ - whether this shopper has no history at all, meaning no likes, dislikes,
401
+ purchases, basket items or views. Searches and other interactions don't count
409
402
  - how many products the component may place (`context.maxItems`)
410
403
  - the candidate list: one line per product, with its SKU, title, category, rating
411
404
  and tags
@@ -421,7 +414,7 @@ Everything above, and:
421
414
  - purchased SKUs, and what is in the basket
422
415
  - the most-viewed SKUs, each with its view count
423
416
  - recent searches
424
- - every category they lean towards, strongest first
417
+ - up to six categories they lean towards, strongest first
425
418
  - the other kinds of interaction, each with a count
426
419
 
427
420
  ### Left out of both
@@ -499,8 +492,14 @@ for `ttlMs`, 60,000 milliseconds by default, so one minute. It holds up to
499
492
  `maxEntries`, 10,000 by default. Once it's full, the entry read longest ago is
500
493
  the first to go.
501
494
 
502
- An entry holds the generated spec and `generatedAt`, the epoch milliseconds when
503
- the model produced it. That's the whole of it. No payload, no shopper, no prompt.
495
+ An entry holds the generated spec, as the model returned it before screening,
496
+ and `generatedAt`, the epoch milliseconds when the model produced it. No payload
497
+ and no prompt. In cohort mode it holds no shopper id and everyone in the cohort
498
+ shares it, unless its segment or candidates were picked for one shopper. In
499
+ per-shopper mode the model wrote it from that shopper's signals, so its text can
500
+ repeat them, a search term for instance, and the entry is personal data until it
501
+ goes. Expiry is checked when an entry is read, so an expired one stays in memory
502
+ until it's read again or pushed out by a newer one.
504
503
 
505
504
  The port is two methods, and an optional third:
506
505
 
@@ -542,7 +541,99 @@ These are the numbers worth keeping:
542
541
  `cache: 'timeout'`, rather than as a rising bill.
543
542
  - **Spend** — sum `usage` over the events where `calledModel` is true. Requests
544
543
  that joined an in-flight generation carry the same `usage`, so summing over
545
- every event counts one call many times.
544
+ every event counts one call many times. A call the model answered but the
545
+ adapter turned down (a `max_tokens` stop, a refusal, no tool call, or a reply
546
+ that doesn't fit the schema) was billed too, and arrives as a
547
+ `'provider-error'` with no `usage`, so this sum is lower than your bill.
548
+
549
+ ## Running it in a regulated shop
550
+
551
+ This isn't legal advice, and which rules apply depends on where you sell. It's
552
+ what the package does, so you can answer what a privacy or consumer-law review
553
+ will ask.
554
+
555
+ **What leaves your server.** Nothing, unless you pass a `provider`, or a `cache`
556
+ whose store lives somewhere else. With a provider, cohort mode sends the page
557
+ and locale, the category, the segment you set, the shopper's strongest category,
558
+ whether they have any history, and the candidate products. There's no shopper id
559
+ in it, but it can still be personal data: the strongest category and the history
560
+ flag come from that shopper's data, the segment goes exactly as you wrote it,
561
+ and the candidate list is about them too if you pick or order it per shopper.
562
+ Per-shopper mode adds the shopper's own signals. Both lists are under [What the
563
+ model sees](#what-the-model-sees). `user.id` is never sent in either mode. The
564
+ provider should be your processor, under a contract that says so; for Anthropic,
565
+ see the `@rudra-js/anthropic` README.
566
+
567
+ **A shopper who objects to profiling.** Leave `signals` and `user.segment` out
568
+ of their payload, and in per-shopper mode `context.searchQuery` and
569
+ `user.isReturning` too. They get the cold-start component, built from the page
570
+ and the catalog, not from them. With those left out, `generateDeterministic(input)`
571
+ gives the same kind of component without calling the model or the cache. Called
572
+ with signals, it still personalises.
573
+
574
+ **Deleting a shopper's data.** The package keeps nothing about a shopper except
575
+ the cache. In cohort mode an entry holds no shopper id and everyone in the
576
+ cohort shares it, so usually there's no one shopper's entry to delete. The
577
+ exception is a cohort entry whose segment or candidate list was picked for one
578
+ shopper: like every per-shopper entry, it's about that shopper. Nothing maps a
579
+ shopper to their entries, so keep `ttlMs` short or pass `createNullSpecCache()`.
580
+ `onEvent` sends no shopper id, but in per-shopper mode its `key` is derived from
581
+ one, and `violations` can name SKUs from a shopper's basket or purchases, so
582
+ treat logs of it as personal data.
583
+
584
+ **Caching pages.** A personalised page says things like "Goes with your cart" and
585
+ "You looked at this recently" in its HTML. Serve those pages with
586
+ `Cache-Control: private` or `no-store`, so a shared cache never hands one
587
+ shopper's page to another.
588
+
589
+ **Telling shoppers a model wrote the copy.** The wrapper's `data-rudra-source`
590
+ says `llm` or `cache` when a model wrote the copy and `fallback` when it didn't,
591
+ so it's a machine-readable marker you can build on, for example to show a note.
592
+ Whether you must label AI-written copy, and how, depends on where you sell; in
593
+ the EU, Article 50 of the AI Act is the rule to ask about.
594
+
595
+ **Claims.** The copy on your page is your own advertising, whoever wrote it. The
596
+ screen catches prices, discounts, stock, ratings, delivery, urgency and generic
597
+ environmental claims, but it's a list, and rewordings get through (SECURITY.md
598
+ explains why, and the tests list the known ones). Anything it misses is still
599
+ yours to stand behind.
600
+
601
+ **Children.** In the UK, the ICO's Children's Code covers any shop that
602
+ under-18s are more likely than not to use, not only one aimed at them. Its
603
+ standard 12 asks for profiling to be off by default unless you can show a
604
+ compelling reason, judged by the child's best interests, and for safeguards when
605
+ it's on. For a shop like that, don't use per-shopper mode. For a shopper under
606
+ 18, leave `signals` and `user.segment` out of their payload unless they've
607
+ switched personalisation on, as for a shopper who objects to profiling. If you
608
+ can't tell a shopper's age with confidence that fits the risk, treat every
609
+ shopper as under 18. Cohort mode alone doesn't turn profiling off: the default
610
+ `rank: 'signals'` still orders products by the shopper's signals in cohort mode
611
+ and in the fallback component, even with no provider, and cohort mode still
612
+ sends their strongest category, whether they have any history, and any segment
613
+ to the model.
614
+
615
+ **Marketplaces.** If other sellers list products on your site, EU platform rules
616
+ (Article 27 of the Digital Services Act) ask you, unless you're a micro or small
617
+ business, to set out in your terms your recommender's main parameters, why they
618
+ weigh as they do, and any options shoppers have to change them. If shoppers can
619
+ pick between options, such as switching personalisation off, the control has to
620
+ sit where the products are shown. Which parameters apply depends on your mode
621
+ and your `rank` setting. `rank` sets the order in cohort mode and in the
622
+ fallback component. With `rank: 'given'` that's the order you sent, so describe
623
+ your own ranking. With `rank: 'signals'` it comes from the shopper's category
624
+ interest (the category they're browsing counts too), how often they've viewed a
625
+ product, its rating, and tags it shares with products they've engaged with.
626
+ Category weighs most. In per-shopper mode the model picks which candidates to
627
+ show and in what order, from what's under
628
+ [What the model sees](#what-the-model-sees). In cohort mode the model picks the
629
+ hero's product, from the cohort list there. In every mode, anything out of stock, disliked or
630
+ being looked at right now is left out, and so is anything bought or in the
631
+ basket, except as part of a bundle. A bundle is picked by what's in the basket,
632
+ then what they viewed, then the category being browsed.
633
+
634
+ **Accessibility.** Every card is one link with a readable name, and headings
635
+ only render when they have text. Contrast, focus styles and layout come from
636
+ your stylesheet, so those are yours to check.
546
637
 
547
638
  ## Licence
548
639
 
@@ -1,10 +1,10 @@
1
- import type { Product, TrackingInput } from './tracking-input.js';
1
+ import type { TrackingInput } from './tracking-input.js';
2
2
  export declare const ALLOWED_PHRASES: string[];
3
- export declare function productFacts(product: Product): string[];
3
+ export declare const NO_FACTS: readonly string[];
4
4
  export interface HostFacts {
5
5
  pooled: string[];
6
6
  bySku: Map<string, string[]>;
7
7
  }
8
8
  export declare function hostFacts(input: TrackingInput): HostFacts;
9
- export declare function claimIn(text: string): string | null;
9
+ export declare function unbackedClaim(raw: string, clamped: string, facts: readonly string[]): string | null;
10
10
  //# sourceMappingURL=claim-screening.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"claim-screening.d.ts","sourceRoot":"","sources":["../src/claim-screening.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAmKlE,eAAO,MAAM,eAAe,UAAkE,CAAC;AAc/F,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAOvD;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAWzD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWnD"}
1
+ {"version":3,"file":"claim-screening.d.ts","sourceRoot":"","sources":["../src/claim-screening.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA4EzD,eAAO,MAAM,eAAe,UAK3B,CAAC;AAKF,eAAO,MAAM,QAAQ,EAAE,SAAS,MAAM,EAAO,CAAC;AAE9C,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAWzD;AAaD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,MAAM,GAAG,IAAI,CASf"}
@@ -1,3 +1,4 @@
1
+ import { foldLookalikes, verify } from '@rudra-js/attested';
1
2
  import { offeredCandidates } from './model-prompt.js';
2
3
  const CLAIM_PATTERNS = [
3
4
  {
@@ -10,7 +11,8 @@ const CLAIM_PATTERNS = [
10
11
  /\bratings?\s+of\s+[0-5]\.\d\b/,
11
12
  /\b[0-5](?:\.\d)?\s+out of\s+(?:5|five)\b/,
12
13
  /\b(?:highly|top|best|well|poorly|five|four)[\s-]rated\b/,
13
- /\brated\s+(?:[0-5]\.\d|(?:[0-5]|three|four|five)\s+(?:stars?|out of))\b/,
14
+ /\brated\s+[0-5]\.\d\b/,
15
+ /\brated\s+(?:[0-5]|three|four|five)\s+(?:stars?|out of)\b/,
14
16
  /\bbest[\s-]?sell(?:er|ers|ing)\b/,
15
17
  /(?:\bnumber one|\bno\.? ?1|#\s?1)[\s-]?sell(?:er|ers|ing)\b/,
16
18
  /\bloved by (?:thousands|hundreds|millions|\d)/,
@@ -43,7 +45,10 @@ const CLAIM_PATTERNS = [
43
45
  /\bhalf[\s-]?(?:price|off)\b/,
44
46
  /\bsaves?\s+\d[\d.,]*\s+off\b/,
45
47
  /\bclearance\s+(?:sale|price|event|deal)\b|\bon clearance\b/,
46
- /\bprice reduced\b|\breduced price\b|\breduced by \d+\s?(?:%|percent)|\breduced\s+(?:this|next|last)\s+week\b/,
48
+ /\bprice reduced\b/,
49
+ /\breduced price\b/,
50
+ /\breduced by \d+\s?(?:%|percent)/,
51
+ /\breduced\s+(?:this|next|last)\s+week\b/,
47
52
  ],
48
53
  },
49
54
  {
@@ -63,121 +68,51 @@ const CLAIM_PATTERNS = [
63
68
  /\blimited stock\b/,
64
69
  /\brestocked?\b|\bsold out\b/,
65
70
  /\b(?:only\s+)?(?:\d+|a few|a handful|a couple|one|few)\s+(?:left|remain(?:s|ing)?)\b/,
71
+ /\bonly\s+(?:two|three|four|five|six|seven|eight|nine|ten)\s+(?:left|remain(?:s|ing)?)\b/,
66
72
  /\bselling fast\b|\b(?:almost|nearly) gone\b|\bwhile stocks last\b/,
67
73
  ],
68
74
  },
69
75
  ];
70
- const CONFUSABLES = {
71
- а: 'a',
72
- в: 'b',
73
- е: 'e',
74
- к: 'k',
75
- м: 'm',
76
- н: 'h',
77
- о: 'o',
78
- р: 'p',
79
- с: 'c',
80
- т: 't',
81
- х: 'x',
82
- у: 'y',
83
- ѕ: 's',
84
- і: 'i',
85
- ј: 'j',
86
- ԁ: 'd',
87
- һ: 'h',
88
- ӏ: 'l',
89
- α: 'a',
90
- ε: 'e',
91
- ι: 'i',
92
- κ: 'k',
93
- ν: 'v',
94
- ο: 'o',
95
- ρ: 'p',
96
- τ: 't',
97
- υ: 'u',
98
- χ: 'x',
99
- ϳ: 'j',
100
- ѵ: 'v',
101
- };
102
- // Capitals that lowercasing reads wrong: Ν becomes ν, which reads as v.
103
- const CAPITALS = {
104
- Β: 'b',
105
- Ζ: 'z',
106
- Η: 'h',
107
- Μ: 'm',
108
- Ν: 'n',
109
- Υ: 'y',
110
- Ү: 'y',
111
- Ӏ: 'i',
112
- Ԛ: 'q',
113
- Ԝ: 'w',
114
- Ϻ: 'm',
115
- Ϝ: 'f',
116
- Ԍ: 'g',
117
- };
118
- const INVISIBLE = /[\p{Cf}\p{Default_Ignorable_Code_Point}\p{Cc}]/gu;
119
- const SPACING = /[\t\n\v\f\r\u0085]/;
120
- const MARKS = /[\p{Mn}\p{Me}]/gu;
121
- function normaliseForClaims(text, byLook) {
122
- // NFKC would turn the lunate sigmas into ς and Σ, which the fold can't read as c.
123
- const compatible = text
124
- .replace(INVISIBLE, (char) => (SPACING.test(char) ? char : ''))
125
- .replace(/[ϲϹ]/g, 'c')
126
- .normalize('NFKC');
127
- let cased = compatible;
128
- if (byLook) {
129
- cased = '';
130
- for (const char of compatible)
131
- cased += CAPITALS[char] ?? char;
132
- }
133
- const lowered = cased.toLowerCase().normalize('NFC').replace(MARKS, '');
134
- let folded = '';
135
- for (const char of lowered)
136
- folded += CONFUSABLES[char] ?? char;
137
- return folded;
138
- }
139
- export const ALLOWED_PHRASES = ['does not feel cheap', "doesn't feel cheap", 'last few miles'];
140
- const DIGIT = /\p{Nd}/u;
141
- const BARE_EXPONENT = /^[+-]?\d+(?:\.\d+)?[eE]([+-]?\d+)$/;
142
- const MAX_EXPONENT = 1000;
143
- function canLayOut(fact) {
144
- const match = BARE_EXPONENT.exec(fact.trim());
145
- if (match === null)
146
- return true;
147
- return Math.abs(Number(match[1] ?? '')) <= MAX_EXPONENT;
148
- }
149
- export function productFacts(product) {
150
- const facts = [];
151
- if (DIGIT.test(product.category) && canLayOut(product.category))
152
- facts.push(product.category);
153
- for (const tag of product.tags) {
154
- if (DIGIT.test(tag) && canLayOut(tag))
155
- facts.push(tag);
156
- }
157
- return facts;
158
- }
76
+ export const ALLOWED_PHRASES = [
77
+ 'does not feel cheap',
78
+ "doesn't feel cheap",
79
+ 'last few miles',
80
+ 'sustainable pace',
81
+ ];
82
+ const FACT_DIGIT = /\p{Nd}/u;
83
+ const NUMERAL = /[\p{Nd}\p{No}\p{Nl}]/u;
84
+ export const NO_FACTS = [];
159
85
  export function hostFacts(input) {
160
86
  const pooled = new Set();
161
87
  const bySku = new Map();
162
88
  for (const product of offeredCandidates(input)) {
163
- const own = productFacts(product);
89
+ const own = [product.category, ...product.tags].filter((text) => FACT_DIGIT.test(text));
164
90
  bySku.set(product.sku, own);
165
91
  for (const fact of own)
166
92
  pooled.add(fact);
167
93
  }
168
94
  return { pooled: [...pooled], bySku };
169
95
  }
170
- export function claimIn(text) {
171
- // Read twice, since Ν reads as N but ν as v.
172
- for (const byLook of [false, true]) {
173
- const normalised = normaliseForClaims(text, byLook);
174
- for (const claim of CLAIM_PATTERNS) {
175
- for (const pattern of claim.patterns) {
176
- if (pattern.test(normalised))
177
- return claim.kind;
178
- }
179
- }
96
+ function kindIn(reading) {
97
+ for (const claim of CLAIM_PATTERNS) {
98
+ if (claim.patterns.some((pattern) => pattern.test(reading)))
99
+ return claim.kind;
180
100
  }
181
101
  return null;
182
102
  }
103
+ function claimIn(text) {
104
+ return kindIn(foldLookalikes(text)) ?? kindIn(foldLookalikes(text, true));
105
+ }
106
+ export function unbackedClaim(raw, clamped, facts) {
107
+ const kind = claimIn(raw) ?? claimIn(clamped);
108
+ if (kind !== null)
109
+ return kind;
110
+ const values = NUMERAL.test(clamped) ? facts : NO_FACTS;
111
+ const result = verify(clamped, { values, allowedPhrases: ALLOWED_PHRASES });
112
+ if (!result.quantity.supported)
113
+ return 'quantity';
114
+ if (!result.wording.supported)
115
+ return 'wording';
116
+ return null;
117
+ }
183
118
  //# sourceMappingURL=claim-screening.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"claim-screening.js","sourceRoot":"","sources":["../src/claim-screening.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,MAAM,cAAc,GAA2C;IAC7D;QACE,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE;YACR,2BAA2B;YAC3B,mDAAmD;YACnD,0BAA0B;YAE1B,kEAAkE;YAElE,+BAA+B;YAE/B,0CAA0C;YAC1C,yDAAyD;YAEzD,yEAAyE;YAEzE,kCAAkC;YAElC,6DAA6D;YAC7D,+CAA+C;SAChD;KACF;IACD;QACE,IAAI,EAAE,OAAO;QACb,QAAQ,EAAE;YACR,SAAS;YACT,6DAA6D;YAE7D,yBAAyB;YAEzB,wBAAwB;YAExB,qBAAqB;YACrB,yBAAyB;YAEzB,6DAA6D;YAE7D,qBAAqB;YACrB,4BAA4B;YAE5B,2DAA2D;YAC3D,yBAAyB;YAEzB,kDAAkD;SACnD;KACF;IACD;QACE,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE;YACR,qEAAqE;YACrE,iGAAiG;YACjG,uBAAuB;YACvB,sCAAsC;YACtC,6BAA6B;YAE7B,8BAA8B;YAE9B,4DAA4D;YAE5D,8GAA8G;SAC/G;KACF;IACD;QACE,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE;YACR,2BAA2B;YAC3B,iDAAiD;YACjD,kMAAkM;YAClM,qDAAqD;YACrD,iBAAiB;SAClB;KACF;IACD;QACE,IAAI,EAAE,OAAO;QACb,QAAQ,EAAE;YACR,gCAAgC;YAChC,mBAAmB;YACnB,6BAA6B;YAE7B,sFAAsF;YACtF,mEAAmE;SACpE;KACF;CACF,CAAC;AAEF,MAAM,WAAW,GAA2B;IAC1C,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;CACP,CAAC;AAEF,wEAAwE;AACxE,MAAM,QAAQ,GAA2B;IACvC,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;CACP,CAAC;AAEF,MAAM,SAAS,GAAG,kDAAkD,CAAC;AAErE,MAAM,OAAO,GAAG,oBAAoB,CAAC;AAErC,MAAM,KAAK,GAAG,kBAAkB,CAAC;AAEjC,SAAS,kBAAkB,CAAC,IAAY,EAAE,MAAe;IACvD,kFAAkF;IAClF,MAAM,UAAU,GAAG,IAAI;SACpB,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;SAC9D,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,SAAS,CAAC,MAAM,CAAC,CAAC;IAErB,IAAI,KAAK,GAAG,UAAU,CAAC;IACvB,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,GAAG,EAAE,CAAC;QACX,KAAK,MAAM,IAAI,IAAI,UAAU;YAAE,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IACjE,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAExE,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,OAAO;QAAE,MAAM,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IAChE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,qBAAqB,EAAE,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;AAE/F,MAAM,KAAK,GAAG,SAAS,CAAC;AAExB,MAAM,aAAa,GAAG,oCAAoC,CAAC;AAE3D,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,YAAY,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9F,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAOD,MAAM,UAAU,SAAS,CAAC,KAAoB;IAC5C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE1C,KAAK,MAAM,OAAO,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QAClC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC5B,KAAK,MAAM,IAAI,IAAI,GAAG;YAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,6CAA6C;IAC7C,KAAK,MAAM,MAAM,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACpD,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;YACnC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACrC,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;oBAAE,OAAO,KAAK,CAAC,IAAI,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"claim-screening.js","sourceRoot":"","sources":["../src/claim-screening.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"component-generator.d.ts","sourceRoot":"","sources":["../src/component-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAkB,KAAK,SAAS,EAAoB,MAAM,wBAAwB,CAAC;AAG1F,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IAEnB,SAAS,EAAE,MAAM,CAAC;IAElB,WAAW,EAAE,OAAO,CAAC;IAErB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CAC9C;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAEpC,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,UAAU,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;IAEtC,IAAI,CAAC,EAAE,SAAS,CAAC;IAEjB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAE5D,qBAAqB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CAAC;CACjE;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAG9C;CACF;AAkGD,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,yBAA8B,GACtC,kBAAkB,CA2LpB"}
1
+ {"version":3,"file":"component-generator.d.ts","sourceRoot":"","sources":["../src/component-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGxE,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CAC9C;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACpC,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;IACtC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5D,qBAAqB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CAAC;CACjE;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAG9C;CACF;AAuGD,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,yBAA8B,GACtC,kBAAkB,CAgBpB"}