@rudra-js/react 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.
package/README.md CHANGED
@@ -1,17 +1,20 @@
1
1
  # @rudra-js/react
2
2
 
3
3
  Renders a component specification from
4
- [`@rudra-js/core`](https://github.com/clivedsouza1010/rudra-js/tree/main/packages/core) as React
5
- Server Components.
4
+ [`@rudra-js/core`](https://github.com/clivedsouza1010/rudra-js/tree/main/packages/core) with React
5
+ 18 or 19.
6
6
 
7
- No client JavaScript. The recommendation area arrives in the initial HTML response and needs no
8
- hydration, so it never pops in the way a client-fetched recommendation rail does, and a crawler that
9
- doesn't run JavaScript still reads it.
7
+ The components are plain functions with no hooks, state or effects. They work with
8
+ `renderToString`, in a client-rendered app, and as Server Components in a setup that supports them,
9
+ such as the Next.js App Router. Rendered on the server, the
10
+ recommendation area arrives in the initial HTML response and needs no client JavaScript, so it never
11
+ pops in the way a client-fetched recommendation rail does, and a crawler that doesn't run JavaScript
12
+ still reads it.
10
13
 
11
14
  ## Install
12
15
 
13
16
  ```sh
14
- npm install @rudra-js/react @rudra-js/core react zod@^4
17
+ npm install @rudra-js/react @rudra-js/core react
15
18
  ```
16
19
 
17
20
  Both `@rudra-js/core` and `react` are peer dependencies. The specification you pass in comes from
package/dist/index.d.ts CHANGED
@@ -1,9 +1,3 @@
1
- /**
2
- * @rudra-js/react — renders a component specification as React Server Components.
3
- *
4
- * Carries no client JavaScript. The recommendation area arrives in the initial
5
- * HTML response and needs no hydration.
6
- */
7
1
  export { RudraComponent, type ProductCatalog, type RudraComponentProps, } from './rudra-component.js';
8
2
  export { defaultRegistry, extendRegistry, type BlockRegistry, type BlockRenderer, } from './registry.js';
9
3
  export { defaultFormatBundlePrice, defaultFormatPrice, defaultHrefForSku, type BlockRenderContext, } from './render-context.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,eAAe,EACf,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,eAAe,EACf,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -1,14 +1,5 @@
1
- /**
2
- * @rudra-js/react — renders a component specification as React Server Components.
3
- *
4
- * Carries no client JavaScript. The recommendation area arrives in the initial
5
- * HTML response and needs no hydration.
6
- */
7
1
  export { RudraComponent, } from './rudra-component.js';
8
2
  export { defaultRegistry, extendRegistry, } from './registry.js';
9
3
  export { defaultFormatBundlePrice, defaultFormatPrice, defaultHrefForSku, } from './render-context.js';
10
- // The six default renderers reach hosts through `defaultRegistry`, which is
11
- // also the thing they need in order to override one. `ProductCard` is exported
12
- // on its own because a custom grid renderer still wants the standard card.
13
4
  export { ProductCard } from './blocks/product-card.js';
14
5
  //# 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;;;;;GAKG;AAEH,OAAO,EACL,cAAc,GAGf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,eAAe,EACf,cAAc,GAGf,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,iBAAiB,GAElB,MAAM,qBAAqB,CAAC;AAE7B,4EAA4E;AAC5E,+EAA+E;AAC/E,2EAA2E;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,eAAe,EACf,cAAc,GAGf,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,iBAAiB,GAElB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC"}
@@ -1,13 +1,3 @@
1
- /**
2
- * What each kind of block actually renders.
3
- *
4
- * This is the seam that makes a generated component safe *and* usable. The
5
- * model picks from a fixed vocabulary of block kinds; the registry decides what
6
- * those kinds look like. A shop can swap any entry for its own design-system
7
- * component without the model knowing, without the specification changing, and
8
- * without giving the model any new ability — it still cannot say anything
9
- * outside the vocabulary.
10
- */
11
1
  import type { ReactNode } from 'react';
12
2
  import type { Block, BlockKind } from '@rudra-js/core';
13
3
  import type { BlockRenderContext } from './render-context.js';
@@ -21,13 +11,5 @@ export type BlockRegistry = {
21
11
  [Kind in BlockKind]: BlockRenderer<Kind>;
22
12
  };
23
13
  export declare const defaultRegistry: BlockRegistry;
24
- /**
25
- * Replaces some renderers and keeps the rest.
26
- *
27
- * Written out kind by kind rather than spread: a spread lets
28
- * `{ hero: maybeRenderer }` put an explicit `undefined` over the default, and
29
- * the page then renders every hero as nothing. Here an absent override means
30
- * the default, however it was written.
31
- */
32
14
  export declare function extendRegistry(overrides: Partial<BlockRegistry>): BlockRegistry;
33
15
  //# sourceMappingURL=registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAU9D,MAAM,MAAM,aAAa,CAAC,IAAI,SAAS,SAAS,IAAI,CAAC,KAAK,EAAE;IAC1D,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACtC,OAAO,EAAE,kBAAkB,CAAC;CAC7B,KAAK,SAAS,CAAC;AAEhB,MAAM,MAAM,aAAa,GAAG;KACzB,IAAI,IAAI,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC;CACzC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,aAO7B,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAS/E"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAU9D,MAAM,MAAM,aAAa,CAAC,IAAI,SAAS,SAAS,IAAI,CAAC,KAAK,EAAE;IAC1D,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACtC,OAAO,EAAE,kBAAkB,CAAC;CAC7B,KAAK,SAAS,CAAC;AAEhB,MAAM,MAAM,aAAa,GAAG;KACzB,IAAI,IAAI,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC;CACzC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,aAO7B,CAAC;AAEF,wBAAgB,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAS/E"}
package/dist/registry.js CHANGED
@@ -1,13 +1,3 @@
1
- /**
2
- * What each kind of block actually renders.
3
- *
4
- * This is the seam that makes a generated component safe *and* usable. The
5
- * model picks from a fixed vocabulary of block kinds; the registry decides what
6
- * those kinds look like. A shop can swap any entry for its own design-system
7
- * component without the model knowing, without the specification changing, and
8
- * without giving the model any new ability — it still cannot say anything
9
- * outside the vocabulary.
10
- */
11
1
  import { BannerRenderer, CarouselRenderer, CopyRenderer, GridRenderer, HeroRenderer, } from './blocks/block-renderers.js';
12
2
  import { BundleRenderer } from './blocks/bundle-block.js';
13
3
  export const defaultRegistry = {
@@ -18,14 +8,6 @@ export const defaultRegistry = {
18
8
  copy: CopyRenderer,
19
9
  bundle: BundleRenderer,
20
10
  };
21
- /**
22
- * Replaces some renderers and keeps the rest.
23
- *
24
- * Written out kind by kind rather than spread: a spread lets
25
- * `{ hero: maybeRenderer }` put an explicit `undefined` over the default, and
26
- * the page then renders every hero as nothing. Here an absent override means
27
- * the default, however it was written.
28
- */
29
11
  export function extendRegistry(overrides) {
30
12
  return {
31
13
  hero: overrides.hero ?? defaultRegistry.hero,
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,YAAY,GACb,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAW1D,MAAM,CAAC,MAAM,eAAe,GAAkB;IAC5C,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;IAClB,QAAQ,EAAE,gBAAgB;IAC1B,MAAM,EAAE,cAAc;IACtB,IAAI,EAAE,YAAY;IAClB,MAAM,EAAE,cAAc;CACvB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiC;IAC9D,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,eAAe,CAAC,IAAI;QAC5C,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,eAAe,CAAC,IAAI;QAC5C,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,eAAe,CAAC,QAAQ;QACxD,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,eAAe,CAAC,MAAM;QAClD,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,eAAe,CAAC,IAAI;QAC5C,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,eAAe,CAAC,MAAM;KACnD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,YAAY,GACb,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAW1D,MAAM,CAAC,MAAM,eAAe,GAAkB;IAC5C,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;IAClB,QAAQ,EAAE,gBAAgB;IAC1B,MAAM,EAAE,cAAc;IACtB,IAAI,EAAE,YAAY;IAClB,MAAM,EAAE,cAAc;CACvB,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,SAAiC;IAC9D,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,eAAe,CAAC,IAAI;QAC5C,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,eAAe,CAAC,IAAI;QAC5C,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,eAAe,CAAC,QAAQ;QACxD,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,eAAe,CAAC,MAAM;QAClD,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,eAAe,CAAC,IAAI;QAC5C,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,eAAe,CAAC,MAAM;KACnD,CAAC;AACJ,CAAC"}
@@ -1,56 +1,13 @@
1
1
  import type { Bundle, Product } from '@rudra-js/core';
2
- /**
3
- * Everything a block renderer needs that does not come from the specification.
4
- *
5
- * Note what is absent from the spec and present here: a price, a title, an
6
- * image, a link. Those are resolved from the host's own catalog, keyed by a SKU
7
- * that `selectProducts` drew from that same catalog and `reconcileSpec` proved
8
- * the model did not invent — both in stock at the time. A bundle member's SKU
9
- * comes from the set the shop supplied, not from the model. That division
10
- * is the whole reason a generated component is safe to put in a page — the
11
- * model decides how the component reads, and the shop decides what is true
12
- * about a product.
13
- */
14
2
  export interface BlockRenderContext {
15
- /** The host's catalog, keyed by SKU. Read-only: it is the caller's own map. */
16
3
  readonly products: ReadonlyMap<string, Product>;
17
- /** Sets the shop sells together, keyed by id. */
18
4
  readonly bundles: ReadonlyMap<string, Bundle>;
19
- /** Host-owned link construction. */
20
5
  readonly hrefForSku: (sku: string) => string;
21
6
  readonly formatPrice: (product: Product) => string;
22
7
  readonly formatBundlePrice: (bundle: Bundle) => string;
23
8
  }
24
9
  export declare function defaultHrefForSku(sku: string): string;
25
- /**
26
- * The product to draw for a SKU, or nothing.
27
- *
28
- * A row the catalog flags out of stock counts as absent. Core only ever names
29
- * an in-stock candidate, but the catalog passed here is read later and can be
30
- * the fresher of the two — and a card that prices and links something the shop
31
- * cannot sell is worse than one card fewer. A catalog that leaves the field
32
- * off renders as it always did.
33
- */
34
10
  export declare function sellableProduct(products: ReadonlyMap<string, Product>, sku: string): Product | undefined;
35
- /**
36
- * Formats a price the way the currency itself is written.
37
- *
38
- * No digit count is specified on purpose. Two decimal places is a dollar-and-
39
- * cent assumption, and forcing it is wrong in both directions: Kuwaiti dinar and
40
- * Bahraini dinar have three, so a real digit of the price disappears, and yen has
41
- * none, so a price gains a fraction that does not exist. Intl already knows the
42
- * right number for each currency.
43
- *
44
- * `locale` decides how the number is punctuated and where the symbol sits.
45
- * Left undefined it falls back to the server's locale, which is almost never
46
- * the shopper's — a shop serving more than one should pass the shopper's.
47
- */
48
11
  export declare function defaultFormatPrice(product: Product, locale?: string): string;
49
- /**
50
- * Formats a bundle's price, in the currency the shop put on the bundle.
51
- *
52
- * The price and the currency come from the same object, so a set whose members
53
- * are priced in another currency still shows the shop's own price correctly.
54
- */
55
12
  export declare function defaultFormatBundlePrice(bundle: Bundle, locale?: string): string;
56
13
  //# sourceMappingURL=render-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-context.d.ts","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEtD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChD,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,oCAAoC;IACpC,QAAQ,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;IACnD,QAAQ,CAAC,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;CACxD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,GAAG,EAAE,MAAM,GACV,OAAO,GAAG,SAAS,CAGrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAkB5E;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAgBhF"}
1
+ {"version":3,"file":"render-context.d.ts","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEhD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE9C,QAAQ,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;IACnD,QAAQ,CAAC,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;CACxD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,GAAG,EAAE,MAAM,GACV,OAAO,GAAG,SAAS,CAGrB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAgB5E;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAgBhF"}
@@ -1,32 +1,10 @@
1
1
  export function defaultHrefForSku(sku) {
2
2
  return `/product/${encodeURIComponent(sku)}`;
3
3
  }
4
- /**
5
- * The product to draw for a SKU, or nothing.
6
- *
7
- * A row the catalog flags out of stock counts as absent. Core only ever names
8
- * an in-stock candidate, but the catalog passed here is read later and can be
9
- * the fresher of the two — and a card that prices and links something the shop
10
- * cannot sell is worse than one card fewer. A catalog that leaves the field
11
- * off renders as it always did.
12
- */
13
4
  export function sellableProduct(products, sku) {
14
5
  const product = products.get(sku);
15
6
  return product?.isInStock === false ? undefined : product;
16
7
  }
17
- /**
18
- * Formats a price the way the currency itself is written.
19
- *
20
- * No digit count is specified on purpose. Two decimal places is a dollar-and-
21
- * cent assumption, and forcing it is wrong in both directions: Kuwaiti dinar and
22
- * Bahraini dinar have three, so a real digit of the price disappears, and yen has
23
- * none, so a price gains a fraction that does not exist. Intl already knows the
24
- * right number for each currency.
25
- *
26
- * `locale` decides how the number is punctuated and where the symbol sits.
27
- * Left undefined it falls back to the server's locale, which is almost never
28
- * the shopper's — a shop serving more than one should pass the shopper's.
29
- */
30
8
  export function defaultFormatPrice(product, locale) {
31
9
  if (!Number.isFinite(product.price)) {
32
10
  throw new TypeError(`price for SKU ${product.sku} is ${String(product.price)}, not a finite number — ` +
@@ -34,8 +12,6 @@ export function defaultFormatPrice(product, locale) {
34
12
  }
35
13
  let formatter;
36
14
  try {
37
- // Only the constructor: guarding format() too would swallow a throwing
38
- // getter on a host's own Product and render the mess as a price.
39
15
  formatter = new Intl.NumberFormat(locale, { style: 'currency', currency: product.currency });
40
16
  }
41
17
  catch {
@@ -43,12 +19,6 @@ export function defaultFormatPrice(product, locale) {
43
19
  }
44
20
  return formatter.format(product.price);
45
21
  }
46
- /**
47
- * Formats a bundle's price, in the currency the shop put on the bundle.
48
- *
49
- * The price and the currency come from the same object, so a set whose members
50
- * are priced in another currency still shows the shop's own price correctly.
51
- */
52
22
  export function defaultFormatBundlePrice(bundle, locale) {
53
23
  if (!Number.isFinite(bundle.price)) {
54
24
  throw new TypeError(`price for bundle ${bundle.id} is ${String(bundle.price)}, not a finite number — ` +
@@ -1 +1 @@
1
- {"version":3,"file":"render-context.js","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAyBA,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,YAAY,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAsC,EACtC,GAAW;IAEX,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,OAAO,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAgB,EAAE,MAAe;IAClE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,SAAS,CACjB,iBAAiB,OAAO,CAAC,GAAG,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,0BAA0B;YAChF,gEAAgE,CACnE,CAAC;IACJ,CAAC;IAED,IAAI,SAA4B,CAAC;IACjC,IAAI,CAAC;QACH,uEAAuE;QACvE,iEAAiE;QACjE,SAAS,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC/F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAChD,CAAC;IAED,OAAO,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAc,EAAE,MAAe;IACtE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CACjB,oBAAoB,MAAM,CAAC,EAAE,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B;YAChF,8DAA8D,CACjE,CAAC;IACJ,CAAC;IAED,IAAI,SAA4B,CAAC;IACjC,IAAI,CAAC;QACH,SAAS,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;IAC9C,CAAC;IAED,OAAO,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"render-context.js","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAYA,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,YAAY,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,QAAsC,EACtC,GAAW;IAEX,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,OAAO,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAgB,EAAE,MAAe;IAClE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,SAAS,CACjB,iBAAiB,OAAO,CAAC,GAAG,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,0BAA0B;YAChF,gEAAgE,CACnE,CAAC;IACJ,CAAC;IAED,IAAI,SAA4B,CAAC;IACjC,IAAI,CAAC;QACH,SAAS,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC/F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAChD,CAAC;IAED,OAAO,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAc,EAAE,MAAe;IACtE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CACjB,oBAAoB,MAAM,CAAC,EAAE,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B;YAChF,8DAA8D,CACjE,CAAC;IACJ,CAAC;IAED,IAAI,SAA4B,CAAC;IACjC,IAAI,CAAC;QACH,SAAS,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;IAC9C,CAAC;IAED,OAAO,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rudra-js/react",
3
- "version": "0.6.0",
4
- "description": "Renders a rudra component specification as React Server Components",
3
+ "version": "0.7.1",
4
+ "description": "Renders a rudra component specification with React 18 or 19, on the server or in the browser",
5
5
  "keywords": [
6
6
  "react",
7
7
  "react-server-components",
@@ -13,7 +13,7 @@
13
13
  ],
14
14
  "license": "MIT",
15
15
  "author": "Clive Dsouza",
16
- "homepage": "https://github.com/clivedsouza1010/rudra-js/tree/main/packages/react#readme",
16
+ "homepage": "https://rudrajs.com",
17
17
  "bugs": {
18
18
  "url": "https://github.com/clivedsouza1010/rudra-js/issues"
19
19
  },
@@ -51,11 +51,11 @@
51
51
  "access": "public"
52
52
  },
53
53
  "peerDependencies": {
54
- "@rudra-js/core": "^0.6.0",
54
+ "@rudra-js/core": "^0.7.1",
55
55
  "react": "^18.2.0 || ^19.0.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@rudra-js/core": "0.6.0",
58
+ "@rudra-js/core": "0.7.1",
59
59
  "@types/react": "^19.3.0",
60
60
  "@types/react-dom": "^19.3.0",
61
61
  "react": "^19.3.0",
package/src/index.ts CHANGED
@@ -1,10 +1,3 @@
1
- /**
2
- * @rudra-js/react — renders a component specification as React Server Components.
3
- *
4
- * Carries no client JavaScript. The recommendation area arrives in the initial
5
- * HTML response and needs no hydration.
6
- */
7
-
8
1
  export {
9
2
  RudraComponent,
10
3
  type ProductCatalog,
@@ -25,7 +18,4 @@ export {
25
18
  type BlockRenderContext,
26
19
  } from './render-context.js';
27
20
 
28
- // The six default renderers reach hosts through `defaultRegistry`, which is
29
- // also the thing they need in order to override one. `ProductCard` is exported
30
- // on its own because a custom grid renderer still wants the standard card.
31
21
  export { ProductCard } from './blocks/product-card.js';
package/src/registry.ts CHANGED
@@ -1,14 +1,3 @@
1
- /**
2
- * What each kind of block actually renders.
3
- *
4
- * This is the seam that makes a generated component safe *and* usable. The
5
- * model picks from a fixed vocabulary of block kinds; the registry decides what
6
- * those kinds look like. A shop can swap any entry for its own design-system
7
- * component without the model knowing, without the specification changing, and
8
- * without giving the model any new ability — it still cannot say anything
9
- * outside the vocabulary.
10
- */
11
-
12
1
  import type { ReactNode } from 'react';
13
2
  import type { Block, BlockKind } from '@rudra-js/core';
14
3
  import type { BlockRenderContext } from './render-context.js';
@@ -39,14 +28,6 @@ export const defaultRegistry: BlockRegistry = {
39
28
  bundle: BundleRenderer,
40
29
  };
41
30
 
42
- /**
43
- * Replaces some renderers and keeps the rest.
44
- *
45
- * Written out kind by kind rather than spread: a spread lets
46
- * `{ hero: maybeRenderer }` put an explicit `undefined` over the default, and
47
- * the page then renders every hero as nothing. Here an absent override means
48
- * the default, however it was written.
49
- */
50
31
  export function extendRegistry(overrides: Partial<BlockRegistry>): BlockRegistry {
51
32
  return {
52
33
  hero: overrides.hero ?? defaultRegistry.hero,
@@ -1,23 +1,10 @@
1
1
  import type { Bundle, Product } from '@rudra-js/core';
2
2
 
3
- /**
4
- * Everything a block renderer needs that does not come from the specification.
5
- *
6
- * Note what is absent from the spec and present here: a price, a title, an
7
- * image, a link. Those are resolved from the host's own catalog, keyed by a SKU
8
- * that `selectProducts` drew from that same catalog and `reconcileSpec` proved
9
- * the model did not invent — both in stock at the time. A bundle member's SKU
10
- * comes from the set the shop supplied, not from the model. That division
11
- * is the whole reason a generated component is safe to put in a page — the
12
- * model decides how the component reads, and the shop decides what is true
13
- * about a product.
14
- */
15
3
  export interface BlockRenderContext {
16
- /** The host's catalog, keyed by SKU. Read-only: it is the caller's own map. */
17
4
  readonly products: ReadonlyMap<string, Product>;
18
- /** Sets the shop sells together, keyed by id. */
5
+
19
6
  readonly bundles: ReadonlyMap<string, Bundle>;
20
- /** Host-owned link construction. */
7
+
21
8
  readonly hrefForSku: (sku: string) => string;
22
9
  readonly formatPrice: (product: Product) => string;
23
10
  readonly formatBundlePrice: (bundle: Bundle) => string;
@@ -27,15 +14,6 @@ export function defaultHrefForSku(sku: string): string {
27
14
  return `/product/${encodeURIComponent(sku)}`;
28
15
  }
29
16
 
30
- /**
31
- * The product to draw for a SKU, or nothing.
32
- *
33
- * A row the catalog flags out of stock counts as absent. Core only ever names
34
- * an in-stock candidate, but the catalog passed here is read later and can be
35
- * the fresher of the two — and a card that prices and links something the shop
36
- * cannot sell is worse than one card fewer. A catalog that leaves the field
37
- * off renders as it always did.
38
- */
39
17
  export function sellableProduct(
40
18
  products: ReadonlyMap<string, Product>,
41
19
  sku: string,
@@ -44,19 +22,6 @@ export function sellableProduct(
44
22
  return product?.isInStock === false ? undefined : product;
45
23
  }
46
24
 
47
- /**
48
- * Formats a price the way the currency itself is written.
49
- *
50
- * No digit count is specified on purpose. Two decimal places is a dollar-and-
51
- * cent assumption, and forcing it is wrong in both directions: Kuwaiti dinar and
52
- * Bahraini dinar have three, so a real digit of the price disappears, and yen has
53
- * none, so a price gains a fraction that does not exist. Intl already knows the
54
- * right number for each currency.
55
- *
56
- * `locale` decides how the number is punctuated and where the symbol sits.
57
- * Left undefined it falls back to the server's locale, which is almost never
58
- * the shopper's — a shop serving more than one should pass the shopper's.
59
- */
60
25
  export function defaultFormatPrice(product: Product, locale?: string): string {
61
26
  if (!Number.isFinite(product.price)) {
62
27
  throw new TypeError(
@@ -67,8 +32,6 @@ export function defaultFormatPrice(product: Product, locale?: string): string {
67
32
 
68
33
  let formatter: Intl.NumberFormat;
69
34
  try {
70
- // Only the constructor: guarding format() too would swallow a throwing
71
- // getter on a host's own Product and render the mess as a price.
72
35
  formatter = new Intl.NumberFormat(locale, { style: 'currency', currency: product.currency });
73
36
  } catch {
74
37
  return `${product.currency} ${product.price}`;
@@ -77,12 +40,6 @@ export function defaultFormatPrice(product: Product, locale?: string): string {
77
40
  return formatter.format(product.price);
78
41
  }
79
42
 
80
- /**
81
- * Formats a bundle's price, in the currency the shop put on the bundle.
82
- *
83
- * The price and the currency come from the same object, so a set whose members
84
- * are priced in another currency still shows the shop's own price correctly.
85
- */
86
43
  export function defaultFormatBundlePrice(bundle: Bundle, locale?: string): string {
87
44
  if (!Number.isFinite(bundle.price)) {
88
45
  throw new TypeError(