rudra-js

productSchema

import { productSchema } from '@rudra-js/core';

productSchema lets you check that a product from your catalog has the shape core expects.

productSchema: z.ZodObject<{ sku: z.ZodString; title: z.ZodString; category: z.ZodString; price: z.ZodNumber; currency: z.ZodDefault<z.ZodString>; imageUrl: z.ZodOptional<z.ZodString>; rating: z.ZodOptional<z.ZodNumber>; reason: z.ZodOptional<z.ZodString>; isInStock: z.ZodDefault<z.ZodBoolean>; tags: z.ZodDefault<z.ZodArray<z.ZodString>>; }, z.core.$strict>

Reference

productSchema

Returns

productSchema.parse(row) returns a Product, with currency, isInStock and tags filled in when you left them out.

Caveats

  • Check every product with productSchema before you pass it to RudraComponent. The products in input.candidates from parseTrackingInput have passed it already.
  • imageUrl takes an http or https URL, or a path on your own site such as /images/tr-102.png. A path a browser reads as another site fails, such as //evil.example/pixel.png.
  • A product with isInStock: false is never shown. Core leaves it out, and the renderer drops it too.
  • reason is your own phrase for the product, shown under its card. In 'per-shopper' mode the model writes the reasons instead.
  • Put facts with numbers in tags, such as 3-season. Core drops any text the model wrote with a number that isn't in your tags or categories.

Fields

NameTypeDefaultConstraints
skustring1 to 128 chars
titlestring1 to 200 chars
categorystring1 to 128 chars
pricenumbermin 0
currencyoptionalstring"USD"pattern ^[A-Z]{3}$
imageUrloptionalstringmax 200 chars
ratingoptionalnumber0 to 5
reasonoptionalstring1 to 120 chars
isInStockoptionalbooleantrue
tagsoptionalstring[][]max 20 items

Usage

Checking your catalog before it reaches the page

Map each row from your store to the product fields, and leave the rest of the row behind. Unknown fields are rejected.

import { productSchema, type Product } from '@rudra-js/core';

interface CatalogRow {
  sku: string;
  name: string;
  department: string;
  price: number;
  image: string | null;
  stock: number;
  supplier: string;
}

export function toProducts(rows: CatalogRow[]): Product[] {
  return rows.flatMap((row) => {
    const result = productSchema.safeParse({
      sku: row.sku,
      title: row.name,
      category: row.department,
      price: row.price,
      imageUrl: row.image ?? undefined,
      isInStock: row.stock > 0,
    });
    if (!result.success) {
      console.warn(`Left out ${row.sku}:`, result.error.issues);
      return [];
    }
    return [result.data];
  });
}

A row that fails is left out, and the rest still reach the page.

Pass the result as candidates. One bad row would otherwise make parseTrackingInput throw for the whole page view.

Troubleshooting

z.array(productSchema) fails in an app on zod 3

Core depends on zod 4. An app on zod 3 gets a second copy of zod for core. The two copies don't mix.

Build on core's schemas with their own methods, such as productSchema.array().

import { productSchema } from '@rudra-js/core';
import { z } from 'zod';

// wrong
export const productsSchema = z.array(productSchema);

// right
export const catalogSchema = productSchema.array();