@revturbine/sdk 0.10.1 → 0.10.3

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
@@ -22,12 +22,19 @@ A change whose "Fail-closed in" is later than its "Landed in" had a window where
22
22
  the old shape was accepted and did nothing. Those windows are the expensive ones.
23
23
 
24
24
  > **What this file is not yet.** A CI guard fails any PR that changes a
25
- > `@public`-tagged export signature without touching this file. That tagging is
26
- > thin: as of `0.8.0` it covers **4** methods on `RevTurbineCustomerSdk`, which
27
- > declares roughly **73**. `can()`, `getPlacementDecision()`, `identify()` and
28
- > `dismiss()` are among the untagged, so a breaking change to one of them will
29
- > not be caught automatically — it depends on whoever writes the PR. Entries here
30
- > are reliable; the absence of an entry is not yet proof that nothing changed.
25
+ > `@public`-tagged export signature without touching this file. That tagging
26
+ > was thin (4 of ~73 methods) until BL-0005 (2026-09-22, no release — TSDoc
27
+ > and snapshot only) widened it to **34** of `RevTurbineCustomerSdk`'s **67**
28
+ > non-private methods, plus 5 provider/hook entry points
29
+ > (`initRevTurbine`, `RevTurbineProvider`, `useCan`, `useEntitlement`,
30
+ > `useGatedAction`) — every method a customer-facing guide, skill, the
31
+ > starter, or a demo app calls directly. The remaining ~33 methods are
32
+ > internal-only paths (called only by the SDK's own placement components,
33
+ > React bindings, or other internal packages) and stay untagged, so a
34
+ > breaking change to one of *those* still will not be caught automatically —
35
+ > it depends on whoever writes the PR. Entries here are reliable; the absence
36
+ > of an entry for a currently-untagged method is not yet proof that nothing
37
+ > changed.
31
38
 
32
39
  > **Note on 0.x.** These packages are pre-1.0, so breaking changes ship in the
33
40
  > **minor** position (`0.6.0` → `0.7.0`), not the major. `npm`'s caret on a `0.x`
@@ -40,6 +47,81 @@ also require a changelog entry.
40
47
 
41
48
  ---
42
49
 
50
+ ## 0.10.3
51
+
52
+ ### The Rust port gains the decision surfaces Python already shipped (BL-0145)
53
+
54
+ **What changed.** `RevTurbineCustomerSdk` on the Rust port (`revturbine`,
55
+ crates.io) now exposes the same decision surfaces the Python port exposes.
56
+ Nothing on the TypeScript or Python side changes; this is a Rust-only,
57
+ purely additive release.
58
+
59
+ | Added | Shape |
60
+ |---|---|
61
+ | `RevTurbineCustomerSdk::can(handle, context)` | The advertised alias of `check_entitlement`, matching the scaffold SDK function surface (canonical `checkEntitlement`, alias `can`). |
62
+ | `RevTurbineCustomerSdk::get_eligible_plans()` | `Vec<EligiblePlan>` — public, segment-eligible plan variations. |
63
+ | `RevTurbineCustomerSdk::get_eligible_addons()` | `Vec<EligibleAddon>` — the add-on twin. |
64
+ | `RevTurbineCustomerSdk::evaluate_trial_status(instances, now_iso, base_plan_handle, usage_balances)` | `TrialEvaluation`, reading `free_trial_rules` / `reverse_trial_rules` from the constructed Playbook. |
65
+ | `revturbine::format_currency_minor_units(amount, currency, locale)` | Free function, because Python exposes it as a module function. |
66
+ | `revturbine::plans` | New module carrying the catalog eligibility port and the formatter. |
67
+ | `UserContext::segment_ids` | Pre-resolved segment ids the catalog methods match against. |
68
+
69
+ `UserContext` gains a field but derives `Default`, so existing construction
70
+ with `..Default::default()` keeps compiling.
71
+
72
+ **Why it was invisible.** The cross-language parity gate was green on the
73
+ catalog and currency scenarios only because the Rust parity runner
74
+ *reimplemented* `eligible_catalog` and `format_currency_minor_units` inline
75
+ rather than calling the crate — so the comparison never touched shipped code,
76
+ and the crate could lack the capability entirely without a single fixture
77
+ going red. The runner now calls `revturbine::plans`, and the inline copies are
78
+ deleted; all 102 comparisons stay byte-identical.
79
+
80
+ ## 0.10.2
81
+
82
+ ### Local mode resolves placements by slot id (BL-0119)
83
+
84
+ **What changed.** In `local_only` mode the browser SDK now resolves a placement
85
+ request against the Playbook's authored `placement_slots[]` registry.
86
+ `getPlacement({ slotId })` — with or without a `componentType` — previously
87
+ returned `null` for a slot the Playbook declared, and a controller/`usePlacement`
88
+ mount of that slot resolved to `placement_not_found`. Looking the same placement
89
+ up by **name** worked, which is what made this look like a config problem rather
90
+ than an SDK one.
91
+
92
+ Two things were missing, and both are now in place:
93
+
94
+ - **`registerSurfaceSlot()` adopts the declared template.** `surface_template_ids`
95
+ on the registered record is what puts the shared resolver on its slot branch,
96
+ where `trigger.slot_id` is matched. Without it the resolver fell back to direct
97
+ lookup, which is keyed by placement **name**/id and therefore can never match a
98
+ slot id — hence `placement_not_found`. An id passed explicitly at the call site
99
+ still wins; the mounting code knows what it renders.
100
+ - **`getPlacement()` falls through to the slot registry.** The local placement
101
+ cache only holds slots a decision has already run through, so a cold lookup
102
+ found nothing and stopped. It now derives the record from `placement_slots`,
103
+ matching on `id` and falling back to `surface_type` when only a component type
104
+ is given — the same derivation scaffold's headless
105
+ `LocalRuntime.slotRecordForConfig` has always performed. Registration on this
106
+ path is local only: reading a placement never writes a surface slot back to the
107
+ control plane.
108
+
109
+ This closes a browser-versus-headless divergence: the same Playbook decided
110
+ differently depending on which runtime read it. No public API change — no
111
+ signature moved, and nothing new is asked of the caller. A slot id that no
112
+ `placement_slots` entry declares still resolves to `null`, unchanged.
113
+
114
+ **Landed in** `0.10.2`. **Fail-closed in** n/a — a previously-`null` lookup now
115
+ returns the placement the Playbook authored.
116
+
117
+ **Proving test:** `web-sdk/local-slot-id-lookup.test.ts` — by-slot-id and
118
+ by-slot-id-plus-component-type both resolve, the controller path no longer
119
+ reports `placement_not_found`, an undeclared slot id still returns `null` and
120
+ gains no invented template, an explicit `surfaceTemplateIds` still wins, and the
121
+ by-name lookup that always worked keeps working (the positive control).
122
+
123
+ ---
124
+
43
125
  ## 0.10.1
44
126
 
45
127
  ### Treatment interactions carry the identified `account_id`