@wireai/activation 0.14.0 → 0.14.2
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 +81 -0
- package/README.md +5 -0
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/coachmarks/index.js +69 -6
- package/dist/coachmarks/index.js.map +1 -1
- package/dist/coachmarks/index.mjs +69 -6
- package/dist/coachmarks/index.mjs.map +1 -1
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +29 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +29 -8
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.js +14 -6
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +14 -6
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.js +14 -6
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +14 -6
- package/dist/reviews/index.mjs.map +1 -1
- package/metro/index.js +3 -1
- package/package.json +1 -1
- package/src/WireOnboarding.tsx +44 -5
- package/src/analytics/currentSession.ts +3 -2
- package/src/coachmarks/SpotlightOverlay.tsx +38 -11
- package/src/coachmarks/expoBlur.ts +135 -0
- package/src/permissions/permissionMemory.ts +39 -14
- package/src/reviews/runtime.ts +75 -23
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,87 @@
|
|
|
3
3
|
All notable changes to `@wireai/activation` (formerly `wireai-onboarding`).
|
|
4
4
|
Historical entries below the rename keep the old package name on purpose.
|
|
5
5
|
|
|
6
|
+
## [0.14.2] - 2026-08-18
|
|
7
|
+
|
|
8
|
+
**PATCH: no public surface gained, lost or renamed a member.** `SpotlightOverlay` keeps its props
|
|
9
|
+
and its export; `expo-blur` stays an OPTIONAL peer.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **The coachmarks subpath no longer force-imports its optional peer `expo-blur`.**
|
|
14
|
+
`SpotlightOverlay` did `import { BlurView } from "expo-blur"` at module scope and built its
|
|
15
|
+
animated blur component at module load, and `coachmarks/index.ts` re-exports the overlay with no
|
|
16
|
+
wildcard escape — so importing ANYTHING from `@wireai/activation/coachmarks`, even
|
|
17
|
+
`setCoachmarkStorage`, dragged `expo-blur` in. A host that took the peer's "optional" at its word
|
|
18
|
+
and skipped installing it hit a Metro resolution failure the moment it touched coachmarks, which
|
|
19
|
+
is why the AI Mobile Launcher tiers (and any Expo Go build) could not adopt the capability. The
|
|
20
|
+
peer is now resolved through a guarded lazy require with a string-literal specifier inside a
|
|
21
|
+
try/catch (the same shape as `icons/expoIcons.ts` / `device/appVersion.ts`), and the animated
|
|
22
|
+
component is built once on first render, so the subpath carries no static top-level import of the
|
|
23
|
+
peer.
|
|
24
|
+
- **Degradation, by design:** when `expo-blur` is absent the spotlight drops the frost to an
|
|
25
|
+
equivalent dimmed scrim. The ring, tooltip and gestures behave identically — a missing
|
|
26
|
+
decorative frost never blanks a tour, never crashes, never renders empty.
|
|
27
|
+
- `metro/index.js` `OPTIONAL_MODULES` and `tsup.config.ts` now treat `expo-blur` as a guarded
|
|
28
|
+
optional peer (moved off the static-peer list), so the built `dist/` collects it as optional and
|
|
29
|
+
the metro helper's empty-module safety net covers it for hosts that disable Metro's
|
|
30
|
+
`allowOptionalDependencies`.
|
|
31
|
+
|
|
32
|
+
## [0.14.1] - 2026-08-18
|
|
33
|
+
|
|
34
|
+
Two rulings from the 0.14.0 audit, applied. Both were reproduced there and left for a decision
|
|
35
|
+
rather than guessed at; both decisions came back on 2026-08-18.
|
|
36
|
+
|
|
37
|
+
**PATCH: no public surface gained, lost or renamed a member.** But read the first entry before you
|
|
38
|
+
upgrade — it is a behaviour change you will see in your funnel, not a silent internal fix.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- **The review/questionnaire gates counted JS PROCESSES, not app-opens, so `minSessions` stalled at
|
|
43
|
+
1 on iOS.** `useLifecycleEvents` already defines an app-open correctly — a foreground after 30+
|
|
44
|
+
minutes in the background — and fires a fresh `app.session_started` for it. The device-side
|
|
45
|
+
counter behind `minSessions` did not follow: it pinned one id for the life of the JS process, and
|
|
46
|
+
iOS suspends apps rather than killing them. So a user who opened the app every day for a week
|
|
47
|
+
produced seven server-side opens while `wire_review_<id>_sessions` stayed at 1, and any gate with
|
|
48
|
+
the fail-closed `minSessions: 2` default could never fire on a phone that is never force-quit —
|
|
49
|
+
which is most phones. **⚠️ UPGRADE NOTE: gates that have been silently dormant will start firing.**
|
|
50
|
+
That is the fix, but if you have been reading a flat prompt rate as "users decline", the number
|
|
51
|
+
will move. The counter still refuses the case it was pinned for in the first place: on a cold
|
|
52
|
+
start the gate renders (and counts) before the root lifecycle effect registers the session id, and
|
|
53
|
+
that first registration NAMES the open already counted rather than starting a second one — so the
|
|
54
|
+
2026-07-16 incident, where `minSessions: 2` became satisfiable inside a user's very first launch,
|
|
55
|
+
stays closed. One accepted under-count remains, deliberately: if the mount open never registers (a
|
|
56
|
+
refused non-durable device key over a broken store) and a later foreground is the first
|
|
57
|
+
registration the launch sees, it is adopted rather than counted. A fail-closed gate may only err
|
|
58
|
+
toward prompting less.
|
|
59
|
+
|
|
60
|
+
- **A permission record that could not be READ is no longer read as "nothing settled".** A timed-out
|
|
61
|
+
or throwing storage read arrived as `[]`, which is indistinguishable from a genuine empty record,
|
|
62
|
+
and the flow acted on that guess twice: it re-showed a permission screen the user may already have
|
|
63
|
+
answered — spending the ONE native prompt iOS ever grants — and the answer then persisted a record
|
|
64
|
+
containing just that one id, OVER whatever the store actually held. A slow adapter could therefore
|
|
65
|
+
permanently shrink the memory that exists to prevent the re-ask. The read now reports whether the
|
|
66
|
+
store answered at all; on "it did not", permission screens are suppressed for that session and
|
|
67
|
+
nothing is written back. A successful read of a corrupt, absent or other-session entry is still a
|
|
68
|
+
genuine empty and still shows the screen. The trade is stated rather than hidden: on a host whose
|
|
69
|
+
adapter never answers, configured permission screens silently never show, so it warns in dev
|
|
70
|
+
naming the adapter.
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- **`@wireai/activation:processOpenId` is re-declared LIVE (was LATCH).** The governance canary in
|
|
75
|
+
`test/canary/globalSlotDiscipline.test.tsx` asserted that this slot must NOT follow the session
|
|
76
|
+
registry, which is what froze the counter above; it now drives the slot through two real opens and
|
|
77
|
+
requires it to move. The cold-start property it used to guard — the first registration of a launch
|
|
78
|
+
must not re-pin — is asserted in `src/reviews/sessionCount.test.ts`, alongside the incident it
|
|
79
|
+
exists for. The slot holds the pinned open id plus the registered id observed when it was pinned,
|
|
80
|
+
because "is this a new open?" is only answerable against what was last seen.
|
|
81
|
+
|
|
82
|
+
Tests 808 → 814, all green, and the deferred `todo` reproduction is now a passing assertion.
|
|
83
|
+
Every fix was falsified first: reverting the counter fix reddens exactly the app-open test plus the
|
|
84
|
+
slot canary and leaves the cold-start test green; collapsing the unknown read back to empty reddens
|
|
85
|
+
both unit tests and the end-to-end canary while its positive control stays green.
|
|
86
|
+
|
|
6
87
|
## [0.14.0] - 2026-08-17
|
|
7
88
|
|
|
8
89
|
A durability release, in two rounds. Round 1 stopped the two counted lifecycle events from riding a
|
package/README.md
CHANGED
|
@@ -1162,6 +1162,11 @@ const gate = useReviewGate({ config: { id: "home", minSessions: 2 }, decision, s
|
|
|
1162
1162
|
await track("journal_done"); // POST stored → revalidation bumps → effect re-fetches → gate can fire
|
|
1163
1163
|
```
|
|
1164
1164
|
|
|
1165
|
+
`minSessions` counts **app-opens**, not mounts and not launches: a foreground after 30+ minutes in
|
|
1166
|
+
the background is a new one. That matters on iOS, where an app is suspended rather than killed — a
|
|
1167
|
+
user who never force-quits still advances the counter. (Before 0.14.1 it counted JS processes, so
|
|
1168
|
+
on those devices it stayed at 1 forever and a `minSessions: 2` gate could never fire.)
|
|
1169
|
+
|
|
1165
1170
|
The questionnaire gate uses the identical shape: swap `fetchReviewDecision` / `useReviewGate`
|
|
1166
1171
|
for `fetchQuestionnaireDecision` / `useQuestionnaireGate` from `@wireai/activation/questionnaire`
|
|
1167
1172
|
(both take a `decision` prop). One `wire.track` call feeds every subscribed gate.
|