@wireai/activation 0.4.0 → 0.8.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/AGENTS.md +1 -1
- package/CHANGELOG.md +92 -0
- package/dist/analytics/index.d.mts +19 -2
- package/dist/analytics/index.d.ts +19 -2
- package/dist/analytics/index.js +258 -18
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +256 -19
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/{eventQueue-CA1d8Fmn.d.mts → currentSession-d9CrBxwe.d.mts} +152 -16
- package/dist/{eventQueue-CrNB9gzH.d.ts → currentSession-f7LWcdWG.d.ts} +152 -16
- package/dist/index.d.mts +96 -32
- package/dist/index.d.ts +96 -32
- package/dist/index.js +214 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +201 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/WireOnboarding.tsx +4 -3
- package/src/analytics/analyticsFacade.ts +120 -12
- package/src/analytics/contextEnvelope.ts +13 -7
- package/src/analytics/currentSession.ts +35 -0
- package/src/analytics/index.ts +3 -0
- package/src/context/deviceId.ts +43 -0
- package/src/context/userContext.ts +210 -0
- package/src/device/appVersion.ts +103 -0
- package/src/device/deviceContext.ts +31 -6
- package/src/device/deviceModel.ts +93 -0
- package/src/identity/userIdentity.ts +5 -0
- package/src/index.ts +25 -0
- package/src/session-analytics/reportSessionStart.ts +7 -0
- package/src/session-analytics/useLifecycleEvents.ts +4 -2
- package/src/session-analytics/useSessionStart.ts +2 -1
- package/src/types.ts +6 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import React__default, { ReactNode } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export { A as AnalyticsEvent, b as ClientEventType,
|
|
3
|
+
import { o as WireOnboardingProps, p as WireOnboardingConfig, O as OnboardingResult, S as StepValidator, q as OnboardingEvent, u as OnboardingCopy, a as ClientEventTarget, D as DeviceContext, C as ClientEvent, e as EnvelopeSource } from './currentSession-f7LWcdWG.js';
|
|
4
|
+
export { A as AnalyticsEvent, b as ClientEventType, v as DeviceFormFactor, w as EXTRA_KEY_PREFIX, x as OnboardingProgress, R as RESERVED_USER_CONTEXT_KEYS, y as ResolveUserContextOptions, z as ResolvedUserContext, g as WIRE_ONBOARDING_EVENTS, h as WireOnboardingEventName, W as WireUserContext, B as collectDeviceContext, k as getCurrentSessionId, F as hashEmailFnv1a, G as isWireScalar, m as makeSessionId, H as namespaceExtra, r as reportClientEvent, l as reportClientEvents, n as resetCurrentSessionId, I as resolveUserContext, s as setCurrentSessionId, t as toAnalyticsEvent } from './currentSession-f7LWcdWG.js';
|
|
5
5
|
import { O as OnboardingTheme } from './types-BKfpdZzX.js';
|
|
6
6
|
export { a as OnboardingButtonStyle, b as OnboardingColors, c as OnboardingFonts, d as OnboardingRadius, e as OnboardingSpacing } from './types-BKfpdZzX.js';
|
|
7
7
|
export { C as CenteredModal, a as CenteredModalHandle, b as CenteredModalProps } from './CenteredModal-Cdgns6--.js';
|
|
@@ -826,6 +826,73 @@ declare const attributionMetadata: (a: OnboardingAttribution) => {
|
|
|
826
826
|
attribution: Record<string, unknown>;
|
|
827
827
|
};
|
|
828
828
|
|
|
829
|
+
/**
|
|
830
|
+
* appVersion — best-effort, DEPENDENCY-FREE auto-detection of the host app's version string.
|
|
831
|
+
*
|
|
832
|
+
* WHY this exists: analytics segments the funnel `by_app_version`, but that breakdown is only
|
|
833
|
+
* populated when a `device.appVersion` rides the event. `config.appVersion` (see types.ts) has
|
|
834
|
+
* always been the way to supply it — but it is easy for a host to forget, and then the release
|
|
835
|
+
* breakdown is silently empty. This module fills that gap: when the host does NOT pass a version,
|
|
836
|
+
* the kit makes a best-effort read of the app version the host already ships in its Expo config,
|
|
837
|
+
* so the breakdown works out of the box. An explicit `config.appVersion` always WINS over this.
|
|
838
|
+
*
|
|
839
|
+
* WHY it adds NO dependency (the kit's hard rule): `expo-constants` / `expo-application` are read
|
|
840
|
+
* through a GUARDED, VARIABLE-specifier `require`. Passing a variable (not a string literal) keeps
|
|
841
|
+
* Metro/esbuild from statically resolving the module, so a host that does NOT have it installed
|
|
842
|
+
* (e.g. bare React Native) never fails to bundle — the require simply throws at runtime and is
|
|
843
|
+
* swallowed. Nothing is added to `package.json`; nothing is forced on the host.
|
|
844
|
+
*
|
|
845
|
+
* PRIVACY: an app version string is not PII and identifies no user or device, so surfacing it
|
|
846
|
+
* changes no App Privacy / Data Safety declaration (same guarantee as the rest of deviceContext).
|
|
847
|
+
*
|
|
848
|
+
* NEVER THROWS: every read is guarded; a missing/odd value yields `undefined`, never an exception.
|
|
849
|
+
* Analytics must never be able to break onboarding.
|
|
850
|
+
*/
|
|
851
|
+
/** A `require`-like resolver. Injectable in tests; production uses the guarded runtime require. */
|
|
852
|
+
type OptionalRequire$1 = (moduleName: string) => unknown;
|
|
853
|
+
/**
|
|
854
|
+
* Detect the host app version, preferring `expo-constants` (`expoConfig.version`, then
|
|
855
|
+
* `nativeAppVersion`) and finally `expo-application` (`nativeApplicationVersion`). Returns the
|
|
856
|
+
* first real string, or `undefined` when none of those are available. Pure and never throws.
|
|
857
|
+
*
|
|
858
|
+
* `requireModule` is injectable so tests can exercise the "found" path without the native modules;
|
|
859
|
+
* production defaults to the guarded runtime require above.
|
|
860
|
+
*/
|
|
861
|
+
declare const detectAppVersion: (requireModule?: OptionalRequire$1) => string | undefined;
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* deviceModel — best-effort, DEPENDENCY-FREE detection of the device MODEL on iOS.
|
|
865
|
+
*
|
|
866
|
+
* WHY this exists (the asymmetry it closes): `collectDeviceContext` already reports `device.model`
|
|
867
|
+
* on Android straight from `Platform.constants.Model` (e.g. "SM-G991B"), but iOS `Platform.constants`
|
|
868
|
+
* exposes NO model — only `osVersion` / `interfaceIdiom`. So the analytics `by_model` breakdown was
|
|
869
|
+
* Android-only. This module fills the iOS gap with the SAME zero-dependency technique the kit already
|
|
870
|
+
* uses for `appVersion` (see device/appVersion.ts): a GUARDED, VARIABLE-specifier `require` of the
|
|
871
|
+
* common Expo `expo-device` module. If the host has it, iOS gets a model out of the box; if not, the
|
|
872
|
+
* require simply throws and is swallowed and iOS `model` stays omitted — nothing is forced, nothing
|
|
873
|
+
* is added to `package.json`, and a host without `expo-device` never fails to bundle.
|
|
874
|
+
*
|
|
875
|
+
* WHY it is NOT PII: `expo-device`'s `modelName` ("iPhone 14 Pro") and `modelId` ("iPhone15,2") are a
|
|
876
|
+
* device CLASS shared by millions of units — the same privacy category as the Android `Model` the kit
|
|
877
|
+
* already sends. It is NOT a unique device id / IDFA / fingerprint, so surfacing it changes no App
|
|
878
|
+
* Privacy / Data Safety declaration (identical guarantee to the rest of deviceContext). It deliberately
|
|
879
|
+
* does NOT read `expo-device`'s `deviceName` (that is the user-set name, e.g. "Malik's iPhone", and IS
|
|
880
|
+
* personal data).
|
|
881
|
+
*
|
|
882
|
+
* NEVER THROWS: every read is guarded; a missing/odd value yields `undefined`, never an exception.
|
|
883
|
+
*/
|
|
884
|
+
/** A `require`-like resolver. Injectable in tests; production uses the guarded runtime require. */
|
|
885
|
+
type OptionalRequire = (moduleName: string) => unknown;
|
|
886
|
+
/**
|
|
887
|
+
* Detect the device model via `expo-device`, preferring the human-readable `modelName`
|
|
888
|
+
* ("iPhone 14 Pro") and falling back to the identifier `modelId` ("iPhone15,2"). Returns the first
|
|
889
|
+
* real string, or `undefined` when `expo-device` is absent. Pure and never throws.
|
|
890
|
+
*
|
|
891
|
+
* `requireModule` is injectable so tests can exercise the "found" path without the native module;
|
|
892
|
+
* production defaults to the guarded runtime require above.
|
|
893
|
+
*/
|
|
894
|
+
declare const detectNativeModel: (requireModule?: OptionalRequire) => string | undefined;
|
|
895
|
+
|
|
829
896
|
/** Max accepted user-id length. Longer strings are truncated (never rejected). Keep in sync
|
|
830
897
|
* with the server's `USER_ID_MAX_LENGTH` (analytics/events.py). */
|
|
831
898
|
declare const USER_ID_MAX_LENGTH = 128;
|
|
@@ -873,38 +940,35 @@ type IdentifyOnboardingOptions = {
|
|
|
873
940
|
declare const identifyOnboarding: (opts: IdentifyOnboardingOptions) => Promise<boolean>;
|
|
874
941
|
|
|
875
942
|
/**
|
|
876
|
-
*
|
|
877
|
-
*
|
|
878
|
-
*
|
|
879
|
-
*
|
|
880
|
-
*
|
|
881
|
-
* 1. SESSION MAPPING. It carries the host's opaque `userId` + a stable `deviceKey`, so the
|
|
882
|
-
* backend can group a user's (or a pre-auth device's) opens over time — "when did this
|
|
883
|
-
* user last use the app, and how many times". The device_key links the pre-auth opens to
|
|
884
|
-
* the user once `userId` arrives (here or via `identifyOnboarding`).
|
|
885
|
-
* 2. QUESTIONNAIRE / RETENTION FIRING. It flows into the SAME event stream the server's
|
|
886
|
-
* decision engines read, so a questionnaire trigger `{event:"app.session_started", min_count:N}`
|
|
887
|
-
* (matched within a session) or a questionnaire `min_sessions:N` (distinct device sessions)
|
|
888
|
-
* fires on the user's Nth open with no new server endpoint.
|
|
943
|
+
* deviceId — mint a stable, NON-PII, per-install device id the kit owns when the host supplies
|
|
944
|
+
* none. This is the headline of "device fully automatic": the analytics façade auto-mints ONE id,
|
|
945
|
+
* persists it via the host's `storage` abstraction, and reuses it on every subsequent open — so
|
|
946
|
+
* `user_context.device_key` is ALWAYS present and the server's review/questionnaire gating +
|
|
947
|
+
* A/B stickiness (both key on `device_key`) work out of the box, with zero host wiring.
|
|
889
948
|
*
|
|
890
|
-
*
|
|
891
|
-
* The
|
|
892
|
-
*
|
|
893
|
-
*
|
|
894
|
-
*
|
|
895
|
-
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
949
|
+
* WHY it is NOT PII and adds NO dependency (the kit's hard rules):
|
|
950
|
+
* The id is a random token generated from `Date.now()` + `Math.random()` — it carries NO hardware
|
|
951
|
+
* identifier, NO IDFA/GAID, NO fingerprint. It is a first-party per-install correlation key, the
|
|
952
|
+
* same privacy category as a first-party cookie: it groups a single install's sessions and cannot
|
|
953
|
+
* identify a person or be joined across apps. There is NO `uuid` (or any) dependency — a
|
|
954
|
+
* time+random scheme is sufficient because the id is minted ONCE and then persisted, so global
|
|
955
|
+
* uniqueness across the fleet is not required (a per-install collision is astronomically unlikely
|
|
956
|
+
* and inconsequential — worst case two installs share a bucket).
|
|
898
957
|
*
|
|
899
|
-
*
|
|
900
|
-
*
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
958
|
+
* A host that wants its OWN device id still wins: pass `WireUserContext.deviceKey` and the kit uses
|
|
959
|
+
* that verbatim and never mints/persists an auto id.
|
|
960
|
+
*/
|
|
961
|
+
/** Prefix so an auto-minted id is visibly the kit's (distinguishable from a host-supplied `deviceKey`). */
|
|
962
|
+
declare const AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
963
|
+
/** The storage key the façade persists the auto-minted id under (namespaced per `appId`). */
|
|
964
|
+
declare const deviceIdStorageKey: (appId?: string) => string;
|
|
965
|
+
/**
|
|
966
|
+
* Mint a fresh per-install device id. Dependency-free (`Date.now()` + `Math.random()`), never
|
|
967
|
+
* throws, and returns a NEW value on every call — the façade mints ONCE and persists, so this is
|
|
968
|
+
* called at most once per install (then the persisted value is reused). Two random chunks plus the
|
|
969
|
+
* timestamp keep the token wide enough that a per-install collision is not a practical concern.
|
|
907
970
|
*/
|
|
971
|
+
declare const mintDeviceId: () => string;
|
|
908
972
|
|
|
909
973
|
/** The canonical event name for an app-open. A trigger keys off this exact string. */
|
|
910
974
|
declare const SESSION_STARTED_EVENT: "app.session_started";
|
|
@@ -1132,4 +1196,4 @@ interface UseLifecycleEventsOptions {
|
|
|
1132
1196
|
*/
|
|
1133
1197
|
declare const useLifecycleEvents: (config: LifecycleConfig | undefined, options?: UseLifecycleEventsOptions) => void;
|
|
1134
1198
|
|
|
1135
|
-
export { AnimatedSparkle, BACKGROUND_SESSION_MS, type CachedFeatures, CardHandoff, type CardHandoffProps, type CardHandoffVariant, ChipSelectCard, ClientEvent, ClientEventTarget, CompletionView, DEFAULT_FEATURES_TTL_MS, DemoOnboarding, type DemoOnboardingProps, DeviceContext, DoneBlock, ErrorBlock, FIRST_OPEN_EVENT, type IdentifyOnboardingOptions, IllustrationProvider, type IllustrationRegistry, InterstitialCard, type LifecycleConfig, type LifecycleEventInput, LoadingBlock, LoadingScreen, NumberStepperCard, type OnboardingAttribution, Button as OnboardingButton, OnboardingCopy, OnboardingEvent, type OnboardingFlagOptions, OnboardingFlow, OnboardingResult, OnboardingScaffold, OnboardingTheme, OnboardingThemeProvider, type ReportFirstOpenOptions, type ReportSessionStartOptions, type ResolveFeaturesOptions, SESSION_STARTED_EVENT, SelectionCard, type SessionStartConfig, StatusCard, StepProgress, StepValidator, TextInputCard, type ThemeFromBrandInput, USER_ID_MAX_LENGTH, type UseLifecycleEventsOptions, type UseSessionStartOptions, type WireConfigOverrides, WireFeatures, WireFeaturesConfig, WireFeaturesProvider, type WireFeaturesProviderProps, type WireLifecycleOptions, WireOnboarding, WireOnboardingConfig, WireOnboardingProps, WireOnboardingStorage, attributionMetadata, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, identifyOnboarding, isFeaturesFresh, isOnboardingEnabled, mergeTheme, motionSpec, onboardingComponents, parseWireFeatures, readCachedFeatures, readProgress, reportFirstOpen, reportSessionStart, resetFirstOpenLatch, resetSessionStartGuard, sanitizeUserId, themeFromBrand, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
|
|
1199
|
+
export { AUTO_DEVICE_ID_PREFIX, AnimatedSparkle, BACKGROUND_SESSION_MS, type CachedFeatures, CardHandoff, type CardHandoffProps, type CardHandoffVariant, ChipSelectCard, ClientEvent, ClientEventTarget, CompletionView, DEFAULT_FEATURES_TTL_MS, DemoOnboarding, type DemoOnboardingProps, DeviceContext, DoneBlock, ErrorBlock, FIRST_OPEN_EVENT, type IdentifyOnboardingOptions, IllustrationProvider, type IllustrationRegistry, InterstitialCard, type LifecycleConfig, type LifecycleEventInput, LoadingBlock, LoadingScreen, NumberStepperCard, type OnboardingAttribution, Button as OnboardingButton, OnboardingCopy, OnboardingEvent, type OnboardingFlagOptions, OnboardingFlow, OnboardingResult, OnboardingScaffold, OnboardingTheme, OnboardingThemeProvider, type ReportFirstOpenOptions, type ReportSessionStartOptions, type ResolveFeaturesOptions, SESSION_STARTED_EVENT, SelectionCard, type SessionStartConfig, StatusCard, StepProgress, StepValidator, TextInputCard, type ThemeFromBrandInput, USER_ID_MAX_LENGTH, type UseLifecycleEventsOptions, type UseSessionStartOptions, type WireConfigOverrides, WireFeatures, WireFeaturesConfig, WireFeaturesProvider, type WireFeaturesProviderProps, type WireLifecycleOptions, WireOnboarding, WireOnboardingConfig, WireOnboardingProps, WireOnboardingStorage, attributionMetadata, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, detectAppVersion, detectNativeModel, deviceIdStorageKey, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, identifyOnboarding, isFeaturesFresh, isOnboardingEnabled, mergeTheme, mintDeviceId, motionSpec, onboardingComponents, parseWireFeatures, readCachedFeatures, readProgress, reportFirstOpen, reportSessionStart, resetFirstOpenLatch, resetSessionStartGuard, sanitizeUserId, themeFromBrand, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,12 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
12
12
|
var React14__default = /*#__PURE__*/_interopDefault(React14);
|
|
13
13
|
|
|
14
14
|
var __defProp = Object.defineProperty;
|
|
15
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
16
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
17
|
+
}) : x)(function(x) {
|
|
18
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
19
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
20
|
+
});
|
|
15
21
|
var __export = (target, all) => {
|
|
16
22
|
for (var name in all)
|
|
17
23
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -2349,6 +2355,98 @@ var onboardingComponents = [
|
|
|
2349
2355
|
NumberStepperCard,
|
|
2350
2356
|
InterstitialCard
|
|
2351
2357
|
];
|
|
2358
|
+
|
|
2359
|
+
// src/device/appVersion.ts
|
|
2360
|
+
var coerceVersion = (value) => {
|
|
2361
|
+
if (typeof value !== "string") return void 0;
|
|
2362
|
+
const trimmed = value.trim();
|
|
2363
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
2364
|
+
};
|
|
2365
|
+
var runtimeRequire = (moduleName) => {
|
|
2366
|
+
try {
|
|
2367
|
+
if (typeof __require !== "function") return void 0;
|
|
2368
|
+
return __require(moduleName);
|
|
2369
|
+
} catch {
|
|
2370
|
+
return void 0;
|
|
2371
|
+
}
|
|
2372
|
+
};
|
|
2373
|
+
var interop = (mod) => {
|
|
2374
|
+
if (!mod || typeof mod !== "object") return void 0;
|
|
2375
|
+
const def = mod.default;
|
|
2376
|
+
if (def && typeof def === "object") return def;
|
|
2377
|
+
return mod;
|
|
2378
|
+
};
|
|
2379
|
+
var safeInterop = (requireModule, moduleName) => {
|
|
2380
|
+
try {
|
|
2381
|
+
return interop(requireModule(moduleName));
|
|
2382
|
+
} catch {
|
|
2383
|
+
return void 0;
|
|
2384
|
+
}
|
|
2385
|
+
};
|
|
2386
|
+
var detectAppVersion = (requireModule = runtimeRequire) => {
|
|
2387
|
+
try {
|
|
2388
|
+
const constants = safeInterop(requireModule, "expo-constants");
|
|
2389
|
+
if (constants) {
|
|
2390
|
+
const expoConfig = constants.expoConfig;
|
|
2391
|
+
if (expoConfig && typeof expoConfig === "object") {
|
|
2392
|
+
const fromExpoConfig = coerceVersion(expoConfig.version);
|
|
2393
|
+
if (fromExpoConfig) return fromExpoConfig;
|
|
2394
|
+
}
|
|
2395
|
+
const fromNative = coerceVersion(constants.nativeAppVersion);
|
|
2396
|
+
if (fromNative) return fromNative;
|
|
2397
|
+
}
|
|
2398
|
+
const application = safeInterop(requireModule, "expo-application");
|
|
2399
|
+
if (application) {
|
|
2400
|
+
const fromApplication = coerceVersion(application.nativeApplicationVersion);
|
|
2401
|
+
if (fromApplication) return fromApplication;
|
|
2402
|
+
}
|
|
2403
|
+
} catch {
|
|
2404
|
+
}
|
|
2405
|
+
return void 0;
|
|
2406
|
+
};
|
|
2407
|
+
|
|
2408
|
+
// src/device/deviceModel.ts
|
|
2409
|
+
var coerceModel = (value) => {
|
|
2410
|
+
if (typeof value !== "string") return void 0;
|
|
2411
|
+
const trimmed = value.trim();
|
|
2412
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
2413
|
+
};
|
|
2414
|
+
var runtimeRequire2 = (moduleName) => {
|
|
2415
|
+
try {
|
|
2416
|
+
if (typeof __require !== "function") return void 0;
|
|
2417
|
+
return __require(moduleName);
|
|
2418
|
+
} catch {
|
|
2419
|
+
return void 0;
|
|
2420
|
+
}
|
|
2421
|
+
};
|
|
2422
|
+
var interop2 = (mod) => {
|
|
2423
|
+
if (!mod || typeof mod !== "object") return void 0;
|
|
2424
|
+
const def = mod.default;
|
|
2425
|
+
if (def && typeof def === "object") return def;
|
|
2426
|
+
return mod;
|
|
2427
|
+
};
|
|
2428
|
+
var safeInterop2 = (requireModule, moduleName) => {
|
|
2429
|
+
try {
|
|
2430
|
+
return interop2(requireModule(moduleName));
|
|
2431
|
+
} catch {
|
|
2432
|
+
return void 0;
|
|
2433
|
+
}
|
|
2434
|
+
};
|
|
2435
|
+
var detectNativeModel = (requireModule = runtimeRequire2) => {
|
|
2436
|
+
try {
|
|
2437
|
+
const device = safeInterop2(requireModule, "expo-device");
|
|
2438
|
+
if (device) {
|
|
2439
|
+
const modelName = coerceModel(device.modelName);
|
|
2440
|
+
if (modelName) return modelName;
|
|
2441
|
+
const modelId = coerceModel(device.modelId);
|
|
2442
|
+
if (modelId) return modelId;
|
|
2443
|
+
}
|
|
2444
|
+
} catch {
|
|
2445
|
+
}
|
|
2446
|
+
return void 0;
|
|
2447
|
+
};
|
|
2448
|
+
|
|
2449
|
+
// src/device/deviceContext.ts
|
|
2352
2450
|
var deriveFormFactor = (iosIdiom, width, height) => {
|
|
2353
2451
|
if (iosIdiom === "pad") return "tablet";
|
|
2354
2452
|
if (iosIdiom === "phone") return "phone";
|
|
@@ -2394,6 +2492,8 @@ var collectDeviceContext = () => {
|
|
|
2394
2492
|
ctx.interfaceIdiom = idiom;
|
|
2395
2493
|
iosIdiom = idiom;
|
|
2396
2494
|
}
|
|
2495
|
+
const iosModel = detectNativeModel();
|
|
2496
|
+
if (iosModel) ctx.model = iosModel;
|
|
2397
2497
|
}
|
|
2398
2498
|
} catch {
|
|
2399
2499
|
}
|
|
@@ -2418,9 +2518,21 @@ var collectDeviceContext = () => {
|
|
|
2418
2518
|
if (resolved.timeZone) ctx.timeZone = resolved.timeZone;
|
|
2419
2519
|
} catch {
|
|
2420
2520
|
}
|
|
2521
|
+
const appVersion = detectAppVersion();
|
|
2522
|
+
if (appVersion) ctx.appVersion = appVersion;
|
|
2421
2523
|
return ctx;
|
|
2422
2524
|
};
|
|
2423
2525
|
|
|
2526
|
+
// src/analytics/currentSession.ts
|
|
2527
|
+
var _currentSessionId;
|
|
2528
|
+
var setCurrentSessionId = (id) => {
|
|
2529
|
+
if (typeof id === "string" && id.length > 0) _currentSessionId = id;
|
|
2530
|
+
};
|
|
2531
|
+
var getCurrentSessionId = () => _currentSessionId;
|
|
2532
|
+
var resetCurrentSessionId = () => {
|
|
2533
|
+
_currentSessionId = void 0;
|
|
2534
|
+
};
|
|
2535
|
+
|
|
2424
2536
|
// src/session/persistedSession.ts
|
|
2425
2537
|
var DEFAULT_SESSION_TTL_MS = 36e5;
|
|
2426
2538
|
var READ_TIMEOUT_MS = 1500;
|
|
@@ -2499,6 +2611,7 @@ var identifyOnboarding = async (opts) => {
|
|
|
2499
2611
|
contextId = stored == null ? void 0 : stored.id;
|
|
2500
2612
|
}
|
|
2501
2613
|
}
|
|
2614
|
+
if (!contextId) contextId = getCurrentSessionId();
|
|
2502
2615
|
if (!contextId) return false;
|
|
2503
2616
|
reportClientEvent(
|
|
2504
2617
|
{ serverUrl: opts.config.serverUrl, apiKey: opts.config.apiKey },
|
|
@@ -3155,6 +3268,89 @@ var attributionMetadata = (a) => {
|
|
|
3155
3268
|
return { attribution };
|
|
3156
3269
|
};
|
|
3157
3270
|
|
|
3271
|
+
// src/context/userContext.ts
|
|
3272
|
+
var RESERVED_USER_CONTEXT_KEYS = [
|
|
3273
|
+
"device_key",
|
|
3274
|
+
"app_version",
|
|
3275
|
+
"app_build",
|
|
3276
|
+
"network_type",
|
|
3277
|
+
"session_count",
|
|
3278
|
+
"returning",
|
|
3279
|
+
"platform",
|
|
3280
|
+
"user_email",
|
|
3281
|
+
"user_email_hashed"
|
|
3282
|
+
];
|
|
3283
|
+
var EXTRA_KEY_PREFIX = "custom.";
|
|
3284
|
+
var isWireScalar = (value) => {
|
|
3285
|
+
const t = typeof value;
|
|
3286
|
+
if (t === "string" || t === "boolean") return true;
|
|
3287
|
+
if (t === "number") return Number.isFinite(value);
|
|
3288
|
+
return false;
|
|
3289
|
+
};
|
|
3290
|
+
var hashEmailFnv1a = (email) => {
|
|
3291
|
+
const normalized = email.trim().toLowerCase();
|
|
3292
|
+
let hash = 2166136261;
|
|
3293
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
3294
|
+
hash ^= normalized.charCodeAt(i);
|
|
3295
|
+
hash = Math.imul(hash, 16777619);
|
|
3296
|
+
}
|
|
3297
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
3298
|
+
};
|
|
3299
|
+
var cleanString = (value) => {
|
|
3300
|
+
if (typeof value !== "string") return void 0;
|
|
3301
|
+
const trimmed = value.trim();
|
|
3302
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
3303
|
+
};
|
|
3304
|
+
var namespaceExtra = (extra) => {
|
|
3305
|
+
const out = {};
|
|
3306
|
+
if (!extra || typeof extra !== "object") return out;
|
|
3307
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
3308
|
+
const cleanKey = cleanString(key);
|
|
3309
|
+
if (!cleanKey) continue;
|
|
3310
|
+
if (!isWireScalar(value)) continue;
|
|
3311
|
+
out[`${EXTRA_KEY_PREFIX}${cleanKey}`] = value;
|
|
3312
|
+
}
|
|
3313
|
+
return out;
|
|
3314
|
+
};
|
|
3315
|
+
var resolveUserContext = (ctx = {}, opts = {}) => {
|
|
3316
|
+
var _a;
|
|
3317
|
+
const result = {};
|
|
3318
|
+
const bucket = {};
|
|
3319
|
+
const userId = sanitizeUserId(ctx.userId);
|
|
3320
|
+
if (userId) result.userId = userId;
|
|
3321
|
+
const deviceKey = cleanString(ctx.deviceKey);
|
|
3322
|
+
if (deviceKey) {
|
|
3323
|
+
result.deviceKey = deviceKey;
|
|
3324
|
+
bucket.device_key = deviceKey;
|
|
3325
|
+
}
|
|
3326
|
+
const appVersion = (_a = cleanString(ctx.appVersion)) != null ? _a : cleanString(opts.autoAppVersion);
|
|
3327
|
+
if (appVersion) {
|
|
3328
|
+
result.appVersion = appVersion;
|
|
3329
|
+
bucket.app_version = appVersion;
|
|
3330
|
+
}
|
|
3331
|
+
const email = cleanString(ctx.userEmail);
|
|
3332
|
+
if (email) {
|
|
3333
|
+
if (ctx.hashEmail) {
|
|
3334
|
+
bucket.user_email = hashEmailFnv1a(email);
|
|
3335
|
+
bucket.user_email_hashed = true;
|
|
3336
|
+
} else {
|
|
3337
|
+
bucket.user_email = email;
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
Object.assign(bucket, namespaceExtra(ctx.extra));
|
|
3341
|
+
if (Object.keys(bucket).length > 0) result.userContext = bucket;
|
|
3342
|
+
return result;
|
|
3343
|
+
};
|
|
3344
|
+
|
|
3345
|
+
// src/context/deviceId.ts
|
|
3346
|
+
var AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
3347
|
+
var deviceIdStorageKey = (appId) => `wireai:analytics:deviceKey:${appId != null ? appId : "default"}`;
|
|
3348
|
+
var randomChunk = () => Math.floor(Math.random() * 4294967296).toString(36).padStart(6, "0");
|
|
3349
|
+
var mintDeviceId = () => {
|
|
3350
|
+
const time = Date.now().toString(36);
|
|
3351
|
+
return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
|
|
3352
|
+
};
|
|
3353
|
+
|
|
3158
3354
|
// src/session-analytics/reportSessionStart.ts
|
|
3159
3355
|
var SESSION_STARTED_EVENT = "app.session_started";
|
|
3160
3356
|
var _emitted = /* @__PURE__ */ new Set();
|
|
@@ -3167,6 +3363,7 @@ var reportSessionStart = (opts) => {
|
|
|
3167
3363
|
const target = opts.target;
|
|
3168
3364
|
if (!opts.sink && !(target == null ? void 0 : target.serverUrl)) return;
|
|
3169
3365
|
const sessionId = (_a = opts.sessionId) != null ? _a : makeSessionId();
|
|
3366
|
+
setCurrentSessionId(sessionId);
|
|
3170
3367
|
if (opts.once !== false) {
|
|
3171
3368
|
if (_emitted.has(sessionId)) return;
|
|
3172
3369
|
if (_emitted.size >= _GUARD_MAX) {
|
|
@@ -3223,7 +3420,7 @@ var useSessionStart = (config, options = {}) => {
|
|
|
3223
3420
|
if (opts.enabled === false) return;
|
|
3224
3421
|
const target = { serverUrl: cfg.serverUrl, apiKey: (_a = cfg.apiKey) != null ? _a : "" };
|
|
3225
3422
|
const device = collectDeviceContext();
|
|
3226
|
-
if (cfg.appVersion
|
|
3423
|
+
if (cfg.appVersion) device.appVersion = cfg.appVersion;
|
|
3227
3424
|
reportSessionStart({
|
|
3228
3425
|
target,
|
|
3229
3426
|
// A fresh per-open id each fire; the emitter's once-guard dedupes within the open.
|
|
@@ -3597,7 +3794,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
3597
3794
|
const { config: cfg, options: opts } = latest.current;
|
|
3598
3795
|
if (opts.enabled !== false) {
|
|
3599
3796
|
const device = collectDeviceContext();
|
|
3600
|
-
if (
|
|
3797
|
+
if (cfg == null ? void 0 : cfg.appVersion) device.appVersion = cfg.appVersion;
|
|
3601
3798
|
reportFirstOpen({
|
|
3602
3799
|
target: targetOf(cfg),
|
|
3603
3800
|
sink: resolveSink(),
|
|
@@ -3619,7 +3816,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
3619
3816
|
if (opts.enabled === false) return;
|
|
3620
3817
|
if (!(cfg == null ? void 0 : cfg.serverUrl) && !opts.sink) return;
|
|
3621
3818
|
const device = collectDeviceContext();
|
|
3622
|
-
if (
|
|
3819
|
+
if (cfg == null ? void 0 : cfg.appVersion) device.appVersion = cfg.appVersion;
|
|
3623
3820
|
reportSessionStart({
|
|
3624
3821
|
target: targetOf(cfg),
|
|
3625
3822
|
sink: resolveSink(),
|
|
@@ -3653,6 +3850,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
3653
3850
|
}, []);
|
|
3654
3851
|
};
|
|
3655
3852
|
|
|
3853
|
+
exports.AUTO_DEVICE_ID_PREFIX = AUTO_DEVICE_ID_PREFIX;
|
|
3656
3854
|
exports.AnimatedSparkle = AnimatedSparkle;
|
|
3657
3855
|
exports.BACKGROUND_SESSION_MS = BACKGROUND_SESSION_MS;
|
|
3658
3856
|
exports.CardHandoff = CardHandoff;
|
|
@@ -3663,6 +3861,7 @@ exports.DEFAULT_FEATURES_TTL_MS = DEFAULT_FEATURES_TTL_MS;
|
|
|
3663
3861
|
exports.DEFAULT_SESSION_TTL_MS = DEFAULT_SESSION_TTL_MS;
|
|
3664
3862
|
exports.DemoOnboarding = DemoOnboarding;
|
|
3665
3863
|
exports.DoneBlock = DoneBlock;
|
|
3864
|
+
exports.EXTRA_KEY_PREFIX = EXTRA_KEY_PREFIX;
|
|
3666
3865
|
exports.ErrorBlock = ErrorBlock;
|
|
3667
3866
|
exports.FIRST_OPEN_EVENT = FIRST_OPEN_EVENT;
|
|
3668
3867
|
exports.IllustrationProvider = IllustrationProvider;
|
|
@@ -3674,6 +3873,7 @@ exports.OnboardingButton = Button;
|
|
|
3674
3873
|
exports.OnboardingFlow = OnboardingFlow;
|
|
3675
3874
|
exports.OnboardingScaffold = OnboardingScaffold;
|
|
3676
3875
|
exports.OnboardingThemeProvider = OnboardingThemeProvider;
|
|
3876
|
+
exports.RESERVED_USER_CONTEXT_KEYS = RESERVED_USER_CONTEXT_KEYS;
|
|
3677
3877
|
exports.SESSION_STARTED_EVENT = SESSION_STARTED_EVENT;
|
|
3678
3878
|
exports.SelectionCard = SelectionCard;
|
|
3679
3879
|
exports.StatusCard = StatusCard;
|
|
@@ -3690,17 +3890,25 @@ exports.defaultIllustrations = defaultIllustrations;
|
|
|
3690
3890
|
exports.defaultOnboardingTheme = defaultOnboardingTheme;
|
|
3691
3891
|
exports.defaultWireFeatures = defaultWireFeatures;
|
|
3692
3892
|
exports.deriveAnswers = deriveAnswers;
|
|
3893
|
+
exports.detectAppVersion = detectAppVersion;
|
|
3894
|
+
exports.detectNativeModel = detectNativeModel;
|
|
3895
|
+
exports.deviceIdStorageKey = deviceIdStorageKey;
|
|
3693
3896
|
exports.featuresCacheKey = featuresCacheKey;
|
|
3694
3897
|
exports.featuresEqual = featuresEqual;
|
|
3695
3898
|
exports.fetchWireFeatures = fetchWireFeatures;
|
|
3696
3899
|
exports.firstOpenStorageKey = firstOpenStorageKey;
|
|
3900
|
+
exports.getCurrentSessionId = getCurrentSessionId;
|
|
3901
|
+
exports.hashEmailFnv1a = hashEmailFnv1a;
|
|
3697
3902
|
exports.identifyOnboarding = identifyOnboarding;
|
|
3698
3903
|
exports.isFeaturesFresh = isFeaturesFresh;
|
|
3699
3904
|
exports.isOnboardingEnabled = isOnboardingEnabled;
|
|
3905
|
+
exports.isWireScalar = isWireScalar;
|
|
3700
3906
|
exports.loadPersistedSession = loadPersistedSession;
|
|
3701
3907
|
exports.makeSessionId = makeSessionId;
|
|
3702
3908
|
exports.mergeTheme = mergeTheme;
|
|
3909
|
+
exports.mintDeviceId = mintDeviceId;
|
|
3703
3910
|
exports.motionSpec = motionSpec_exports;
|
|
3911
|
+
exports.namespaceExtra = namespaceExtra;
|
|
3704
3912
|
exports.onboardingComponents = onboardingComponents;
|
|
3705
3913
|
exports.parseWireFeatures = parseWireFeatures;
|
|
3706
3914
|
exports.peekPersistedSession = peekPersistedSession;
|
|
@@ -3710,11 +3918,14 @@ exports.reportClientEvent = reportClientEvent;
|
|
|
3710
3918
|
exports.reportClientEvents = reportClientEvents;
|
|
3711
3919
|
exports.reportFirstOpen = reportFirstOpen;
|
|
3712
3920
|
exports.reportSessionStart = reportSessionStart;
|
|
3921
|
+
exports.resetCurrentSessionId = resetCurrentSessionId;
|
|
3713
3922
|
exports.resetFirstOpenLatch = resetFirstOpenLatch;
|
|
3714
3923
|
exports.resetSessionStartGuard = resetSessionStartGuard;
|
|
3924
|
+
exports.resolveUserContext = resolveUserContext;
|
|
3715
3925
|
exports.sanitizeUserId = sanitizeUserId;
|
|
3716
3926
|
exports.savePersistedSession = savePersistedSession;
|
|
3717
3927
|
exports.sessionStorageKey = sessionStorageKey;
|
|
3928
|
+
exports.setCurrentSessionId = setCurrentSessionId;
|
|
3718
3929
|
exports.themeFromBrand = themeFromBrand;
|
|
3719
3930
|
exports.toAnalyticsEvent = toAnalyticsEvent;
|
|
3720
3931
|
exports.useIllustration = useIllustration;
|