@wireai/activation 0.2.0 → 0.4.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.
Files changed (63) hide show
  1. package/dist/analytics/index.d.mts +69 -2
  2. package/dist/analytics/index.d.ts +69 -2
  3. package/dist/analytics/index.js +123 -2
  4. package/dist/analytics/index.js.map +1 -1
  5. package/dist/analytics/index.mjs +122 -3
  6. package/dist/analytics/index.mjs.map +1 -1
  7. package/dist/coachmarks/index.js +6 -1
  8. package/dist/coachmarks/index.js.map +1 -1
  9. package/dist/coachmarks/index.mjs +6 -1
  10. package/dist/coachmarks/index.mjs.map +1 -1
  11. package/dist/{eventQueue-CxKi7Qd5.d.mts → eventQueue-CA1d8Fmn.d.mts} +7 -3
  12. package/dist/{eventQueue-rV1dtJJR.d.ts → eventQueue-CrNB9gzH.d.ts} +7 -3
  13. package/dist/index.d.mts +5 -4
  14. package/dist/index.d.ts +5 -4
  15. package/dist/index.js +220 -151
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +221 -152
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/questionnaire/index.d.mts +1 -1
  20. package/dist/questionnaire/index.d.ts +1 -1
  21. package/dist/questionnaire/index.js +29 -5
  22. package/dist/questionnaire/index.js.map +1 -1
  23. package/dist/questionnaire/index.mjs +30 -6
  24. package/dist/questionnaire/index.mjs.map +1 -1
  25. package/dist/reviews/index.d.mts +1 -1
  26. package/dist/reviews/index.d.ts +1 -1
  27. package/dist/reviews/index.js +40 -9
  28. package/dist/reviews/index.js.map +1 -1
  29. package/dist/reviews/index.mjs +42 -11
  30. package/dist/reviews/index.mjs.map +1 -1
  31. package/dist/showcase/index.d.mts +1 -14
  32. package/dist/showcase/index.d.ts +1 -14
  33. package/dist/showcase/index.js +8 -2
  34. package/dist/showcase/index.js.map +1 -1
  35. package/dist/showcase/index.mjs +8 -2
  36. package/dist/showcase/index.mjs.map +1 -1
  37. package/package.json +3 -2
  38. package/src/OnboardingFlow.tsx +8 -4
  39. package/src/WireOnboarding.tsx +8 -3
  40. package/src/analytics/analyticsFacade.ts +184 -0
  41. package/src/analytics/eventQueue.ts +5 -0
  42. package/src/analytics/index.ts +12 -0
  43. package/src/analytics/reportClientEvent.ts +11 -2
  44. package/src/analytics/useAnalytics.ts +42 -0
  45. package/src/analytics/useScreenTracking.ts +23 -1
  46. package/src/cards/InterstitialCard.tsx +1 -1
  47. package/src/cards/NumberStepperCard.tsx +12 -7
  48. package/src/cards/StatusCard.tsx +13 -8
  49. package/src/cards/TextInputCard.tsx +10 -5
  50. package/src/components/AnimatedSparkle.tsx +20 -3
  51. package/src/components/Button.tsx +10 -1
  52. package/src/components/CardHandoff.tsx +2 -6
  53. package/src/components/CardLayout.tsx +16 -10
  54. package/src/components/Illustration.tsx +9 -5
  55. package/src/components/LoadingBlock.tsx +44 -29
  56. package/src/components/LoadingScreen.tsx +3 -2
  57. package/src/components/OnboardingScaffold.tsx +21 -23
  58. package/src/features/WireFeaturesProvider.tsx +7 -2
  59. package/src/questionnaire/QuestionnaireGate.tsx +15 -3
  60. package/src/reviews/ReviewGate.tsx +27 -7
  61. package/src/reviews/ReviewModal.tsx +4 -0
  62. package/src/showcase/FeatureShowcase.tsx +2 -1
  63. package/src/components/loaderChrome.ts +0 -28
@@ -1,5 +1,6 @@
1
1
  export { R as ReportAppEventOptions, r as reportAppEvent } from '../transport-BeO_Brcu.mjs';
2
- export { A as AnalyticsEvent, C as ClientEvent, a as ClientEventTarget, b as ClientEventType, c as ContextEnvelope, d as ContextEnvelopeInput, E as EnvelopeSource, e as EventQueue, f as EventQueueOptions, W as WIRE_ONBOARDING_EVENTS, g as WireOnboardingEventName, h as buildContextEnvelope, i as createEventQueue, m as makeSessionId, r as reportClientEvent, j as reportClientEvents, t as toAnalyticsEvent } from '../eventQueue-CxKi7Qd5.mjs';
2
+ import { E as EventQueueOptions } from '../eventQueue-CA1d8Fmn.mjs';
3
+ export { A as AnalyticsEvent, C as ClientEvent, a as ClientEventTarget, b as ClientEventType, c as ContextEnvelope, d as ContextEnvelopeInput, e as EnvelopeSource, f as EventQueue, W as WIRE_ONBOARDING_EVENTS, g as WireOnboardingEventName, h as buildContextEnvelope, i as createEventQueue, m as makeSessionId, r as reportClientEvent, j as reportClientEvents, t as toAnalyticsEvent } from '../eventQueue-CA1d8Fmn.mjs';
3
4
  import '../types-A6pTxIZV.mjs';
4
5
  import '../types-BKfpdZzX.mjs';
5
6
  import '../types-GL_hQ0TN.mjs';
@@ -89,4 +90,70 @@ interface NavigationRefLike {
89
90
  */
90
91
  declare const useScreenTracking: (navigationRef: NavigationRefLike | undefined, options?: ScreenTrackerOptions) => void;
91
92
 
92
- export { type NavigationRefLike, type NavigationRouteLike, type NavigationStateLike, type ScreenTracker, type ScreenTrackerOptions, createScreenTracker, getActiveRouteName, screenTrackingHandler, useScreenTracking };
93
+ /** Arbitrary non-PII event properties. Serialized to the event's `meta` (a JSON string) on the wire. */
94
+ type AnalyticsProps = Record<string, unknown>;
95
+ /**
96
+ * Tenant transport + context inputs for {@link createAnalytics}. `serverUrl`/`apiKey` are the
97
+ * SAME creds as onboarding (never a second key). The rest feed the context envelope + the queue's
98
+ * offline persistence — all optional.
99
+ */
100
+ type CreateAnalyticsConfig = {
101
+ /** Base server URL (same as `WireOnboardingConfig.serverUrl`); `/v1/events` is appended. */
102
+ serverUrl: string;
103
+ /** Tenant API key; sent as `Authorization: Bearer`. */
104
+ apiKey: string;
105
+ /**
106
+ * Correlation id shared by every event from this instance (and the `identify` event). Defaults
107
+ * to a fresh `makeSessionId()` at creation so all events agree on one id per analytics instance.
108
+ */
109
+ sessionId?: string;
110
+ /** Tenant/app id used to namespace the queue's default storage key (`wireai:evtq:<appId>`). */
111
+ appId?: string;
112
+ /**
113
+ * Host persistence (AsyncStorage-compatible subset) for offline-first durability. When omitted,
114
+ * the queue runs in the documented in-memory mode (survives re-renders, not app kills).
115
+ */
116
+ storage?: EventQueueOptions["storage"];
117
+ /** Host app version, e.g. "1.4.2" (host-injected; stamped onto every event's context). */
118
+ appVersion?: string;
119
+ /** Host native build number, e.g. "412" (host-injected). */
120
+ appBuild?: string;
121
+ /** Host connectivity signal, e.g. "wifi" | "cellular" — read fresh per event via the provider. */
122
+ networkType?: string;
123
+ };
124
+ /** Optional queue tuning knobs, forwarded verbatim to {@link createEventQueue}. */
125
+ type AnalyticsOptions = Partial<Pick<EventQueueOptions, "maxSize" | "batchSize" | "baseBackoffMs" | "maxBackoffMs" | "maxRetries">>;
126
+ /**
127
+ * The developer-facing analytics surface. `track`/`screen`/`identify` are the Segment/PostHog-shaped
128
+ * API; `flush`/`notifyOnline`/`size` expose the underlying queue so a host can drive reconnect
129
+ * draining (load-bearing for offline-first) and inspect the pending buffer.
130
+ */
131
+ type Analytics = {
132
+ /** Record an in-app event: `event_type='app_event'`, `question_key=<event>`, `props`→`meta`. */
133
+ track(event: string, props?: AnalyticsProps): void;
134
+ /** Record a screen view: `event_type='app_event'`, `question_key='screen'`, `meta={ screen, ...props }`. */
135
+ screen(name: string, props?: AnalyticsProps): void;
136
+ /** Bind the host's opaque user id (per-session, in-memory) and emit an `identify` event. */
137
+ identify(userId: string, traits?: AnalyticsProps): void;
138
+ /** Attempt an immediate drain of the pending buffer. Fire-and-forget. */
139
+ flush(): void;
140
+ /** Host reconnect signal: reset backoff and drain now. Fire-and-forget. */
141
+ notifyOnline(): void;
142
+ /** Current pending (in-memory) count. */
143
+ size(): number;
144
+ };
145
+ /**
146
+ * Create a bound analytics instance. Seeds one correlation `sessionId`, builds an offline-first
147
+ * queue over the tenant transport, and passes a fresh-per-event context envelope PROVIDER so the
148
+ * connectivity type is read at enqueue time. The bound user id starts unset (see `identify`).
149
+ */
150
+ declare const createAnalytics: (config: CreateAnalyticsConfig, options?: AnalyticsOptions) => Analytics;
151
+
152
+ /**
153
+ * Build a per-mount analytics instance. `config`/`options` are read once at first render (the
154
+ * instance is stable for the component's lifetime, held in a ref). Returns the {@link Analytics}
155
+ * surface so the component can `track` / `screen` / `identify` and drive `notifyOnline` on reconnect.
156
+ */
157
+ declare const useAnalytics: (config: CreateAnalyticsConfig, options?: AnalyticsOptions) => Analytics;
158
+
159
+ export { type Analytics, type AnalyticsOptions, type AnalyticsProps, type CreateAnalyticsConfig, EventQueueOptions, type NavigationRefLike, type NavigationRouteLike, type NavigationStateLike, type ScreenTracker, type ScreenTrackerOptions, createAnalytics, createScreenTracker, getActiveRouteName, screenTrackingHandler, useAnalytics, useScreenTracking };
@@ -1,5 +1,6 @@
1
1
  export { R as ReportAppEventOptions, r as reportAppEvent } from '../transport-DLpd1v5_.js';
2
- export { A as AnalyticsEvent, C as ClientEvent, a as ClientEventTarget, b as ClientEventType, c as ContextEnvelope, d as ContextEnvelopeInput, E as EnvelopeSource, e as EventQueue, f as EventQueueOptions, W as WIRE_ONBOARDING_EVENTS, g as WireOnboardingEventName, h as buildContextEnvelope, i as createEventQueue, m as makeSessionId, r as reportClientEvent, j as reportClientEvents, t as toAnalyticsEvent } from '../eventQueue-rV1dtJJR.js';
2
+ import { E as EventQueueOptions } from '../eventQueue-CrNB9gzH.js';
3
+ export { A as AnalyticsEvent, C as ClientEvent, a as ClientEventTarget, b as ClientEventType, c as ContextEnvelope, d as ContextEnvelopeInput, e as EnvelopeSource, f as EventQueue, W as WIRE_ONBOARDING_EVENTS, g as WireOnboardingEventName, h as buildContextEnvelope, i as createEventQueue, m as makeSessionId, r as reportClientEvent, j as reportClientEvents, t as toAnalyticsEvent } from '../eventQueue-CrNB9gzH.js';
3
4
  import '../types-BhpXJGlg.js';
4
5
  import '../types-BKfpdZzX.js';
5
6
  import '../types-GL_hQ0TN.js';
@@ -89,4 +90,70 @@ interface NavigationRefLike {
89
90
  */
90
91
  declare const useScreenTracking: (navigationRef: NavigationRefLike | undefined, options?: ScreenTrackerOptions) => void;
91
92
 
92
- export { type NavigationRefLike, type NavigationRouteLike, type NavigationStateLike, type ScreenTracker, type ScreenTrackerOptions, createScreenTracker, getActiveRouteName, screenTrackingHandler, useScreenTracking };
93
+ /** Arbitrary non-PII event properties. Serialized to the event's `meta` (a JSON string) on the wire. */
94
+ type AnalyticsProps = Record<string, unknown>;
95
+ /**
96
+ * Tenant transport + context inputs for {@link createAnalytics}. `serverUrl`/`apiKey` are the
97
+ * SAME creds as onboarding (never a second key). The rest feed the context envelope + the queue's
98
+ * offline persistence — all optional.
99
+ */
100
+ type CreateAnalyticsConfig = {
101
+ /** Base server URL (same as `WireOnboardingConfig.serverUrl`); `/v1/events` is appended. */
102
+ serverUrl: string;
103
+ /** Tenant API key; sent as `Authorization: Bearer`. */
104
+ apiKey: string;
105
+ /**
106
+ * Correlation id shared by every event from this instance (and the `identify` event). Defaults
107
+ * to a fresh `makeSessionId()` at creation so all events agree on one id per analytics instance.
108
+ */
109
+ sessionId?: string;
110
+ /** Tenant/app id used to namespace the queue's default storage key (`wireai:evtq:<appId>`). */
111
+ appId?: string;
112
+ /**
113
+ * Host persistence (AsyncStorage-compatible subset) for offline-first durability. When omitted,
114
+ * the queue runs in the documented in-memory mode (survives re-renders, not app kills).
115
+ */
116
+ storage?: EventQueueOptions["storage"];
117
+ /** Host app version, e.g. "1.4.2" (host-injected; stamped onto every event's context). */
118
+ appVersion?: string;
119
+ /** Host native build number, e.g. "412" (host-injected). */
120
+ appBuild?: string;
121
+ /** Host connectivity signal, e.g. "wifi" | "cellular" — read fresh per event via the provider. */
122
+ networkType?: string;
123
+ };
124
+ /** Optional queue tuning knobs, forwarded verbatim to {@link createEventQueue}. */
125
+ type AnalyticsOptions = Partial<Pick<EventQueueOptions, "maxSize" | "batchSize" | "baseBackoffMs" | "maxBackoffMs" | "maxRetries">>;
126
+ /**
127
+ * The developer-facing analytics surface. `track`/`screen`/`identify` are the Segment/PostHog-shaped
128
+ * API; `flush`/`notifyOnline`/`size` expose the underlying queue so a host can drive reconnect
129
+ * draining (load-bearing for offline-first) and inspect the pending buffer.
130
+ */
131
+ type Analytics = {
132
+ /** Record an in-app event: `event_type='app_event'`, `question_key=<event>`, `props`→`meta`. */
133
+ track(event: string, props?: AnalyticsProps): void;
134
+ /** Record a screen view: `event_type='app_event'`, `question_key='screen'`, `meta={ screen, ...props }`. */
135
+ screen(name: string, props?: AnalyticsProps): void;
136
+ /** Bind the host's opaque user id (per-session, in-memory) and emit an `identify` event. */
137
+ identify(userId: string, traits?: AnalyticsProps): void;
138
+ /** Attempt an immediate drain of the pending buffer. Fire-and-forget. */
139
+ flush(): void;
140
+ /** Host reconnect signal: reset backoff and drain now. Fire-and-forget. */
141
+ notifyOnline(): void;
142
+ /** Current pending (in-memory) count. */
143
+ size(): number;
144
+ };
145
+ /**
146
+ * Create a bound analytics instance. Seeds one correlation `sessionId`, builds an offline-first
147
+ * queue over the tenant transport, and passes a fresh-per-event context envelope PROVIDER so the
148
+ * connectivity type is read at enqueue time. The bound user id starts unset (see `identify`).
149
+ */
150
+ declare const createAnalytics: (config: CreateAnalyticsConfig, options?: AnalyticsOptions) => Analytics;
151
+
152
+ /**
153
+ * Build a per-mount analytics instance. `config`/`options` are read once at first render (the
154
+ * instance is stable for the component's lifetime, held in a ref). Returns the {@link Analytics}
155
+ * surface so the component can `track` / `screen` / `identify` and drive `notifyOnline` on reconnect.
156
+ */
157
+ declare const useAnalytics: (config: CreateAnalyticsConfig, options?: AnalyticsOptions) => Analytics;
158
+
159
+ export { type Analytics, type AnalyticsOptions, type AnalyticsProps, type CreateAnalyticsConfig, EventQueueOptions, type NavigationRefLike, type NavigationRouteLike, type NavigationStateLike, type ScreenTracker, type ScreenTrackerOptions, createAnalytics, createScreenTracker, getActiveRouteName, screenTrackingHandler, useAnalytics, useScreenTracking };
@@ -64,8 +64,29 @@ var createScreenTracker = (options = {}) => {
64
64
  };
65
65
  var screenTrackingHandler = (tracker) => (state) => tracker.track(getActiveRouteName(state));
66
66
  var useScreenTracking = (navigationRef, options = {}) => {
67
+ const optionsRef = react.useRef(options);
68
+ optionsRef.current = options;
67
69
  const trackerRef = react.useRef(void 0);
68
- if (!trackerRef.current) trackerRef.current = createScreenTracker(options);
70
+ if (!trackerRef.current) {
71
+ const dynamicOptions = {
72
+ get target() {
73
+ return optionsRef.current.target;
74
+ },
75
+ get sessionId() {
76
+ return optionsRef.current.sessionId;
77
+ },
78
+ get deviceKey() {
79
+ return optionsRef.current.deviceKey;
80
+ },
81
+ get onScreen() {
82
+ return optionsRef.current.onScreen;
83
+ },
84
+ get shouldTrack() {
85
+ return optionsRef.current.shouldTrack;
86
+ }
87
+ };
88
+ trackerRef.current = createScreenTracker(dynamicOptions);
89
+ }
69
90
  react.useEffect(() => {
70
91
  const tracker = trackerRef.current;
71
92
  if (!tracker || !(navigationRef == null ? void 0 : navigationRef.addListener)) return;
@@ -338,6 +359,8 @@ var createEventQueue = (options) => {
338
359
  })();
339
360
  const postBatch = async (events) => {
340
361
  if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return false;
362
+ const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
363
+ const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
341
364
  try {
342
365
  const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
343
366
  const headers = { "Content-Type": "application/json" };
@@ -345,11 +368,14 @@ var createEventQueue = (options) => {
345
368
  const res = await fetch(url, {
346
369
  method: "POST",
347
370
  headers,
348
- body: JSON.stringify({ events })
371
+ body: JSON.stringify({ events }),
372
+ signal: controller == null ? void 0 : controller.signal
349
373
  });
350
374
  return !!(res && res.ok);
351
375
  } catch {
352
376
  return false;
377
+ } finally {
378
+ clearTimeout(timer);
353
379
  }
354
380
  };
355
381
  const clearRetry = () => {
@@ -423,8 +449,102 @@ var createEventQueue = (options) => {
423
449
  return { enqueue, flush, notifyOnline, size };
424
450
  };
425
451
 
452
+ // src/identity/userIdentity.ts
453
+ var USER_ID_MAX_LENGTH = 128;
454
+ var sanitizeUserId = (raw) => {
455
+ if (typeof raw !== "string") return void 0;
456
+ const trimmed = raw.trim();
457
+ if (!trimmed) return void 0;
458
+ return trimmed.length > USER_ID_MAX_LENGTH ? trimmed.slice(0, USER_ID_MAX_LENGTH) : trimmed;
459
+ };
460
+
461
+ // src/analytics/analyticsFacade.ts
462
+ var createAnalytics = (config, options = {}) => {
463
+ var _a, _b;
464
+ const sessionId = (_a = config.sessionId) != null ? _a : makeSessionId();
465
+ const envelope = () => buildContextEnvelope({
466
+ sessionId,
467
+ appVersion: config.appVersion,
468
+ appBuild: config.appBuild,
469
+ networkType: config.networkType
470
+ });
471
+ const queue = createEventQueue({
472
+ target: { serverUrl: config.serverUrl, apiKey: config.apiKey },
473
+ storage: config.storage,
474
+ appId: config.appId,
475
+ envelope,
476
+ ...options
477
+ });
478
+ let boundUserId;
479
+ const storageKey = `wireai:analytics:userId:${(_b = config.appId) != null ? _b : "default"}`;
480
+ if (config.storage) {
481
+ void config.storage.getItem(storageKey).then((saved) => {
482
+ if (saved) boundUserId = saved;
483
+ }).catch(() => {
484
+ });
485
+ }
486
+ const track = (event, props) => {
487
+ if (!event) return;
488
+ const clientEvent = {
489
+ event_type: "app_event",
490
+ session_id: sessionId,
491
+ question_key: event
492
+ };
493
+ if (props && Object.keys(props).length > 0) clientEvent.meta = JSON.stringify(props);
494
+ if (boundUserId) clientEvent.user_id = boundUserId;
495
+ queue.enqueue(clientEvent);
496
+ };
497
+ const screen = (name, props) => {
498
+ if (!name) return;
499
+ const meta = { screen: name, ...props != null ? props : {} };
500
+ const clientEvent = {
501
+ event_type: "app_event",
502
+ session_id: sessionId,
503
+ question_key: "screen",
504
+ meta: JSON.stringify(meta)
505
+ };
506
+ if (boundUserId) clientEvent.user_id = boundUserId;
507
+ queue.enqueue(clientEvent);
508
+ };
509
+ const identify = (userId, traits) => {
510
+ const clean = sanitizeUserId(userId);
511
+ if (!clean) return;
512
+ boundUserId = clean;
513
+ if (config.storage) {
514
+ void config.storage.setItem(storageKey, clean).catch(() => {
515
+ });
516
+ }
517
+ const clientEvent = {
518
+ event_type: "identify",
519
+ session_id: sessionId,
520
+ user_id: clean
521
+ };
522
+ if (traits && Object.keys(traits).length > 0) clientEvent.meta = JSON.stringify(traits);
523
+ queue.enqueue(clientEvent);
524
+ };
525
+ return {
526
+ track,
527
+ screen,
528
+ identify,
529
+ flush: queue.flush,
530
+ notifyOnline: queue.notifyOnline,
531
+ size: queue.size
532
+ };
533
+ };
534
+ var useAnalytics = (config, options = {}) => {
535
+ const ref = react.useRef(void 0);
536
+ const prevKeys = react.useRef("");
537
+ const currentKeys = `${config.serverUrl}|${config.apiKey}|${config.appId}`;
538
+ if (!ref.current || prevKeys.current !== currentKeys) {
539
+ prevKeys.current = currentKeys;
540
+ ref.current = createAnalytics(config, options);
541
+ }
542
+ return ref.current;
543
+ };
544
+
426
545
  exports.WIRE_ONBOARDING_EVENTS = WIRE_ONBOARDING_EVENTS;
427
546
  exports.buildContextEnvelope = buildContextEnvelope;
547
+ exports.createAnalytics = createAnalytics;
428
548
  exports.createEventQueue = createEventQueue;
429
549
  exports.createScreenTracker = createScreenTracker;
430
550
  exports.getActiveRouteName = getActiveRouteName;
@@ -434,6 +554,7 @@ exports.reportClientEvent = reportClientEvent;
434
554
  exports.reportClientEvents = reportClientEvents;
435
555
  exports.screenTrackingHandler = screenTrackingHandler;
436
556
  exports.toAnalyticsEvent = toAnalyticsEvent;
557
+ exports.useAnalytics = useAnalytics;
437
558
  exports.useScreenTracking = useScreenTracking;
438
559
  //# sourceMappingURL=index.js.map
439
560
  //# sourceMappingURL=index.js.map