@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
package/dist/index.mjs
CHANGED
|
@@ -1162,6 +1162,28 @@ var readProgress = (response) => {
|
|
|
1162
1162
|
};
|
|
1163
1163
|
};
|
|
1164
1164
|
|
|
1165
|
+
// src/utils/withDeadline.ts
|
|
1166
|
+
var DEADLINE_EXPIRED = /* @__PURE__ */ Symbol("wire-deadline-expired");
|
|
1167
|
+
var DEFAULT_DEADLINE_MS = 15e3;
|
|
1168
|
+
var withDeadline = async (exchange, timeoutMs = DEFAULT_DEADLINE_MS) => {
|
|
1169
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
1170
|
+
let timer;
|
|
1171
|
+
const expired = new Promise((resolve) => {
|
|
1172
|
+
timer = setTimeout(() => {
|
|
1173
|
+
controller == null ? void 0 : controller.abort();
|
|
1174
|
+
resolve(DEADLINE_EXPIRED);
|
|
1175
|
+
}, timeoutMs);
|
|
1176
|
+
});
|
|
1177
|
+
const running = exchange(controller == null ? void 0 : controller.signal);
|
|
1178
|
+
try {
|
|
1179
|
+
return await Promise.race([running, expired]);
|
|
1180
|
+
} finally {
|
|
1181
|
+
clearTimeout(timer);
|
|
1182
|
+
void running.catch(() => {
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1165
1187
|
// src/analytics/reportClientEvent.ts
|
|
1166
1188
|
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1167
1189
|
var toWireEvents = (events) => events.map((event) => {
|
|
@@ -1233,14 +1255,17 @@ var reportClientEventsOutcome = async (target, events) => {
|
|
|
1233
1255
|
try {
|
|
1234
1256
|
const req = buildEventsRequest(target, events);
|
|
1235
1257
|
if (!req) return "refused";
|
|
1236
|
-
const
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
console.warn
|
|
1242
|
-
|
|
1243
|
-
|
|
1258
|
+
const outcome = await withDeadline(async (signal) => {
|
|
1259
|
+
const res = await fetch(req.url, { ...req.init, signal });
|
|
1260
|
+
if (!res || !res.ok) return "unreachable";
|
|
1261
|
+
const ack = await readEventsAck(res);
|
|
1262
|
+
if (!ack || ack.skipped <= 0) return "delivered";
|
|
1263
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
1264
|
+
console.warn(describeDiscarded(ack));
|
|
1265
|
+
}
|
|
1266
|
+
return "refused";
|
|
1267
|
+
});
|
|
1268
|
+
return outcome === DEADLINE_EXPIRED ? "unreachable" : outcome;
|
|
1244
1269
|
} catch {
|
|
1245
1270
|
return "unreachable";
|
|
1246
1271
|
}
|
|
@@ -3472,11 +3497,11 @@ var provenanceRegistry = () => {
|
|
|
3472
3497
|
return created;
|
|
3473
3498
|
};
|
|
3474
3499
|
var provenanceKey = (space, scope) => `${space}:${scope != null ? scope : "default"}`;
|
|
3500
|
+
var isUsableIdentityValue = (value) => typeof value === "string" && value.trim().length > 0;
|
|
3475
3501
|
var resolveIdentity = (input) => {
|
|
3476
3502
|
var _a;
|
|
3477
|
-
if (
|
|
3503
|
+
if (!isUsableIdentityValue(input.value)) return void 0;
|
|
3478
3504
|
const value = input.value.trim();
|
|
3479
|
-
if (!value) return void 0;
|
|
3480
3505
|
const durable = (_a = input.durable) != null ? _a : input.source === "host";
|
|
3481
3506
|
if (input.source === "host") {
|
|
3482
3507
|
provenanceRegistry().host.set(provenanceKey(input.space, input.scope), value);
|
|
@@ -3581,6 +3606,7 @@ var resetAutoDeviceKeys = () => {
|
|
|
3581
3606
|
registry.keys.clear();
|
|
3582
3607
|
registry.hydrating.clear();
|
|
3583
3608
|
(_a = registry.pending) == null ? void 0 : _a.clear();
|
|
3609
|
+
registry.ambient = void 0;
|
|
3584
3610
|
};
|
|
3585
3611
|
|
|
3586
3612
|
// src/analytics/currentSession.ts
|
|
@@ -3588,25 +3614,51 @@ var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
|
3588
3614
|
"@wireai/activation:currentSessionId"
|
|
3589
3615
|
);
|
|
3590
3616
|
var globalSlot = globalThis;
|
|
3617
|
+
var SESSION_ID_LISTENERS_SLOT = /* @__PURE__ */ Symbol.for(
|
|
3618
|
+
"@wireai/activation:currentSessionIdListeners"
|
|
3619
|
+
);
|
|
3620
|
+
var listenerSlot = globalThis;
|
|
3621
|
+
var listeners = () => {
|
|
3622
|
+
const existing = listenerSlot[SESSION_ID_LISTENERS_SLOT];
|
|
3623
|
+
if (existing) return existing;
|
|
3624
|
+
const created = /* @__PURE__ */ new Set();
|
|
3625
|
+
listenerSlot[SESSION_ID_LISTENERS_SLOT] = created;
|
|
3626
|
+
return created;
|
|
3627
|
+
};
|
|
3628
|
+
var writeCurrentSessionId = (id) => {
|
|
3629
|
+
if (globalSlot[CURRENT_SESSION_ID_SLOT] === id) return;
|
|
3630
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = id;
|
|
3631
|
+
for (const listener of Array.from(listeners())) {
|
|
3632
|
+
try {
|
|
3633
|
+
listener();
|
|
3634
|
+
} catch {
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
};
|
|
3591
3638
|
var setCurrentSessionId = (id) => {
|
|
3592
3639
|
if (typeof id === "string" && id.length > 0) {
|
|
3593
|
-
|
|
3640
|
+
writeCurrentSessionId(id);
|
|
3594
3641
|
}
|
|
3595
3642
|
};
|
|
3596
3643
|
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
3597
3644
|
var resetCurrentSessionId = () => {
|
|
3598
|
-
|
|
3645
|
+
writeCurrentSessionId(void 0);
|
|
3599
3646
|
};
|
|
3600
3647
|
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.";
|
|
3601
3648
|
var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
3602
3649
|
"@wireai/activation:currentSessionIdMintWarned"
|
|
3603
3650
|
);
|
|
3604
3651
|
var warnSlot = globalThis;
|
|
3652
|
+
var MINTED_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
3653
|
+
"@wireai/activation:mintedSessionId"
|
|
3654
|
+
);
|
|
3655
|
+
var mintedSlot = globalThis;
|
|
3605
3656
|
var ensureCurrentSessionId = () => {
|
|
3606
3657
|
const existing = globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
3607
3658
|
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
3608
3659
|
const minted = makeSessionId();
|
|
3609
|
-
|
|
3660
|
+
writeCurrentSessionId(minted);
|
|
3661
|
+
mintedSlot[MINTED_SESSION_ID_SLOT] = minted;
|
|
3610
3662
|
if (!warnSlot[MINT_WARNED_SLOT] && warnInDev(MINT_WARNING)) {
|
|
3611
3663
|
warnSlot[MINT_WARNED_SLOT] = true;
|
|
3612
3664
|
}
|
|
@@ -4446,17 +4498,31 @@ var failOpen = async (storage, key) => {
|
|
|
4446
4498
|
const cached2 = await readCachedFeatures(storage, key);
|
|
4447
4499
|
return (_a = cached2 == null ? void 0 : cached2.features) != null ? _a : defaultWireFeatures;
|
|
4448
4500
|
};
|
|
4449
|
-
var
|
|
4501
|
+
var NO_ANSWER = /* @__PURE__ */ Symbol("wire-features-no-answer");
|
|
4502
|
+
var fetchFeaturesJson = async (url, apiKey, timeoutMs) => {
|
|
4450
4503
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4504
|
+
let timer;
|
|
4505
|
+
const expired = new Promise((resolve) => {
|
|
4506
|
+
timer = setTimeout(() => {
|
|
4507
|
+
controller == null ? void 0 : controller.abort();
|
|
4508
|
+
resolve(NO_ANSWER);
|
|
4509
|
+
}, timeoutMs);
|
|
4510
|
+
});
|
|
4511
|
+
const exchange = (async () => {
|
|
4512
|
+
const res = await fetch(url, {
|
|
4454
4513
|
method: "GET",
|
|
4455
4514
|
headers: { Accept: "application/json", Authorization: `Bearer ${apiKey}` },
|
|
4456
4515
|
signal: controller == null ? void 0 : controller.signal
|
|
4457
4516
|
});
|
|
4517
|
+
if (!res || !res.ok) return NO_ANSWER;
|
|
4518
|
+
return await res.json();
|
|
4519
|
+
})();
|
|
4520
|
+
try {
|
|
4521
|
+
return await Promise.race([exchange, expired]);
|
|
4458
4522
|
} finally {
|
|
4459
4523
|
clearTimeout(timer);
|
|
4524
|
+
void exchange.catch(() => {
|
|
4525
|
+
});
|
|
4460
4526
|
}
|
|
4461
4527
|
};
|
|
4462
4528
|
var fetchWireFeatures = async (config) => {
|
|
@@ -4466,9 +4532,8 @@ var fetchWireFeatures = async (config) => {
|
|
|
4466
4532
|
const url = `${config.serverUrl.replace(/\/$/, "")}/v1/features`;
|
|
4467
4533
|
const timeoutMs = config.timeoutMs && config.timeoutMs > 0 ? config.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
4468
4534
|
try {
|
|
4469
|
-
const
|
|
4470
|
-
if (
|
|
4471
|
-
const json = await res.json();
|
|
4535
|
+
const json = await fetchFeaturesJson(url, config.apiKey, timeoutMs);
|
|
4536
|
+
if (json === NO_ANSWER) return failOpen(storage, key);
|
|
4472
4537
|
const features = parseWireFeatures(json);
|
|
4473
4538
|
if (storage) writeCachedFeatures(storage, key, features);
|
|
4474
4539
|
return features;
|
|
@@ -4482,50 +4547,65 @@ var useStableValue = (value, isEqual) => {
|
|
|
4482
4547
|
return ref.current;
|
|
4483
4548
|
};
|
|
4484
4549
|
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);
|
|
4485
|
-
var
|
|
4550
|
+
var useWireFeaturesState = (config) => {
|
|
4486
4551
|
const stableConfig = useStableValue(config, sameFetchKey);
|
|
4487
4552
|
const [flags, setFlags] = useState(defaultWireFeatures);
|
|
4488
4553
|
const canFetch = !!(stableConfig == null ? void 0 : stableConfig.serverUrl) && !!(stableConfig == null ? void 0 : stableConfig.apiKey);
|
|
4554
|
+
const [settled, setSettled] = useState(!canFetch);
|
|
4489
4555
|
useEffect(() => {
|
|
4490
4556
|
if (!canFetch) {
|
|
4491
4557
|
setFlags((prev) => featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures);
|
|
4558
|
+
setSettled(true);
|
|
4492
4559
|
return;
|
|
4493
4560
|
}
|
|
4494
4561
|
let alive = true;
|
|
4495
4562
|
void fetchWireFeatures(stableConfig).then((next) => {
|
|
4496
4563
|
if (!alive) return;
|
|
4497
4564
|
setFlags((prev) => featuresEqual(prev, next) ? prev : next);
|
|
4565
|
+
setSettled(true);
|
|
4498
4566
|
});
|
|
4499
4567
|
return () => {
|
|
4500
4568
|
alive = false;
|
|
4501
4569
|
};
|
|
4502
4570
|
}, [canFetch, stableConfig]);
|
|
4503
|
-
return flags;
|
|
4571
|
+
return useMemo(() => ({ flags, settled }), [flags, settled]);
|
|
4504
4572
|
};
|
|
4573
|
+
var useWireFeatures = (config) => useWireFeaturesState(config).flags;
|
|
4505
4574
|
var CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.context");
|
|
4506
|
-
var
|
|
4507
|
-
if (!
|
|
4508
|
-
|
|
4575
|
+
var featuresContextGlobal = globalThis;
|
|
4576
|
+
if (!featuresContextGlobal[CONTEXT_SYMBOL]) {
|
|
4577
|
+
featuresContextGlobal[CONTEXT_SYMBOL] = createContext(null);
|
|
4509
4578
|
}
|
|
4510
|
-
var WireFeaturesContext =
|
|
4579
|
+
var WireFeaturesContext = featuresContextGlobal[CONTEXT_SYMBOL];
|
|
4580
|
+
var SETTLED_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.settled.context");
|
|
4581
|
+
var settledContextGlobal = globalThis;
|
|
4582
|
+
if (!settledContextGlobal[SETTLED_SYMBOL]) {
|
|
4583
|
+
settledContextGlobal[SETTLED_SYMBOL] = createContext(true);
|
|
4584
|
+
}
|
|
4585
|
+
var WireFeaturesSettledContext = settledContextGlobal[SETTLED_SYMBOL];
|
|
4511
4586
|
var WireFeaturesProvider = ({
|
|
4512
4587
|
config,
|
|
4513
4588
|
flags,
|
|
4514
4589
|
children
|
|
4515
4590
|
}) => {
|
|
4516
|
-
const fetched =
|
|
4517
|
-
const value = flags != null ? flags : fetched;
|
|
4591
|
+
const fetched = useWireFeaturesState(flags ? void 0 : config);
|
|
4592
|
+
const value = flags != null ? flags : fetched.flags;
|
|
4593
|
+
const settled = flags ? true : fetched.settled;
|
|
4518
4594
|
const stable = useMemo(() => value, [value]);
|
|
4519
|
-
return /* @__PURE__ */ jsx(WireFeaturesContext.Provider, { value: stable, children });
|
|
4595
|
+
return /* @__PURE__ */ jsx(WireFeaturesSettledContext.Provider, { value: settled, children: /* @__PURE__ */ jsx(WireFeaturesContext.Provider, { value: stable, children }) });
|
|
4520
4596
|
};
|
|
4521
4597
|
WireFeaturesProvider.displayName = "WireFeaturesProvider";
|
|
4522
4598
|
var useWireFeaturesContext = () => useContext(WireFeaturesContext);
|
|
4523
|
-
var useResolvedFeatures = (options) =>
|
|
4599
|
+
var useResolvedFeatures = (options) => useResolvedFeaturesState(options).flags;
|
|
4600
|
+
var useResolvedFeaturesState = (options) => {
|
|
4524
4601
|
var _a, _b, _c;
|
|
4525
4602
|
const ctx = useWireFeaturesContext();
|
|
4603
|
+
const ctxSettled = useContext(WireFeaturesSettledContext);
|
|
4526
4604
|
const shouldFetch = !(options == null ? void 0 : options.flags) && ctx == null;
|
|
4527
|
-
const fetched =
|
|
4528
|
-
|
|
4605
|
+
const fetched = useWireFeaturesState(shouldFetch ? options == null ? void 0 : options.config : void 0);
|
|
4606
|
+
const flags = (_c = (_b = (_a = options == null ? void 0 : options.flags) != null ? _a : ctx) != null ? _b : fetched.flags) != null ? _c : defaultWireFeatures;
|
|
4607
|
+
const settled = (options == null ? void 0 : options.flags) ? true : ctx != null ? ctxSettled : fetched.settled;
|
|
4608
|
+
return useMemo(() => ({ flags, settled }), [flags, settled]);
|
|
4529
4609
|
};
|
|
4530
4610
|
|
|
4531
4611
|
// src/config/wireConfigFromEnv.ts
|
|
@@ -5269,8 +5349,12 @@ var useSessionStart = (config, options = {}) => {
|
|
|
5269
5349
|
fireOpen(void 0);
|
|
5270
5350
|
return;
|
|
5271
5351
|
}
|
|
5272
|
-
void
|
|
5352
|
+
void withTimeout(
|
|
5353
|
+
hydrateDeviceIdentity({ appId: cfg.appId, storage: cfg.storage }),
|
|
5354
|
+
READ_TIMEOUT_MS
|
|
5355
|
+
).then((read) => {
|
|
5273
5356
|
if (cancelled) return;
|
|
5357
|
+
const identity = read === READ_TIMED_OUT ? void 0 : read;
|
|
5274
5358
|
fireOpen((identity == null ? void 0 : identity.durable) ? identity.value : void 0);
|
|
5275
5359
|
});
|
|
5276
5360
|
};
|
|
@@ -5370,8 +5454,9 @@ var emitFirstOpen = (opts) => {
|
|
|
5370
5454
|
routeLifecycleEvent(buildLifecycleEvent(FIRST_OPEN_EVENT, opts), opts);
|
|
5371
5455
|
};
|
|
5372
5456
|
var reportFirstOpen = (opts) => {
|
|
5373
|
-
var _a;
|
|
5374
|
-
|
|
5457
|
+
var _a, _b;
|
|
5458
|
+
if (!opts.sink && !((_a = opts.target) == null ? void 0 : _a.serverUrl)) return;
|
|
5459
|
+
const appId = (_b = opts.appId) != null ? _b : "default";
|
|
5375
5460
|
if (_firstOpenLatched.has(appId)) return;
|
|
5376
5461
|
_firstOpenLatched.add(appId);
|
|
5377
5462
|
const storage = opts.storage;
|
|
@@ -5456,8 +5541,12 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5456
5541
|
fire(void 0);
|
|
5457
5542
|
return;
|
|
5458
5543
|
}
|
|
5459
|
-
void
|
|
5544
|
+
void withTimeout(
|
|
5545
|
+
hydrateDeviceIdentity({ appId: cfg.appId, storage: cfg.storage }),
|
|
5546
|
+
READ_TIMEOUT_MS
|
|
5547
|
+
).then((read) => {
|
|
5460
5548
|
if (cancelled) return;
|
|
5549
|
+
const identity = read === READ_TIMED_OUT ? void 0 : read;
|
|
5461
5550
|
fire((identity == null ? void 0 : identity.durable) ? identity.value : void 0);
|
|
5462
5551
|
});
|
|
5463
5552
|
};
|