@wireai/activation 0.12.2 → 0.13.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 +145 -0
- package/README.md +5 -3
- package/dist/analytics/index.d.mts +2 -2
- package/dist/analytics/index.d.ts +2 -2
- package/dist/analytics/index.js +114 -35
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +114 -36
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/{currentSession-BxEB37xt.d.ts → currentSession-D7zabMXK.d.ts} +161 -9
- package/dist/{currentSession-BlCeDP0f.d.mts → currentSession-_GynvhzT.d.mts} +161 -9
- package/dist/index.d.mts +5 -15
- package/dist/index.d.ts +5 -15
- package/dist/index.js +232 -135
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +229 -134
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.js +8 -6
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +8 -6
- package/dist/reviews/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/OnboardingFlow.tsx +10 -3
- package/src/WireOnboarding.tsx +115 -32
- package/src/activation/wireActivation.ts +13 -7
- package/src/analytics/analyticsFacade.ts +11 -10
- package/src/analytics/currentSession.ts +6 -20
- package/src/analytics/eventQueue.ts +69 -1
- package/src/analytics/index.ts +1 -1
- package/src/analytics/reportClientEvent.ts +92 -29
- package/src/config/wireConfigFromEnv.ts +1 -10
- package/src/context/deviceId.ts +77 -16
- package/src/context/userContext.ts +4 -15
- package/src/identity/identityRecord.ts +123 -0
- package/src/identity/userIdentity.ts +45 -9
- package/src/index.ts +6 -4
- package/src/session-analytics/useLifecycleEvents.ts +10 -1
- package/src/types.ts +14 -0
- package/src/utils/deriveAnswers.ts +6 -2
- package/src/utils/readProgress.ts +4 -0
- package/src/utils/warnInDev.ts +33 -0
- package/src/components/DoneBlock.tsx +0 -37
package/src/types.ts
CHANGED
|
@@ -272,6 +272,20 @@ export type OnboardingProgress = {
|
|
|
272
272
|
total: number;
|
|
273
273
|
/** Base-question key for the CURRENT screen, when known (used to pick a validator). */
|
|
274
274
|
key?: string;
|
|
275
|
+
/**
|
|
276
|
+
* STABLE per-slot identity for the CURRENT screen, when the backend sends one. Preferred over
|
|
277
|
+
* {@link key} for both the answer key and the validator lookup.
|
|
278
|
+
*
|
|
279
|
+
* WHY IT EXISTS: `key` is authored from the question's prompt text (the tenant flows slugify it and
|
|
280
|
+
* cut at 32 chars), so re-wording a question mints a NEW key — the answer a host reads as
|
|
281
|
+
* `answers.interests` silently becomes `answers.what_are_you_into_v2`, with no error anywhere. A
|
|
282
|
+
* slot is the question's identity independent of its wording.
|
|
283
|
+
*
|
|
284
|
+
* FULLY ADDITIVE AND CURRENTLY INERT: no server emits it yet. Every fallback is PER-CARD, so a
|
|
285
|
+
* thread that mixes slotted and unslotted cards (the real shape during a rollout) keys each one
|
|
286
|
+
* correctly, and a backend that never sends it produces byte-identical behaviour to 0.12.2.
|
|
287
|
+
*/
|
|
288
|
+
slot_id?: string;
|
|
275
289
|
/** Whether the CURRENT screen may be skipped (backend-marked; default false → no Skip shown). */
|
|
276
290
|
skippable?: boolean;
|
|
277
291
|
};
|
|
@@ -29,10 +29,14 @@ export const deriveAnswers = (messages: Message[]): Record<string, unknown> => {
|
|
|
29
29
|
if (!m || m.role !== "assistant" || !r || r.action !== "render") continue;
|
|
30
30
|
|
|
31
31
|
const props = (r.props ?? {}) as Record<string, unknown>;
|
|
32
|
-
const progress = props.progress as { key?: string } | undefined;
|
|
32
|
+
const progress = props.progress as { key?: string; slot_id?: string } | undefined;
|
|
33
33
|
const questionText =
|
|
34
34
|
(props.title as string) ?? (props.label as string) ?? (props.question as string);
|
|
35
|
-
|
|
35
|
+
// slot_id > key > question text, decided PER CARD. A thread that mixes slotted and unslotted
|
|
36
|
+
// cards is the real shape during a server rollout, so caching a "this backend does slots" verdict
|
|
37
|
+
// off the first card would mis-key every later one. Against a backend that sends no `slot_id`
|
|
38
|
+
// this is byte-identical to 0.12.2.
|
|
39
|
+
const key = progress?.slot_id || progress?.key || questionText;
|
|
36
40
|
|
|
37
41
|
const reply = messages[i + 1];
|
|
38
42
|
if (key && reply?.role === "user") {
|
|
@@ -15,6 +15,8 @@ type PartialProgress = {
|
|
|
15
15
|
step?: number;
|
|
16
16
|
total?: number;
|
|
17
17
|
key?: string;
|
|
18
|
+
/** The stable per-slot identity, when the backend sends one. See `OnboardingProgress.slot_id`. */
|
|
19
|
+
slot_id?: string;
|
|
18
20
|
skippable?: boolean;
|
|
19
21
|
};
|
|
20
22
|
|
|
@@ -28,6 +30,8 @@ export const readProgress = (response?: WireAIResponse): PartialProgress => {
|
|
|
28
30
|
step: typeof p.step === "number" ? p.step : undefined,
|
|
29
31
|
total: typeof p.total === "number" ? p.total : undefined,
|
|
30
32
|
key: typeof p.key === "string" ? p.key : undefined,
|
|
33
|
+
// Whitelisted the same way as every other field: an old backend simply omits it.
|
|
34
|
+
slot_id: typeof p.slot_id === "string" ? p.slot_id : undefined,
|
|
31
35
|
skippable: typeof p.skippable === "boolean" ? p.skippable : undefined,
|
|
32
36
|
};
|
|
33
37
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* warnInDev — the ONE developer-warning primitive.
|
|
3
|
+
*
|
|
4
|
+
* The kit warns a developer in four places (the onboarding host, the env-config helper, the analytics
|
|
5
|
+
* façade, the current-session registry) and until 0.13.0 each carried its own copy of the same five
|
|
6
|
+
* lines. Three were byte-identical; the fourth differed only by returning whether it actually warned.
|
|
7
|
+
* Four copies of a guard is four chances for one of them to drift out of the `__DEV__` gate, which is
|
|
8
|
+
* the failure that matters: a warning that runs in production is a string built for nobody.
|
|
9
|
+
*
|
|
10
|
+
* ON THE TREE-SHAKING NOTE THIS REPLACES: `analytics/currentSession` used to justify its copy as
|
|
11
|
+
* keeping the module import-free for the tree-shaken `./analytics` bundle. That rationale had already
|
|
12
|
+
* lapsed — the module imports `makeSessionId` from `./reportClientEvent` — and this module has no
|
|
13
|
+
* imports of its own, so it adds one leaf to the graph and nothing to the bundle. The `treeShake`
|
|
14
|
+
* canary still holds the real guarantee (the analytics graph reaches no UI module).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** RN sets this global; absent under node/SSR. Read defensively, never assumed. */
|
|
18
|
+
declare const __DEV__: boolean | undefined;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Emit a one-line developer warning, but ONLY in a dev build (RN `__DEV__`). No-op in prod/tests.
|
|
22
|
+
*
|
|
23
|
+
* Returns whether it ACTUALLY warned, which a caller holding a once-flag must honour: marking
|
|
24
|
+
* "already warned" after a no-op would burn the single warning in production, and the one dev build
|
|
25
|
+
* that needed it would then run silent.
|
|
26
|
+
*/
|
|
27
|
+
export const warnInDev = (message: string): boolean => {
|
|
28
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
29
|
+
console.warn(message);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DoneBlock — a brief themed "all set" state shown when the flow completes, while
|
|
3
|
-
* the host persists results and navigates away. Purely cosmetic; the real
|
|
4
|
-
* terminal signal is the SDK's StatusCard (handled in OnboardingFlow).
|
|
5
|
-
*/
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { StyleSheet, Text, View } from "react-native";
|
|
8
|
-
import { useOnboardingTheme } from "../theme/ThemeContext";
|
|
9
|
-
import { bodyStyle, headingStyle } from "../theme/typography";
|
|
10
|
-
|
|
11
|
-
export type DoneBlockProps = {
|
|
12
|
-
title?: string;
|
|
13
|
-
message?: string;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const _DoneBlock: React.FC<DoneBlockProps> = ({
|
|
17
|
-
title = "You're all set",
|
|
18
|
-
message = "Personalizing your experience…",
|
|
19
|
-
}) => {
|
|
20
|
-
const t = useOnboardingTheme();
|
|
21
|
-
return (
|
|
22
|
-
<View style={[styles.center, { gap: t.spacing.sm, padding: t.spacing.lg }]}>
|
|
23
|
-
<Text style={[headingStyle(t.fonts), { color: t.colors.success, textAlign: "center" }]}>
|
|
24
|
-
{title}
|
|
25
|
-
</Text>
|
|
26
|
-
<Text style={[bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }]}>
|
|
27
|
-
{message}
|
|
28
|
-
</Text>
|
|
29
|
-
</View>
|
|
30
|
-
);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export const DoneBlock = React.memo(_DoneBlock);
|
|
34
|
-
|
|
35
|
-
const styles = StyleSheet.create({
|
|
36
|
-
center: { flex: 1, alignItems: "center", justifyContent: "center" },
|
|
37
|
-
});
|