@wireai/activation 0.15.0 → 0.16.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 +95 -20
- package/CHANGELOG.md +707 -0
- package/INTEGRATION_PROMPT.md +61 -23
- package/README.md +100 -25
- package/dist/analytics/index.d.mts +32 -10
- package/dist/analytics/index.d.ts +32 -10
- package/dist/analytics/index.js +288 -127
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +288 -127
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/coachmarks/index.d.mts +14 -0
- package/dist/coachmarks/index.d.ts +14 -0
- package/dist/coachmarks/index.js +73 -20
- package/dist/coachmarks/index.js.map +1 -1
- package/dist/coachmarks/index.mjs +73 -20
- package/dist/coachmarks/index.mjs.map +1 -1
- package/dist/{currentSession-orZy5p1e.d.mts → currentSession-Bz7G6lno.d.mts} +25 -35
- package/dist/{currentSession-CFSRZ2wg.d.ts → currentSession-z-CZ55ad.d.ts} +25 -35
- package/dist/index.d.mts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +125 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +125 -36
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.d.mts +0 -13
- package/dist/questionnaire/index.d.ts +0 -13
- package/dist/questionnaire/index.js +154 -43
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +155 -44
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.js +159 -91
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +160 -92
- package/dist/reviews/index.mjs.map +1 -1
- package/dist/showcase/index.js +59 -18
- package/dist/showcase/index.js.map +1 -1
- package/dist/showcase/index.mjs +60 -19
- package/dist/showcase/index.mjs.map +1 -1
- package/llms.txt +9 -9
- package/package.json +6 -9
- package/src/analytics/currentSession.ts +141 -4
- package/src/analytics/index.ts +6 -1
- package/src/analytics/reportClientEvent.ts +19 -10
- package/src/analytics/useAnalytics.ts +74 -15
- package/src/analytics/wireDoctor.ts +152 -7
- package/src/coachmarks/CoachmarkProvider.tsx +26 -5
- package/src/coachmarks/runtime.ts +53 -0
- package/src/coachmarks/useCoachmarkTour.ts +51 -1
- package/src/context/deviceId.ts +49 -15
- package/src/features/WireFeaturesProvider.tsx +72 -12
- package/src/features/fetchWireFeatures.ts +49 -11
- package/src/features/useWireFeatures.ts +39 -3
- package/src/identity/identityRecord.ts +15 -2
- package/src/questionnaire/QuestionnaireGate.tsx +40 -1
- package/src/questionnaire/transport.ts +22 -8
- package/src/questionnaire/useQuestionnaireGate.ts +58 -7
- package/src/reviews/ReviewGate.tsx +39 -0
- package/src/reviews/idempotency.ts +38 -0
- package/src/reviews/runtime.ts +39 -10
- package/src/reviews/transport.ts +22 -8
- package/src/reviews/useReviewGate.ts +57 -7
- package/src/session-analytics/lifecycle.ts +16 -0
- package/src/session-analytics/useLifecycleEvents.ts +30 -2
- package/src/session-analytics/useSessionStart.ts +22 -2
- package/src/showcase/FeatureShowcase.tsx +50 -3
- package/src/types.ts +5 -4
- package/src/utils/withDeadline.ts +70 -0
|
@@ -81,33 +81,6 @@ type DeviceContext = {
|
|
|
81
81
|
*/
|
|
82
82
|
declare const collectDeviceContext: () => DeviceContext;
|
|
83
83
|
|
|
84
|
-
/**
|
|
85
|
-
* reportClientEvent — forward DEVICE-ONLY onboarding events to the Wire AI analytics
|
|
86
|
-
* backend (`POST {serverUrl}/v1/events`), completing the funnel for events the server
|
|
87
|
-
* can't observe on its own.
|
|
88
|
-
*
|
|
89
|
-
* The backend already records the server-observable funnel during the A2A flow
|
|
90
|
-
* (`session_started`, `screen_shown`, `answer_submitted`, `completed`, `llm_fallback`,
|
|
91
|
-
* and even `screen_skipped` — it derives that from the kit's skip sentinel). The one
|
|
92
|
-
* event no server request can capture is `dropped`: the user closing the app / unmounting
|
|
93
|
-
* the flow without finishing. That's what this reporter is for.
|
|
94
|
-
*
|
|
95
|
-
* Contract (server: routers/onboarding.py → analytics/events.py):
|
|
96
|
-
* POST {serverUrl}/v1/events
|
|
97
|
-
* Authorization: Bearer {apiKey}
|
|
98
|
-
* { "events": [ { event_type, session_id, screen_index?, component?, question_key?,
|
|
99
|
-
* latency_ms?, meta?, device?, user_context? } ] }
|
|
100
|
-
* The server fills `app_id` + `environment` from the resolving key (never send app_id),
|
|
101
|
-
* and silently skips malformed events — one bad payload never fails the batch.
|
|
102
|
-
*
|
|
103
|
-
* ⚠️ Correlation: `session_id` MUST equal the A2A `contextId` the server uses to key the
|
|
104
|
-
* server-side events, or the funnel report (which groups by `session_id`) treats this as a
|
|
105
|
-
* phantom session. See `makeSessionId` + WireOnboarding for how the kit seeds it.
|
|
106
|
-
*
|
|
107
|
-
* Fire-and-forget: this never throws into the UI and never awaits — analytics must never
|
|
108
|
-
* be able to break onboarding.
|
|
109
|
-
*/
|
|
110
|
-
|
|
111
84
|
/** Event types a CLIENT may report. The rest of the funnel is server-side; sending those
|
|
112
85
|
* here would double-count. `screen_skipped` is included for completeness, but the kit does
|
|
113
86
|
* NOT emit it — the backend already derives it from the skip sentinel (see OnboardingFlow).
|
|
@@ -669,10 +642,11 @@ type WireOnboardingProps = {
|
|
|
669
642
|
* Build the value with the helper so the wire spelling is decided in one place:
|
|
670
643
|
*
|
|
671
644
|
* ```tsx
|
|
672
|
-
* <WireOnboarding userContext={activationJoinContext(deviceKey)} ... />
|
|
673
|
-
* // no device id of your own? pass `storage`
|
|
674
|
-
* //
|
|
675
|
-
*
|
|
645
|
+
* <WireOnboarding config={config} userContext={activationJoinContext(deviceKey)} ... />
|
|
646
|
+
* // no device id of your own? pass the `storage` PROP (it is a prop of this component, never a
|
|
647
|
+
* // field of `WireOnboardingConfig`) and leave this prop alone — the kit injects its own key,
|
|
648
|
+
* // and only after it has confirmed the key actually persists (see `autoJoinKey`).
|
|
649
|
+
* <WireOnboarding config={config} storage={storage} ... />
|
|
676
650
|
* ```
|
|
677
651
|
*
|
|
678
652
|
* ⛔ Do NOT hand-build the auto key with `activationJoinContext(resolveAutoDeviceKey({...}))`.
|
|
@@ -1402,8 +1376,19 @@ declare const resolveAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => stri
|
|
|
1402
1376
|
* follows it. Only a caller that can afford one storage read should use this; the fire-and-forget
|
|
1403
1377
|
* event paths must stay on the sync function.
|
|
1404
1378
|
*
|
|
1405
|
-
* Never throws or rejects: a missing
|
|
1406
|
-
*
|
|
1379
|
+
* Never throws or rejects: a missing or rejecting adapter resolves to the in-memory id, and with no
|
|
1380
|
+
* `storage` it resolves immediately (there is nothing to hydrate from).
|
|
1381
|
+
*
|
|
1382
|
+
* ⛔ A HUNG ADAPTER IS THE ONE CASE IT DOES NOT COVER, and this line used to claim it did. The read
|
|
1383
|
+
* underneath is a bare `storage.getItem` with no ceiling of its own, so an adapter that neither
|
|
1384
|
+
* resolves nor rejects (a locked keychain, a wedged native bridge) leaves THIS PROMISE PENDING
|
|
1385
|
+
* FOREVER — it does not fall back to the in-memory id, it simply never answers. Anything that gates
|
|
1386
|
+
* a user-visible or metric-bearing action on the result must therefore race it against a ceiling of
|
|
1387
|
+
* its own: `<WireOnboarding>` does (`AUTO_JOIN_HYDRATION_TIMEOUT_MS`), and so do the two lifecycle
|
|
1388
|
+
* hooks (`session-analytics/useLifecycleEvents` + `useSessionStart`, through the shared
|
|
1389
|
+
* `withTimeout` + `READ_TIMEOUT_MS` in `session/persistedSession`) — a hung read there had silently
|
|
1390
|
+
* killed `app.session_started` and `app.first_open` for the whole process. Use the SYNC
|
|
1391
|
+
* {@link resolveAutoDeviceKey} when you cannot afford to wait at all.
|
|
1407
1392
|
*
|
|
1408
1393
|
* ⚠️ IT RETURNS A BARE STRING, so it CANNOT say whether the id survives the launch — a degraded
|
|
1409
1394
|
* adapter resolves to the in-memory mint and reads identically to a persisted one. No kit surface
|
|
@@ -1425,7 +1410,9 @@ declare const hydrateAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => Prom
|
|
|
1425
1410
|
*
|
|
1426
1411
|
* Resolves `undefined` only when there is no usable id at all. With no `storage` it resolves
|
|
1427
1412
|
* immediately with `durable: false` — a process-scoped id is exactly what "no persistence" means.
|
|
1428
|
-
* Never throws or rejects
|
|
1413
|
+
* Never throws or rejects — but, exactly like {@link hydrateAutoDeviceKey} above, it can also never
|
|
1414
|
+
* SETTLE on a hung adapter (the underlying `getItem` carries no ceiling), so every caller races it
|
|
1415
|
+
* against one of its own. See the note there.
|
|
1429
1416
|
*/
|
|
1430
1417
|
declare const hydrateDeviceIdentity: (opts?: ResolveAutoDeviceKeyOptions) => Promise<IdentityRecord | undefined>;
|
|
1431
1418
|
/** Test-only: forget every auto id + hydration flag so a unit test starts from a clean registry. */
|
|
@@ -1438,7 +1425,10 @@ declare const resetAutoDeviceKeys: () => void;
|
|
|
1438
1425
|
declare const setCurrentSessionId: (id: string | undefined) => void;
|
|
1439
1426
|
/** The current per-open `session_id`, or `undefined` when no app-open has been registered yet. */
|
|
1440
1427
|
declare const getCurrentSessionId: () => string | undefined;
|
|
1441
|
-
/** Test-only: forget the current session id so a unit test starts from a clean registry.
|
|
1428
|
+
/** Test-only: forget the current session id so a unit test starts from a clean registry. Does NOT
|
|
1429
|
+
* clear the MINTED-id record (see `isMintedSessionId`), for the same reason the warn latch below is
|
|
1430
|
+
* not cleared: `makeSessionId()` never repeats, so a remembered fallback id can never again equal a
|
|
1431
|
+
* live one, and the record is overwritten by the next mint anyway. */
|
|
1442
1432
|
declare const resetCurrentSessionId: () => void;
|
|
1443
1433
|
/**
|
|
1444
1434
|
* The current per-open `session_id`, MINTING and registering one when no app-open has been
|
|
@@ -81,33 +81,6 @@ type DeviceContext = {
|
|
|
81
81
|
*/
|
|
82
82
|
declare const collectDeviceContext: () => DeviceContext;
|
|
83
83
|
|
|
84
|
-
/**
|
|
85
|
-
* reportClientEvent — forward DEVICE-ONLY onboarding events to the Wire AI analytics
|
|
86
|
-
* backend (`POST {serverUrl}/v1/events`), completing the funnel for events the server
|
|
87
|
-
* can't observe on its own.
|
|
88
|
-
*
|
|
89
|
-
* The backend already records the server-observable funnel during the A2A flow
|
|
90
|
-
* (`session_started`, `screen_shown`, `answer_submitted`, `completed`, `llm_fallback`,
|
|
91
|
-
* and even `screen_skipped` — it derives that from the kit's skip sentinel). The one
|
|
92
|
-
* event no server request can capture is `dropped`: the user closing the app / unmounting
|
|
93
|
-
* the flow without finishing. That's what this reporter is for.
|
|
94
|
-
*
|
|
95
|
-
* Contract (server: routers/onboarding.py → analytics/events.py):
|
|
96
|
-
* POST {serverUrl}/v1/events
|
|
97
|
-
* Authorization: Bearer {apiKey}
|
|
98
|
-
* { "events": [ { event_type, session_id, screen_index?, component?, question_key?,
|
|
99
|
-
* latency_ms?, meta?, device?, user_context? } ] }
|
|
100
|
-
* The server fills `app_id` + `environment` from the resolving key (never send app_id),
|
|
101
|
-
* and silently skips malformed events — one bad payload never fails the batch.
|
|
102
|
-
*
|
|
103
|
-
* ⚠️ Correlation: `session_id` MUST equal the A2A `contextId` the server uses to key the
|
|
104
|
-
* server-side events, or the funnel report (which groups by `session_id`) treats this as a
|
|
105
|
-
* phantom session. See `makeSessionId` + WireOnboarding for how the kit seeds it.
|
|
106
|
-
*
|
|
107
|
-
* Fire-and-forget: this never throws into the UI and never awaits — analytics must never
|
|
108
|
-
* be able to break onboarding.
|
|
109
|
-
*/
|
|
110
|
-
|
|
111
84
|
/** Event types a CLIENT may report. The rest of the funnel is server-side; sending those
|
|
112
85
|
* here would double-count. `screen_skipped` is included for completeness, but the kit does
|
|
113
86
|
* NOT emit it — the backend already derives it from the skip sentinel (see OnboardingFlow).
|
|
@@ -669,10 +642,11 @@ type WireOnboardingProps = {
|
|
|
669
642
|
* Build the value with the helper so the wire spelling is decided in one place:
|
|
670
643
|
*
|
|
671
644
|
* ```tsx
|
|
672
|
-
* <WireOnboarding userContext={activationJoinContext(deviceKey)} ... />
|
|
673
|
-
* // no device id of your own? pass `storage`
|
|
674
|
-
* //
|
|
675
|
-
*
|
|
645
|
+
* <WireOnboarding config={config} userContext={activationJoinContext(deviceKey)} ... />
|
|
646
|
+
* // no device id of your own? pass the `storage` PROP (it is a prop of this component, never a
|
|
647
|
+
* // field of `WireOnboardingConfig`) and leave this prop alone — the kit injects its own key,
|
|
648
|
+
* // and only after it has confirmed the key actually persists (see `autoJoinKey`).
|
|
649
|
+
* <WireOnboarding config={config} storage={storage} ... />
|
|
676
650
|
* ```
|
|
677
651
|
*
|
|
678
652
|
* ⛔ Do NOT hand-build the auto key with `activationJoinContext(resolveAutoDeviceKey({...}))`.
|
|
@@ -1402,8 +1376,19 @@ declare const resolveAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => stri
|
|
|
1402
1376
|
* follows it. Only a caller that can afford one storage read should use this; the fire-and-forget
|
|
1403
1377
|
* event paths must stay on the sync function.
|
|
1404
1378
|
*
|
|
1405
|
-
* Never throws or rejects: a missing
|
|
1406
|
-
*
|
|
1379
|
+
* Never throws or rejects: a missing or rejecting adapter resolves to the in-memory id, and with no
|
|
1380
|
+
* `storage` it resolves immediately (there is nothing to hydrate from).
|
|
1381
|
+
*
|
|
1382
|
+
* ⛔ A HUNG ADAPTER IS THE ONE CASE IT DOES NOT COVER, and this line used to claim it did. The read
|
|
1383
|
+
* underneath is a bare `storage.getItem` with no ceiling of its own, so an adapter that neither
|
|
1384
|
+
* resolves nor rejects (a locked keychain, a wedged native bridge) leaves THIS PROMISE PENDING
|
|
1385
|
+
* FOREVER — it does not fall back to the in-memory id, it simply never answers. Anything that gates
|
|
1386
|
+
* a user-visible or metric-bearing action on the result must therefore race it against a ceiling of
|
|
1387
|
+
* its own: `<WireOnboarding>` does (`AUTO_JOIN_HYDRATION_TIMEOUT_MS`), and so do the two lifecycle
|
|
1388
|
+
* hooks (`session-analytics/useLifecycleEvents` + `useSessionStart`, through the shared
|
|
1389
|
+
* `withTimeout` + `READ_TIMEOUT_MS` in `session/persistedSession`) — a hung read there had silently
|
|
1390
|
+
* killed `app.session_started` and `app.first_open` for the whole process. Use the SYNC
|
|
1391
|
+
* {@link resolveAutoDeviceKey} when you cannot afford to wait at all.
|
|
1407
1392
|
*
|
|
1408
1393
|
* ⚠️ IT RETURNS A BARE STRING, so it CANNOT say whether the id survives the launch — a degraded
|
|
1409
1394
|
* adapter resolves to the in-memory mint and reads identically to a persisted one. No kit surface
|
|
@@ -1425,7 +1410,9 @@ declare const hydrateAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => Prom
|
|
|
1425
1410
|
*
|
|
1426
1411
|
* Resolves `undefined` only when there is no usable id at all. With no `storage` it resolves
|
|
1427
1412
|
* immediately with `durable: false` — a process-scoped id is exactly what "no persistence" means.
|
|
1428
|
-
* Never throws or rejects
|
|
1413
|
+
* Never throws or rejects — but, exactly like {@link hydrateAutoDeviceKey} above, it can also never
|
|
1414
|
+
* SETTLE on a hung adapter (the underlying `getItem` carries no ceiling), so every caller races it
|
|
1415
|
+
* against one of its own. See the note there.
|
|
1429
1416
|
*/
|
|
1430
1417
|
declare const hydrateDeviceIdentity: (opts?: ResolveAutoDeviceKeyOptions) => Promise<IdentityRecord | undefined>;
|
|
1431
1418
|
/** Test-only: forget every auto id + hydration flag so a unit test starts from a clean registry. */
|
|
@@ -1438,7 +1425,10 @@ declare const resetAutoDeviceKeys: () => void;
|
|
|
1438
1425
|
declare const setCurrentSessionId: (id: string | undefined) => void;
|
|
1439
1426
|
/** The current per-open `session_id`, or `undefined` when no app-open has been registered yet. */
|
|
1440
1427
|
declare const getCurrentSessionId: () => string | undefined;
|
|
1441
|
-
/** Test-only: forget the current session id so a unit test starts from a clean registry.
|
|
1428
|
+
/** Test-only: forget the current session id so a unit test starts from a clean registry. Does NOT
|
|
1429
|
+
* clear the MINTED-id record (see `isMintedSessionId`), for the same reason the warn latch below is
|
|
1430
|
+
* not cleared: `makeSessionId()` never repeats, so a remembered fallback id can never again equal a
|
|
1431
|
+
* live one, and the record is overwritten by the next mint anyway. */
|
|
1442
1432
|
declare const resetCurrentSessionId: () => void;
|
|
1443
1433
|
/**
|
|
1444
1434
|
* The current per-open `session_id`, MINTING and registering one when no app-open has been
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import React__default, { ReactNode } from 'react';
|
|
3
|
-
import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, K as WirePermissionStatus, P as PermissionStage, L as WirePermissionOutcome, S as StepValidator, M as OnboardingEvent, N as OnboardingCopy, C as ClientEventTarget, Q as DeviceContext, T as PermissionScreenConfig, W as WireUserContext, c as ClientEvent, U as PermissionPlacement, g as EnvelopeSource } from './currentSession-
|
|
4
|
-
export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, b as ClearUserContextOptions, d as ClientEventType, V as DEFAULT_PERMISSION_COPY, X as DeviceFormFactor, D as DeviceKeyStorage, Y as EXTRA_KEY_PREFIX, Z as GENERIC_PERMISSION_COPY, _ as IdentifyOnboardingBinding, $ as IdentifyOnboardingOptions, a0 as IdentityRecord, a1 as IdentitySource, a2 as IdentitySpace, a3 as NOTIFICATIONS_PERMISSION_COPY, a4 as OnboardingProgress, a5 as PermissionScreenCopy, R as ResolveAutoDeviceKeyOptions, a6 as ResolveUserContextOptions, a7 as ResolvedUserContext, a8 as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, a9 as WIRE_PERMISSION_EVENTS, j as WireOnboardingEventName, aa as WirePermissionEventName, ab as WirePermissionKind, ac as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, ad as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, ae as hashEmailFnv1a, af as hostIdentity, ag as hydrateAutoDeviceKey, ah as hydrateDeviceIdentity, ai as identifyOnboarding, aj as isWireScalar, s as looksLikeEmail, t as makeSessionId, ak as mintDeviceId, al as namespaceExtra, am as normalizePermissionStatus, an as permissionEventName, ao as permissionEventProps, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, ap as resetIdentityProvenance, F as resolveAutoDeviceKey, aq as resolveIdentity, ar as resolvePermissionCopy, as as resolveUserContext, at as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-
|
|
3
|
+
import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, K as WirePermissionStatus, P as PermissionStage, L as WirePermissionOutcome, S as StepValidator, M as OnboardingEvent, N as OnboardingCopy, C as ClientEventTarget, Q as DeviceContext, T as PermissionScreenConfig, W as WireUserContext, c as ClientEvent, U as PermissionPlacement, g as EnvelopeSource } from './currentSession-Bz7G6lno.mjs';
|
|
4
|
+
export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, b as ClearUserContextOptions, d as ClientEventType, V as DEFAULT_PERMISSION_COPY, X as DeviceFormFactor, D as DeviceKeyStorage, Y as EXTRA_KEY_PREFIX, Z as GENERIC_PERMISSION_COPY, _ as IdentifyOnboardingBinding, $ as IdentifyOnboardingOptions, a0 as IdentityRecord, a1 as IdentitySource, a2 as IdentitySpace, a3 as NOTIFICATIONS_PERMISSION_COPY, a4 as OnboardingProgress, a5 as PermissionScreenCopy, R as ResolveAutoDeviceKeyOptions, a6 as ResolveUserContextOptions, a7 as ResolvedUserContext, a8 as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, a9 as WIRE_PERMISSION_EVENTS, j as WireOnboardingEventName, aa as WirePermissionEventName, ab as WirePermissionKind, ac as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, ad as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, ae as hashEmailFnv1a, af as hostIdentity, ag as hydrateAutoDeviceKey, ah as hydrateDeviceIdentity, ai as identifyOnboarding, aj as isWireScalar, s as looksLikeEmail, t as makeSessionId, ak as mintDeviceId, al as namespaceExtra, am as normalizePermissionStatus, an as permissionEventName, ao as permissionEventProps, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, ap as resetIdentityProvenance, F as resolveAutoDeviceKey, aq as resolveIdentity, ar as resolvePermissionCopy, as as resolveUserContext, at as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-Bz7G6lno.mjs';
|
|
5
5
|
import { O as OnboardingTheme } from './types-BKfpdZzX.mjs';
|
|
6
6
|
export { a as OnboardingButtonStyle, b as OnboardingColors, c as OnboardingFonts, d as OnboardingRadius, e as OnboardingSpacing } from './types-BKfpdZzX.mjs';
|
|
7
7
|
export { C as CenteredModal, a as CenteredModalHandle, b as CenteredModalProps } from './CenteredModal-C3qQBHsA.mjs';
|
|
@@ -1301,6 +1301,7 @@ declare const readProgress: (response?: WireAIResponse) => PartialProgress;
|
|
|
1301
1301
|
*/
|
|
1302
1302
|
declare const fetchWireFeatures: (config?: WireFeaturesConfig) => Promise<WireFeatures>;
|
|
1303
1303
|
|
|
1304
|
+
/** The flags alone — the original hook, unchanged for every consumer that cannot act on `settled`. */
|
|
1304
1305
|
declare const useWireFeatures: (config?: WireFeaturesConfig) => WireFeatures;
|
|
1305
1306
|
|
|
1306
1307
|
/**
|
|
@@ -2165,6 +2166,8 @@ interface ReportFirstOpenOptions extends LifecycleEventInput {
|
|
|
2165
2166
|
* • Race guard: an in-memory latch is set SYNCHRONOUSLY before the async read, so two
|
|
2166
2167
|
* near-simultaneous calls fire at most once.
|
|
2167
2168
|
* • Without `storage`: degraded mode — fires once per PROCESS via the latch only (documented).
|
|
2169
|
+
* • WITHOUT A TRANSPORT: no-op, and — the point of the check — the once-ever flag is NOT spent.
|
|
2170
|
+
* See below.
|
|
2168
2171
|
*/
|
|
2169
2172
|
declare const reportFirstOpen: (opts: ReportFirstOpenOptions) => void;
|
|
2170
2173
|
/** Options for {@link wireLifecycleEvents}: the shared lifecycle inputs + first-open persistence. */
|
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 { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, K as WirePermissionStatus, P as PermissionStage, L as WirePermissionOutcome, S as StepValidator, M as OnboardingEvent, N as OnboardingCopy, C as ClientEventTarget, Q as DeviceContext, T as PermissionScreenConfig, W as WireUserContext, c as ClientEvent, U as PermissionPlacement, g as EnvelopeSource } from './currentSession-
|
|
4
|
-
export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, b as ClearUserContextOptions, d as ClientEventType, V as DEFAULT_PERMISSION_COPY, X as DeviceFormFactor, D as DeviceKeyStorage, Y as EXTRA_KEY_PREFIX, Z as GENERIC_PERMISSION_COPY, _ as IdentifyOnboardingBinding, $ as IdentifyOnboardingOptions, a0 as IdentityRecord, a1 as IdentitySource, a2 as IdentitySpace, a3 as NOTIFICATIONS_PERMISSION_COPY, a4 as OnboardingProgress, a5 as PermissionScreenCopy, R as ResolveAutoDeviceKeyOptions, a6 as ResolveUserContextOptions, a7 as ResolvedUserContext, a8 as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, a9 as WIRE_PERMISSION_EVENTS, j as WireOnboardingEventName, aa as WirePermissionEventName, ab as WirePermissionKind, ac as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, ad as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, ae as hashEmailFnv1a, af as hostIdentity, ag as hydrateAutoDeviceKey, ah as hydrateDeviceIdentity, ai as identifyOnboarding, aj as isWireScalar, s as looksLikeEmail, t as makeSessionId, ak as mintDeviceId, al as namespaceExtra, am as normalizePermissionStatus, an as permissionEventName, ao as permissionEventProps, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, ap as resetIdentityProvenance, F as resolveAutoDeviceKey, aq as resolveIdentity, ar as resolvePermissionCopy, as as resolveUserContext, at as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-
|
|
3
|
+
import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, K as WirePermissionStatus, P as PermissionStage, L as WirePermissionOutcome, S as StepValidator, M as OnboardingEvent, N as OnboardingCopy, C as ClientEventTarget, Q as DeviceContext, T as PermissionScreenConfig, W as WireUserContext, c as ClientEvent, U as PermissionPlacement, g as EnvelopeSource } from './currentSession-z-CZ55ad.js';
|
|
4
|
+
export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, b as ClearUserContextOptions, d as ClientEventType, V as DEFAULT_PERMISSION_COPY, X as DeviceFormFactor, D as DeviceKeyStorage, Y as EXTRA_KEY_PREFIX, Z as GENERIC_PERMISSION_COPY, _ as IdentifyOnboardingBinding, $ as IdentifyOnboardingOptions, a0 as IdentityRecord, a1 as IdentitySource, a2 as IdentitySpace, a3 as NOTIFICATIONS_PERMISSION_COPY, a4 as OnboardingProgress, a5 as PermissionScreenCopy, R as ResolveAutoDeviceKeyOptions, a6 as ResolveUserContextOptions, a7 as ResolvedUserContext, a8 as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, a9 as WIRE_PERMISSION_EVENTS, j as WireOnboardingEventName, aa as WirePermissionEventName, ab as WirePermissionKind, ac as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, ad as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, ae as hashEmailFnv1a, af as hostIdentity, ag as hydrateAutoDeviceKey, ah as hydrateDeviceIdentity, ai as identifyOnboarding, aj as isWireScalar, s as looksLikeEmail, t as makeSessionId, ak as mintDeviceId, al as namespaceExtra, am as normalizePermissionStatus, an as permissionEventName, ao as permissionEventProps, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, ap as resetIdentityProvenance, F as resolveAutoDeviceKey, aq as resolveIdentity, ar as resolvePermissionCopy, as as resolveUserContext, at as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-z-CZ55ad.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';
|
|
@@ -1301,6 +1301,7 @@ declare const readProgress: (response?: WireAIResponse) => PartialProgress;
|
|
|
1301
1301
|
*/
|
|
1302
1302
|
declare const fetchWireFeatures: (config?: WireFeaturesConfig) => Promise<WireFeatures>;
|
|
1303
1303
|
|
|
1304
|
+
/** The flags alone — the original hook, unchanged for every consumer that cannot act on `settled`. */
|
|
1304
1305
|
declare const useWireFeatures: (config?: WireFeaturesConfig) => WireFeatures;
|
|
1305
1306
|
|
|
1306
1307
|
/**
|
|
@@ -2165,6 +2166,8 @@ interface ReportFirstOpenOptions extends LifecycleEventInput {
|
|
|
2165
2166
|
* • Race guard: an in-memory latch is set SYNCHRONOUSLY before the async read, so two
|
|
2166
2167
|
* near-simultaneous calls fire at most once.
|
|
2167
2168
|
* • Without `storage`: degraded mode — fires once per PROCESS via the latch only (documented).
|
|
2169
|
+
* • WITHOUT A TRANSPORT: no-op, and — the point of the check — the once-ever flag is NOT spent.
|
|
2170
|
+
* See below.
|
|
2168
2171
|
*/
|
|
2169
2172
|
declare const reportFirstOpen: (opts: ReportFirstOpenOptions) => void;
|
|
2170
2173
|
/** Options for {@link wireLifecycleEvents}: the shared lifecycle inputs + first-open persistence. */
|
package/dist/index.js
CHANGED
|
@@ -1168,6 +1168,28 @@ var readProgress = (response) => {
|
|
|
1168
1168
|
};
|
|
1169
1169
|
};
|
|
1170
1170
|
|
|
1171
|
+
// src/utils/withDeadline.ts
|
|
1172
|
+
var DEADLINE_EXPIRED = /* @__PURE__ */ Symbol("wire-deadline-expired");
|
|
1173
|
+
var DEFAULT_DEADLINE_MS = 15e3;
|
|
1174
|
+
var withDeadline = async (exchange, timeoutMs = DEFAULT_DEADLINE_MS) => {
|
|
1175
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
1176
|
+
let timer;
|
|
1177
|
+
const expired = new Promise((resolve) => {
|
|
1178
|
+
timer = setTimeout(() => {
|
|
1179
|
+
controller == null ? void 0 : controller.abort();
|
|
1180
|
+
resolve(DEADLINE_EXPIRED);
|
|
1181
|
+
}, timeoutMs);
|
|
1182
|
+
});
|
|
1183
|
+
const running = exchange(controller == null ? void 0 : controller.signal);
|
|
1184
|
+
try {
|
|
1185
|
+
return await Promise.race([running, expired]);
|
|
1186
|
+
} finally {
|
|
1187
|
+
clearTimeout(timer);
|
|
1188
|
+
void running.catch(() => {
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1171
1193
|
// src/analytics/reportClientEvent.ts
|
|
1172
1194
|
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1173
1195
|
var toWireEvents = (events) => events.map((event) => {
|
|
@@ -1239,14 +1261,17 @@ var reportClientEventsOutcome = async (target, events) => {
|
|
|
1239
1261
|
try {
|
|
1240
1262
|
const req = buildEventsRequest(target, events);
|
|
1241
1263
|
if (!req) return "refused";
|
|
1242
|
-
const
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
console.warn
|
|
1248
|
-
|
|
1249
|
-
|
|
1264
|
+
const outcome = await withDeadline(async (signal) => {
|
|
1265
|
+
const res = await fetch(req.url, { ...req.init, signal });
|
|
1266
|
+
if (!res || !res.ok) return "unreachable";
|
|
1267
|
+
const ack = await readEventsAck(res);
|
|
1268
|
+
if (!ack || ack.skipped <= 0) return "delivered";
|
|
1269
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
1270
|
+
console.warn(describeDiscarded(ack));
|
|
1271
|
+
}
|
|
1272
|
+
return "refused";
|
|
1273
|
+
});
|
|
1274
|
+
return outcome === DEADLINE_EXPIRED ? "unreachable" : outcome;
|
|
1250
1275
|
} catch {
|
|
1251
1276
|
return "unreachable";
|
|
1252
1277
|
}
|
|
@@ -3478,11 +3503,11 @@ var provenanceRegistry = () => {
|
|
|
3478
3503
|
return created;
|
|
3479
3504
|
};
|
|
3480
3505
|
var provenanceKey = (space, scope) => `${space}:${scope != null ? scope : "default"}`;
|
|
3506
|
+
var isUsableIdentityValue = (value) => typeof value === "string" && value.trim().length > 0;
|
|
3481
3507
|
var resolveIdentity = (input) => {
|
|
3482
3508
|
var _a;
|
|
3483
|
-
if (
|
|
3509
|
+
if (!isUsableIdentityValue(input.value)) return void 0;
|
|
3484
3510
|
const value = input.value.trim();
|
|
3485
|
-
if (!value) return void 0;
|
|
3486
3511
|
const durable = (_a = input.durable) != null ? _a : input.source === "host";
|
|
3487
3512
|
if (input.source === "host") {
|
|
3488
3513
|
provenanceRegistry().host.set(provenanceKey(input.space, input.scope), value);
|
|
@@ -3587,6 +3612,7 @@ var resetAutoDeviceKeys = () => {
|
|
|
3587
3612
|
registry.keys.clear();
|
|
3588
3613
|
registry.hydrating.clear();
|
|
3589
3614
|
(_a = registry.pending) == null ? void 0 : _a.clear();
|
|
3615
|
+
registry.ambient = void 0;
|
|
3590
3616
|
};
|
|
3591
3617
|
|
|
3592
3618
|
// src/analytics/currentSession.ts
|
|
@@ -3594,25 +3620,51 @@ var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
|
3594
3620
|
"@wireai/activation:currentSessionId"
|
|
3595
3621
|
);
|
|
3596
3622
|
var globalSlot = globalThis;
|
|
3623
|
+
var SESSION_ID_LISTENERS_SLOT = /* @__PURE__ */ Symbol.for(
|
|
3624
|
+
"@wireai/activation:currentSessionIdListeners"
|
|
3625
|
+
);
|
|
3626
|
+
var listenerSlot = globalThis;
|
|
3627
|
+
var listeners = () => {
|
|
3628
|
+
const existing = listenerSlot[SESSION_ID_LISTENERS_SLOT];
|
|
3629
|
+
if (existing) return existing;
|
|
3630
|
+
const created = /* @__PURE__ */ new Set();
|
|
3631
|
+
listenerSlot[SESSION_ID_LISTENERS_SLOT] = created;
|
|
3632
|
+
return created;
|
|
3633
|
+
};
|
|
3634
|
+
var writeCurrentSessionId = (id) => {
|
|
3635
|
+
if (globalSlot[CURRENT_SESSION_ID_SLOT] === id) return;
|
|
3636
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = id;
|
|
3637
|
+
for (const listener of Array.from(listeners())) {
|
|
3638
|
+
try {
|
|
3639
|
+
listener();
|
|
3640
|
+
} catch {
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
};
|
|
3597
3644
|
var setCurrentSessionId = (id) => {
|
|
3598
3645
|
if (typeof id === "string" && id.length > 0) {
|
|
3599
|
-
|
|
3646
|
+
writeCurrentSessionId(id);
|
|
3600
3647
|
}
|
|
3601
3648
|
};
|
|
3602
3649
|
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
3603
3650
|
var resetCurrentSessionId = () => {
|
|
3604
|
-
|
|
3651
|
+
writeCurrentSessionId(void 0);
|
|
3605
3652
|
};
|
|
3606
3653
|
var MINT_WARNING = "[wireai] No app-open session was registered, so a session id was minted for this event (the server drops an event that has no session_id, and still answers 200). Mount useLifecycleEvents at your app root so events correlate to a real app-open.";
|
|
3607
3654
|
var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
3608
3655
|
"@wireai/activation:currentSessionIdMintWarned"
|
|
3609
3656
|
);
|
|
3610
3657
|
var warnSlot = globalThis;
|
|
3658
|
+
var MINTED_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
3659
|
+
"@wireai/activation:mintedSessionId"
|
|
3660
|
+
);
|
|
3661
|
+
var mintedSlot = globalThis;
|
|
3611
3662
|
var ensureCurrentSessionId = () => {
|
|
3612
3663
|
const existing = globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
3613
3664
|
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
3614
3665
|
const minted = makeSessionId();
|
|
3615
|
-
|
|
3666
|
+
writeCurrentSessionId(minted);
|
|
3667
|
+
mintedSlot[MINTED_SESSION_ID_SLOT] = minted;
|
|
3616
3668
|
if (!warnSlot[MINT_WARNED_SLOT] && warnInDev(MINT_WARNING)) {
|
|
3617
3669
|
warnSlot[MINT_WARNED_SLOT] = true;
|
|
3618
3670
|
}
|
|
@@ -4452,17 +4504,31 @@ var failOpen = async (storage, key) => {
|
|
|
4452
4504
|
const cached2 = await readCachedFeatures(storage, key);
|
|
4453
4505
|
return (_a = cached2 == null ? void 0 : cached2.features) != null ? _a : defaultWireFeatures;
|
|
4454
4506
|
};
|
|
4455
|
-
var
|
|
4507
|
+
var NO_ANSWER = /* @__PURE__ */ Symbol("wire-features-no-answer");
|
|
4508
|
+
var fetchFeaturesJson = async (url, apiKey, timeoutMs) => {
|
|
4456
4509
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4510
|
+
let timer;
|
|
4511
|
+
const expired = new Promise((resolve) => {
|
|
4512
|
+
timer = setTimeout(() => {
|
|
4513
|
+
controller == null ? void 0 : controller.abort();
|
|
4514
|
+
resolve(NO_ANSWER);
|
|
4515
|
+
}, timeoutMs);
|
|
4516
|
+
});
|
|
4517
|
+
const exchange = (async () => {
|
|
4518
|
+
const res = await fetch(url, {
|
|
4460
4519
|
method: "GET",
|
|
4461
4520
|
headers: { Accept: "application/json", Authorization: `Bearer ${apiKey}` },
|
|
4462
4521
|
signal: controller == null ? void 0 : controller.signal
|
|
4463
4522
|
});
|
|
4523
|
+
if (!res || !res.ok) return NO_ANSWER;
|
|
4524
|
+
return await res.json();
|
|
4525
|
+
})();
|
|
4526
|
+
try {
|
|
4527
|
+
return await Promise.race([exchange, expired]);
|
|
4464
4528
|
} finally {
|
|
4465
4529
|
clearTimeout(timer);
|
|
4530
|
+
void exchange.catch(() => {
|
|
4531
|
+
});
|
|
4466
4532
|
}
|
|
4467
4533
|
};
|
|
4468
4534
|
var fetchWireFeatures = async (config) => {
|
|
@@ -4472,9 +4538,8 @@ var fetchWireFeatures = async (config) => {
|
|
|
4472
4538
|
const url = `${config.serverUrl.replace(/\/$/, "")}/v1/features`;
|
|
4473
4539
|
const timeoutMs = config.timeoutMs && config.timeoutMs > 0 ? config.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
4474
4540
|
try {
|
|
4475
|
-
const
|
|
4476
|
-
if (
|
|
4477
|
-
const json = await res.json();
|
|
4541
|
+
const json = await fetchFeaturesJson(url, config.apiKey, timeoutMs);
|
|
4542
|
+
if (json === NO_ANSWER) return failOpen(storage, key);
|
|
4478
4543
|
const features = parseWireFeatures(json);
|
|
4479
4544
|
if (storage) writeCachedFeatures(storage, key, features);
|
|
4480
4545
|
return features;
|
|
@@ -4488,50 +4553,65 @@ var useStableValue = (value, isEqual) => {
|
|
|
4488
4553
|
return ref.current;
|
|
4489
4554
|
};
|
|
4490
4555
|
var sameFetchKey = (a, b) => (a == null ? void 0 : a.serverUrl) === (b == null ? void 0 : b.serverUrl) && (a == null ? void 0 : a.apiKey) === (b == null ? void 0 : b.apiKey) && (a == null ? void 0 : a.appId) === (b == null ? void 0 : b.appId);
|
|
4491
|
-
var
|
|
4556
|
+
var useWireFeaturesState = (config) => {
|
|
4492
4557
|
const stableConfig = useStableValue(config, sameFetchKey);
|
|
4493
4558
|
const [flags, setFlags] = React19.useState(defaultWireFeatures);
|
|
4494
4559
|
const canFetch = !!(stableConfig == null ? void 0 : stableConfig.serverUrl) && !!(stableConfig == null ? void 0 : stableConfig.apiKey);
|
|
4560
|
+
const [settled, setSettled] = React19.useState(!canFetch);
|
|
4495
4561
|
React19.useEffect(() => {
|
|
4496
4562
|
if (!canFetch) {
|
|
4497
4563
|
setFlags((prev) => featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures);
|
|
4564
|
+
setSettled(true);
|
|
4498
4565
|
return;
|
|
4499
4566
|
}
|
|
4500
4567
|
let alive = true;
|
|
4501
4568
|
void fetchWireFeatures(stableConfig).then((next) => {
|
|
4502
4569
|
if (!alive) return;
|
|
4503
4570
|
setFlags((prev) => featuresEqual(prev, next) ? prev : next);
|
|
4571
|
+
setSettled(true);
|
|
4504
4572
|
});
|
|
4505
4573
|
return () => {
|
|
4506
4574
|
alive = false;
|
|
4507
4575
|
};
|
|
4508
4576
|
}, [canFetch, stableConfig]);
|
|
4509
|
-
return flags;
|
|
4577
|
+
return React19.useMemo(() => ({ flags, settled }), [flags, settled]);
|
|
4510
4578
|
};
|
|
4579
|
+
var useWireFeatures = (config) => useWireFeaturesState(config).flags;
|
|
4511
4580
|
var CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.context");
|
|
4512
|
-
var
|
|
4513
|
-
if (!
|
|
4514
|
-
|
|
4581
|
+
var featuresContextGlobal = globalThis;
|
|
4582
|
+
if (!featuresContextGlobal[CONTEXT_SYMBOL]) {
|
|
4583
|
+
featuresContextGlobal[CONTEXT_SYMBOL] = React19.createContext(null);
|
|
4515
4584
|
}
|
|
4516
|
-
var WireFeaturesContext =
|
|
4585
|
+
var WireFeaturesContext = featuresContextGlobal[CONTEXT_SYMBOL];
|
|
4586
|
+
var SETTLED_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.settled.context");
|
|
4587
|
+
var settledContextGlobal = globalThis;
|
|
4588
|
+
if (!settledContextGlobal[SETTLED_SYMBOL]) {
|
|
4589
|
+
settledContextGlobal[SETTLED_SYMBOL] = React19.createContext(true);
|
|
4590
|
+
}
|
|
4591
|
+
var WireFeaturesSettledContext = settledContextGlobal[SETTLED_SYMBOL];
|
|
4517
4592
|
var WireFeaturesProvider = ({
|
|
4518
4593
|
config,
|
|
4519
4594
|
flags,
|
|
4520
4595
|
children
|
|
4521
4596
|
}) => {
|
|
4522
|
-
const fetched =
|
|
4523
|
-
const value = flags != null ? flags : fetched;
|
|
4597
|
+
const fetched = useWireFeaturesState(flags ? void 0 : config);
|
|
4598
|
+
const value = flags != null ? flags : fetched.flags;
|
|
4599
|
+
const settled = flags ? true : fetched.settled;
|
|
4524
4600
|
const stable = React19.useMemo(() => value, [value]);
|
|
4525
|
-
return /* @__PURE__ */ jsxRuntime.jsx(WireFeaturesContext.Provider, { value: stable, children });
|
|
4601
|
+
return /* @__PURE__ */ jsxRuntime.jsx(WireFeaturesSettledContext.Provider, { value: settled, children: /* @__PURE__ */ jsxRuntime.jsx(WireFeaturesContext.Provider, { value: stable, children }) });
|
|
4526
4602
|
};
|
|
4527
4603
|
WireFeaturesProvider.displayName = "WireFeaturesProvider";
|
|
4528
4604
|
var useWireFeaturesContext = () => React19.useContext(WireFeaturesContext);
|
|
4529
|
-
var useResolvedFeatures = (options) =>
|
|
4605
|
+
var useResolvedFeatures = (options) => useResolvedFeaturesState(options).flags;
|
|
4606
|
+
var useResolvedFeaturesState = (options) => {
|
|
4530
4607
|
var _a, _b, _c;
|
|
4531
4608
|
const ctx = useWireFeaturesContext();
|
|
4609
|
+
const ctxSettled = React19.useContext(WireFeaturesSettledContext);
|
|
4532
4610
|
const shouldFetch = !(options == null ? void 0 : options.flags) && ctx == null;
|
|
4533
|
-
const fetched =
|
|
4534
|
-
|
|
4611
|
+
const fetched = useWireFeaturesState(shouldFetch ? options == null ? void 0 : options.config : void 0);
|
|
4612
|
+
const flags = (_c = (_b = (_a = options == null ? void 0 : options.flags) != null ? _a : ctx) != null ? _b : fetched.flags) != null ? _c : defaultWireFeatures;
|
|
4613
|
+
const settled = (options == null ? void 0 : options.flags) ? true : ctx != null ? ctxSettled : fetched.settled;
|
|
4614
|
+
return React19.useMemo(() => ({ flags, settled }), [flags, settled]);
|
|
4535
4615
|
};
|
|
4536
4616
|
|
|
4537
4617
|
// src/config/wireConfigFromEnv.ts
|
|
@@ -5275,8 +5355,12 @@ var useSessionStart = (config, options = {}) => {
|
|
|
5275
5355
|
fireOpen(void 0);
|
|
5276
5356
|
return;
|
|
5277
5357
|
}
|
|
5278
|
-
void
|
|
5358
|
+
void withTimeout(
|
|
5359
|
+
hydrateDeviceIdentity({ appId: cfg.appId, storage: cfg.storage }),
|
|
5360
|
+
READ_TIMEOUT_MS
|
|
5361
|
+
).then((read) => {
|
|
5279
5362
|
if (cancelled) return;
|
|
5363
|
+
const identity = read === READ_TIMED_OUT ? void 0 : read;
|
|
5280
5364
|
fireOpen((identity == null ? void 0 : identity.durable) ? identity.value : void 0);
|
|
5281
5365
|
});
|
|
5282
5366
|
};
|
|
@@ -5376,8 +5460,9 @@ var emitFirstOpen = (opts) => {
|
|
|
5376
5460
|
routeLifecycleEvent(buildLifecycleEvent(FIRST_OPEN_EVENT, opts), opts);
|
|
5377
5461
|
};
|
|
5378
5462
|
var reportFirstOpen = (opts) => {
|
|
5379
|
-
var _a;
|
|
5380
|
-
|
|
5463
|
+
var _a, _b;
|
|
5464
|
+
if (!opts.sink && !((_a = opts.target) == null ? void 0 : _a.serverUrl)) return;
|
|
5465
|
+
const appId = (_b = opts.appId) != null ? _b : "default";
|
|
5381
5466
|
if (_firstOpenLatched.has(appId)) return;
|
|
5382
5467
|
_firstOpenLatched.add(appId);
|
|
5383
5468
|
const storage = opts.storage;
|
|
@@ -5462,8 +5547,12 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5462
5547
|
fire(void 0);
|
|
5463
5548
|
return;
|
|
5464
5549
|
}
|
|
5465
|
-
void
|
|
5550
|
+
void withTimeout(
|
|
5551
|
+
hydrateDeviceIdentity({ appId: cfg.appId, storage: cfg.storage }),
|
|
5552
|
+
READ_TIMEOUT_MS
|
|
5553
|
+
).then((read) => {
|
|
5466
5554
|
if (cancelled) return;
|
|
5555
|
+
const identity = read === READ_TIMED_OUT ? void 0 : read;
|
|
5467
5556
|
fire((identity == null ? void 0 : identity.durable) ? identity.value : void 0);
|
|
5468
5557
|
});
|
|
5469
5558
|
};
|