@wireai/activation 0.4.0 → 0.7.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/src/index.ts CHANGED
@@ -117,11 +117,34 @@ export type { OnboardingAttribution } from "./attribution/attribution";
117
117
  // ─── Device context (privacy-label-neutral, dependency-free) ──────────────────
118
118
  export { collectDeviceContext } from "./device/deviceContext";
119
119
  export type { DeviceContext, DeviceFormFactor } from "./device/deviceContext";
120
+ export { detectAppVersion } from "./device/appVersion";
120
121
 
121
122
  // ─── User identity (opaque pseudonymous id; late binding, dependency-free) ────
122
123
  export { identifyOnboarding, sanitizeUserId, USER_ID_MAX_LENGTH } from "./identity/userIdentity";
123
124
  export type { IdentifyOnboardingOptions } from "./identity/userIdentity";
124
125
 
126
+ // ─── Rich user context (one object → every event's user_context; opt-in email PII) ────
127
+ export {
128
+ resolveUserContext,
129
+ namespaceExtra,
130
+ hashEmailFnv1a,
131
+ isWireScalar,
132
+ RESERVED_USER_CONTEXT_KEYS,
133
+ EXTRA_KEY_PREFIX,
134
+ } from "./context/userContext";
135
+ export type {
136
+ WireUserContext,
137
+ ResolvedUserContext,
138
+ ResolveUserContextOptions,
139
+ } from "./context/userContext";
140
+
141
+ // ─── Current per-open session registry (identify/app-events reuse the live session) ───
142
+ export {
143
+ getCurrentSessionId,
144
+ setCurrentSessionId,
145
+ resetCurrentSessionId,
146
+ } from "./analytics/currentSession";
147
+
125
148
  // ─── Session mapping (one `app.session_started` per app-open → /v1/events) ─────
126
149
  export {
127
150
  reportSessionStart,
@@ -31,6 +31,7 @@
31
31
  * awaits, and swallows a missing target / bad URL / missing fetch / network error. Analytics must
32
32
  * never be able to break the app.
33
33
  */
34
+ import { setCurrentSessionId } from "../analytics/currentSession";
34
35
  import { makeSessionId, type ClientEvent, type ClientEventTarget } from "../analytics/reportClientEvent";
35
36
  import type { DeviceContext } from "../device/deviceContext";
36
37
  import { sanitizeUserId } from "../identity/userIdentity";
@@ -104,6 +105,12 @@ export const reportSessionStart = (opts: ReportSessionStartOptions): void => {
104
105
 
105
106
  const sessionId = opts.sessionId ?? makeSessionId();
106
107
 
108
+ // Register this open as the CURRENT per-open session so the analytics façade's `identify` /
109
+ // app-events reuse the id the server sees here (via `app.session_started`) instead of minting a
110
+ // fresh one it would back-fill into a phantom session. Idempotent; set before the once-guard so
111
+ // even a guard-deduped repeat keeps the current id pointing at this open.
112
+ setCurrentSessionId(sessionId);
113
+
107
114
  // Once-per-open guard: skip a repeat emit for a session id we already sent.
108
115
  if (opts.once !== false) {
109
116
  if (_emitted.has(sessionId)) return;
@@ -119,7 +119,8 @@ export const useLifecycleEvents = (
119
119
  const { config: cfg, options: opts } = latest.current;
120
120
  if (opts.enabled !== false) {
121
121
  const device = collectDeviceContext();
122
- if (cfg?.appVersion && !device.appVersion) device.appVersion = cfg.appVersion;
122
+ // `device.appVersion` is auto-detected best-effort; an explicit host version always wins.
123
+ if (cfg?.appVersion) device.appVersion = cfg.appVersion;
123
124
  reportFirstOpen({
124
125
  target: targetOf(cfg),
125
126
  sink: resolveSink(),
@@ -142,7 +143,8 @@ export const useLifecycleEvents = (
142
143
  if (opts.enabled === false) return;
143
144
  if (!cfg?.serverUrl && !opts.sink) return;
144
145
  const device = collectDeviceContext();
145
- if (cfg?.appVersion && !device.appVersion) device.appVersion = cfg.appVersion;
146
+ // `device.appVersion` is auto-detected best-effort; an explicit host version always wins.
147
+ if (cfg?.appVersion) device.appVersion = cfg.appVersion;
146
148
  reportSessionStart({
147
149
  target: targetOf(cfg),
148
150
  sink: resolveSink(),
@@ -72,7 +72,8 @@ export const useSessionStart = (
72
72
  if (opts.enabled === false) return;
73
73
  const target: ClientEventTarget = { serverUrl: cfg.serverUrl, apiKey: cfg.apiKey ?? "" };
74
74
  const device = collectDeviceContext();
75
- if (cfg.appVersion && !device.appVersion) device.appVersion = cfg.appVersion;
75
+ // `device.appVersion` is auto-detected best-effort; an explicit host version always wins.
76
+ if (cfg.appVersion) device.appVersion = cfg.appVersion;
76
77
  reportSessionStart({
77
78
  target,
78
79
  // A fresh per-open id each fire; the emitter's once-guard dedupes within the open.
package/src/types.ts CHANGED
@@ -24,11 +24,12 @@ export type WireOnboardingConfig = {
24
24
  */
25
25
  metadata?: Record<string, unknown>;
26
26
  /**
27
- * Host app version string (e.g. "1.4.2"). HOST-INJECTED — the kit adds no dependency to
28
- * read it; hosts typically pass it from `expo-constants`
29
- * (`Constants.expoConfig?.version`). Forwarded to the backend on the session metadata and
30
- * on client events (merged into the `device` snapshot as `device.appVersion`) so analytics
31
- * can segment the funnel by app version. Optional; omit if unknown.
27
+ * Host app version string (e.g. "1.4.2"). OPTIONALwhen omitted, the kit makes a best-effort
28
+ * auto-detection from `expo-constants` (`Constants.expoConfig?.version` / `nativeAppVersion`) or
29
+ * `expo-application` (`nativeApplicationVersion`) WITHOUT adding a dependency (a host that lacks
30
+ * those modules just gets no version see device/appVersion.ts). Pass this to override the
31
+ * auto-detected value (it always wins). Forwarded to the backend on the session metadata and on
32
+ * client events (as `device.appVersion`) so analytics can segment the funnel by app version.
32
33
  */
33
34
  appVersion?: string;
34
35
  };