@rudra-js/core 0.5.0 → 0.7.0

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 (67) hide show
  1. package/README.md +64 -16
  2. package/dist/claim-screening.d.ts +10 -0
  3. package/dist/claim-screening.d.ts.map +1 -0
  4. package/dist/claim-screening.js +157 -0
  5. package/dist/claim-screening.js.map +1 -0
  6. package/dist/component-generator.d.ts +0 -70
  7. package/dist/component-generator.d.ts.map +1 -1
  8. package/dist/component-generator.js +27 -135
  9. package/dist/component-generator.js.map +1 -1
  10. package/dist/component-spec.d.ts +4 -159
  11. package/dist/component-spec.d.ts.map +1 -1
  12. package/dist/component-spec.js +0 -92
  13. package/dist/component-spec.js.map +1 -1
  14. package/dist/fallback-component.d.ts +0 -6
  15. package/dist/fallback-component.d.ts.map +1 -1
  16. package/dist/fallback-component.js +0 -21
  17. package/dist/fallback-component.js.map +1 -1
  18. package/dist/fit-to-shopper.d.ts +5 -1
  19. package/dist/fit-to-shopper.d.ts.map +1 -1
  20. package/dist/fit-to-shopper.js +5 -14
  21. package/dist/fit-to-shopper.js.map +1 -1
  22. package/dist/index.d.ts +1 -8
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +0 -7
  25. package/dist/index.js.map +1 -1
  26. package/dist/model-prompt.d.ts +1 -30
  27. package/dist/model-prompt.d.ts.map +1 -1
  28. package/dist/model-prompt.js +19 -78
  29. package/dist/model-prompt.js.map +1 -1
  30. package/dist/product-selection.d.ts +0 -29
  31. package/dist/product-selection.d.ts.map +1 -1
  32. package/dist/product-selection.js +1 -22
  33. package/dist/product-selection.js.map +1 -1
  34. package/dist/provider.d.ts +0 -55
  35. package/dist/provider.d.ts.map +1 -1
  36. package/dist/provider.js +0 -12
  37. package/dist/provider.js.map +1 -1
  38. package/dist/reconciliation.d.ts +6 -61
  39. package/dist/reconciliation.d.ts.map +1 -1
  40. package/dist/reconciliation.js +76 -466
  41. package/dist/reconciliation.js.map +1 -1
  42. package/dist/signal-digest.d.ts +1 -18
  43. package/dist/signal-digest.d.ts.map +1 -1
  44. package/dist/signal-digest.js +27 -66
  45. package/dist/signal-digest.js.map +1 -1
  46. package/dist/spec-cache.d.ts +8 -70
  47. package/dist/spec-cache.d.ts.map +1 -1
  48. package/dist/spec-cache.js +5 -68
  49. package/dist/spec-cache.js.map +1 -1
  50. package/dist/tracking-input.d.ts +0 -49
  51. package/dist/tracking-input.d.ts.map +1 -1
  52. package/dist/tracking-input.js +5 -105
  53. package/dist/tracking-input.js.map +1 -1
  54. package/package.json +7 -6
  55. package/src/claim-screening.ts +190 -0
  56. package/src/component-generator.ts +45 -240
  57. package/src/component-spec.ts +17 -118
  58. package/src/fallback-component.ts +0 -22
  59. package/src/fit-to-shopper.ts +12 -16
  60. package/src/index.ts +1 -8
  61. package/src/model-prompt.ts +25 -84
  62. package/src/product-selection.ts +6 -52
  63. package/src/provider.ts +4 -60
  64. package/src/reconciliation.ts +104 -499
  65. package/src/signal-digest.ts +34 -83
  66. package/src/spec-cache.ts +10 -97
  67. package/src/tracking-input.ts +17 -111
@@ -1,96 +1,22 @@
1
1
  import { z } from 'zod';
2
- /**
3
- * The component specification — the only thing a language model is ever allowed
4
- * to return.
5
- *
6
- * The model chooses layout, ordering, emphasis and copy. Which products to show
7
- * and why is its call only in per-shopper mode, apart from the product a hero
8
- * names. It never returns markup, code, URLs, prices, product titles, or
9
- * images. Every field is an enum, a bounded number, a SKU reference resolved
10
- * against the candidate set, or free text that is clamped and escaped before it
11
- * renders. That is what makes generated output safe to put in a server-rendered
12
- * response.
13
- *
14
- * Two constraints shape this file, and both are easy to undo by accident:
15
- *
16
- * 1. It doubles as a provider structured-output schema, so it avoids
17
- * recursion, string length bounds, and numeric ranges. Providers reject
18
- * schemas that use them in strict mode. Bounds are enforced afterwards, in
19
- * reconciliation, where a violation can be repaired rather than fatal.
20
- * 2. Optionality is `.nullable()` rather than `.optional()`, because strict
21
- * structured outputs require every declared property to be present. "No
22
- * badge" has to be expressible as `null`, not as a missing key.
23
- *
24
- * A test asserts both by converting this schema to JSON Schema, so neither is
25
- * left to a comment nobody reads.
26
- *
27
- * Note the asymmetry with `tracking-input`, which rejects unknown fields: these
28
- * schemas strip them. The inputs differ in kind. An unrecognised key in a host
29
- * payload is a typo, and silently dropping it costs the shopper their history,
30
- * so it must be loud. An unrecognised key from a model is drift, and discarding
31
- * it is exactly right — rejecting the whole spec over one stray key would cost
32
- * the shopper the component. Either way the renderer only ever sees the fields
33
- * declared here, which is the property that matters.
34
- */
35
- /** Overall voice of the component. */
36
2
  export const TONES = ['neutral', 'enthusiastic', 'urgent', 'editorial'];
37
- /** What a banner is claiming. */
38
3
  export const BANNER_TONES = ['info', 'promo', 'urgency', 'restock'];
39
- /** How prominently a single product is placed. */
40
4
  export const EMPHASIS = ['normal', 'featured'];
41
- /**
42
- * Why a product was chosen — the recommendation strategy behind the pick.
43
- *
44
- * This is a closed set rather than free text because every value here is a
45
- * factual claim about the shopper, and a claim the server can check. "Because
46
- * you viewed this" written as prose is unverifiable: nothing downstream can
47
- * tell whether the shopper viewed anything. Declared as `most_viewed`,
48
- * reconciliation can look it up in the digest and drop the claim if it is
49
- * false.
50
- *
51
- * Every value is deliberately checkable against data already in hand — the
52
- * signal digest or the host's own catalog. A basis that needs data the
53
- * framework never receives (`new_arrival`, `back_in_stock`, `trending_today`)
54
- * is not in this list, because it could only ever be taken on trust.
55
- */
56
5
  export const RECOMMENDATION_BASES = [
57
- /** Same category as the product being viewed. */
58
6
  'similar_to_current',
59
- /** The shopper has viewed this product. */
60
7
  'most_viewed',
61
- /** Goes with something already in the cart. */
62
8
  'complements_cart',
63
- /** Goes with something the shopper has bought. */
64
9
  'complements_purchase',
65
- /** In a category the shopper's signals favour. */
66
10
  'liked_category',
67
- /** No claim about this shopper at all — the safe default. */
68
11
  'popular',
69
12
  ];
70
- /** A reference to one product from the candidate set, and why it was chosen. */
71
13
  export const productReferenceSchema = z.object({
72
- /** Must be a SKU the host supplied in `TrackingInput.candidates`. */
73
14
  sku: z.string(),
74
- /**
75
- * The strategy behind this pick. Reconciliation checks it against the
76
- * shopper's signals, so the model cannot assert a relationship that is not
77
- * there.
78
- */
79
15
  basis: z.enum(RECOMMENDATION_BASES),
80
- /**
81
- * How the basis is phrased for the shopper, e.g. "Pairs with the boots you
82
- * bought". Free text, but it has to be consistent with `basis`, which is not.
83
- *
84
- * Nullable because reconciliation clears it when it cannot verify the basis:
85
- * a pick may still be worth showing when the stated reason for it is not
86
- * true, but the prose asserting that reason must not render.
87
- */
88
16
  reason: z.string().nullable(),
89
- /** Short accent label, e.g. "Worth a look". Null when nothing warrants one. */
90
17
  badge: z.string().nullable(),
91
18
  emphasis: z.enum(EMPHASIS),
92
19
  });
93
- /** One large featured statement, optionally anchored to a single product. */
94
20
  const heroBlockSchema = z.object({
95
21
  kind: z.literal('hero'),
96
22
  headline: z.string(),
@@ -98,42 +24,35 @@ const heroBlockSchema = z.object({
98
24
  sku: z.string().nullable(),
99
25
  ctaLabel: z.string().nullable(),
100
26
  });
101
- /** The general-purpose choice when several products are comparably relevant. */
102
27
  const gridBlockSchema = z.object({
103
28
  kind: z.literal('grid'),
104
29
  title: z.string().nullable(),
105
30
  columns: z.union([z.literal(2), z.literal(3), z.literal(4)]),
106
31
  items: z.array(productReferenceSchema),
107
32
  });
108
- /** A horizontally scanned row, for when order implies a ranking. */
109
33
  const carouselBlockSchema = z.object({
110
34
  kind: z.literal('carousel'),
111
35
  title: z.string().nullable(),
112
36
  items: z.array(productReferenceSchema),
113
37
  });
114
- /** A single line of merchandising copy. */
115
38
  const bannerBlockSchema = z.object({
116
39
  kind: z.literal('banner'),
117
40
  tone: z.enum(BANNER_TONES),
118
41
  text: z.string(),
119
42
  ctaLabel: z.string().nullable(),
120
43
  });
121
- /** Short editorial prose, for explaining the theme of a selection. */
122
44
  const copyBlockSchema = z.object({
123
45
  kind: z.literal('copy'),
124
46
  title: z.string().nullable(),
125
47
  body: z.string(),
126
48
  });
127
- /** A set the shop sells together. The shop picks which one; this only asks for it. */
128
49
  const bundleBlockSchema = z.object({
129
50
  kind: z.literal('bundle'),
130
51
  title: z.string().nullable(),
131
52
  body: z.string().nullable(),
132
53
  ctaLabel: z.string().nullable(),
133
- // Filled in per request. Whatever the model puts here is thrown away.
134
54
  bundleId: z.string().nullable(),
135
55
  });
136
- /** Blocks do not nest. The vocabulary is closed on purpose. */
137
56
  export const blockSchema = z.discriminatedUnion('kind', [
138
57
  heroBlockSchema,
139
58
  gridBlockSchema,
@@ -142,28 +61,17 @@ export const blockSchema = z.discriminatedUnion('kind', [
142
61
  copyBlockSchema,
143
62
  bundleBlockSchema,
144
63
  ]);
145
- /**
146
- * Exactly what the model must return. Provenance — version, slot, latency, which
147
- * provider answered — is added by the server afterwards, and is deliberately not
148
- * part of the model's burden.
149
- */
150
64
  export const generatedSpecSchema = z.object({
151
65
  tone: z.enum(TONES),
152
66
  headline: z.string(),
153
67
  subheadline: z.string().nullable(),
154
68
  blocks: z.array(blockSchema),
155
- /**
156
- * One sentence on why this arrangement was chosen. For engineers reading
157
- * generation logs, not for shoppers; it is not rendered by default.
158
- */
159
69
  rationale: z.string(),
160
70
  });
161
71
  export const SPEC_VERSION = '1';
162
- /** Throws a `ZodError` if the value is not a well-formed generated spec. */
163
72
  export function parseGeneratedSpec(value) {
164
73
  return generatedSpecSchema.parse(value);
165
74
  }
166
- /** Non-throwing variant, for validating untrusted model output. */
167
75
  export function safeParseGeneratedSpec(value) {
168
76
  return generatedSpecSchema.safeParse(value);
169
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"component-spec.js","sourceRoot":"","sources":["../src/component-spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"component-spec.js","sourceRoot":"","sources":["../src/component-spec.ts"],"names":[],"mappings":"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"}
@@ -2,11 +2,5 @@ import type { GeneratedSpec } from './component-spec.js';
2
2
  import type { SignalDigest } from './signal-digest.js';
3
3
  import { type SelectOptions } from './product-selection.js';
4
4
  import type { TrackingInput } from './tracking-input.js';
5
- /**
6
- * Builds a renderable spec from signals alone. Never throws. Returns a spec with
7
- * no blocks only when there is genuinely nothing in stock left to show, which
8
- * the renderer treats as "render nothing" — an empty recommendation region is
9
- * worse than none.
10
- */
11
5
  export declare function buildFallbackSpec(input: TrackingInput, digest: SignalDigest, options?: SelectOptions): GeneratedSpec;
12
6
  //# sourceMappingURL=fallback-component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fallback-component.d.ts","sourceRoot":"","sources":["../src/fallback-component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAoB,MAAM,qBAAqB,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAkB,KAAK,aAAa,EAAoB,MAAM,wBAAwB,CAAC;AAC9F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAkDzD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAiBf"}
1
+ {"version":3,"file":"fallback-component.d.ts","sourceRoot":"","sources":["../src/fallback-component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAoB,MAAM,qBAAqB,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAkB,KAAK,aAAa,EAAoB,MAAM,wBAAwB,CAAC;AAC9F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAkCzD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CAiBf"}
@@ -1,18 +1,4 @@
1
1
  import { selectProducts } from './product-selection.js';
2
- /**
3
- * The deterministic component — what renders when no model does.
4
- *
5
- * The manuscript names data latency as the central risk of moving
6
- * personalisation onto the server path: any delay in the recommendation engine
7
- * blocks the page. This module is the answer. It is pure, synchronous, and
8
- * cannot fail, so the server always has something correct to render — whether
9
- * the model is slow, erroring, rate-limited, or simply not configured.
10
- *
11
- * It reads the same digest and uses the same selector the model path does, so a
12
- * degraded render is a weaker version of the same decision rather than an
13
- * unrelated one. Only the presentation is fixed.
14
- */
15
- /** A featured lead only reads as deliberate when something follows it. */
16
2
  const MIN_PICKS_FOR_A_FEATURED_LEAD = 3;
17
3
  function headlineFor(digest) {
18
4
  if (digest.isColdStart) {
@@ -27,7 +13,6 @@ function headlineFor(digest) {
27
13
  }
28
14
  return { headline: 'You might also like', subheadline: null };
29
15
  }
30
- /** Wide enough to fill, never wider. */
31
16
  function columnsFor(itemCount) {
32
17
  if (itemCount >= 4)
33
18
  return 4;
@@ -44,12 +29,6 @@ function toProductReference(pick, index, total) {
44
29
  emphasis: index === 0 && total >= MIN_PICKS_FOR_A_FEATURED_LEAD ? 'featured' : 'normal',
45
30
  };
46
31
  }
47
- /**
48
- * Builds a renderable spec from signals alone. Never throws. Returns a spec with
49
- * no blocks only when there is genuinely nothing in stock left to show, which
50
- * the renderer treats as "render nothing" — an empty recommendation region is
51
- * worse than none.
52
- */
53
32
  export function buildFallbackSpec(input, digest, options = {}) {
54
33
  const picks = selectProducts(input, digest, options).slice(0, digest.maxItems);
55
34
  const { headline, subheadline } = headlineFor(digest);
@@ -1 +1 @@
1
- {"version":3,"file":"fallback-component.js","sourceRoot":"","sources":["../src/fallback-component.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAwC,MAAM,wBAAwB,CAAC;AAG9F;;;;;;;;;;;;GAYG;AAEH,0EAA0E;AAC1E,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAExC,SAAS,WAAW,CAAC,MAAoB;IACvC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAChE,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;IACzD,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,WAAW,EAAE,EAAE,CAAC;IACjF,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAChE,CAAC;AAED,wCAAwC;AACxC,SAAS,UAAU,CAAC,SAAiB;IACnC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7B,IAAI,SAAS,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAiB,EAAE,KAAa,EAAE,KAAa;IACzE,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,KAAK,KAAK,CAAC,IAAI,KAAK,IAAI,6BAA6B,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ;KACxF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAoB,EACpB,MAAoB,EACpB,OAAO,GAAkB,EAAE;IAE3B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/E,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAExF,OAAO;QACL,IAAI,EAAE,SAAS;QACf,QAAQ;QACR,WAAW;QACX,MAAM,EACJ,KAAK,CAAC,MAAM,KAAK,CAAC;YAChB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;QAC/E,SAAS,EAAE,MAAM,CAAC,WAAW;YAC3B,CAAC,CAAC,oEAAoE;YACtE,CAAC,CAAC,8EAA8E;KACnF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"fallback-component.js","sourceRoot":"","sources":["../src/fallback-component.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAwC,MAAM,wBAAwB,CAAC;AAG9F,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAExC,SAAS,WAAW,CAAC,MAAoB;IACvC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAChE,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;IACzD,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,WAAW,EAAE,EAAE,CAAC;IACjF,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAChE,CAAC;AAED,SAAS,UAAU,CAAC,SAAiB;IACnC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7B,IAAI,SAAS,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAiB,EAAE,KAAa,EAAE,KAAa;IACzE,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,KAAK,KAAK,CAAC,IAAI,KAAK,IAAI,6BAA6B,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ;KACxF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAoB,EACpB,MAAoB,EACpB,OAAO,GAAkB,EAAE;IAE3B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/E,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAExF,OAAO;QACL,IAAI,EAAE,SAAS;QACf,QAAQ;QACR,WAAW;QACX,MAAM,EACJ,KAAK,CAAC,MAAM,KAAK,CAAC;YAChB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;QAC/E,SAAS,EAAE,MAAM,CAAC,WAAW;YAC3B,CAAC,CAAC,oEAAoE;YACtE,CAAC,CAAC,8EAA8E;KACnF,CAAC;AACJ,CAAC"}
@@ -1,4 +1,8 @@
1
1
  import type { GeneratedSpec } from './component-spec.js';
2
2
  import type { ProductPick } from './product-selection.js';
3
- export declare function fitToShopper(spec: GeneratedSpec, picks: readonly ProductPick[], maxItems: number, ourReasons?: Map<string, string>): GeneratedSpec;
3
+ export interface FittedSpec {
4
+ spec: GeneratedSpec;
5
+ ourReasons: Map<string, string>;
6
+ }
7
+ export declare function fitToShopper(spec: GeneratedSpec, picks: readonly ProductPick[], maxItems: number): FittedSpec;
4
8
  //# sourceMappingURL=fit-to-shopper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fit-to-shopper.d.ts","sourceRoot":"","sources":["../src/fit-to-shopper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,aAAa,EAAoB,MAAM,qBAAqB,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAI1D,wBAAgB,YAAY,CAC1B,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,aAAa,CAsCf"}
1
+ {"version":3,"file":"fit-to-shopper.d.ts","sourceRoot":"","sources":["../src/fit-to-shopper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,aAAa,EAAoB,MAAM,qBAAqB,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,QAAQ,EAAE,MAAM,GACf,UAAU,CAgCZ"}
@@ -1,29 +1,20 @@
1
- // The model picks the shape. Selection picks the grid and carousel products and
2
- // what may be said about them, so a shared component still fits one shopper.
3
- export function fitToShopper(spec, picks, maxItems, ourReasons) {
4
- // Picks are ordered best first. Every slot takes the next one.
1
+ export function fitToShopper(spec, picks, maxItems) {
5
2
  let next = 0;
6
3
  const limit = Math.min(picks.length, maxItems);
4
+ const ourReasons = new Map();
7
5
  const blocks = [];
8
6
  for (const block of spec.blocks) {
9
- // A hero is left alone. Its headline and body were written about the
10
- // product it names, so swapping the product would leave copy that describes
11
- // something else. Reconciliation drops the link if this shopper cannot see
12
- // that product, and the words stay.
13
7
  if (block.kind === 'grid' || block.kind === 'carousel') {
14
8
  const items = [];
15
9
  for (const item of block.items) {
16
10
  if (next >= limit)
17
- break; // shrink, never pad
11
+ break;
18
12
  const chosen = picks[next];
19
- // Every reason here is written by this library or handed to it by the
20
- // shop. None of them is the model's, whatever the model wrote in this slot.
21
- ourReasons?.set(chosen.product.sku, chosen.reason);
13
+ ourReasons.set(chosen.product.sku, chosen.reason);
22
14
  items.push({
23
15
  sku: chosen.product.sku,
24
16
  basis: chosen.basis,
25
17
  reason: chosen.reason,
26
- // The badge was written about a different product.
27
18
  badge: null,
28
19
  emphasis: item.emphasis,
29
20
  });
@@ -34,6 +25,6 @@ export function fitToShopper(spec, picks, maxItems, ourReasons) {
34
25
  }
35
26
  blocks.push(block);
36
27
  }
37
- return { ...spec, blocks };
28
+ return { spec: { ...spec, blocks }, ourReasons };
38
29
  }
39
30
  //# sourceMappingURL=fit-to-shopper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fit-to-shopper.js","sourceRoot":"","sources":["../src/fit-to-shopper.ts"],"names":[],"mappings":"AAGA,gFAAgF;AAChF,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAC1B,IAAmB,EACnB,KAA6B,EAC7B,QAAgB,EAChB,UAAgC;IAEhC,+DAA+D;IAC/D,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE/C,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,qEAAqE;QACrE,4EAA4E;QAC5E,2EAA2E;QAC3E,oCAAoC;QAEpC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACvD,MAAM,KAAK,GAAuB,EAAE,CAAC;YACrC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC/B,IAAI,IAAI,IAAI,KAAK;oBAAE,MAAM,CAAC,oBAAoB;gBAC9C,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAE,CAAC;gBAC5B,sEAAsE;gBACtE,4EAA4E;gBAC5E,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;gBACnD,KAAK,CAAC,IAAI,CAAC;oBACT,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG;oBACvB,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,mDAAmD;oBACnD,KAAK,EAAE,IAAI;oBACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;iBACxB,CAAC,CAAC;gBACH,IAAI,IAAI,CAAC,CAAC;YACZ,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACjC,SAAS;QACX,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"fit-to-shopper.js","sourceRoot":"","sources":["../src/fit-to-shopper.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,YAAY,CAC1B,IAAmB,EACnB,KAA6B,EAC7B,QAAgB;IAEhB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE/C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACvD,MAAM,KAAK,GAAuB,EAAE,CAAC;YACrC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC/B,IAAI,IAAI,IAAI,KAAK;oBAAE,MAAM;gBACzB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAE,CAAC;gBAE5B,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;gBAClD,KAAK,CAAC,IAAI,CAAC;oBACT,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG;oBACvB,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;oBAErB,KAAK,EAAE,IAAI;oBACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;iBACxB,CAAC,CAAC;gBACH,IAAI,IAAI,CAAC,CAAC;YACZ,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACjC,SAAS;QACX,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC;AACnD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,13 +1,6 @@
1
- /**
2
- * @rudra-js/core — the contracts and logic that turn one tracking payload into one
3
- * renderable component specification.
4
- *
5
- * Carries no React and no model-vendor SDK, so it can be unit tested in
6
- * isolation and imported from any server runtime.
7
- */
8
1
  export { FIELD_LIMITS, productSchema, skuSignalSchema, viewSignalSchema, purchaseSignalSchema, interactionSchema, renderContextSchema, trackingSignalsSchema, bundleSchema, trackingInputSchema, parseTrackingInput, safeParseTrackingInput, type Product, type SkuSignal, type ViewSignal, type PurchaseSignal, type Interaction, type RenderContext, type TrackingSignals, type Bundle, type TrackingInput, type TrackingInputDraft, type TrackingInputResult, } from './tracking-input.js';
9
2
  export { DIGEST_LIMITS, buildDigest, toCohortDigest, type CategoryAffinity, type InteractionCount, type SignalDigest, type ViewedProduct, } from './signal-digest.js';
10
- export { BANNER_TONES, EMPHASIS, RECOMMENDATION_BASES, SPEC_VERSION, TONES, blockSchema, generatedSpecSchema, parseGeneratedSpec, productReferenceSchema, safeParseGeneratedSpec, type BannerBlock, type Block, type BlockKind, type BundleBlock, type CarouselBlock, type ComponentSpec, type CopyBlock, type GeneratedSpec, type GridBlock, type HeroBlock, type ProductReference, type RecommendationBasis, type DegradedReason, type SpecSource, } from './component-spec.js';
3
+ export { BANNER_TONES, EMPHASIS, RECOMMENDATION_BASES, SPEC_VERSION, TONES, blockSchema, generatedSpecSchema, parseGeneratedSpec, productReferenceSchema, safeParseGeneratedSpec, type BannerBlock, type Block, type BlockKind, type BundleBlock, type CarouselBlock, type ComponentSpec, type CopyBlock, type GeneratedSpec, type GeneratedSpecResult, type GridBlock, type HeroBlock, type ProductReference, type RecommendationBasis, type DegradedReason, type SpecSource, } from './component-spec.js';
11
4
  export { reconcileSpec, type ReconcileResult } from './reconciliation.js';
12
5
  export { selectProducts, type ProductPick } from './product-selection.js';
13
6
  export { createFixedSpecProvider, type ComponentProvider, type ProviderRequest, type ProviderResult, type TokenUsage, } from './provider.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,YAAY,EACZ,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,aAAa,EACb,WAAW,EACX,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,oBAAoB,EACpB,YAAY,EACZ,KAAK,EACL,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1E,OAAO,EACL,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,SAAS,GACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEjE,OAAO,EACL,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,eAAe,GACrB,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,aAAa,EACb,WAAW,EACX,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,oBAAoB,EACpB,YAAY,EACZ,KAAK,EACL,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1E,OAAO,EACL,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,SAAS,GACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEjE,OAAO,EACL,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,eAAe,GACrB,MAAM,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -1,10 +1,3 @@
1
- /**
2
- * @rudra-js/core — the contracts and logic that turn one tracking payload into one
3
- * renderable component specification.
4
- *
5
- * Carries no React and no model-vendor SDK, so it can be unit tested in
6
- * isolation and imported from any server runtime.
7
- */
8
1
  export { FIELD_LIMITS, productSchema, skuSignalSchema, viewSignalSchema, purchaseSignalSchema, interactionSchema, renderContextSchema, trackingSignalsSchema, bundleSchema, trackingInputSchema, parseTrackingInput, safeParseTrackingInput, } from './tracking-input.js';
9
2
  export { DIGEST_LIMITS, buildDigest, toCohortDigest, } from './signal-digest.js';
10
3
  export { BANNER_TONES, EMPHASIS, RECOMMENDATION_BASES, SPEC_VERSION, TONES, blockSchema, generatedSpecSchema, parseGeneratedSpec, productReferenceSchema, safeParseGeneratedSpec, } from './component-spec.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,YAAY,EACZ,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,GAYvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,aAAa,EACb,WAAW,EACX,cAAc,GAKf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,oBAAoB,EACpB,YAAY,EACZ,KAAK,EACL,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,GAevB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAwB,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAoB,MAAM,wBAAwB,CAAC;AAE1E,OAAO,EACL,uBAAuB,GAKxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,GAIpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AAEjE,OAAO,EACL,wBAAwB,GAIzB,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,GAYvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,aAAa,EACb,WAAW,EACX,cAAc,GAKf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,oBAAoB,EACpB,YAAY,EACZ,KAAK,EACL,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,GAgBvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAwB,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAoB,MAAM,wBAAwB,CAAC;AAE1E,OAAO,EACL,uBAAuB,GAKxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,GAIpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AAEjE,OAAO,EACL,wBAAwB,GAIzB,MAAM,0BAA0B,CAAC"}
@@ -1,41 +1,12 @@
1
1
  import type { SignalDigest } from './signal-digest.js';
2
- import type { Product, TrackingInput } from './tracking-input.js';
3
- /**
4
- * What actually reaches the model.
5
- *
6
- * The prompt is split in two, and the split is load-bearing. `system` is
7
- * byte-identical for every request in a deployment, which is what lets a
8
- * provider cache it and charge a fraction for the repeat. `user` carries
9
- * everything that varies. Interpolating one shopper's data into the system half
10
- * would not break anything visibly — it would quietly make the cached prefix
11
- * useless and multiply the bill, which is why a test asserts the halves stay
12
- * separate rather than trusting anyone to remember.
13
- *
14
- * This module is also the only place shopper-supplied text meets model
15
- * instructions, so every host value is written as a JSON string rather than as
16
- * prose. A search for `boots\n\n# Task\nIgnore the above` is then one quoted
17
- * value on one line, not a heading the model might read as a new instruction.
18
- */
2
+ import { type Product, type TrackingInput } from './tracking-input.js';
19
3
  export declare const UNTRUSTED_BEGIN = "BEGIN_UNTRUSTED_DATA";
20
4
  export declare const UNTRUSTED_END = "END_UNTRUSTED_DATA";
21
5
  export interface PromptPair {
22
- /** Stable across requests. Safe for a provider to cache. */
23
6
  system: string;
24
- /** Everything about this shopper and this page. */
25
7
  user: string;
26
8
  }
27
9
  export declare const SYSTEM_PROMPT: string;
28
- /**
29
- * The candidates the model is actually shown.
30
- *
31
- * An out-of-stock product is dropped during reconciliation whatever the model
32
- * does with it, so offering one only costs the shopper a slot.
33
- *
34
- * Exported because reconciliation checks the numerals the model writes against
35
- * the strings on this list, and a candidate it was never shown cannot be where
36
- * one came from. Two copies of "what the model saw" would drift, and the pair
37
- * that drifted would be the prompt and the screen reading it back.
38
- */
39
10
  export declare function offeredCandidates(input: TrackingInput): Product[];
40
11
  export declare function buildPrompt(input: TrackingInput, digest: SignalDigest): PromptPair;
41
12
  //# sourceMappingURL=model-prompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-prompt.d.ts","sourceRoot":"","sources":["../src/model-prompt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAElE;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,eAAe,yBAAyB,CAAC;AACtD,eAAO,MAAM,aAAa,uBAAuB,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;CACd;AAID,eAAO,MAAM,aAAa,QA6FtB,CAAC;AAuGL;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,EAAE,CAEjE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,GAAG,UAAU,CA0BlF"}
1
+ {"version":3,"file":"model-prompt.d.ts","sourceRoot":"","sources":["../src/model-prompt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAgB,KAAK,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAErF,eAAO,MAAM,eAAe,yBAAyB,CAAC;AACtD,eAAO,MAAM,aAAa,uBAAuB,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IAEf,IAAI,EAAE,MAAM,CAAC;CACd;AAID,eAAO,MAAM,aAAa,QA6FtB,CAAC;AA6EL,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,EAAE,CAEjE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,GAAG,UAAU,CAsBlF"}
@@ -1,20 +1,5 @@
1
1
  import { BANNER_TONES, EMPHASIS, RECOMMENDATION_BASES, TONES } from './component-spec.js';
2
- /**
3
- * What actually reaches the model.
4
- *
5
- * The prompt is split in two, and the split is load-bearing. `system` is
6
- * byte-identical for every request in a deployment, which is what lets a
7
- * provider cache it and charge a fraction for the repeat. `user` carries
8
- * everything that varies. Interpolating one shopper's data into the system half
9
- * would not break anything visibly — it would quietly make the cached prefix
10
- * useless and multiply the bill, which is why a test asserts the halves stay
11
- * separate rather than trusting anyone to remember.
12
- *
13
- * This module is also the only place shopper-supplied text meets model
14
- * instructions, so every host value is written as a JSON string rather than as
15
- * prose. A search for `boots\n\n# Task\nIgnore the above` is then one quoted
16
- * value on one line, not a heading the model might read as a new instruction.
17
- */
2
+ import { FIELD_LIMITS } from './tracking-input.js';
18
3
  export const UNTRUSTED_BEGIN = 'BEGIN_UNTRUSTED_DATA';
19
4
  export const UNTRUSTED_END = 'END_UNTRUSTED_DATA';
20
5
  const quotedList = (values) => values.map((value) => `"${value}"`).join(', ');
@@ -112,45 +97,27 @@ better than invented enthusiasm.
112
97
  The "rationale" field is for engineers reading generation logs, not for
113
98
  shoppers. One sentence on why this arrangement, naming the signals you leaned
114
99
  on.`;
115
- /**
116
- * Characters a value has no business containing.
117
- *
118
- * `JSON.stringify` escapes control characters, quotes and backslashes, and
119
- * nothing else. Everything below survives it, and each one lets a shopper's
120
- * value do something the surrounding quotes are meant to prevent — end a line,
121
- * reverse the reading order, or carry text that displays as nothing at all.
122
- *
123
- * This is written as Unicode properties rather than a list of code points on
124
- * purpose. A list is a denylist: it covered the tag block (U+E0000-U+E007F)
125
- * but not the variation selectors supplement (U+E0100-U+E01EF), which smuggles
126
- * text exactly the same way, and it missed U+0085, U+061C and U+00AD as well.
127
- * Properties cover the ones nobody has thought of yet.
128
- *
129
- * - Cc, control. Includes U+0085, a mandatory line break that is not U+000A.
130
- * - Cf, format. Zero-width characters, the bidirectional overrides and
131
- * isolates, and the tag block, which mirrors all of ASCII invisibly.
132
- * - Zl and Zp, the line and paragraph separators.
133
- * - Cn and Co, unassigned and private use — undefined rendering by definition.
134
- * - The variation selectors supplement, which is assigned and therefore not
135
- * caught by Cn, and is invisible.
136
- *
137
- * The zero-width joiner is the one exception. It is a format character, but it
138
- * is also how a family emoji is spelled, so escaping it mangles ordinary
139
- * product titles. Emoji presentation selectors (U+FE00-U+FE0F) are excluded for
140
- * the same reason.
141
- */
142
- const UNPRINTABLE = /(?!\u200D)[\p{Cc}\p{Cf}\p{Cn}\p{Co}\p{Zl}\p{Zp}\u{E0100}-\u{E01EF}]/gu;
143
- /**
144
- * Host-supplied text, written so it cannot introduce structure of its own.
145
- *
146
- * Quoting handles the obvious half. Escaping the characters above handles the
147
- * half that looks identical to a reader: after this, a value occupies exactly
148
- * one line, reads in one direction, and contains nothing a log cannot show.
149
- */
150
- const quote = (value) => JSON.stringify(value).replace(UNPRINTABLE, (character) => {
100
+ const UNPRINTABLE = /(?!\u{200D}|\u{FE0E}|\u{FE0F})[\p{Cc}\p{Cf}\p{Cn}\p{Co}\p{Zl}\p{Zp}\p{Default_Ignorable_Code_Point}]/gu;
101
+ const MAX_QUOTED = FIELD_LIMITS.shortText * 2;
102
+ const escapeUnprintable = (text) => text.replace(UNPRINTABLE, (character) => {
151
103
  const codePoint = character.codePointAt(0);
152
104
  return `\\u{${codePoint.toString(16).toUpperCase()}}`;
153
105
  });
106
+ const quote = (value) => {
107
+ const escaped = escapeUnprintable(JSON.stringify(value));
108
+ if (escaped.length <= MAX_QUOTED)
109
+ return escaped;
110
+ let length = 2;
111
+ let kept = '';
112
+ for (const character of value) {
113
+ const cost = escapeUnprintable(JSON.stringify(character)).length - 2;
114
+ if (length + cost > MAX_QUOTED)
115
+ break;
116
+ length += cost;
117
+ kept += character;
118
+ }
119
+ return escapeUnprintable(JSON.stringify(kept));
120
+ };
154
121
  function section(heading, body) {
155
122
  if (!body || body.length === 0)
156
123
  return null;
@@ -179,7 +146,6 @@ function describeShopper(digest) {
179
146
  ];
180
147
  return lines.filter((line) => line !== null).join('\n');
181
148
  }
182
- /** One candidate per line. Facts the model must not restate are left out. */
183
149
  function describeCandidate(product) {
184
150
  const parts = [quote(product.sku), quote(product.title), quote(product.category)];
185
151
  if (product.rating !== undefined)
@@ -188,37 +154,12 @@ function describeCandidate(product) {
188
154
  parts.push(`tags ${product.tags.map(quote).join('/')}`);
189
155
  return `- ${parts.join(' | ')}`;
190
156
  }
191
- /**
192
- * How many candidates reach the prompt.
193
- *
194
- * The payload contract caps the candidate list at 200, and every field on a
195
- * product at its own length — which multiplies out to a prompt far larger than
196
- * is sensible to send or pay for. The contract deliberately does not impose an
197
- * aggregate budget, on the grounds that trimming to fit is this layer's job.
198
- * This is that trim. The host's ordering is its merchandising priority, so the
199
- * first ones through are the ones it put first.
200
- */
201
157
  const MAX_CANDIDATES = 60;
202
- /**
203
- * The candidates the model is actually shown.
204
- *
205
- * An out-of-stock product is dropped during reconciliation whatever the model
206
- * does with it, so offering one only costs the shopper a slot.
207
- *
208
- * Exported because reconciliation checks the numerals the model writes against
209
- * the strings on this list, and a candidate it was never shown cannot be where
210
- * one came from. Two copies of "what the model saw" would drift, and the pair
211
- * that drifted would be the prompt and the screen reading it back.
212
- */
213
158
  export function offeredCandidates(input) {
214
159
  return input.candidates.filter((product) => product.isInStock).slice(0, MAX_CANDIDATES);
215
160
  }
216
161
  export function buildPrompt(input, digest) {
217
162
  const offered = offeredCandidates(input);
218
- // The markers are OWASP's labelled-block recommendation. They are safe as
219
- // boundaries because every value between them is quoted and stripped of
220
- // anything that could end a line, so no shopper value can occupy a line by
221
- // itself — which is the only way one could impersonate a marker.
222
163
  const user = `${UNTRUSTED_BEGIN}
223
164
 
224
165
  ## Shopper
@@ -1 +1 @@
1
- {"version":3,"file":"model-prompt.js","sourceRoot":"","sources":["../src/model-prompt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"model-prompt.js","sourceRoot":"","sources":["../src/model-prompt.ts"],"names":[],"mappings":"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"}
@@ -1,43 +1,14 @@
1
1
  import type { RecommendationBasis } from './component-spec.js';
2
2
  import type { SignalDigest } from './signal-digest.js';
3
3
  import type { Product, TrackingInput } from './tracking-input.js';
4
- /**
5
- * The deterministic selector — which products to show, in what order, and why.
6
- *
7
- * This is the half of the problem a language model is not needed for. Given the
8
- * same digest, it always returns the same picks, it cannot fail, and it costs
9
- * nothing. It exists for three reasons, in ascending order of importance:
10
- *
11
- * 1. It is what renders when the model is slow, erroring, or not configured.
12
- * 2. It supplies the candidate ordering the model is asked to work from.
13
- * 3. It is the control arm. If a generated component cannot be told apart from
14
- * this, the model has not earned its place, and the evaluation has to be
15
- * able to ask that question honestly.
16
- */
17
4
  export interface ProductPick {
18
5
  product: Product;
19
- /** Why this product, stated so reconciliation can check it. */
20
6
  basis: RecommendationBasis;
21
- /** How the basis reads to a shopper. The host's own sentence when it wrote one. */
22
7
  reason: string;
23
- /** Unnormalised. Only the ordering is meaningful. */
24
8
  score: number;
25
9
  }
26
- /**
27
- * Scores every eligible candidate and returns them best first.
28
- *
29
- * Ties break on SKU so the order is total: two runs over the same payload
30
- * produce the same list, which is what makes the control arm reproducible.
31
- */
32
- /** How the picks are ordered once the unplaceable ones are gone. */
33
10
  export type RankOrder = 'signals' | 'given';
34
11
  export interface SelectOptions {
35
- /**
36
- * `signals` scores each candidate and orders by that score. `given` keeps the
37
- * order you sent, for a shop whose own ranking is better than four weights.
38
- * Either way the exclusions and the stock check still apply, and every pick
39
- * still carries a basis reconciliation can verify.
40
- */
41
12
  rank?: RankOrder;
42
13
  }
43
14
  export declare function selectProducts(input: TrackingInput, digest: SignalDigest, options?: SelectOptions): ProductPick[];
@@ -1 +1 @@
1
- {"version":3,"file":"product-selection.d.ts","sourceRoot":"","sources":["../src/product-selection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAElE;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,mBAAmB,CAAC;IAC3B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;CACf;AAyED;;;;;GAKG;AACH,oEAAoE;AACpE,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,aAAkB,GAC1B,WAAW,EAAE,CAkDf"}
1
+ {"version":3,"file":"product-selection.d.ts","sourceRoot":"","sources":["../src/product-selection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAElE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IAEjB,KAAK,EAAE,mBAAmB,CAAC;IAE3B,MAAM,EAAE,MAAM,CAAC;IAEf,KAAK,EAAE,MAAM,CAAC;CACf;AAwDD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,aAAkB,GAC1B,WAAW,EAAE,CAgDf"}