@wireai/activation 0.13.2 → 0.13.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/AGENTS.md +4 -3
- package/CHANGELOG.md +228 -4
- package/README.md +16 -2
- package/dist/analytics/index.d.mts +4 -4
- package/dist/analytics/index.d.ts +4 -4
- package/dist/analytics/index.js +104 -470
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +102 -468
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/coachmarks/index.d.mts +14 -1
- package/dist/coachmarks/index.d.ts +14 -1
- package/dist/coachmarks/index.js +58 -14
- package/dist/coachmarks/index.js.map +1 -1
- package/dist/coachmarks/index.mjs +58 -14
- package/dist/coachmarks/index.mjs.map +1 -1
- package/dist/{currentSession-ClkLjcJ0.d.mts → currentSession-BoWtr3Jp.d.mts} +2 -2
- package/dist/{currentSession-DOVZEWJl.d.ts → currentSession-CCOMlaQ2.d.ts} +2 -2
- package/dist/{decision-Bl_M2y3r.d.mts → decision-Cjw0jbEj.d.mts} +1 -1
- package/dist/{decision-Cau5KmP6.d.ts → decision-GfpXAsk_.d.ts} +1 -1
- package/dist/index.d.mts +25 -2
- package/dist/index.d.ts +25 -2
- package/dist/index.js +220 -573
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +217 -570
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.d.mts +42 -10
- package/dist/questionnaire/index.d.ts +42 -10
- package/dist/questionnaire/index.js +45 -13
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +45 -13
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.d.mts +4 -4
- package/dist/reviews/index.d.ts +4 -4
- package/dist/reviews/index.js +27 -7
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +27 -7
- package/dist/reviews/index.mjs.map +1 -1
- package/dist/showcase/index.js +7 -2
- package/dist/showcase/index.js.map +1 -1
- package/dist/showcase/index.mjs +7 -2
- package/dist/showcase/index.mjs.map +1 -1
- package/dist/{transport-DsRe4epC.d.ts → transport-CefdERPs.d.mts} +44 -5
- package/dist/{transport-CF_eHwzC.d.mts → transport-DFuPyCoJ.d.ts} +44 -5
- package/dist/{types-Buj9Lw9t.d.ts → types-UVYt9BJe.d.ts} +41 -1
- package/dist/{types-CNUqMK0D.d.mts → types-l2tfg23P.d.mts} +41 -1
- package/package.json +4 -1
- package/src/OnboardingFlow.tsx +44 -15
- package/src/WireOnboarding.tsx +7 -6
- package/src/activation/wireActivation.ts +3 -3
- package/src/analytics/analyticsFacade.ts +2 -2
- package/src/analytics/eventQueue.ts +83 -20
- package/src/cards/CardGridSelectCard.tsx +1 -1
- package/src/cards/ChipSelectCard.tsx +2 -2
- package/src/cards/SelectionCard.tsx +1 -1
- package/src/coachmarks/runtime.ts +62 -2
- package/src/coachmarks/useCoachmarkTour.ts +62 -14
- package/src/context/deviceId.ts +4 -4
- package/src/features/WireFeaturesProvider.tsx +4 -1
- package/src/identity/userIdentity.ts +1 -1
- package/src/questionnaire/QuestionnaireGate.tsx +90 -10
- package/src/questionnaire/index.ts +9 -1
- package/src/questionnaire/transport.ts +46 -11
- package/src/reviews/ReviewGate.tsx +61 -6
- package/src/reviews/index.ts +10 -1
- package/src/reviews/transport.ts +58 -12
- package/src/session-analytics/useLifecycleEvents.ts +1 -1
- package/src/theme/ThemeContext.tsx +42 -1
- package/src/types.ts +1 -1
- package/src/utils/submitResult.ts +39 -0
|
@@ -3,12 +3,53 @@
|
|
|
3
3
|
* themed card via `useOnboardingTheme()`. WHY a context rather than prop-drilling:
|
|
4
4
|
* the cards are mounted by the SDK's ComponentRenderer (not by us), so we can't
|
|
5
5
|
* pass them props directly — they must read the theme from context.
|
|
6
|
+
*
|
|
7
|
+
* ── WHY THE CONTEXT LIVES ON A `Symbol.for` SLOT ───────────────────────────────────────────────
|
|
8
|
+
*
|
|
9
|
+
* It was a plain `createContext(...)`, and tsup inlines this module into FIVE dist bundles
|
|
10
|
+
* (`.`, `./reviews`, `./questionnaire`, `./coachmarks`, `./showcase`) — so each one carried its own
|
|
11
|
+
* context instance. `OnboardingThemeProvider` is exported ONLY from the root barrel, so on any
|
|
12
|
+
* `dist` resolution path (plain RN CLI, `unstable_enablePackageExports: false`, node, jest, SSR) a
|
|
13
|
+
* host that wrapped its tree in `<OnboardingThemeProvider>` and rendered `<ReviewGate>` from
|
|
14
|
+
* `@wireai/activation/reviews` was writing one context and reading another. Nothing crashed and
|
|
15
|
+
* nothing warned: `useOnboardingTheme`'s `ctx ?? mergeTheme()` fallback quietly served the NEUTRAL
|
|
16
|
+
* default theme, so the tenant's brand simply did not apply to the gate. On-device this was masked
|
|
17
|
+
* only because Metro's `react-native` export condition collapses every subpath back to this one
|
|
18
|
+
* `src/` file — the same "bundler accident, not a guarantee" the `currentSession` header names, and
|
|
19
|
+
* the same root cause as the optional-peers bug 0.13.3 fixed.
|
|
20
|
+
*
|
|
21
|
+
* The bundler-agnostic fix, exactly as `coachmarks/runtime`, `analytics/currentSession`,
|
|
22
|
+
* `context/deviceId` and `features/WireFeaturesProvider` already do it: park the ONE context on a
|
|
23
|
+
* well-known `globalThis` slot keyed by `Symbol.for(...)`. `Symbol.for` uses the runtime-global
|
|
24
|
+
* symbol registry, so every inlined copy of this module resolves the SAME symbol, finds the slot
|
|
25
|
+
* already filled, and reuses that instance — one context identity however many copies exist.
|
|
26
|
+
* `globalThis` is present and identical in Hermes/React Native, Node and SSR (we never touch
|
|
27
|
+
* `window`), so this is safe on every host. `test/canary/themeContextIdentity.test.tsx` measures it
|
|
28
|
+
* in the BUILT artifact, because the defect is invisible in source.
|
|
6
29
|
*/
|
|
7
30
|
import React, { createContext, useContext, useMemo } from "react";
|
|
8
31
|
import { mergeTheme } from "./defaultTheme";
|
|
9
32
|
import type { OnboardingTheme } from "./types";
|
|
10
33
|
|
|
11
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Well-known key into the runtime-global symbol registry. `Symbol.for` (NOT a plain `Symbol()`) is
|
|
36
|
+
* what makes this cross-bundle: it returns the SAME symbol for the same string across every copy of
|
|
37
|
+
* this module, so duplicated inlined copies all address one slot.
|
|
38
|
+
*/
|
|
39
|
+
const THEME_CONTEXT_SLOT: unique symbol = Symbol.for("@wireai/activation:themeContext");
|
|
40
|
+
|
|
41
|
+
type GlobalWithThemeContext = typeof globalThis & {
|
|
42
|
+
[THEME_CONTEXT_SLOT]?: React.Context<OnboardingTheme | null>;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const themeGlobal = globalThis as GlobalWithThemeContext;
|
|
46
|
+
|
|
47
|
+
// First copy to load creates the context; every later copy adopts it. Never overwrite an existing
|
|
48
|
+
// slot — a second `createContext` here would re-split the identity this guard exists to keep whole.
|
|
49
|
+
if (!themeGlobal[THEME_CONTEXT_SLOT]) {
|
|
50
|
+
themeGlobal[THEME_CONTEXT_SLOT] = createContext<OnboardingTheme | null>(null);
|
|
51
|
+
}
|
|
52
|
+
const OnboardingThemeContext = themeGlobal[THEME_CONTEXT_SLOT] as React.Context<OnboardingTheme | null>;
|
|
12
53
|
|
|
13
54
|
export type OnboardingThemeProviderProps = {
|
|
14
55
|
/** Partial overrides merged over the neutral default theme. */
|
package/src/types.ts
CHANGED
|
@@ -340,7 +340,7 @@ export type OnboardingProgress = {
|
|
|
340
340
|
* `answers.interests` silently becomes `answers.what_are_you_into_v2`, with no error anywhere. A
|
|
341
341
|
* slot is the question's identity independent of its wording.
|
|
342
342
|
*
|
|
343
|
-
* FULLY ADDITIVE, AND LIVE SINCE 2026-07-28
|
|
343
|
+
* FULLY ADDITIVE, AND LIVE SINCE 2026-07-28. The deployed server sends it on
|
|
344
344
|
* `progress` for every AI-GENERATED question, as `adaptive_<n>` 1-based over adaptive answers, and
|
|
345
345
|
* for a CONFIGURED question only when the tenant set one. Where the tenant set none the field is
|
|
346
346
|
* simply absent. Every fallback is PER-CARD, so a thread that mixes slotted and unslotted cards
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* submitResult — the ONE delivery verdict every user-payload POST in this kit reports back.
|
|
3
|
+
*
|
|
4
|
+
* WHY IT IS SHARED, AND WHY IT IS NOT A BOOLEAN. Two surfaces post a payload the user typed —
|
|
5
|
+
* `submitReview` (`reviews/transport.ts`) and `submitQuestionnaireResponse`
|
|
6
|
+
* (`questionnaire/transport.ts`) — and NEITHER has a persisted queue behind it (unlike
|
|
7
|
+
* `analytics/eventQueue`, with its `maxSize`/`maxRetries`/backoff). Both sit behind a permanent
|
|
8
|
+
* once-gate: the host writes `wire_review_<id>_seen` / `wire_questionnaire_<id>_<ver>_seen` when the
|
|
9
|
+
* gate resolves, so a payload that dies in the socket is lost AND the user is never asked again.
|
|
10
|
+
* Reading the fate of the request is the only thing that lets a caller keep an undelivered payload
|
|
11
|
+
* recoverable, so the two surfaces must answer the same question in the same words.
|
|
12
|
+
*
|
|
13
|
+
* • `accepted` — the server answered 2xx. Stored.
|
|
14
|
+
* • `rejected` — the server ANSWERED, non-2xx. It was reached; it either stored the payload and
|
|
15
|
+
* lost the ack (a gateway 502 after the write commits) or deliberately refused it.
|
|
16
|
+
* Either way the server owns the outcome and a re-post risks a SECOND row.
|
|
17
|
+
* • `unsent` — nothing came back at all: no target, a missing/throwing `fetch`, a rejected
|
|
18
|
+
* request. **This is the ONLY outcome a caller may retry on.**
|
|
19
|
+
*
|
|
20
|
+
* ── WHY THREE VALUES AND NOT TWO (pinned here so the defect cannot move surface) ───────────────
|
|
21
|
+
*
|
|
22
|
+
* `submitReview` was a boolean for exactly one unpublished release, and the boolean WAS the bug.
|
|
23
|
+
* `false` meant both "nothing reached the server" and "the server answered non-2xx", and the caller
|
|
24
|
+
* treats `false` as "still owed" and re-posts. But the server mints its own row id
|
|
25
|
+
* (`create_review` / `_new_id()`), and neither `CreateReviewRequest` nor the questionnaire response
|
|
26
|
+
* body carries a client id, so there is NO idempotency key on the wire: a 502 returned AFTER the
|
|
27
|
+
* insert commits means the re-post writes a SECOND row. A response of any status proves the server
|
|
28
|
+
* was reached, and that is a different question from whether it liked the payload.
|
|
29
|
+
*
|
|
30
|
+
* ── THE RESIDUAL, STATED HONESTLY ─────────────────────────────────────────────────────────────
|
|
31
|
+
*
|
|
32
|
+
* `unsent` is not proof the server never got it. A connection dropped after the request was written
|
|
33
|
+
* — or after the row committed — surfaces as a thrown/rejected `fetch`, exactly like an offline
|
|
34
|
+
* device. Retrying only on `unsent` is therefore SAFER, not SAFE. Closing that window needs a
|
|
35
|
+
* CLIENT-MINTED IDEMPOTENCY KEY the server upserts on, which is a server change and not something
|
|
36
|
+
* the kit can fake. Until it exists, prefer losing a row over inventing one: a lost detractor is a
|
|
37
|
+
* gap in the data, a duplicated one is a lie in the data.
|
|
38
|
+
*/
|
|
39
|
+
export type SubmitResult = "accepted" | "rejected" | "unsent";
|