@revturbine/sdk 0.10.9 → 0.11.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 CHANGED
@@ -47,6 +47,171 @@ also require a changelog entry.
47
47
 
48
48
  ---
49
49
 
50
+ ## 0.11.0
51
+
52
+ ### `Playbook` is the canonical name for every option that carries one (BL-0156)
53
+
54
+ **What changed.** `ExportedConfig` is dead vocabulary. Plan 118 renamed the
55
+ domain object to **Playbook** and plan 104 renamed the schema type to
56
+ `RevTurbineConfig`, but the *option*, *keyword* and *method* names that carry a
57
+ Playbook around the SDK still spelled it `exportedConfig` / `exported_config`.
58
+ Every one of them now has a `Playbook`-named counterpart; every old name stays
59
+ as a deprecated alias that still works.
60
+
61
+ TypeScript:
62
+
63
+ | deprecated | canonical |
64
+ |---|---|
65
+ | `localRuntime.exportedConfig` | `localRuntime.playbook` (already shipped; see below) |
66
+ | `localRuntime.resolvers.resolveExportedConfig` | `localRuntime.resolvers.resolvePlaybook` |
67
+ | `configProvider.getExportedConfig()` | `configProvider.getPlaybook()` |
68
+ | `sdk.getExportedConfig()` | `sdk.getPlaybook()` |
69
+ | `getPolicy().exportedConfigVersion` | `getPolicy().playbookVersion` |
70
+ | `BrowserRuntimeOptions.exportedConfig` | `BrowserRuntimeOptions.playbook` |
71
+ | `LocalEvaluationServerOptions.exportedConfig` | `LocalEvaluationServerOptions.playbook` |
72
+ | `ExportedConfigProvider` (type) | `RevTurbineConfigProvider`, also exported as `PlaybookProvider` |
73
+ | `ExportedConfig` (type) | `Playbook` |
74
+
75
+ Python: `RevTurbineCustomerSdk(exported_config=…)`, `LocalRuntime(exported_config=…)`,
76
+ `LocalRuntime.get_exported_config()`, `create_static_placement_resolver(exported_config=…)`,
77
+ `derive_local_entitlement_from_configured_rules(exported_config=…)`,
78
+ `configured_plan_name_from_exported_config`, `parse_exported_config_or_throw` and
79
+ `static.ExportedConfig` all gained `playbook`-spelled counterparts.
80
+
81
+ Rust: `configured_plan_name_from_exported_config` is now
82
+ `configured_plan_name_from_playbook`, with the old name kept as the crate's
83
+ first `#[deprecated]` item. Rust has no keyword arguments, so nothing else in
84
+ that port was a caller-visible name.
85
+
86
+ `RevTurbineConfigProvider`'s two accessors are both OPTIONAL on the interface so
87
+ that an existing `implements RevTurbineConfigProvider` clause keeps compiling;
88
+ the `configProvider` option intersects it with a union that still requires
89
+ exactly one of them, so a provider implementing neither is a compile error where
90
+ it is passed.
91
+
92
+ Reading a deprecated name logs **one** development-build warning per runtime
93
+ (one `DeprecationWarning` per process on Python) naming the canonical
94
+ replacement — one flag for every alias, so an integration still spelling several
95
+ of them gets a single line, not a wall. Production builds are silent. Requires
96
+ `@revt-eng/core` ≥ 0.1.331, which made the same rename upstream (scaffold #380).
97
+
98
+ Ruling: Kent, 2026-09-23 (BL-0156).
99
+
100
+ **Landed in** `0.11.0`. **Fail-closed in** `0.12.0` for the `ExportedConfig`
101
+ aliases — until then every old name works and warns once.
102
+
103
+ **Proving test:** `web-sdk/playbook-option.test.ts` (canonical never warns, the
104
+ alias still resolves, one warning across three read sites, both spellings reach
105
+ the same Playbook at every public read site),
106
+ `server-python/tests/test_playbook_option.py` (the same contract plus
107
+ `get_exported_config()`), and `server-rust/src/user_context.rs`
108
+ `playbook_rename_tests` (the alias returns exactly what the canonical returns).
109
+
110
+ ### `localRuntime.playbook` is the canonical local-mode config key
111
+
112
+ **What changed.** The record this file owed and never carried: `localRuntime.playbook`
113
+ shipped earlier as the canonical spelling of `localRuntime.exportedConfig`, with
114
+ `resolveLocalPlaybook()` as the single resolver deciding precedence, but no
115
+ changelog entry was ever written for it — so an integration could not date the
116
+ change. It is recorded here rather than back-dated, because the version it
117
+ landed in is not the version this entry appears in and pretending otherwise is
118
+ exactly the confusion the "Landed in" / "Fail-closed in" split exists to
119
+ prevent. As of `0.11.0` the alias also warns once (it did not before).
120
+
121
+ **Landed in** a release before `0.11.0` (additive; the alias never stopped
122
+ working). **Fail-closed in** `0.12.0`, with the rest of the `ExportedConfig`
123
+ aliases.
124
+
125
+ **Proving test:** `web-sdk/playbook-option.test.ts` — `resolveLocalPlaybook`
126
+ accepts either key, prefers `playbook`, and warns only for the alias.
127
+
128
+ ### `publicKey` is the only browser credential; `ingestPublicKey` is gone (BL-0113)
129
+
130
+ **What changed.** `0.10.0` made `publicKey` the browser credential and kept
131
+ `apiKey` and `ingestPublicKey` working as aliases "for one minor". This is that
132
+ minor:
133
+
134
+ - **`ingestPublicKey` is removed from `RevTurbineInitOptions`.** Passing it is
135
+ now a **type error**, including when the options are built in a variable
136
+ (`ExactInitOptions` rejects it there too).
137
+ - **`apiKey` is no longer read as a browser credential.** It still exists, and
138
+ still means exactly one thing: the secret **server key**, for
139
+ `@revturbine/sdk/server` and for the headless SDK on a backend. A browser init
140
+ that supplies only `apiKey` now resolves **no** browser credential and fails
141
+ at init, instead of silently sending a server key as the bearer.
142
+
143
+ `resolveBrowserPublicKey()` reads `publicKey` and nothing else, and
144
+ `resetBrowserKeyAliasWarning()` is gone with the warning it reset. Keyless
145
+ local-only init is unchanged, and the plan 95 anonymous `sdk_init` beacon now
146
+ keys off `publicKey` alone.
147
+
148
+ Ruling: Kent, 2026-09-23 (BL-0113), closing the window plan 257 opened.
149
+
150
+ **Landed in** `0.11.0`. **Fail-closed in** `0.11.0` — the same release. This IS
151
+ the fail-closed half of the `0.10.0` entry below; there is no further tolerance
152
+ window.
153
+
154
+ **Proving test:** `web-sdk/public-key-option.test.ts` (`publicKey` is the bearer
155
+ on ingest and every control-plane fetch; `apiKey` resolves nothing; nothing
156
+ warns), `web-sdk/customer-side-ingest.test.ts` (the bearer no longer carries the
157
+ server key), and `web-sdk/init-options-exactness.test-d.ts` (`ingestPublicKey`
158
+ is a type error inline AND in a variable — if the key is ever re-added, `tsc`
159
+ fails on the unused `@ts-expect-error`).
160
+
161
+ ---
162
+
163
+ ## 0.10.10
164
+
165
+ ### Trial tokens now reach placement content, not only `getPersonalizationTokens()` (BL-0169)
166
+
167
+ **What changed.** A placement whose copy authors `{{trial_days_remaining}}` or
168
+ `{{trial_days_total}}` now renders the live values. Content interpolation sources
169
+ its token map from the **output content itself**, so a provider-derived token
170
+ only reaches the copy if the resolver writes it onto that content first. The
171
+ usage lane always did (`usage_current`, `usage_limit`, `usage_percent`,
172
+ `usage_remaining`, `reset_date`); the trial lane never did, so a `trial_ending`
173
+ body reading `"{{trial_days_remaining}} days left"` shipped the literal
174
+ `{{trial_days_remaining}}` to the end user — on every port. The two names were
175
+ derived for `getPersonalizationTokens()` all along, which is why the gap survived:
176
+ a host that read the token map saw the right numbers and a host that authored the
177
+ token into copy did not.
178
+
179
+ | | before | now |
180
+ |---|---|---|
181
+ | `getPersonalizationTokens().trial_days_remaining` | live value | live value (unchanged) |
182
+ | content `{{trial_days_remaining}}` / `{{trial_days_total}}` | literal `{{…}}` | live value |
183
+ | `outputContent.trial_days_remaining` / `_total` | absent | written when the plan provider carries a finite number |
184
+
185
+ Identical on all three ports (`ts:local-resolver.ts`,
186
+ `py:core/placements/local_resolver.py`, `rs:placements/static_resolver.rs`): the
187
+ same two token names, a `Number.isFinite` guard, provider state winning over an
188
+ authored content value of the same key, and the value passed through **without
189
+ widening** — `day_number` 7 + `days_remaining` 3 is `trial_days_total` `10`, never
190
+ `10.0` (BL-0155).
191
+
192
+ **Who this reaches.** Any host whose placement copy authors either token. Copy
193
+ that hard-coded one of those keys as a decorative literal now has it overwritten
194
+ by live state — the same precedence the usage lane and
195
+ `derivePlacementPersonalizationTokens` have always had. `{{trial_plan_name}}` and
196
+ `{{trial_features_used}}` remain **not yet wired** by the placement-studio-ui
197
+ spec's own note and are untouched.
198
+
199
+ **Landed in.** `0.10.10`.
200
+
201
+ **Fail-closed in.** `0.10.10` — absence still leaves the raw token standing
202
+ rather than rendering `0`, so a host with no trial provider state sees exactly
203
+ what it saw before.
204
+
205
+ **Proving test.** Parity scenario `trial_tokens_in_content` under
206
+ `"normalize": "preserve-representation"` (byte-locked ts ≡ py ≡ rs), plus
207
+ `py:tests/placements/test_local_resolver.py` and `rs:tests/static_resolver.rs`
208
+ `trial_tokens_are_injected_from_plan_provider_state` and its two neighbours,
209
+ mirroring the scaffold-side `local-resolver.test.ts` cases. Needs
210
+ `@revt-eng/core` 0.1.331 or later (scaffold #382) for the TS half; this release
211
+ pins 0.1.332.
212
+
213
+ ---
214
+
50
215
  ## 0.10.9
51
216
 
52
217
  ### The trial-status overlay no longer changes a number's type (BL-0155)
@@ -87,11 +252,21 @@ representation simply changes.
87
252
  `server-python/tests/test_trial_overlay_upsert.py::test_overlay_preserves_integer_numeric_representation`
88
253
  and `server-rust/src/sdk.rs::overlay_preserves_integer_numeric_representation`
89
254
  — both assert the *type*, not just the value, because `100 == 100.0` in Python.
90
- The parity corpus cannot see this class of divergence (`tests/parity/normalize.*`
91
- rule 4 collapses integral floats by design); scenario
92
- `trial_overlay_integer_fields` locks that the three ports still decide
255
+ Scenario `trial_overlay_integer_fields` locks that the three ports still decide
93
256
  identically from an all-integer trial status.
94
257
 
258
+ > **Correction, 2026-09-23 (BL-0158, harness-only — no release).** This entry
259
+ > originally said the parity corpus *cannot* see this class of divergence,
260
+ > because `tests/parity/normalize.*` rule 4 collapses integral floats by design
261
+ > and nothing in the corpus returned the overlaid provider state. Both halves
262
+ > are now closed and the sentence no longer holds:
263
+ > `trial_overlay_integer_fields` drives `resolveProviders` (already shipped on
264
+ > all three runtimes) under a new per-scenario
265
+ > `"normalize": "preserve-representation"` flag that turns the collapse off, so
266
+ > `14` and `14.0` are different bytes and the cross-language byte-diff fails on
267
+ > a widened field. No public API changed; the harness gained the ability to see
268
+ > provider state.
269
+
95
270
  ---
96
271
 
97
272
  ## 0.10.8
@@ -483,8 +658,9 @@ and the plan 95 anonymous `sdk_init` beacon still fires only when no public key
483
658
 
484
659
  Ruling: Kent, 2026-09-21, devkit PR #808 (closed).
485
660
 
486
- **Landed in** `0.10.0`. **Fail-closed in** not yet — the aliases are removed one
487
- minor later; until then the old shape works and warns.
661
+ **Landed in** `0.10.0`. **Fail-closed in** `0.11.0` — `ingestPublicKey` was
662
+ removed from the options type and `apiKey` stopped being read as a browser
663
+ credential (BL-0113); see the `0.11.0` entry above.
488
664
 
489
665
  **Proving test:** `web-sdk/public-key-option.test.ts` — precedence, the single
490
666
  bearer across ingest and control-plane fetches, the browser-only warning, and