pricing-renderer 0.1.0 → 0.2.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 (175) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/README.md +142 -25
  3. package/THIRD_PARTY_NOTICES.md +84 -6
  4. package/custom-elements.json +233 -3
  5. package/dist/core/apply-selection.d.ts +8 -0
  6. package/dist/core/apply-selection.d.ts.map +1 -0
  7. package/dist/core/apply-selection.js +76 -0
  8. package/dist/core/apply-selection.js.map +1 -0
  9. package/dist/core/breakdown.d.ts +13 -0
  10. package/dist/core/breakdown.d.ts.map +1 -0
  11. package/dist/core/breakdown.js +56 -0
  12. package/dist/core/breakdown.js.map +1 -0
  13. package/dist/core/compare-sides.d.ts +16 -0
  14. package/dist/core/compare-sides.d.ts.map +1 -0
  15. package/dist/core/compare-sides.js +73 -0
  16. package/dist/core/compare-sides.js.map +1 -0
  17. package/dist/core/compare-values.d.ts +13 -0
  18. package/dist/core/compare-values.d.ts.map +1 -0
  19. package/dist/core/compare-values.js +100 -0
  20. package/dist/core/compare-values.js.map +1 -0
  21. package/dist/core/compare.d.ts +7 -0
  22. package/dist/core/compare.d.ts.map +1 -0
  23. package/dist/core/compare.js +301 -0
  24. package/dist/core/compare.js.map +1 -0
  25. package/dist/core/expression.js +2 -2
  26. package/dist/core/expression.js.map +1 -1
  27. package/dist/core/i18n.d.ts.map +1 -1
  28. package/dist/core/i18n.js +136 -2
  29. package/dist/core/i18n.js.map +1 -1
  30. package/dist/core/index.d.ts +9 -2
  31. package/dist/core/index.d.ts.map +1 -1
  32. package/dist/core/index.js +6 -1
  33. package/dist/core/index.js.map +1 -1
  34. package/dist/core/normalize.d.ts.map +1 -1
  35. package/dist/core/normalize.js +70 -9
  36. package/dist/core/normalize.js.map +1 -1
  37. package/dist/core/plan-relation.d.ts +8 -0
  38. package/dist/core/plan-relation.d.ts.map +1 -0
  39. package/dist/core/plan-relation.js +15 -0
  40. package/dist/core/plan-relation.js.map +1 -0
  41. package/dist/core/price-stabilizer.d.ts +3 -0
  42. package/dist/core/price-stabilizer.d.ts.map +1 -0
  43. package/dist/core/price-stabilizer.js +73 -0
  44. package/dist/core/price-stabilizer.js.map +1 -0
  45. package/dist/core/resolve.d.ts +7 -1
  46. package/dist/core/resolve.d.ts.map +1 -1
  47. package/dist/core/resolve.js +66 -21
  48. package/dist/core/resolve.js.map +1 -1
  49. package/dist/core/search-params.d.ts +15 -0
  50. package/dist/core/search-params.d.ts.map +1 -0
  51. package/dist/core/search-params.js +175 -0
  52. package/dist/core/search-params.js.map +1 -0
  53. package/dist/core/selection-change.d.ts +10 -0
  54. package/dist/core/selection-change.d.ts.map +1 -0
  55. package/dist/core/selection-change.js +124 -0
  56. package/dist/core/selection-change.js.map +1 -0
  57. package/dist/core/types.d.ts +209 -0
  58. package/dist/core/types.d.ts.map +1 -1
  59. package/dist/core/units.d.ts +13 -0
  60. package/dist/core/units.d.ts.map +1 -0
  61. package/dist/core/units.js +84 -0
  62. package/dist/core/units.js.map +1 -0
  63. package/dist/core/utils.d.ts.map +1 -1
  64. package/dist/core/utils.js +91 -32
  65. package/dist/core/utils.js.map +1 -1
  66. package/dist/core/view-model.d.ts +3 -1
  67. package/dist/core/view-model.d.ts.map +1 -1
  68. package/dist/core/view-model.js +113 -7
  69. package/dist/core/view-model.js.map +1 -1
  70. package/dist/element/change-review-define.d.ts +4 -0
  71. package/dist/element/change-review-define.d.ts.map +1 -0
  72. package/dist/element/change-review-define.js +17 -0
  73. package/dist/element/change-review-define.js.map +1 -0
  74. package/dist/element/change-review-element.d.ts +53 -0
  75. package/dist/element/change-review-element.d.ts.map +1 -0
  76. package/dist/element/change-review-element.js +104 -0
  77. package/dist/element/change-review-element.js.map +1 -0
  78. package/dist/element/change-review-events.d.ts +10 -0
  79. package/dist/element/change-review-events.d.ts.map +1 -0
  80. package/dist/element/change-review-events.js +2 -0
  81. package/dist/element/change-review-events.js.map +1 -0
  82. package/dist/element/change-review-renderers.d.ts +23 -0
  83. package/dist/element/change-review-renderers.d.ts.map +1 -0
  84. package/dist/element/change-review-renderers.js +391 -0
  85. package/dist/element/change-review-renderers.js.map +1 -0
  86. package/dist/element/cta.d.ts +45 -0
  87. package/dist/element/cta.d.ts.map +1 -0
  88. package/dist/element/cta.js +90 -0
  89. package/dist/element/cta.js.map +1 -0
  90. package/dist/element/current-selection.d.ts +13 -0
  91. package/dist/element/current-selection.d.ts.map +1 -0
  92. package/dist/element/current-selection.js +57 -0
  93. package/dist/element/current-selection.js.map +1 -0
  94. package/dist/element/define-change-review.d.ts +2 -0
  95. package/dist/element/define-change-review.d.ts.map +1 -0
  96. package/dist/element/define-change-review.js +7 -0
  97. package/dist/element/define-change-review.js.map +1 -0
  98. package/dist/element/events.d.ts +32 -6
  99. package/dist/element/events.d.ts.map +1 -1
  100. package/dist/element/hide-sections.d.ts +6 -0
  101. package/dist/element/hide-sections.d.ts.map +1 -0
  102. package/dist/element/hide-sections.js +26 -0
  103. package/dist/element/hide-sections.js.map +1 -0
  104. package/dist/element/index.d.ts +8 -1
  105. package/dist/element/index.d.ts.map +1 -1
  106. package/dist/element/index.js +3 -0
  107. package/dist/element/index.js.map +1 -1
  108. package/dist/element/pricing-renderer-element.d.ts +95 -7
  109. package/dist/element/pricing-renderer-element.d.ts.map +1 -1
  110. package/dist/element/pricing-renderer-element.js +563 -217
  111. package/dist/element/pricing-renderer-element.js.map +1 -1
  112. package/dist/element/renderers.d.ts +49 -4
  113. package/dist/element/renderers.d.ts.map +1 -1
  114. package/dist/element/renderers.js +795 -263
  115. package/dist/element/renderers.js.map +1 -1
  116. package/dist/element/same-input.d.ts +3 -0
  117. package/dist/element/same-input.d.ts.map +1 -0
  118. package/dist/element/same-input.js +56 -0
  119. package/dist/element/same-input.js.map +1 -0
  120. package/dist/react/headless.d.ts +9 -0
  121. package/dist/react/headless.d.ts.map +1 -0
  122. package/dist/react/headless.js +8 -0
  123. package/dist/react/headless.js.map +1 -0
  124. package/dist/react/index.d.ts +54 -4
  125. package/dist/react/index.d.ts.map +1 -1
  126. package/dist/react/index.js +156 -6
  127. package/dist/react/index.js.map +1 -1
  128. package/dist/react/provider.d.ts +17 -0
  129. package/dist/react/provider.d.ts.map +1 -0
  130. package/dist/react/provider.js +38 -0
  131. package/dist/react/provider.js.map +1 -0
  132. package/dist/react/selection-store.d.ts +51 -0
  133. package/dist/react/selection-store.d.ts.map +1 -0
  134. package/dist/react/selection-store.js +239 -0
  135. package/dist/react/selection-store.js.map +1 -0
  136. package/dist/react/slots-types.d.ts +38 -0
  137. package/dist/react/slots-types.d.ts.map +1 -0
  138. package/dist/react/slots-types.js +2 -0
  139. package/dist/react/slots-types.js.map +1 -0
  140. package/dist/react/slots.d.ts +40 -0
  141. package/dist/react/slots.d.ts.map +1 -0
  142. package/dist/react/slots.js +165 -0
  143. package/dist/react/slots.js.map +1 -0
  144. package/dist/react/use-pricing-selection.d.ts +10 -0
  145. package/dist/react/use-pricing-selection.d.ts.map +1 -0
  146. package/dist/react/use-pricing-selection.js +44 -0
  147. package/dist/react/use-pricing-selection.js.map +1 -0
  148. package/dist/react/use-pricing-source.d.ts +34 -0
  149. package/dist/react/use-pricing-source.d.ts.map +1 -0
  150. package/dist/react/use-pricing-source.js +186 -0
  151. package/dist/react/use-pricing-source.js.map +1 -0
  152. package/dist/react/use-renderer-state.d.ts +22 -0
  153. package/dist/react/use-renderer-state.d.ts.map +1 -0
  154. package/dist/react/use-renderer-state.js +112 -0
  155. package/dist/react/use-renderer-state.js.map +1 -0
  156. package/dist/space/index.d.ts +26 -0
  157. package/dist/space/index.d.ts.map +1 -0
  158. package/dist/space/index.js +71 -0
  159. package/dist/space/index.js.map +1 -0
  160. package/dist/styles/base.css +1030 -76
  161. package/dist/styles/theme-minimal.css +103 -0
  162. package/dist/styles/theme.css +22 -3
  163. package/dist/styles/tokens-shadcn.css +40 -0
  164. package/dist/yaml/index.d.ts +2 -0
  165. package/dist/yaml/index.d.ts.map +1 -1
  166. package/dist/yaml/index.js +1 -0
  167. package/dist/yaml/index.js.map +1 -1
  168. package/dist/yaml/load.d.ts.map +1 -1
  169. package/dist/yaml/load.js +106 -17
  170. package/dist/yaml/load.js.map +1 -1
  171. package/dist/yaml/source.d.ts +18 -0
  172. package/dist/yaml/source.d.ts.map +1 -0
  173. package/dist/yaml/source.js +46 -0
  174. package/dist/yaml/source.js.map +1 -0
  175. package/package.json +25 -13
package/CHANGELOG.md CHANGED
@@ -1,5 +1,137 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.0] - 2026-11-07
4
+
5
+ ### Highlights
6
+
7
+ - **Subscription changes.** A review screen, `<pricing-change-review>`, shows what
8
+ changes between the current subscription and the new one: plan, price and its
9
+ difference, add-ons, features and usage limits. The core gains
10
+ `compareSelections`, `reconcileSelection` and the DOM-free selection logic
11
+ (`planSelectionChange`, `applySelectionChange` and friends); the element shows
12
+ the customer's current plan through `currentSelection` and loads a known one
13
+ with `applySelection`; `pricing-renderer/space` adds helpers for SPACE
14
+ contracts.
15
+ - **Headless React.** `PricingRendererProvider` and the
16
+ `pricing-renderer/react/headless` entry, with the `usePricingSelection`,
17
+ `usePricingSource` and `usePricingRendererState` hooks, render props for the
18
+ CTA, summary actions, loading and error states, a `style` prop and typed DOM
19
+ events.
20
+ - **Fitting a host.** `slots`, `hideSections` (whole sections and the pieces of
21
+ the hero), `density`, new design tokens, `theme-minimal.css`,
22
+ `tokens-shadcn.css`, a host `.dark` class, stable state attributes and
23
+ per-locale unit labels.
24
+ - **Large pricings.** Compact add-on cards above eight add-ons with a filter,
25
+ "Only differences", comparison groups by tag, fluid plan columns, a sticky
26
+ plan header and an "Add-on" marker in the comparison.
27
+ - **Add-ons and totals.** An add-on that cannot be combined with one already
28
+ selected is shown as unavailable, naming what blocks it; quantity relations
29
+ sit on the face of the card; a price breakdown explains the estimated total,
30
+ which no longer hides inside the add-on layout.
31
+ - **Loading and robustness.** `loadPricingSource`, timeouts and size limits on
32
+ every step of a load, cancellation of stale loads, a human error sentence and
33
+ instance-prefixed, collision-free ids.
34
+ - **Accessibility and security.** Focus stays inside the renderer when content
35
+ collapses, variable controls describe their help and errors, and
36
+ `isSafeLink` rejects more unsafe link forms.
37
+
38
+ ### Publication
39
+
40
+ Published by hand with npm two-factor authentication while GitHub Actions was
41
+ unavailable, so this version carries no npm provenance attestation. The release
42
+ workflow publishes later versions with provenance again.
43
+
44
+ ### Behaviour changes to check when upgrading
45
+
46
+ - An add-on excluded by a selected add-on is no longer selectable; the
47
+ confirmation dialog no longer opens for exclusions.
48
+ - Price expressions use strict equality for `==` and `!=`.
49
+ - Add-on quantities are always kept on the min/step grid.
50
+ - The comparison groups rows by their declared `tag`, and "Only differences"
51
+ starts checked for long comparisons when the attribute is absent.
52
+ - The estimated total is no longer rendered inside the add-on layout, which
53
+ drops its sidebar column.
54
+
55
+ ### All changes
56
+
57
+ #### Minor
58
+
59
+ - An add-on that cannot be combined with one already selected is now shown as unavailable, the way an add-on the plan does not offer already was: not selectable, its checkbox disabled, and a status on the card that names what blocks it — "Not available with Github Codespaces 2 Core" — instead of letting a click open a dialog to say so.
60
+ - Add `applySelection(selection)` to `<pricing-renderer>` to load a known selection, such as the customer's current subscription, once the pricing is ready.
61
+ - Review screen for a subscription change.
62
+ - Export the selection logic of the element from the core, free of the DOM: `planSelectionChange`, `applySelectionChange`, `selectionWithVariable`, `selectionWithAddOnQuantity`, `selectionInEffect` and `createPriceStabilizer`, with the types `SelectionChangeIntent`, `SelectionChangeResult`, `PriceStabilizer` and `PendingAddOnChange`.
63
+ - `<pricing-renderer>` can show the customer's current subscription.
64
+ - New opt-in `density` attribute, property and React prop (`'comfortable' | 'compact'`).
65
+ - New optional design tokens for fitting the renderer into a host design system.
66
+ - Add `reconcileSelection`, which deselects add-ons that are unavailable for the selected plan and the add-ons that depend on them.
67
+ - `<pricing-renderer>` accepts a `slots` property (no attribute) with the names `plan-cta`, `summary-actions`, `loading` and `error`.
68
+ - `<pricing-renderer>` and the React `PricingRenderer` accept `hideSections` (attribute `hide-sections`), a list of section names (`hero`, `metadata`, `billing`, `variables`, `plans`, `comparison`, `addOns`, `summary`) that are not rendered.
69
+ - Darken the renderer and the change review when a host `.dark` class is on an ancestor and the element has `theme="auto"`, with the same tokens as `data-theme='dark'`.
70
+ - Add `loadPricingSource` to `pricing-renderer/yaml`.
71
+ - Type `addEventListener` for `pricing-ready`, `pricing-selection-change`, `pricing-action` and `pricing-diagnostic` through a global `HTMLElementEventMap` augmentation, so `element.addEventListener('pricing-action', (event) => event.detail.actionId)` needs no cast.
72
+ - Add the warning `PR_VALUE_TYPE_MISMATCH` when a `NUMERIC` feature or usage limit has a text value (default value, plan or add-on override, `{ value: ...
73
+ - Explain the estimated total with a price breakdown.
74
+ - Add `PricingRendererProvider` and the `pricing-renderer/react/headless` entry.
75
+ - `PricingRenderer` accepts the render props `renderPlanCta`, `renderSummaryActions`, `renderLoading` and `renderError`.
76
+ - `PricingRenderer` in the React adapter now accepts a `style` prop.
77
+ - Four changes to what the renderer puts on the page.
78
+ - Three fixes to the change review, all found by driving it on a real page.
79
+ - `<pricing-change-review>` takes a `hideSections` property (and a `hide-sections` attribute) with three parts: `actions`, for a diff the reader cannot act on; `relation`, the Upgrade/Downgrade badge, which follows the declaration order of `pricing.plans` and reads as a ladder that two configurations compared side by side do not have; and `title`, for a host whose own heading already names the comparison.
80
+ - Security hardening: `isSafeLink`, and so plan CTA links, comparison-row documentation links and the metadata URL, now rejects values that start with `//` or `/\` (also with tab or newline characters between them) because browsers read them as links to another host.
81
+ - Export `selectionToSearchParams` and `selectionFromSearchParams` from the core, free of the DOM, with the option types `SelectionToSearchParamsOptions` and `SelectionFromSearchParamsOptions`.
82
+ - Pricings with more than 8 add-ons now render compact add-on cards of equal height per row, with the availability, dependency, exclusion and quantity details moved into the existing details disclosure (new i18n key `pricing.moreDetails`).
83
+ - The React adapter now accepts the new `onlyDifferences`, `toolbarThreshold` and `addOnFilterThreshold` props.
84
+ - The comparison groups now follow the declared `tag` of features and usage limits (visual change, approved by the owner).
85
+ - New `only-differences` attribute sets the initial state of the comparison's "Only differences" checkbox; when it is absent, the checkbox starts checked for pricings with more than 100 comparison rows and 2 plans or more (visual change by default for those pricings, approved by the owner).
86
+ - Add `presentation.unitLabels` to set the singular and plural text of a usage unit per locale and unit, with `*` as the default for every locale.
87
+ - Plan and add-on cards and plan calls to action now expose stable state attributes: `data-featured` on plan cards, `data-available` on add-on cards and `data-kind` (`primary` or `secondary`) on plan calls to action.
88
+ - Subscription change flow: comparison, SPACE helpers and guide.
89
+ - New core function `compareSelections({ before, after, currentUsage?, expression?
90
+ - New subpath `pricing-renderer/space` with helpers for SPACE contracts.
91
+ - The running total no longer sits inside the add-on layout.
92
+ - Add `pricing-renderer/theme-minimal.css`, a flat neutral theme (grayscale, no shadows, borders in the text color) that replaces `theme.css` and serves as a starting point for custom themes.
93
+ - Add `pricing-renderer/tokens-shadcn.css`, an overlay that maps the renderer tokens to the shadcn/ui variables (`--primary`, `--card`, `--border`, `--radius`, ...) and follows the host `.dark` class.
94
+ - Add the `usePricingSelection` hook to the `pricing-renderer/react/headless` entry, also re-exported from `pricing-renderer/react`.
95
+ - Add the `usePricingSource` hook to the `pricing-renderer/react/headless` entry, also re-exported from `pricing-renderer/react`.
96
+ - Add the `usePricingRendererState` hook to the `pricing-renderer/react/headless` entry, also re-exported from `pricing-renderer/react`.
97
+
98
+ #### Patch
99
+
100
+ - The "Add-on" marker now shows only in plans where a providing add-on can be bought; other cells keep the dash.
101
+ - Price expressions now use strict equality for `==`, `===`, `!=` and `!==`, so `-0` equals `0` and `NaN` never equals `NaN`.
102
+ - Add-on quantities are now always on the min/step grid: a quantity above the maximum is capped to the largest valid value (min 1, step 5, max 8 bills 6, not 8), and the quantity stepper uses the same limit.
103
+ - `maxQuantityOnGrid` never exceeds the maximum because of floating-point error, and the quantity stepper in controlled mode shows and steps from the normalized quantity.
104
+ - Add a design systems guide (`docs/design-systems.md`) for Tailwind CSS, CSS Modules and shadcn/ui hosts.
105
+ - Document the events, the diagnostic codes and the Vue 3 integration.
106
+ - Document which pricing version a page shows (`docs/pricing-versions.md`): signed-out visitors see the version SPACE assigns to new contracts, signed-in customers see their contract's version, and a SPACE-managed service is read through SPACE and not straight from SPHERE.
107
+ - Document private pricings.
108
+ - The element now uses the core selection functions (`planSelectionChange`, `applySelectionChange`, `selectionWithVariable`, `selectionWithAddOnQuantity`, `selectionInEffect` and `createPriceStabilizer`).
109
+ - The focus ring on a comparison row that takes focus after "Show N more" is now drawn inside the row, so the sticky first column and the scroll container no longer hide it.
110
+ - Ids derived from data (variable paths, group ids, add-on ids) now escape every character other than letters and digits as `_<hex>_`, so two different values can no longer produce the same id; these ids are not a documented hook.
111
+ - Count the response body while `loadPricingFromUrl` reads it.
112
+ - Apply `timeoutMs` and the abort signal to every step of `loadPricingFromUrl`: the `headers` function, `loadPricing`, `fetch` and the body read.
113
+ - Show add-on names instead of identifiers in the "Requires ..." and "Cannot be combined with ..." lines of an add-on card and in the "Will add" and "Will remove" lines of the confirmation dialog.
114
+ - Below 840px the top padding, the spacing between the rows above the plans (metadata, variables and diagnostics included) and the margin under the plans heading are capped exactly as they already are from 840px, so the first plan price fits in the first screen of a phone.
115
+ - Show a status message instead of an empty comparison table when a pricing has no plans.
116
+ - Show a human sentence in the element's error state instead of raw diagnostic codes.
117
+ - Compare the `pricing`, `request` and `normalizeOptions` properties of the element by content (arrays and plain objects; functions, `Headers`, `AbortSignal` and other class instances by identity).
118
+ - Give every `id` the element renders an instance prefix (`pr-<n>-plans-title`, `pr-<n>-dialog-title`, `pr-<n>-group-<id>`, and so on), so two renderers on one page no longer share ids and their `aria-labelledby`, `aria-describedby`, `aria-controls` and `for` references stay inside their own renderer.
119
+ - Apply `request.maxBytes` (default 2 MiB) to a string returned by a custom `loadPricing`, measured in UTF-8 bytes.
120
+ - Localize the loading text of the element: while it is idle or loading, the hidden heading and the status text use `loadingLabel` when set, and otherwise `pricing.loading` from the element's `locale` and `messages`, instead of a fixed English string.
121
+ - A short comparison with `only-differences` now shows every row.
122
+ - Resume a pricing load when the element is moved to another parent while it is still loading: the element observes its size again, measures its width and reloads, instead of staying on the loading skeleton.
123
+ - Cancel the pricing load in flight when `reload()` finds no source or more than one: a slow `src` load that finishes after `src` was removed no longer emits `pricing-ready` or replaces the `PR_SOURCE_MISSING` / `PR_SOURCE_CONFLICT` error.
124
+ - Tell the host how to fix a `PR_VERSION_UNSUPPORTED_MAJOR` error: for a major below 3 the message says to upgrade the pricing to 3.x on the server, for example with `retrievePricingFromText` from `pricing4ts/server`; for a major above 3 it says the version is not supported yet.
125
+ - Pluralize the usage unit next to a numeric value in the comparison when the UI locale is English, so `3 unit` reads `3 units`.
126
+ - Hydrating the React wrapper (`PricingRenderer`, `PricingChangeReview`) after the element has rendered no longer logs a recoverable hydration error and no longer replaces the host node.
127
+ - Keyboard focus now stays inside the renderer when "Show N more" disappears in the comparison or in the add-on grid.
128
+ - Clear the pending add-on change on direct commits, show loading after a stopped reload, and name the headless re-exports of the React entry.
129
+ - The comparison no longer renders a row description that is empty or only repeats the row name.
130
+ - Show an "Add-on" marker instead of the em dash in comparison rows that only an add-on provides (Spanish: Complemento).
131
+ - Plan cards now fill the width in fluid columns chosen by the number of plans and the renderer width (at most 3 columns from 840px and at most 4 from 1040px, without a single card left alone in the last row where it can be avoided), and from 840px the hero title is smaller (3rem at most) and the plan cards and the space above them are tighter, so the first plan button fits in the first screen at 1440px.
132
+ - The comparison table now scrolls vertically inside a box of at most 80% of the viewport height, so the plan header stays visible while reading a long table (the first column stays pinned and the mobile card layout is unchanged).
133
+ - Point each variable control's `aria-describedby` at its help text and its error message, which now carry the ids `<control id>-help` and `<control id>-error`, so a screen reader reads both when the control is focused.
134
+
3
135
  ## [0.1.0] - 2026-07-29
4
136
 
5
137
  - Initial public npm release of the DOM-free TypeScript core, light-DOM Web
@@ -18,4 +150,5 @@
18
150
  - Public documentation, Vanilla/React/Next examples, visual baselines, and
19
151
  GitHub Release CD with npm provenance.
20
152
 
153
+ [0.2.0]: https://github.com/javiercavlop/pricing-renderer/releases/tag/v0.2.0
21
154
  [0.1.0]: https://github.com/javiercavlop/pricing-renderer/releases/tag/v0.1.0
package/README.md CHANGED
@@ -36,7 +36,7 @@ Source, issues, release notes, and immutable version tags remain public in
36
36
 
37
37
  ## Review the live example
38
38
 
39
- Node.js 22+ and pnpm are required for local development:
39
+ Node.js 22.12 or newer and pnpm are required for local development (the development tools, Vite and Vitest, require it; the published library still supports `engines.node` `>=22`):
40
40
 
41
41
  ```bash
42
42
  git clone https://github.com/javiercavlop/pricing-renderer.git
@@ -57,6 +57,10 @@ pnpm demo:react
57
57
  pnpm demo:next
58
58
  ```
59
59
 
60
+ A Vue 3 + Vite example is in [`examples/vue`](./examples/vue); run it with
61
+ `pnpm build && pnpm --filter pricing-renderer-vue-demo dev`. See the
62
+ [examples index](./examples/README.md).
63
+
60
64
  The Next example keeps the page server-rendered and isolates only the interactive
61
65
  renderer behind a client boundary.
62
66
 
@@ -79,22 +83,30 @@ yarn add pricing-renderer
79
83
  Pin the initial release when an exact, reproducible version is required:
80
84
 
81
85
  ```bash
82
- npm install pricing-renderer@0.1.0
86
+ npm install pricing-renderer@0.2.0
87
+ ```
88
+
89
+ Then import the renderer and its complete default stylesheet:
90
+
91
+ ```ts
92
+ import 'pricing-renderer/define';
93
+ import 'pricing-renderer/styles.css';
83
94
  ```
84
95
 
85
96
  Release locations:
86
97
 
87
98
  - npm package and provenance:
88
99
  [`pricing-renderer`](https://www.npmjs.com/package/pricing-renderer)
89
- - `0.1.0` release notes and source tag:
90
- [`v0.1.0`](https://github.com/javiercavlop/pricing-renderer/releases/tag/v0.1.0)
100
+ - `0.2.0` release notes and source tag:
101
+ [`v0.2.0`](https://github.com/javiercavlop/pricing-renderer/releases/tag/v0.2.0)
91
102
  - all GitHub releases:
92
103
  [`javiercavlop/pricing-renderer/releases`](https://github.com/javiercavlop/pricing-renderer/releases)
93
104
 
94
105
  Every published GitHub Release triggers the npm CD workflow. Stable releases
95
106
  publish under the `latest` dist-tag and GitHub prereleases under `next`; a tag,
96
107
  version, documentation, test, accessibility, visual, or package-validation
97
- failure stops before `npm publish`.
108
+ failure stops before `npm publish`. Publication uses npm Trusted Publishing with
109
+ short-lived GitHub OIDC credentials; the workflow contains no npm access token.
98
110
 
99
111
  Confirm the installed registry version with:
100
112
 
@@ -105,18 +117,47 @@ npm view pricing-renderer version
105
117
  See the [release guide](./docs/releasing.md) for provenance, validation, and
106
118
  Trusted Publishing details.
107
119
 
120
+ ### Reusable, tokenless CD
121
+
122
+ The same [release workflow](./.github/workflows/release.yml) handles
123
+ `release.published` automatically and exposes `workflow_call` with a required
124
+ `release_tag` input. A caller in this repository can reuse all release gates:
125
+
126
+ ```yaml
127
+ jobs:
128
+ publish:
129
+ permissions:
130
+ contents: read
131
+ id-token: write
132
+ uses: ./.github/workflows/release.yml
133
+ with:
134
+ release_tag: v0.2.0
135
+ ```
136
+
137
+ The workflow queries GitHub before checkout and refuses drafts, missing releases,
138
+ or mismatched tags. It derives `latest` versus `next` from the published Release,
139
+ so callers cannot silently override the npm channel. No secrets are accepted or
140
+ forwarded. npm trust remains intentionally bound to this package, repository,
141
+ workflow filename, and `npm` environment; another package must configure its own
142
+ Trusted Publisher.
143
+
108
144
  ## Package entry points
109
145
 
110
- | Import | Purpose |
111
- | ----------------------------- | --------------------------------------------------------------- |
112
- | `pricing-renderer` | Types, normalization, resolution, expressions, i18n, view-model |
113
- | `pricing-renderer/yaml` | YAML parsing and bounded remote loading |
114
- | `pricing-renderer/element` | Web Component class without global registration |
115
- | `pricing-renderer/define` | Register `<pricing-renderer>` |
116
- | `pricing-renderer/react` | Typed React 18/19 adapter |
117
- | `pricing-renderer/base.css` | Required structural and accessibility styles |
118
- | `pricing-renderer/theme.css` | Default professional visual tokens |
119
- | `pricing-renderer/styles.css` | Base and theme combined |
146
+ | Import | Purpose |
147
+ | --------------------------------------- | --------------------------------------------------------------- |
148
+ | `pricing-renderer` | Types, normalization, resolution, expressions, i18n, view-model |
149
+ | `pricing-renderer/yaml` | YAML parsing and bounded remote loading |
150
+ | `pricing-renderer/element` | Web Component class without global registration |
151
+ | `pricing-renderer/define` | Register `<pricing-renderer>` |
152
+ | `pricing-renderer/define-change-review` | Register `<pricing-change-review>` |
153
+ | `pricing-renderer/react` | Typed React 18/19 adapter |
154
+ | `pricing-renderer/react/headless` | React provider and hooks, no Lit and no element |
155
+ | `pricing-renderer/space` | SPACE helpers: selection, novation body, version, usage |
156
+ | `pricing-renderer/base.css` | Required structural and accessibility styles |
157
+ | `pricing-renderer/theme.css` | Default professional visual tokens |
158
+ | `pricing-renderer/styles.css` | Base and theme combined |
159
+ | `pricing-renderer/tokens-shadcn.css` | Overlay that maps the tokens to shadcn/ui variables |
160
+ | `pricing-renderer/theme-minimal.css` | Flat grayscale theme that replaces `theme.css` |
120
161
 
121
162
  ## Web Component
122
163
 
@@ -139,8 +180,9 @@ configurePricingRenderer({
139
180
 
140
181
  Every option is optional. The deterministic built-in defaults are English
141
182
  (`en-US`), `/pricing`, selectable plans/add-ons, visible CTAs, and editable
142
- variables. `pricingPath` is the canonical host-application route; the library
143
- exposes it but does not mutate or install routes in the host router. An
183
+ variables. `pricingPath` is deprecated metadata kept for compatibility: the
184
+ host router owns the route, and nothing in the renderer reads the value (it is
185
+ only validated, stored and reflected as the `pricing-path` attribute). An
144
186
  individual element or React instance can override any value. With
145
187
  `variablesEnabled: false`, formulas use the iPricing variable defaults.
146
188
 
@@ -163,8 +205,9 @@ Render a public remote source:
163
205
  ></pricing-renderer>
164
206
  ```
165
207
 
166
- `locale` and `pricingPath` are initial host configuration. Set them globally,
167
- when the renderer is created, or through the equivalent properties/React props.
208
+ `locale` is initial host configuration (`pricingPath` is deprecated and has no
209
+ effect). Set it globally, when the renderer is created, or through the
210
+ equivalent property/React prop.
168
211
  The library does not inject a language picker into production UI. The showcase
169
212
  picker only demonstrates that locale configuration can be changed reactively.
170
213
 
@@ -210,6 +253,29 @@ The React entry is safe to import during SSR and is marked as a client
210
253
  component. Full pricing content appears after hydration; deep light-DOM SSR is
211
254
  not a v1 contract.
212
255
 
256
+ The adapter assigns every prop on each render. The element compares `pricing`,
257
+ `request` and `normalizeOptions` by content, so an inline `request` object with
258
+ the same content does not reload. Functions, `Headers`, `AbortSignal` and other
259
+ class instances are compared by identity, and so are `loadPricing`, `yaml` and
260
+ `src`. Keep `loadPricing` and a `headers` function stable with `useCallback` or
261
+ `useMemo`, or the element fetches again on every render. Call `reload()` on a ref
262
+ to force a fetch, also after you changed an input object in place. The rules are
263
+ in the [API reference](./docs/api-reference.md#when-a-property-starts-a-new-load).
264
+
265
+ `pricing-renderer/react` needs both `react` and `react-dom` (the render props use
266
+ `createPortal`). For your own pricing UI, `pricing-renderer/react/headless` has no Lit and no
267
+ element: `PricingRendererProvider` shares settings without writing a global,
268
+ `usePricingSource` loads a pricing, `usePricingSelection` holds the selection and view model, and
269
+ `usePricingRendererState(ref)` reads the state of a rendered `PricingRenderer`.
270
+ `PricingRenderer` also takes `renderPlanCta`, `renderSummaryActions`, `renderLoading` and
271
+ `renderError` to draw your React output inside the renderer. See the
272
+ [React guide](./docs/react.md) and the [Next.js guide](./docs/nextjs.md).
273
+
274
+ ## Vue 3
275
+
276
+ The element works in Vue 3 with `isCustomElement` and the `.prop` modifier for
277
+ object properties. See the [Vue 3 and Vite guide](./docs/vue.md).
278
+
213
279
  ## Plan highlights, inheritance, and badges
214
280
 
215
281
  Card highlights can reference both features and usage limits without ambiguity.
@@ -323,26 +389,55 @@ Public events:
323
389
  - `pricing-selection-change`
324
390
  - `pricing-action`
325
391
  - `pricing-diagnostic`
392
+ - `pricing-slots-change`
393
+
394
+ `addEventListener` is typed for these five names through the global
395
+ `HTMLElementEventMap`, so a listener needs no cast. See the
396
+ [events reference](./docs/events.md) for each payload and the diagnostic codes.
326
397
 
327
398
  ## Private remote sources
328
399
 
329
- Secrets are property-only and never reflected into markup:
400
+ Never put a SPHERE or SPACE key in browser code. The safe default is a same-origin
401
+ server route that holds the key and serves the pricing:
402
+
403
+ ```ts
404
+ renderer.src = '/api/pricing';
405
+ renderer.request = {
406
+ credentials: 'same-origin',
407
+ timeoutMs: 15_000,
408
+ maxBytes: 2 * 1024 * 1024,
409
+ };
410
+ ```
411
+
412
+ `request` is property-only and never reflected into markup. Use `request.headers`
413
+ only for short-lived tokens of the signed-in user, and never put a token in `src`:
330
414
 
331
415
  ```ts
332
- renderer.src = 'https://api.example.com/private/pricing.yml';
333
416
  renderer.request = {
334
- credentials: 'include',
335
417
  headers: async () => ({
336
418
  Authorization: `Bearer ${await refreshAccessToken()}`,
337
419
  }),
338
- timeoutMs: 15_000,
339
- maxBytes: 2 * 1024 * 1024,
340
420
  };
341
421
  ```
342
422
 
423
+ See [Private pricings](./docs/private-pricings.md) for Next.js and Express recipes,
424
+ version pinning and ETag revalidation.
425
+
343
426
  Use `loadPricing` for OAuth exchanges, signed URLs, proxies, or SDK clients. The
344
427
  built-in loader permits HTTP/HTTPS `GET` only, defaults to
345
428
  `credentials: "omit"`, honors cancellation, and never fetches during SSR.
429
+ `request.timeoutMs` and an abort cut off every step: a `loadPricing`, `fetch` or
430
+ `headers` function that ignores the signal fails the load with
431
+ `PR_SOURCE_ABORTED`, its late result is ignored, and its work is not stopped. A
432
+ signal already aborted at the start fails the load with `PR_SOURCE_ABORTED`.
433
+ `request.maxBytes` (2 MiB by default) also limits a string that `loadPricing`
434
+ returns, measured in UTF-8 bytes; a larger one fails with `PR_SOURCE_TOO_LARGE`.
435
+ An object result is not measured.
436
+
437
+ If the first load fails, the element shows a short sentence for the kind of
438
+ failure (source, format, version or other) and a closed "Technical details"
439
+ disclosure with the diagnostic codes. The codes are also in the `pricing-diagnostic`
440
+ event. See [Error state](./docs/api-reference.md#error-state).
346
441
 
347
442
  ## Theming
348
443
 
@@ -381,15 +476,27 @@ Extend future/vendor syntax with `PricingSyntaxAdapter`, translations with
381
476
  `registerMessageCatalog`, and domain calculations through explicitly
382
477
  allow-listed expression functions. See [Extending](./docs/extending.md).
383
478
 
479
+ ## Used by
480
+
481
+ - [Delivera](https://github.com/isa-group/Delivera) uses Pricing Renderer to show its pricing plans.
482
+
384
483
  ## Documentation
385
484
 
386
485
  - [Architecture](./docs/architecture.md)
387
486
  - [API reference](./docs/api-reference.md)
388
487
  - [Project and instance configuration](./docs/configuration.md)
488
+ - [Events and diagnostics](./docs/events.md)
489
+ - [React: provider, hooks and render props](./docs/react.md)
490
+ - [Next.js (App Router)](./docs/nextjs.md)
491
+ - [Vue 3 and Vite](./docs/vue.md)
492
+ - [Private pricings](./docs/private-pricings.md)
493
+ - [Preload the current subscription](./docs/preload-current-subscription.md)
494
+ - [Change a subscription](./docs/change-subscription-flow.md)
389
495
  - [Demo and release acceptance](./docs/demo.md)
390
496
  - [Release and npm publication](./docs/releasing.md)
391
497
  - [Extending the library](./docs/extending.md)
392
498
  - [Theming](./docs/theming.md)
499
+ - [Design systems](./docs/design-systems.md)
393
500
  - [Internationalization](./docs/i18n.md)
394
501
  - [Pricing2Yaml compatibility](./docs/pricing2yaml-compatibility.md)
395
502
  - [Contributing](./CONTRIBUTING.md)
@@ -409,7 +516,9 @@ pnpm package:check
409
516
  ```
410
517
 
411
518
  Browser coverage includes Chromium, Firefox, WebKit, Axe, keyboard paths, and
412
- visual breakpoints. Releases use Changesets, SemVer, npm provenance, and MIT.
519
+ visual breakpoints. Releases use Changesets, SemVer, and MIT, and the release
520
+ workflow publishes them with npm provenance. 0.2.0 is the exception: it was
521
+ published by hand while GitHub Actions was unavailable, so it carries none.
413
522
 
414
523
  ## Security
415
524
 
@@ -417,3 +526,11 @@ Pricing text is escaped, links use safe schemes, and expressions cannot reach
417
526
  globals or prototypes. `private` only controls presentation—remove confidential
418
527
  data before delivering YAML to a browser. Report vulnerabilities through the
419
528
  process in [SECURITY.md](./SECURITY.md).
529
+
530
+ ## Funding
531
+
532
+ This work has been partially supported by PID2024-155693NB-C44, funded by
533
+ MICIU/AEI/10.13039/501100011033/FEDER, EU; SOL2024-31777 funded by the PPIT of
534
+ the Universidad de Sevilla under the Programa Operativo FEDER Andalucía
535
+ 2021-2027; and the Horizonte 2027 doctoral training program funded by Junta de
536
+ Andalucía (Consejería de Universidad, Investigación e Innovación).
@@ -1,10 +1,88 @@
1
1
  # Third-party notices
2
2
 
3
- This project was informed by the following MIT-licensed projects. Their editor
4
- implementations are not bundled into this package.
3
+ ## Projects that informed this package
5
4
 
6
- - Sphere — Copyright Alex-GF and contributors — https://github.com/Alex-GF/Sphere
7
- - Pricing4TS — Copyright Alex-GF and contributors — https://github.com/Alex-GF/Pricing4TS
5
+ This project was informed by the following projects, both licensed under the
6
+ MIT License (reproduced below). Their editor implementations are not bundled
7
+ into this package.
8
8
 
9
- The full dependency license inventory is generated from the lockfile for every
10
- release. Each runtime dependency retains its own copyright and license terms.
9
+ - Sphere — Copyright (c) 2024 Alejandro García Fernández — https://github.com/Alex-GF/Sphere
10
+ - Pricing4TS — Copyright (c) 2024 Alejandro García Fernández — https://github.com/Alex-GF/Pricing4TS
11
+
12
+ The copyright lines above were checked against the `LICENSE` file at the
13
+ default branch of each repository.
14
+
15
+ ```text
16
+ MIT License
17
+
18
+ Copyright (c) 2024 Alejandro García Fernández
19
+
20
+ Permission is hereby granted, free of charge, to any person obtaining a copy
21
+ of this software and associated documentation files (the "Software"), to deal
22
+ in the Software without restriction, including without limitation the rights
23
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24
+ copies of the Software, and to permit persons to whom the Software is
25
+ furnished to do so, subject to the following conditions:
26
+
27
+ The above copyright notice and this permission notice shall be included in all
28
+ copies or substantial portions of the Software.
29
+
30
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36
+ SOFTWARE.
37
+ ```
38
+
39
+ ## Direct runtime dependencies
40
+
41
+ Versions and licenses were read from the `package.json` files installed in
42
+ `node_modules` after `pnpm install --frozen-lockfile`. They reflect the
43
+ lockfile at the time of writing and are maintained by hand; this file is not
44
+ regenerated automatically.
45
+
46
+ | Package | Version | License |
47
+ | ---------- | ------- | ------------ |
48
+ | lit | 3.3.3 | BSD-3-Clause |
49
+ | @lit/react | 1.0.8 | BSD-3-Clause |
50
+ | yaml | 2.9.0 | ISC |
51
+
52
+ ## Optional peer dependencies
53
+
54
+ These are not bundled or installed by this package. They are only needed for
55
+ the React entry point. Versions are the ones installed for development.
56
+
57
+ | Package | Version | License |
58
+ | --------- | ------- | ------- |
59
+ | react | 19.2.8 | MIT |
60
+ | react-dom | 19.2.8 | MIT |
61
+
62
+ ## Transitive dependencies
63
+
64
+ Transitive dependencies are not listed here. Each dependency retains its own
65
+ copyright and license terms; see the package contents in `node_modules` or the
66
+ lockfile for the full set.
67
+
68
+ ## Test data (not distributed)
69
+
70
+ `tests/fixtures/spectra/` holds 37 iPricings from two sources:
71
+
72
+ - SaaS Pricing Evaluation Corpus for Temporal Representation & Analysis
73
+ (SPECTRA) — Francisco Javier Cavero, José Antonio Parejo, Juan C. Alonso and
74
+ Antonio Ruiz-Cortés, 2025, Zenodo — version 1.2.0,
75
+ <https://doi.org/10.5281/zenodo.17237164> — licensed under
76
+ [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). 30 files.
77
+ - pricing-intelligence-labpack (isa-group), extended copy of SPECTRA — commit
78
+ `49e90f5a3d4dde91d15614e5c8269085888b6f0b` of
79
+ <https://github.com/isa-group/pricing-intelligence-labpack> — licensed under
80
+ [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) according to the
81
+ `LICENSE.txt` at the root of the repository. 7 files: CircleCI, Okta,
82
+ Shopify, Trello, Webflow, ZenHub and Zoom.
83
+
84
+ Modifications: the files were upgraded from Pricing2Yaml 2.1 to 3.1 with
85
+ pricing4ts 0.12.1 and one Trello value was corrected; the fixture README lists
86
+ the changes.
87
+
88
+ The tests use these files; the published package does not contain them.