pricing-renderer 0.1.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.
- package/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +419 -0
- package/THIRD_PARTY_NOTICES.md +10 -0
- package/custom-elements.json +122 -0
- package/dist/core/config.d.ts +6 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +62 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/expression.d.ts +4 -0
- package/dist/core/expression.d.ts.map +1 -0
- package/dist/core/expression.js +583 -0
- package/dist/core/expression.js.map +1 -0
- package/dist/core/i18n.d.ts +10 -0
- package/dist/core/i18n.d.ts.map +1 -0
- package/dist/core/i18n.js +207 -0
- package/dist/core/i18n.js.map +1 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +8 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/normalize.d.ts +4 -0
- package/dist/core/normalize.d.ts.map +1 -0
- package/dist/core/normalize.js +301 -0
- package/dist/core/normalize.js.map +1 -0
- package/dist/core/resolve.d.ts +6 -0
- package/dist/core/resolve.d.ts.map +1 -0
- package/dist/core/resolve.js +123 -0
- package/dist/core/resolve.js.map +1 -0
- package/dist/core/types.d.ts +308 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/utils.d.ts +16 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +125 -0
- package/dist/core/utils.js.map +1 -0
- package/dist/core/view-model.d.ts +9 -0
- package/dist/core/view-model.d.ts.map +1 -0
- package/dist/core/view-model.js +427 -0
- package/dist/core/view-model.js.map +1 -0
- package/dist/element/define.d.ts +6 -0
- package/dist/element/define.d.ts.map +1 -0
- package/dist/element/define.js +20 -0
- package/dist/element/define.js.map +1 -0
- package/dist/element/events.d.ts +31 -0
- package/dist/element/events.d.ts.map +1 -0
- package/dist/element/events.js +2 -0
- package/dist/element/events.js.map +1 -0
- package/dist/element/index.d.ts +8 -0
- package/dist/element/index.d.ts.map +1 -0
- package/dist/element/index.js +2 -0
- package/dist/element/index.js.map +1 -0
- package/dist/element/pricing-renderer-element.d.ts +152 -0
- package/dist/element/pricing-renderer-element.d.ts.map +1 -0
- package/dist/element/pricing-renderer-element.js +598 -0
- package/dist/element/pricing-renderer-element.js.map +1 -0
- package/dist/element/renderers.d.ts +43 -0
- package/dist/element/renderers.d.ts.map +1 -0
- package/dist/element/renderers.js +866 -0
- package/dist/element/renderers.js.map +1 -0
- package/dist/react/index.d.ts +55 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +23 -0
- package/dist/react/index.js.map +1 -0
- package/dist/styles/base.css +1110 -0
- package/dist/styles/styles.css +2 -0
- package/dist/styles/theme.css +84 -0
- package/dist/yaml/index.d.ts +4 -0
- package/dist/yaml/index.d.ts.map +1 -0
- package/dist/yaml/index.js +3 -0
- package/dist/yaml/index.js.map +1 -0
- package/dist/yaml/load.d.ts +3 -0
- package/dist/yaml/load.d.ts.map +1 -0
- package/dist/yaml/load.js +97 -0
- package/dist/yaml/load.js.map +1 -0
- package/dist/yaml/parser.d.ts +4 -0
- package/dist/yaml/parser.d.ts.map +1 -0
- package/dist/yaml/parser.js +80 -0
- package/dist/yaml/parser.js.map +1 -0
- package/dist/yaml/types.d.ts +27 -0
- package/dist/yaml/types.d.ts.map +1 -0
- package/dist/yaml/types.js +2 -0
- package/dist/yaml/types.js.map +1 -0
- package/package.json +132 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-07-29
|
|
4
|
+
|
|
5
|
+
- Initial public npm release of the DOM-free TypeScript core, light-DOM Web
|
|
6
|
+
Component, and React 18/19 adapter.
|
|
7
|
+
- Complete Pricing2Yaml 3.1 normalization, best-effort 3.x compatibility,
|
|
8
|
+
bounded remote loading, and safe AST-based expression evaluation.
|
|
9
|
+
- Responsive commercial and catalog experiences with selectable plan/add-on
|
|
10
|
+
cards, billing, editable variables, constrained quantities, dependency and
|
|
11
|
+
exclusion confirmation, partial totals, and quote pricing.
|
|
12
|
+
- Typed feature and usage-limit highlights with manual, automatic, and hybrid
|
|
13
|
+
modes; verified “Everything in …, plus” inheritance; and configurable plan
|
|
14
|
+
badges.
|
|
15
|
+
- Project, YAML, and per-instance presentation configuration; English and
|
|
16
|
+
Spanish catalogs; accessible light/dark/forced-color themes; RTL; reduced
|
|
17
|
+
motion; CSS tokens; and stable `data-pr-part` hooks.
|
|
18
|
+
- Public documentation, Vanilla/React/Next examples, visual baselines, and
|
|
19
|
+
GitHub Release CD with npm provenance.
|
|
20
|
+
|
|
21
|
+
[0.1.0]: https://github.com/javiercavlop/pricing-renderer/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Francisco Javier Cavero
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# Pricing Renderer
|
|
2
|
+
|
|
3
|
+
[](https://github.com/javiercavlop/pricing-renderer/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/javiercavlop/pricing-renderer/actions/workflows/release.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/pricing-renderer)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](./docs/pricing2yaml-compatibility.md)
|
|
8
|
+
|
|
9
|
+
Turn a Pricing2Yaml document into an elegant, responsive, interactive pricing
|
|
10
|
+
experience. Use the complete light-DOM Web Component, its React 18/19 adapter,
|
|
11
|
+
or the DOM-free TypeScript core to build a product-specific UI.
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
The library is distributed as the public
|
|
16
|
+
[`pricing-renderer`](https://www.npmjs.com/package/pricing-renderer) package.
|
|
17
|
+
Source, issues, release notes, and immutable version tags remain public in
|
|
18
|
+
[`javiercavlop/pricing-renderer`](https://github.com/javiercavlop/pricing-renderer).
|
|
19
|
+
|
|
20
|
+
## Why it exists
|
|
21
|
+
|
|
22
|
+
- Complete Pricing2Yaml 3.1 normalization and best-effort compatibility for
|
|
23
|
+
other 3.x revisions.
|
|
24
|
+
- Reactive formulas parsed into a restricted AST—never `eval` or `Function`.
|
|
25
|
+
- Variables, billing periods, plan comparison, partial totals, quote prices,
|
|
26
|
+
compatible add-ons, dependency/exclusion confirmation, and constrained
|
|
27
|
+
quantities.
|
|
28
|
+
- Commercial and catalog modes with container-responsive layouts from 320 px.
|
|
29
|
+
- Accessible semantic HTML, keyboard flows, dark mode, forced colors, RTL, zoom,
|
|
30
|
+
and reduced motion.
|
|
31
|
+
- CSS variables and stable `data-pr-part` hooks that let the renderer belong to
|
|
32
|
+
the host product.
|
|
33
|
+
- Extensible syntax adapters, locale registry, allow-listed expression
|
|
34
|
+
functions, and preserved custom data.
|
|
35
|
+
- SSR-safe imports and a client-island React/Next integration.
|
|
36
|
+
|
|
37
|
+
## Review the live example
|
|
38
|
+
|
|
39
|
+
Node.js 22+ and pnpm are required for local development:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git clone https://github.com/javiercavlop/pricing-renderer.git
|
|
43
|
+
cd pricing-renderer
|
|
44
|
+
corepack enable
|
|
45
|
+
pnpm install
|
|
46
|
+
pnpm demo
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The showcase uses the real built package. Change seats, boolean/select
|
|
50
|
+
variables, billing, plans, add-ons, quantities, language, theme, and rendering
|
|
51
|
+
mode while watching resolved state and events.
|
|
52
|
+
|
|
53
|
+
Additional runnable integrations:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpm demo:react
|
|
57
|
+
pnpm demo:next
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The Next example keeps the page server-rendered and isolates only the interactive
|
|
61
|
+
renderer behind a client boundary.
|
|
62
|
+
|
|
63
|
+

|
|
64
|
+
|
|
65
|
+
See the full [demo validation guide](./docs/demo.md).
|
|
66
|
+
|
|
67
|
+
## Installation and package location
|
|
68
|
+
|
|
69
|
+
Install the latest stable release from npm:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm install pricing-renderer
|
|
73
|
+
# or
|
|
74
|
+
pnpm add pricing-renderer
|
|
75
|
+
# or
|
|
76
|
+
yarn add pricing-renderer
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Pin the initial release when an exact, reproducible version is required:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install pricing-renderer@0.1.0
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Release locations:
|
|
86
|
+
|
|
87
|
+
- npm package and provenance:
|
|
88
|
+
[`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)
|
|
91
|
+
- all GitHub releases:
|
|
92
|
+
[`javiercavlop/pricing-renderer/releases`](https://github.com/javiercavlop/pricing-renderer/releases)
|
|
93
|
+
|
|
94
|
+
Every published GitHub Release triggers the npm CD workflow. Stable releases
|
|
95
|
+
publish under the `latest` dist-tag and GitHub prereleases under `next`; a tag,
|
|
96
|
+
version, documentation, test, accessibility, visual, or package-validation
|
|
97
|
+
failure stops before `npm publish`.
|
|
98
|
+
|
|
99
|
+
Confirm the installed registry version with:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npm view pricing-renderer version
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
See the [release guide](./docs/releasing.md) for provenance, validation, and
|
|
106
|
+
Trusted Publishing details.
|
|
107
|
+
|
|
108
|
+
## Package entry points
|
|
109
|
+
|
|
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 |
|
|
120
|
+
|
|
121
|
+
## Web Component
|
|
122
|
+
|
|
123
|
+
Initialize project-wide defaults before creating renderer instances:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { configurePricingRenderer } from 'pricing-renderer';
|
|
127
|
+
|
|
128
|
+
configurePricingRenderer({
|
|
129
|
+
locale: 'en-US',
|
|
130
|
+
pricingPath: '/pricing',
|
|
131
|
+
selectionEnabled: true,
|
|
132
|
+
ctaEnabled: true,
|
|
133
|
+
variablesEnabled: true,
|
|
134
|
+
presentation: {
|
|
135
|
+
planInheritance: 'auto',
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Every option is optional. The deterministic built-in defaults are English
|
|
141
|
+
(`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
|
|
144
|
+
individual element or React instance can override any value. With
|
|
145
|
+
`variablesEnabled: false`, formulas use the iPricing variable defaults.
|
|
146
|
+
|
|
147
|
+
Import registration and styles once:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import 'pricing-renderer/define';
|
|
151
|
+
import 'pricing-renderer/styles.css';
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Render a public remote source:
|
|
155
|
+
|
|
156
|
+
```html
|
|
157
|
+
<pricing-renderer
|
|
158
|
+
src="https://cdn.example.com/pricing.yml"
|
|
159
|
+
locale="en-US"
|
|
160
|
+
pricing-path="/pricing"
|
|
161
|
+
mode="commercial"
|
|
162
|
+
theme="auto"
|
|
163
|
+
></pricing-renderer>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`locale` and `pricingPath` are initial host configuration. Set them globally,
|
|
167
|
+
when the renderer is created, or through the equivalent properties/React props.
|
|
168
|
+
The library does not inject a language picker into production UI. The showcase
|
|
169
|
+
picker only demonstrates that locale configuration can be changed reactively.
|
|
170
|
+
|
|
171
|
+
Or assign a YAML string or iPricing-compatible object as a JavaScript property:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
const renderer = document.querySelector('pricing-renderer');
|
|
175
|
+
renderer.yaml = pricingYaml;
|
|
176
|
+
// renderer.pricing = iPricing;
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Exactly one of `pricing`, `yaml`, or `src` is accepted. Conflicts produce a
|
|
180
|
+
structured diagnostic instead of implicit precedence.
|
|
181
|
+
|
|
182
|
+
## React and Next
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
'use client';
|
|
186
|
+
|
|
187
|
+
import { PricingRenderer } from 'pricing-renderer/react';
|
|
188
|
+
import 'pricing-renderer/styles.css';
|
|
189
|
+
|
|
190
|
+
export function PricingPage({ pricing }: { pricing: Record<string, unknown> }) {
|
|
191
|
+
return (
|
|
192
|
+
<PricingRenderer
|
|
193
|
+
pricing={pricing}
|
|
194
|
+
locale="en-US"
|
|
195
|
+
pricingPath="/pricing"
|
|
196
|
+
theme="auto"
|
|
197
|
+
onSelectionChange={(event) => {
|
|
198
|
+
console.info(event.detail.selection, event.detail.resolved);
|
|
199
|
+
}}
|
|
200
|
+
onAction={(event) => {
|
|
201
|
+
event.preventDefault();
|
|
202
|
+
openCheckout(event.detail);
|
|
203
|
+
}}
|
|
204
|
+
/>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The React entry is safe to import during SSR and is marked as a client
|
|
210
|
+
component. Full pricing content appears after hydration; deep light-DOM SSR is
|
|
211
|
+
not a v1 contract.
|
|
212
|
+
|
|
213
|
+
## Plan highlights, inheritance, and badges
|
|
214
|
+
|
|
215
|
+
Card highlights can reference both features and usage limits without ambiguity.
|
|
216
|
+
The hybrid mode renders configured items first and fills remaining slots from
|
|
217
|
+
the plan data:
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
configurePricingRenderer({
|
|
221
|
+
presentation: {
|
|
222
|
+
planInheritance: 'auto',
|
|
223
|
+
planBadges: {
|
|
224
|
+
growth: [
|
|
225
|
+
{
|
|
226
|
+
id: 'most-popular',
|
|
227
|
+
label: 'Most popular',
|
|
228
|
+
tone: 'accent',
|
|
229
|
+
emphasize: true,
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
planHighlights: {
|
|
234
|
+
growth: {
|
|
235
|
+
mode: 'hybrid',
|
|
236
|
+
maxItems: 5,
|
|
237
|
+
items: [
|
|
238
|
+
{ id: 'auditLog', kind: 'feature' },
|
|
239
|
+
{ id: 'storage', kind: 'usage-limit' },
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The same `PricingPresentation` object can be supplied per component or under
|
|
248
|
+
`custom.pricingRenderer` in YAML. Instance props win over project defaults,
|
|
249
|
+
which win over YAML-owned presentation values.
|
|
250
|
+
|
|
251
|
+
`planInheritance: "auto"` checks the complete feature/limit set against the
|
|
252
|
+
previous visible plan. “Everything in …, plus” only appears when the current
|
|
253
|
+
plan is demonstrably at least as capable; otherwise the claim is omitted.
|
|
254
|
+
Per-plan `inheritsFrom` can select a specific base plan or disable inheritance.
|
|
255
|
+
Badges are optional, support `accent`, `success`, `warning`, and `neutral`
|
|
256
|
+
tones, and only emphasize a card when `emphasize: true`.
|
|
257
|
+
|
|
258
|
+
## Variables and multicontractable add-ons
|
|
259
|
+
|
|
260
|
+
Primitive variables referenced by formulas become controls automatically.
|
|
261
|
+
Presentation metadata can upgrade them to sliders or selects:
|
|
262
|
+
|
|
263
|
+
```yaml
|
|
264
|
+
custom:
|
|
265
|
+
pricingRenderer:
|
|
266
|
+
planBadges:
|
|
267
|
+
growth:
|
|
268
|
+
- id: most-popular
|
|
269
|
+
label: Most popular
|
|
270
|
+
tone: accent
|
|
271
|
+
emphasize: true
|
|
272
|
+
variableControls:
|
|
273
|
+
- path: seats
|
|
274
|
+
type: slider
|
|
275
|
+
label: Team seats
|
|
276
|
+
min: 1
|
|
277
|
+
max: 250
|
|
278
|
+
step: 1
|
|
279
|
+
- path: region
|
|
280
|
+
type: select
|
|
281
|
+
label: Billing region
|
|
282
|
+
options:
|
|
283
|
+
- value: eu
|
|
284
|
+
label: Europe
|
|
285
|
+
- value: us
|
|
286
|
+
label: United States
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Add-ons with `subscriptionConstraints` receive a quantity stepper that respects
|
|
290
|
+
`minQuantity`, `maxQuantity`, and `quantityStep`. Dependency and exclusion
|
|
291
|
+
changes are listed in an accessible confirmation dialog before mutation.
|
|
292
|
+
Usage-limit values expressed as `.inf` render as the localized **Unlimited**
|
|
293
|
+
label.
|
|
294
|
+
|
|
295
|
+
## CTAs and host-owned checkout
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
renderer.presentation = {
|
|
299
|
+
ctas: [
|
|
300
|
+
{
|
|
301
|
+
id: 'start-growth',
|
|
302
|
+
planId: 'growth',
|
|
303
|
+
label: 'Start free trial',
|
|
304
|
+
href: '/checkout/growth',
|
|
305
|
+
metadata: { source: 'pricing-page' },
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
renderer.addEventListener('pricing-action', (event) => {
|
|
311
|
+
event.preventDefault();
|
|
312
|
+
openCheckout(event.detail);
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
The cancelable event includes the selected plan, billing period, variables,
|
|
317
|
+
add-ons, resolved prices, subtotal, quote state, and metadata. Checkout,
|
|
318
|
+
authentication, and contracting remain host responsibilities.
|
|
319
|
+
|
|
320
|
+
Public events:
|
|
321
|
+
|
|
322
|
+
- `pricing-ready`
|
|
323
|
+
- `pricing-selection-change`
|
|
324
|
+
- `pricing-action`
|
|
325
|
+
- `pricing-diagnostic`
|
|
326
|
+
|
|
327
|
+
## Private remote sources
|
|
328
|
+
|
|
329
|
+
Secrets are property-only and never reflected into markup:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
renderer.src = 'https://api.example.com/private/pricing.yml';
|
|
333
|
+
renderer.request = {
|
|
334
|
+
credentials: 'include',
|
|
335
|
+
headers: async () => ({
|
|
336
|
+
Authorization: `Bearer ${await refreshAccessToken()}`,
|
|
337
|
+
}),
|
|
338
|
+
timeoutMs: 15_000,
|
|
339
|
+
maxBytes: 2 * 1024 * 1024,
|
|
340
|
+
};
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Use `loadPricing` for OAuth exchanges, signed URLs, proxies, or SDK clients. The
|
|
344
|
+
built-in loader permits HTTP/HTTPS `GET` only, defaults to
|
|
345
|
+
`credentials: "omit"`, honors cancellation, and never fetches during SSR.
|
|
346
|
+
|
|
347
|
+
## Theming
|
|
348
|
+
|
|
349
|
+
```css
|
|
350
|
+
pricing-renderer {
|
|
351
|
+
--pr-color-accent: #0057ff;
|
|
352
|
+
--pr-radius-lg: 0.75rem;
|
|
353
|
+
--pr-shell-padding: clamp(1rem, 3vw, 2.5rem);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
pricing-renderer [data-pr-part='plan-card'] {
|
|
357
|
+
font-family: var(--brand-font);
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Import `styles.css` for the full theme or only `base.css` and supply your own.
|
|
362
|
+
CSS variables and `data-pr-part` values are stable; internal `pr-*` classes are
|
|
363
|
+
not. Read the [theming guide](./docs/theming.md) before applying broad host
|
|
364
|
+
resets to light-DOM content.
|
|
365
|
+
|
|
366
|
+

|
|
367
|
+
|
|
368
|
+
## Headless and extension APIs
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
import {
|
|
372
|
+
createPricingViewModel,
|
|
373
|
+
normalizePricing,
|
|
374
|
+
registerMessageCatalog,
|
|
375
|
+
resolvePricing,
|
|
376
|
+
} from 'pricing-renderer';
|
|
377
|
+
import { loadPricingFromUrl, parsePricingYaml } from 'pricing-renderer/yaml';
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Extend future/vendor syntax with `PricingSyntaxAdapter`, translations with
|
|
381
|
+
`registerMessageCatalog`, and domain calculations through explicitly
|
|
382
|
+
allow-listed expression functions. See [Extending](./docs/extending.md).
|
|
383
|
+
|
|
384
|
+
## Documentation
|
|
385
|
+
|
|
386
|
+
- [Architecture](./docs/architecture.md)
|
|
387
|
+
- [API reference](./docs/api-reference.md)
|
|
388
|
+
- [Project and instance configuration](./docs/configuration.md)
|
|
389
|
+
- [Demo and release acceptance](./docs/demo.md)
|
|
390
|
+
- [Release and npm publication](./docs/releasing.md)
|
|
391
|
+
- [Extending the library](./docs/extending.md)
|
|
392
|
+
- [Theming](./docs/theming.md)
|
|
393
|
+
- [Internationalization](./docs/i18n.md)
|
|
394
|
+
- [Pricing2Yaml compatibility](./docs/pricing2yaml-compatibility.md)
|
|
395
|
+
- [Contributing](./CONTRIBUTING.md)
|
|
396
|
+
- [Security policy](./SECURITY.md)
|
|
397
|
+
- [Changelog](./CHANGELOG.md)
|
|
398
|
+
|
|
399
|
+
## Quality gates
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
pnpm lint
|
|
403
|
+
pnpm format:check
|
|
404
|
+
pnpm typecheck
|
|
405
|
+
pnpm test
|
|
406
|
+
pnpm build
|
|
407
|
+
pnpm test:browser
|
|
408
|
+
pnpm package:check
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Browser coverage includes Chromium, Firefox, WebKit, Axe, keyboard paths, and
|
|
412
|
+
visual breakpoints. Releases use Changesets, SemVer, npm provenance, and MIT.
|
|
413
|
+
|
|
414
|
+
## Security
|
|
415
|
+
|
|
416
|
+
Pricing text is escaped, links use safe schemes, and expressions cannot reach
|
|
417
|
+
globals or prototypes. `private` only controls presentation—remove confidential
|
|
418
|
+
data before delivering YAML to a browser. Report vulnerabilities through the
|
|
419
|
+
process in [SECURITY.md](./SECURITY.md).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
This project was informed by the following MIT-licensed projects. Their editor
|
|
4
|
+
implementations are not bundled into this package.
|
|
5
|
+
|
|
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
|
|
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.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"readme": "README.md",
|
|
4
|
+
"modules": [
|
|
5
|
+
{
|
|
6
|
+
"kind": "javascript-module",
|
|
7
|
+
"path": "dist/element/pricing-renderer-element.js",
|
|
8
|
+
"declarations": [
|
|
9
|
+
{
|
|
10
|
+
"kind": "class",
|
|
11
|
+
"name": "PricingRendererElement",
|
|
12
|
+
"customElement": true,
|
|
13
|
+
"tagName": "pricing-renderer",
|
|
14
|
+
"description": "A responsive, themeable renderer for Pricing2Yaml 3.x.",
|
|
15
|
+
"members": [
|
|
16
|
+
{ "kind": "field", "name": "pricing", "type": { "text": "IPricingLike" } },
|
|
17
|
+
{ "kind": "field", "name": "yaml", "type": { "text": "string" } },
|
|
18
|
+
{ "kind": "field", "name": "src", "type": { "text": "string" }, "attribute": "src" },
|
|
19
|
+
{
|
|
20
|
+
"kind": "field",
|
|
21
|
+
"name": "request",
|
|
22
|
+
"type": { "text": "PricingRequestOptions" }
|
|
23
|
+
},
|
|
24
|
+
{ "kind": "field", "name": "loadPricing", "type": { "text": "PricingLoader" } },
|
|
25
|
+
{
|
|
26
|
+
"kind": "field",
|
|
27
|
+
"name": "normalizeOptions",
|
|
28
|
+
"type": { "text": "NormalizePricingOptions" }
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"kind": "field",
|
|
32
|
+
"name": "expressionOptions",
|
|
33
|
+
"type": { "text": "ExpressionOptions" }
|
|
34
|
+
},
|
|
35
|
+
{ "kind": "field", "name": "selection", "type": { "text": "PricingSelection" } },
|
|
36
|
+
{
|
|
37
|
+
"kind": "field",
|
|
38
|
+
"name": "defaultSelection",
|
|
39
|
+
"type": { "text": "Partial<PricingSelection>" }
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"kind": "field",
|
|
43
|
+
"name": "presentation",
|
|
44
|
+
"type": { "text": "PricingPresentation" }
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"kind": "field",
|
|
48
|
+
"name": "locale",
|
|
49
|
+
"type": { "text": "string" },
|
|
50
|
+
"attribute": "locale"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"kind": "field",
|
|
54
|
+
"name": "pricingPath",
|
|
55
|
+
"type": { "text": "string" },
|
|
56
|
+
"attribute": "pricing-path"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"kind": "field",
|
|
60
|
+
"name": "selectionEnabled",
|
|
61
|
+
"type": { "text": "boolean" }
|
|
62
|
+
},
|
|
63
|
+
{ "kind": "field", "name": "ctaEnabled", "type": { "text": "boolean" } },
|
|
64
|
+
{
|
|
65
|
+
"kind": "field",
|
|
66
|
+
"name": "variablesEnabled",
|
|
67
|
+
"type": { "text": "boolean" }
|
|
68
|
+
},
|
|
69
|
+
{ "kind": "field", "name": "messages", "type": { "text": "MessageCatalog" } },
|
|
70
|
+
{
|
|
71
|
+
"kind": "field",
|
|
72
|
+
"name": "mode",
|
|
73
|
+
"type": { "text": "\"commercial\" | \"catalog\"" },
|
|
74
|
+
"attribute": "mode"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"kind": "field",
|
|
78
|
+
"name": "layout",
|
|
79
|
+
"type": { "text": "\"auto\" | \"compact\" | \"table\"" },
|
|
80
|
+
"attribute": "layout"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"kind": "field",
|
|
84
|
+
"name": "visibility",
|
|
85
|
+
"type": { "text": "\"public-only\" | \"all\"" },
|
|
86
|
+
"attribute": "visibility"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"kind": "field",
|
|
90
|
+
"name": "theme",
|
|
91
|
+
"type": { "text": "\"light\" | \"dark\" | \"auto\"" },
|
|
92
|
+
"attribute": "theme"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"kind": "field",
|
|
96
|
+
"name": "loadingLabel",
|
|
97
|
+
"type": { "text": "string" },
|
|
98
|
+
"attribute": "loading-label"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"kind": "method",
|
|
102
|
+
"name": "reload",
|
|
103
|
+
"return": { "type": { "text": "Promise<void>" } }
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"events": [
|
|
107
|
+
{ "name": "pricing-ready", "type": { "text": "CustomEvent<PricingReadyDetail>" } },
|
|
108
|
+
{
|
|
109
|
+
"name": "pricing-selection-change",
|
|
110
|
+
"type": { "text": "CustomEvent<PricingSelectionChangeDetail>" }
|
|
111
|
+
},
|
|
112
|
+
{ "name": "pricing-action", "type": { "text": "CustomEvent<PricingActionDetail>" } },
|
|
113
|
+
{
|
|
114
|
+
"name": "pricing-diagnostic",
|
|
115
|
+
"type": { "text": "CustomEvent<PricingDiagnosticDetail>" }
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PricingRendererConfig } from './types.js';
|
|
2
|
+
export declare const DEFAULT_PRICING_RENDERER_CONFIG: Readonly<PricingRendererConfig>;
|
|
3
|
+
export declare function createPricingRendererConfig(options?: Partial<PricingRendererConfig>): Readonly<PricingRendererConfig>;
|
|
4
|
+
export declare function configurePricingRenderer(options?: Partial<PricingRendererConfig>): Readonly<PricingRendererConfig>;
|
|
5
|
+
export declare function getPricingRendererConfig(): Readonly<PricingRendererConfig>;
|
|
6
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CAAC,qBAAqB,CAO1E,CAAC;AAyCH,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,OAAO,CAAC,qBAAqB,CAAM,GAC3C,QAAQ,CAAC,qBAAqB,CAAC,CA+BjC;AAED,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,OAAO,CAAC,qBAAqB,CAAM,GAC3C,QAAQ,CAAC,qBAAqB,CAAC,CAGjC;AAED,wBAAgB,wBAAwB,IAAI,QAAQ,CAAC,qBAAqB,CAAC,CAE1E"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export const DEFAULT_PRICING_RENDERER_CONFIG = Object.freeze({
|
|
2
|
+
locale: 'en-US',
|
|
3
|
+
pricingPath: '/pricing',
|
|
4
|
+
selectionEnabled: true,
|
|
5
|
+
ctaEnabled: true,
|
|
6
|
+
variablesEnabled: true,
|
|
7
|
+
presentation: Object.freeze({}),
|
|
8
|
+
});
|
|
9
|
+
let configuredDefaults = DEFAULT_PRICING_RENDERER_CONFIG;
|
|
10
|
+
function normalizeLocale(locale) {
|
|
11
|
+
const value = locale.trim();
|
|
12
|
+
if (!value)
|
|
13
|
+
throw new TypeError('Pricing renderer locale cannot be empty.');
|
|
14
|
+
try {
|
|
15
|
+
return Intl.getCanonicalLocales(value)[0] ?? value;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
throw new TypeError(`Invalid pricing renderer locale: ${locale}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function normalizePricingPath(pricingPath) {
|
|
22
|
+
const value = pricingPath.trim();
|
|
23
|
+
if (!value.startsWith('/') ||
|
|
24
|
+
value.startsWith('//') ||
|
|
25
|
+
value.includes('?') ||
|
|
26
|
+
value.includes('#')) {
|
|
27
|
+
throw new TypeError('Pricing renderer pricingPath must be an absolute application pathname such as "/pricing".');
|
|
28
|
+
}
|
|
29
|
+
return value.length > 1 ? value.replace(/\/+$/, '') : value;
|
|
30
|
+
}
|
|
31
|
+
function normalizeFeatureFlag(value, fallback, name) {
|
|
32
|
+
if (value === undefined)
|
|
33
|
+
return fallback;
|
|
34
|
+
if (typeof value !== 'boolean') {
|
|
35
|
+
throw new TypeError(`Pricing renderer ${name} must be a boolean.`);
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
export function createPricingRendererConfig(options = {}) {
|
|
40
|
+
if (options.presentation !== undefined &&
|
|
41
|
+
(typeof options.presentation !== 'object' ||
|
|
42
|
+
options.presentation === null ||
|
|
43
|
+
Array.isArray(options.presentation))) {
|
|
44
|
+
throw new TypeError('Pricing renderer presentation must be an object.');
|
|
45
|
+
}
|
|
46
|
+
return Object.freeze({
|
|
47
|
+
locale: normalizeLocale(options.locale ?? DEFAULT_PRICING_RENDERER_CONFIG.locale),
|
|
48
|
+
pricingPath: normalizePricingPath(options.pricingPath ?? DEFAULT_PRICING_RENDERER_CONFIG.pricingPath),
|
|
49
|
+
selectionEnabled: normalizeFeatureFlag(options.selectionEnabled, DEFAULT_PRICING_RENDERER_CONFIG.selectionEnabled, 'selectionEnabled'),
|
|
50
|
+
ctaEnabled: normalizeFeatureFlag(options.ctaEnabled, DEFAULT_PRICING_RENDERER_CONFIG.ctaEnabled, 'ctaEnabled'),
|
|
51
|
+
variablesEnabled: normalizeFeatureFlag(options.variablesEnabled, DEFAULT_PRICING_RENDERER_CONFIG.variablesEnabled, 'variablesEnabled'),
|
|
52
|
+
presentation: Object.freeze({ ...(options.presentation ?? {}) }),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export function configurePricingRenderer(options = {}) {
|
|
56
|
+
configuredDefaults = createPricingRendererConfig(options);
|
|
57
|
+
return configuredDefaults;
|
|
58
|
+
}
|
|
59
|
+
export function getPricingRendererConfig() {
|
|
60
|
+
return configuredDefaults;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ExpressionEvaluation, ExpressionOptions } from './types.js';
|
|
2
|
+
export declare function collectExpressionDependencies(source: string): string[];
|
|
3
|
+
export declare function evaluatePriceExpression(source: string, variables: Record<string, unknown>, options?: ExpressionOptions): ExpressionEvaluation;
|
|
4
|
+
//# sourceMappingURL=expression.d.ts.map
|