parseTrackingInput
import { parseTrackingInput } from '@rudra-js/core';Call parseTrackingInput to check one page view and fill in the defaults you left out. Pass what it returns to generator.generate.
parseTrackingInput(value: unknown) => TrackingInputReference
parseTrackingInput(value)
Parameters
value: The page view: the shopper inuser, the page incontext, what the shopper has done insignals, and the products you're willing to show incandidates. Type it asTrackingInputDraftwhen you build it in your own code. Every field is listed under Fields.
Returns
The page view as a TrackingInput, with every default filled in. When the page view doesn't fit, it throws a ZodError instead.
Caveats
- Pass the parsed
input.candidatestoRudraComponent, never the raw objects. The parsed list has the defaults filled in, such ascurrencyandisInStock. - Unknown fields are rejected. A misspelled
recentSearchesmakes the parse fail. metaon an interaction is the one place that takes keys of your own. Any key works except__proto__.- Every text field and every list has a size limit.
FIELD_LIMITSholds the numbers, so your own code can check against them. - SKUs in
candidateshave to be unique. context.localetakes one language tag, such asen-US. AnAccept-Languageheader fails.- In an app on zod 3,
error instanceof ZodErrorisfalse. Readerror.issuesinstead.
safeParseTrackingInput(value)
Call safeParseTrackingInput to check a page view without throwing.
Parameters
value: The same page viewparseTrackingInputtakes.
Returns
A TrackingInputResult. When result.success is true, result.data is the parsed page view. When it is false, result.error.issues lists what failed.
Fields
| Name | Type | Default | Constraints |
|---|---|---|---|
schemaVersionoptional | "1" | "1" | |
user | object | ||
user.id | string | 1 to 128 chars | |
user.segmentoptional | string | 1 to 128 chars | |
user.isReturningoptional | boolean | ||
context | RenderContext | ||
context.surface | string | 1 to 128 chars | |
context.slotoptional | string | "recommendations" | 1 to 128 chars |
context.currentSkuoptional | string | 1 to 128 chars | |
context.currentCategoryoptional | string | 1 to 128 chars | |
context.searchQueryoptional | string | max 200 chars | |
context.localeoptional | string | "en-US" | max 35 chars, pattern ^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$ |
context.maxItemsoptional | integer | 4 | 1 to 12 |
signalsoptional | object | {} | |
signals.likesoptional | SkuSignal[] | [] | max 500 items |
signals.likes.sku | string | 1 to 128 chars | |
signals.likes.categoryoptional | string | 1 to 128 chars | |
signals.likes.atoptional | integer | 0 to 4102444800000 | |
signals.likes.weightoptional | number | 0 to 1 | |
signals.dislikesoptional | SkuSignal[] | [] | max 500 items |
signals.dislikes.sku | string | 1 to 128 chars | |
signals.dislikes.categoryoptional | string | 1 to 128 chars | |
signals.dislikes.atoptional | integer | 0 to 4102444800000 | |
signals.dislikes.weightoptional | number | 0 to 1 | |
signals.mostViewedoptional | ViewSignal[] | [] | max 500 items |
signals.mostViewed.sku | string | 1 to 128 chars | |
signals.mostViewed.categoryoptional | string | 1 to 128 chars | |
signals.mostViewed.atoptional | integer | 0 to 4102444800000 | |
signals.mostViewed.weightoptional | number | 0 to 1 | |
signals.mostViewed.viewsoptional | integer | 1 | max 9007199254740991, > 0 |
signals.mostViewed.dwellMsoptional | number | min 0 | |
signals.lastPurchasedoptional | PurchaseSignal[] | [] | max 500 items |
signals.lastPurchased.sku | string | 1 to 128 chars | |
signals.lastPurchased.categoryoptional | string | 1 to 128 chars | |
signals.lastPurchased.atoptional | integer | 0 to 4102444800000 | |
signals.lastPurchased.weightoptional | number | 0 to 1 | |
signals.lastPurchased.quantityoptional | integer | 1 | max 9007199254740991, > 0 |
signals.lastPurchased.priceoptional | number | min 0 | |
signals.cartoptional | SkuSignal[] | [] | max 500 items |
signals.cart.sku | string | 1 to 128 chars | |
signals.cart.categoryoptional | string | 1 to 128 chars | |
signals.cart.atoptional | integer | 0 to 4102444800000 | |
signals.cart.weightoptional | number | 0 to 1 | |
signals.recentSearchesoptional | string[] | [] | max 500 items |
signals.interactionsoptional | Interaction[] | [] | max 500 items |
signals.interactions.type | string | 1 to 128 chars | |
signals.interactions.skuoptional | string | 1 to 128 chars | |
signals.interactions.categoryoptional | string | 1 to 128 chars | |
signals.interactions.atoptional | integer | 0 to 4102444800000 | |
signals.interactions.valueoptional | string | number | boolean | ||
signals.interactions.metaoptional | any | ||
candidates | Product[] | 1 to 200 items | |
bundlesoptional | Bundle[] | [] | max 20 items |
Usage
Building the input from a product page view
Build one page view for each render. This one is for a shopper looking at the trail shoe TR-102.
import { parseTrackingInput } from '@rudra-js/core';
export const input = parseTrackingInput({
user: { id: 'shopper-1', isReturning: true },
context: { surface: 'pdp', currentSku: 'TR-102', currentCategory: 'Trail Running' },
signals: {
likes: [{ sku: 'TR-104' }],
mostViewed: [{ sku: 'TR-104', views: 3 }],
recentSearches: ['waterproof trail shoe'],
},
candidates: [
{
sku: 'TR-102',
title: 'Switchback Trail Shoe GTX',
category: 'Trail Running',
price: 174,
imageUrl: '/images/tr-102.png',
},
{
sku: 'TR-104',
title: 'Ridgeline Trail Shoe',
category: 'Trail Running',
price: 139,
imageUrl: 'https://cdn.example.com/tr-104.png',
tags: ['waterproof'],
},
{
sku: 'SK-201',
title: 'Merino Hiking Socks',
category: 'Socks',
price: 22,
},
],
});You have to send user.id, context.surface and at least one candidate. The rest is optional.
Leave out signals for a first-time visitor. Every list in it defaults to [].
Checking input without throwing
Use safeParseTrackingInput when the page view comes from outside your code, such as a request body.
import { safeParseTrackingInput, type TrackingInput } from '@rudra-js/core';
export function readPageView(body: unknown): TrackingInput | null {
const result = safeParseTrackingInput(body);
if (!result.success) {
console.warn('The page view did not fit:', result.error.issues);
return null;
}
return result.data;
}Each issue has a path and a message, such as Unrecognized key: "recentSearch".