@revturbine/sdk 0.10.8 → 0.10.10
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 +109 -0
- package/dist/headless.d.ts +81 -13
- package/dist/headless.js +2 -2
- package/dist/headless.js.map +1 -1
- package/dist/index.d.ts +81 -13
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/types/server-node/local-server.d.ts.map +1 -1
- package/dist/types/web-sdk/browser-runtime.d.ts.map +1 -1
- package/dist/types/web-sdk/customer-side.d.ts.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -47,6 +47,115 @@ also require a changelog entry.
|
|
|
47
47
|
|
|
48
48
|
---
|
|
49
49
|
|
|
50
|
+
## 0.10.10
|
|
51
|
+
|
|
52
|
+
### Trial tokens now reach placement content, not only `getPersonalizationTokens()` (BL-0169)
|
|
53
|
+
|
|
54
|
+
**What changed.** A placement whose copy authors `{{trial_days_remaining}}` or
|
|
55
|
+
`{{trial_days_total}}` now renders the live values. Content interpolation sources
|
|
56
|
+
its token map from the **output content itself**, so a provider-derived token
|
|
57
|
+
only reaches the copy if the resolver writes it onto that content first. The
|
|
58
|
+
usage lane always did (`usage_current`, `usage_limit`, `usage_percent`,
|
|
59
|
+
`usage_remaining`, `reset_date`); the trial lane never did, so a `trial_ending`
|
|
60
|
+
body reading `"{{trial_days_remaining}} days left"` shipped the literal
|
|
61
|
+
`{{trial_days_remaining}}` to the end user — on every port. The two names were
|
|
62
|
+
derived for `getPersonalizationTokens()` all along, which is why the gap survived:
|
|
63
|
+
a host that read the token map saw the right numbers and a host that authored the
|
|
64
|
+
token into copy did not.
|
|
65
|
+
|
|
66
|
+
| | before | now |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `getPersonalizationTokens().trial_days_remaining` | live value | live value (unchanged) |
|
|
69
|
+
| content `{{trial_days_remaining}}` / `{{trial_days_total}}` | literal `{{…}}` | live value |
|
|
70
|
+
| `outputContent.trial_days_remaining` / `_total` | absent | written when the plan provider carries a finite number |
|
|
71
|
+
|
|
72
|
+
Identical on all three ports (`ts:local-resolver.ts`,
|
|
73
|
+
`py:core/placements/local_resolver.py`, `rs:placements/static_resolver.rs`): the
|
|
74
|
+
same two token names, a `Number.isFinite` guard, provider state winning over an
|
|
75
|
+
authored content value of the same key, and the value passed through **without
|
|
76
|
+
widening** — `day_number` 7 + `days_remaining` 3 is `trial_days_total` `10`, never
|
|
77
|
+
`10.0` (BL-0155).
|
|
78
|
+
|
|
79
|
+
**Who this reaches.** Any host whose placement copy authors either token. Copy
|
|
80
|
+
that hard-coded one of those keys as a decorative literal now has it overwritten
|
|
81
|
+
by live state — the same precedence the usage lane and
|
|
82
|
+
`derivePlacementPersonalizationTokens` have always had. `{{trial_plan_name}}` and
|
|
83
|
+
`{{trial_features_used}}` remain **not yet wired** by the placement-studio-ui
|
|
84
|
+
spec's own note and are untouched.
|
|
85
|
+
|
|
86
|
+
**Landed in.** `0.10.10`.
|
|
87
|
+
|
|
88
|
+
**Fail-closed in.** `0.10.10` — absence still leaves the raw token standing
|
|
89
|
+
rather than rendering `0`, so a host with no trial provider state sees exactly
|
|
90
|
+
what it saw before.
|
|
91
|
+
|
|
92
|
+
**Proving test.** Parity scenario `trial_tokens_in_content` under
|
|
93
|
+
`"normalize": "preserve-representation"` (byte-locked ts ≡ py ≡ rs), plus
|
|
94
|
+
`py:tests/placements/test_local_resolver.py` and `rs:tests/static_resolver.rs`
|
|
95
|
+
`trial_tokens_are_injected_from_plan_provider_state` and its two neighbours,
|
|
96
|
+
mirroring the scaffold-side `local-resolver.test.ts` cases. Needs
|
|
97
|
+
`@revt-eng/core` 0.1.331 or later (scaffold #382) for the TS half; this release
|
|
98
|
+
pins 0.1.332.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 0.10.9
|
|
103
|
+
|
|
104
|
+
### The trial-status overlay no longer changes a number's type (BL-0155)
|
|
105
|
+
|
|
106
|
+
**What changed.** The `UserTrialStatus` → `trial_*` PlanProviderState overlay now
|
|
107
|
+
passes each numeric field through on every port, instead of two ports widening
|
|
108
|
+
it to a float. The canonical behaviour is the TS SDK's
|
|
109
|
+
`synthesizeProviderContext` (`web-sdk/customer-side.ts`, the `planTrialFields`
|
|
110
|
+
block), which spreads every `UserTrialStatus` member verbatim: an integer
|
|
111
|
+
`progress_percent` stays an integer on the provider state, and
|
|
112
|
+
`trial_days_total` is `day_number + days_remaining` in the inputs' own kind, so
|
|
113
|
+
`7 + 3` is `14`. The two server ports diverged from it:
|
|
114
|
+
|
|
115
|
+
| field | Python before | Rust before | all three now |
|
|
116
|
+
|---|---|---|---|
|
|
117
|
+
| `progress_percent` → `trial_progress_percent` | `float(v)` | verbatim | verbatim |
|
|
118
|
+
| `days_remaining` → `trial_days_remaining` | `float(v)` | verbatim | verbatim |
|
|
119
|
+
| `usage_consumed` → `trial_usage_consumed` | `float(v)` | verbatim | verbatim |
|
|
120
|
+
| `usage_limit` → `trial_usage_limit` | `float(v)` | verbatim | verbatim |
|
|
121
|
+
| `in_trial` → `trial_active` | `bool(v)` | verbatim | verbatim |
|
|
122
|
+
| derived `trial_days_total` | `float(a) + float(b)` | `f64` sum | `int + int` → `int`; any fractional half → float |
|
|
123
|
+
|
|
124
|
+
**Who this reaches.** Only a host that reads the provider state it built —
|
|
125
|
+
`revturbine::overlay_trial_status_on_plan_provider` on Rust, or the merged
|
|
126
|
+
`PlanProviderState` on Python — and cares whether it holds `70` or `70.0`
|
|
127
|
+
(serializing it, comparing it to an integer, or logging it). No decision
|
|
128
|
+
changes: every reader in the trial-gating layer widens on read, which is exactly
|
|
129
|
+
why the divergence survived. A non-numeric `day_number`/`days_remaining` half
|
|
130
|
+
now derives no total on Python instead of raising, matching Rust's existing
|
|
131
|
+
numeric guard.
|
|
132
|
+
|
|
133
|
+
**Landed in.** `0.10.9`.
|
|
134
|
+
|
|
135
|
+
**Fail-closed in.** `0.10.9` — there was no tolerated-old-shape window; the
|
|
136
|
+
representation simply changes.
|
|
137
|
+
|
|
138
|
+
**Proving test.**
|
|
139
|
+
`server-python/tests/test_trial_overlay_upsert.py::test_overlay_preserves_integer_numeric_representation`
|
|
140
|
+
and `server-rust/src/sdk.rs::overlay_preserves_integer_numeric_representation`
|
|
141
|
+
— both assert the *type*, not just the value, because `100 == 100.0` in Python.
|
|
142
|
+
Scenario `trial_overlay_integer_fields` locks that the three ports still decide
|
|
143
|
+
identically from an all-integer trial status.
|
|
144
|
+
|
|
145
|
+
> **Correction, 2026-09-23 (BL-0158, harness-only — no release).** This entry
|
|
146
|
+
> originally said the parity corpus *cannot* see this class of divergence,
|
|
147
|
+
> because `tests/parity/normalize.*` rule 4 collapses integral floats by design
|
|
148
|
+
> and nothing in the corpus returned the overlaid provider state. Both halves
|
|
149
|
+
> are now closed and the sentence no longer holds:
|
|
150
|
+
> `trial_overlay_integer_fields` drives `resolveProviders` (already shipped on
|
|
151
|
+
> all three runtimes) under a new per-scenario
|
|
152
|
+
> `"normalize": "preserve-representation"` flag that turns the collapse off, so
|
|
153
|
+
> `14` and `14.0` are different bytes and the cross-language byte-diff fails on
|
|
154
|
+
> a widened field. No public API changed; the harness gained the ability to see
|
|
155
|
+
> provider state.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
50
159
|
## 0.10.8
|
|
51
160
|
|
|
52
161
|
### `{{recommended_plan_name}}` and `{{plan_name}}` no longer reach end users as raw tokens (BL-0121)
|
package/dist/headless.d.ts
CHANGED
|
@@ -13185,8 +13185,8 @@ declare const PlaybookObjectSchema: z.ZodObject<{
|
|
|
13185
13185
|
/**
|
|
13186
13186
|
* The one canonical config schema. Parses BOTH a canonical Playbook and a legacy
|
|
13187
13187
|
* `export-config.json` (via `normalizeConfigHeaderInput`) into the canonical
|
|
13188
|
-
* Playbook shape, so `
|
|
13189
|
-
* aliases of it. `.meta({ id: 'Playbook' })` rides on this preprocess wrapper
|
|
13188
|
+
* Playbook shape, so `RevTurbineConfigSchema` (and its deprecated
|
|
13189
|
+
* `ExportedConfigSchema` alias) are true aliases of it. `.meta({ id: 'Playbook' })` rides on this preprocess wrapper
|
|
13190
13190
|
* (the underlying object is id-less) so the OpenAPI / JSON-Schema generators emit
|
|
13191
13191
|
* a single well-formed `Playbook` component. Introspection that needs `.shape`
|
|
13192
13192
|
* must read `PlaybookObjectSchema`, not this wrapper.
|
|
@@ -17164,6 +17164,50 @@ type JsonObject = {
|
|
|
17164
17164
|
[key: string]: JsonValue;
|
|
17165
17165
|
};
|
|
17166
17166
|
|
|
17167
|
+
/**
|
|
17168
|
+
* BL-0156 — `playbook` is the canonical option name; `exportedConfig` is a
|
|
17169
|
+
* deprecated alias.
|
|
17170
|
+
*
|
|
17171
|
+
* "ExportedConfig" is dead vocabulary. Plan 118 renamed the domain object to
|
|
17172
|
+
* **Playbook** and plan 104 renamed the schema type to `RevTurbineConfig`, but
|
|
17173
|
+
* the *option* and *parameter* names that carry a Playbook around core still
|
|
17174
|
+
* spelled it `exportedConfig`. This module is the single place that reconciles
|
|
17175
|
+
* the two spellings, mirroring the plan-257 `publicKey` mechanism:
|
|
17176
|
+
*
|
|
17177
|
+
* - the canonical `playbook` wins whenever it is supplied,
|
|
17178
|
+
* - the deprecated `exportedConfig` is still accepted,
|
|
17179
|
+
* - supplying only the alias emits ONE warning naming the canonical option,
|
|
17180
|
+
* - supplying BOTH with *different* Playbooks throws instead of silently
|
|
17181
|
+
* picking one — that combination is always a caller bug.
|
|
17182
|
+
*
|
|
17183
|
+
* Every read site resolves through `resolvePlaybookOption` /
|
|
17184
|
+
* `requirePlaybookOption`, so the precedence cannot drift per call site.
|
|
17185
|
+
*
|
|
17186
|
+
* **Why the warning is not gated on a development build.** Core's ESLint config
|
|
17187
|
+
* blocks the `process` global outright (`no-restricted-globals`) because core
|
|
17188
|
+
* must run unchanged on edge runtimes and in browsers, so core cannot read
|
|
17189
|
+
* `NODE_ENV` the way `web-sdk/build-mode.ts` does. The warning is therefore
|
|
17190
|
+
* emitted once per runtime in every build — a single line, only for callers
|
|
17191
|
+
* still on the deprecated spelling, and it names the fix.
|
|
17192
|
+
*
|
|
17193
|
+
* Aliases are removed in the minor after the SDK 0.11.0 cascade.
|
|
17194
|
+
*/
|
|
17195
|
+
|
|
17196
|
+
/**
|
|
17197
|
+
* An options bag that must carry a Playbook, under either the canonical
|
|
17198
|
+
* `playbook` name or the deprecated `exportedConfig` alias.
|
|
17199
|
+
*
|
|
17200
|
+
* Modelled as a union so TypeScript still requires one of the two, exactly as
|
|
17201
|
+
* the single required property did before the rename.
|
|
17202
|
+
*/
|
|
17203
|
+
type PlaybookOption = {
|
|
17204
|
+
playbook: RevTurbineConfig;
|
|
17205
|
+
exportedConfig?: RevTurbineConfig;
|
|
17206
|
+
} | {
|
|
17207
|
+
playbook?: RevTurbineConfig;
|
|
17208
|
+
exportedConfig: RevTurbineConfig;
|
|
17209
|
+
};
|
|
17210
|
+
|
|
17167
17211
|
/**
|
|
17168
17212
|
* User context lifecycle — pure functions for building, merging,
|
|
17169
17213
|
* and deriving targeting state from user context + RevTurbineConfig.
|
|
@@ -18118,12 +18162,18 @@ interface LocalPlacementDataset {
|
|
|
18118
18162
|
type LocalPlacementEntry = NonNullable<RevTurbineConfig['placements']>[number];
|
|
18119
18163
|
type LocalPlacementPayload = LocalPlacementEntry['payloads'][number];
|
|
18120
18164
|
type LocalPlacementSurface = LocalPlacementPayload['surfaces'][number];
|
|
18121
|
-
/** Backward-compat JSON entry point. */
|
|
18122
|
-
interface
|
|
18165
|
+
/** Backward-compat JSON entry point, minus the Playbook. */
|
|
18166
|
+
interface StaticPlacementResolverOptionsBase {
|
|
18123
18167
|
placements: LocalPlacementDataset;
|
|
18124
|
-
exportedConfig: RevTurbineConfig;
|
|
18125
18168
|
impressionHistory?: ImpressionHistory;
|
|
18126
18169
|
}
|
|
18170
|
+
/**
|
|
18171
|
+
* Options for {@link createStaticPlacementResolver}.
|
|
18172
|
+
*
|
|
18173
|
+
* The Playbook is supplied as `playbook`; `exportedConfig` is a deprecated
|
|
18174
|
+
* alias removed in the minor after the SDK 0.11.0 cascade (BL-0156).
|
|
18175
|
+
*/
|
|
18176
|
+
type StaticPlacementResolverOptions = StaticPlacementResolverOptionsBase & PlaybookOption;
|
|
18127
18177
|
/**
|
|
18128
18178
|
* Backward-compat JSON entry. Thin wrapper around the unified
|
|
18129
18179
|
* `createLocalPlacementResolver`. Use the unified factory directly
|
|
@@ -18551,8 +18601,8 @@ declare function createHydrationProviders(options: HydrationAdapterOptions): Any
|
|
|
18551
18601
|
* const runtime = new LocalRuntime({
|
|
18552
18602
|
* tenantId: 'tenant_abc',
|
|
18553
18603
|
* userId: 'user_123',
|
|
18554
|
-
*
|
|
18555
|
-
* providers: createStaticProviders({ config:
|
|
18604
|
+
* playbook: myPlaybook,
|
|
18605
|
+
* providers: createStaticProviders({ config: myPlaybook, planHandle: 'pro' }),
|
|
18556
18606
|
* storage: new InMemoryStorage(),
|
|
18557
18607
|
* });
|
|
18558
18608
|
*
|
|
@@ -18561,18 +18611,21 @@ declare function createHydrationProviders(options: HydrationAdapterOptions): Any
|
|
|
18561
18611
|
* ```
|
|
18562
18612
|
*/
|
|
18563
18613
|
|
|
18564
|
-
|
|
18614
|
+
/**
|
|
18615
|
+
* `LocalRuntimeOptions` minus the Playbook, which arrives through
|
|
18616
|
+
* {@link PlaybookOption} so both the canonical `playbook` name and the
|
|
18617
|
+
* deprecated `exportedConfig` alias are accepted.
|
|
18618
|
+
*/
|
|
18619
|
+
interface LocalRuntimeOptionsBase {
|
|
18565
18620
|
/** Tenant identifier. */
|
|
18566
18621
|
tenantId: string;
|
|
18567
18622
|
/** Current user identifier. */
|
|
18568
18623
|
userId: string;
|
|
18569
|
-
/** Full RevTurbineConfig for local resolution. */
|
|
18570
|
-
exportedConfig: RevTurbineConfig;
|
|
18571
18624
|
/** Pre-built domain providers (from createStaticProviders, etc.). */
|
|
18572
18625
|
providers: AnyDomainProvider[];
|
|
18573
18626
|
/**
|
|
18574
18627
|
* Optional pre-built placements dataset. If not provided, derived from
|
|
18575
|
-
* `
|
|
18628
|
+
* `playbook.placements`.
|
|
18576
18629
|
*/
|
|
18577
18630
|
placements?: LocalPlacementDataset;
|
|
18578
18631
|
/**
|
|
@@ -18589,6 +18642,14 @@ interface LocalRuntimeOptions {
|
|
|
18589
18642
|
/** Interaction tracker options. */
|
|
18590
18643
|
interactionOptions?: Partial<Pick<InteractionTrackerOptions, 'defaultDismissCooldownMs' | 'defaultRemindLaterMs'>>;
|
|
18591
18644
|
}
|
|
18645
|
+
/**
|
|
18646
|
+
* Options for {@link LocalRuntime}.
|
|
18647
|
+
*
|
|
18648
|
+
* The Playbook is supplied as `playbook`. The `exportedConfig` spelling is a
|
|
18649
|
+
* deprecated alias kept so consumers can migrate incrementally; it is removed
|
|
18650
|
+
* in the minor after the SDK 0.11.0 cascade (BL-0156).
|
|
18651
|
+
*/
|
|
18652
|
+
type LocalRuntimeOptions = LocalRuntimeOptionsBase & PlaybookOption;
|
|
18592
18653
|
declare class LocalRuntime {
|
|
18593
18654
|
readonly tenantId: string;
|
|
18594
18655
|
readonly registry: DomainProviderRegistry;
|
|
@@ -18596,7 +18657,7 @@ declare class LocalRuntime {
|
|
|
18596
18657
|
readonly interactionTracker: InteractionTracker;
|
|
18597
18658
|
readonly capEnforcer: CapEnforcer;
|
|
18598
18659
|
readonly impressionHistory: ImpressionHistory;
|
|
18599
|
-
private readonly
|
|
18660
|
+
private readonly playbook;
|
|
18600
18661
|
private readonly registeredPlacements;
|
|
18601
18662
|
private userId;
|
|
18602
18663
|
constructor(options: LocalRuntimeOptions);
|
|
@@ -18668,7 +18729,14 @@ declare class LocalRuntime {
|
|
|
18668
18729
|
setUserId(userId: string): void;
|
|
18669
18730
|
/** Current user ID. */
|
|
18670
18731
|
getUserId(): string;
|
|
18671
|
-
/** Return the active
|
|
18732
|
+
/** Return the active Playbook snapshot. */
|
|
18733
|
+
getPlaybook(): RevTurbineConfig;
|
|
18734
|
+
/**
|
|
18735
|
+
* Return the active Playbook snapshot.
|
|
18736
|
+
*
|
|
18737
|
+
* @deprecated Use {@link LocalRuntime.getPlaybook}. Removed in the minor
|
|
18738
|
+
* after the SDK 0.11.0 cascade (BL-0156).
|
|
18739
|
+
*/
|
|
18672
18740
|
getExportedConfig(): RevTurbineConfig;
|
|
18673
18741
|
/**
|
|
18674
18742
|
* Pre-warm caches (impression history, etc.) for synchronous access.
|