flowgrid-sdk 1.6.7 → 1.6.8
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/README.md +58 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +3 -0
- package/dist/browser.js.map +1 -1
- package/dist/flowgrid.min.js +1 -1
- package/dist/flowgrid.min.js.map +1 -1
- package/dist/lib/features/analytics/identify.d.ts +27 -0
- package/dist/lib/features/analytics/identify.js +193 -0
- package/dist/lib/features/analytics/identify.js.map +1 -1
- package/dist/lib/features/analytics/index.d.ts +1 -1
- package/dist/lib/features/core/index.d.ts +1 -1
- package/dist/lib/features/core/track-feature.d.ts +31 -0
- package/dist/lib/features/core/track-feature.js +67 -0
- package/dist/lib/features/core/track-feature.js.map +1 -1
- package/dist/lib/flowgrid.d.ts +17 -0
- package/dist/lib/flowgrid.js +32 -0
- package/dist/lib/flowgrid.js.map +1 -1
- package/dist/lib/tracking/core/feature.d.ts +3 -1
- package/dist/lib/tracking/core/feature.js +3 -1
- package/dist/lib/tracking/core/feature.js.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
45
|
import { FlowGridTransport } from "../../client/transport";
|
|
46
|
+
import { type Teardown } from "../../utils/browser";
|
|
46
47
|
import { UserTraits, MoneyValue, TimeSeriesData } from "../../types";
|
|
47
48
|
/**
|
|
48
49
|
* Configuration for identifying a user.
|
|
@@ -55,6 +56,15 @@ export interface IdentifyConfig {
|
|
|
55
56
|
/** User traits/properties */
|
|
56
57
|
traits?: UserTraits;
|
|
57
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Options for {@link Identify.autoTrackForms} passive identity capture.
|
|
61
|
+
*/
|
|
62
|
+
export interface PassiveIdentityOptions {
|
|
63
|
+
/** Debounce for `input` events before reading a field (ms, default 800). */
|
|
64
|
+
debounceMs?: number;
|
|
65
|
+
/** Extra `path` regex on which NO inputs are captured (replaces the default). */
|
|
66
|
+
excludePath?: RegExp;
|
|
67
|
+
}
|
|
58
68
|
/**
|
|
59
69
|
* Configuration for aliasing user IDs.
|
|
60
70
|
*/
|
|
@@ -249,5 +259,22 @@ export declare class Identify extends FlowGridTransport {
|
|
|
249
259
|
* @returns Promise resolving to reset response
|
|
250
260
|
*/
|
|
251
261
|
reset(userId: string): Promise<IdentifyResponse>;
|
|
262
|
+
/**
|
|
263
|
+
* Passive identity capture — the browser-side form-field tracker (parity with
|
|
264
|
+
* `flowgrid.js`'s `setupPassiveIdentityCapture`). Watches inputs (initial scan
|
|
265
|
+
* + a MutationObserver for fields added by modals/SPAs), classifies each by
|
|
266
|
+
* name/id/placeholder/autocomplete, hashes PII (SHA-256) before it leaves the
|
|
267
|
+
* browser, and posts the captured identity to the flowgrid-core identity store
|
|
268
|
+
* (`POST /api/v1/identity/identify`) so the dashboard can map the anonymous
|
|
269
|
+
* `visitor_id` to a person — and, with a CRM connected, to a known customer.
|
|
270
|
+
*
|
|
271
|
+
* Privacy: requires `analytics` consent; never attaches to password/card/cvv/
|
|
272
|
+
* ssn/pin fields or anything marked `data-analytics="ignore"` / `.no-track`;
|
|
273
|
+
* skips sensitive paths (checkout/account-settings/admin). Off by default in
|
|
274
|
+
* {@link FlowGrid.autoTrack} — enable with `autoTrack({ passiveIdentity: true })`.
|
|
275
|
+
*
|
|
276
|
+
* @returns a teardown that detaches every listener (no-op on the server).
|
|
277
|
+
*/
|
|
278
|
+
autoTrackForms(options?: PassiveIdentityOptions): Teardown;
|
|
252
279
|
}
|
|
253
280
|
export default Identify;
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
47
|
exports.Identify = void 0;
|
|
48
48
|
const transport_1 = require("../../client/transport");
|
|
49
|
+
const browser_1 = require("../../utils/browser");
|
|
49
50
|
// ============================================================================
|
|
50
51
|
// IDENTIFY CLASS
|
|
51
52
|
// ============================================================================
|
|
@@ -211,6 +212,198 @@ class Identify extends transport_1.FlowGridTransport {
|
|
|
211
212
|
timestamp: new Date().toISOString()
|
|
212
213
|
});
|
|
213
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Passive identity capture — the browser-side form-field tracker (parity with
|
|
217
|
+
* `flowgrid.js`'s `setupPassiveIdentityCapture`). Watches inputs (initial scan
|
|
218
|
+
* + a MutationObserver for fields added by modals/SPAs), classifies each by
|
|
219
|
+
* name/id/placeholder/autocomplete, hashes PII (SHA-256) before it leaves the
|
|
220
|
+
* browser, and posts the captured identity to the flowgrid-core identity store
|
|
221
|
+
* (`POST /api/v1/identity/identify`) so the dashboard can map the anonymous
|
|
222
|
+
* `visitor_id` to a person — and, with a CRM connected, to a known customer.
|
|
223
|
+
*
|
|
224
|
+
* Privacy: requires `analytics` consent; never attaches to password/card/cvv/
|
|
225
|
+
* ssn/pin fields or anything marked `data-analytics="ignore"` / `.no-track`;
|
|
226
|
+
* skips sensitive paths (checkout/account-settings/admin). Off by default in
|
|
227
|
+
* {@link FlowGrid.autoTrack} — enable with `autoTrack({ passiveIdentity: true })`.
|
|
228
|
+
*
|
|
229
|
+
* @returns a teardown that detaches every listener (no-op on the server).
|
|
230
|
+
*/
|
|
231
|
+
autoTrackForms(options = {}) {
|
|
232
|
+
if (!(0, browser_1.isBrowser)())
|
|
233
|
+
return () => { };
|
|
234
|
+
if (!transport_1.FlowGridTransport.hasConsent("analytics"))
|
|
235
|
+
return () => { };
|
|
236
|
+
const win = (0, browser_1.getWindow)();
|
|
237
|
+
const doc = (0, browser_1.getDocument)();
|
|
238
|
+
const debounceMs = options.debounceMs ?? 800;
|
|
239
|
+
const EXCLUDED_PATH_RE = options.excludePath ?? /\/(checkout|account\/settings|admin)(\/|$)/i;
|
|
240
|
+
const BLOCKED_RE = /(password|cardnumber|creditcard|\bcard\b|cvv|cvc|ssn|\bpin\b|securitycode)/i;
|
|
241
|
+
const SKIP_RE = /(message|comment|note|feedback|search|query)/i;
|
|
242
|
+
const FIELD_MAP = [
|
|
243
|
+
{ re: /(email|emailaddress)/, trait: "email", pii: true, identify: true },
|
|
244
|
+
{ re: /(phone|tel|mobile|cell)/, trait: "phone", pii: true, identify: false },
|
|
245
|
+
{ re: /(firstname|fname|givenname)/, trait: "firstName", pii: false, identify: false },
|
|
246
|
+
{ re: /(lastname|lname|surname|familyname)/, trait: "lastName", pii: false, identify: false },
|
|
247
|
+
{ re: /(company|organi[sz]ation|businessname)/, trait: "company", pii: false, identify: false },
|
|
248
|
+
{ re: /(streetaddress|addressline|^address$|^street$)/, trait: "address", pii: false, identify: false },
|
|
249
|
+
{ re: /(^city$|town)/, trait: "city", pii: false, identify: false },
|
|
250
|
+
{ re: /(postcode|postalcode|zipcode|^zip$)/, trait: "postcode", pii: false, identify: false },
|
|
251
|
+
// A single full-name / username field maps to firstName; listed last so
|
|
252
|
+
// dedicated first/last-name fields win when both exist.
|
|
253
|
+
{ re: /(fullname|^name$|username|yourname|contactname|displayname)/, trait: "firstName", pii: false, identify: false },
|
|
254
|
+
];
|
|
255
|
+
const captured = {};
|
|
256
|
+
const seen = new WeakSet();
|
|
257
|
+
const debounceTimers = new WeakMap();
|
|
258
|
+
const teardowns = [];
|
|
259
|
+
const fieldEl = (el) => el;
|
|
260
|
+
const normalize = (el) => [fieldEl(el).name, el.id, el.getAttribute("placeholder"), el.getAttribute("autocomplete")]
|
|
261
|
+
.filter(Boolean).join(" ").toLowerCase().replace(/[\s_-]/g, "");
|
|
262
|
+
const isBlocked = (el, key) => {
|
|
263
|
+
const input = fieldEl(el);
|
|
264
|
+
if (input.type === "password" || input.type === "hidden")
|
|
265
|
+
return true;
|
|
266
|
+
if (el.getAttribute("data-analytics") === "ignore")
|
|
267
|
+
return true;
|
|
268
|
+
if (el.classList && el.classList.contains("no-track"))
|
|
269
|
+
return true;
|
|
270
|
+
return BLOCKED_RE.test(key);
|
|
271
|
+
};
|
|
272
|
+
const classify = (el) => {
|
|
273
|
+
const key = normalize(el);
|
|
274
|
+
if (!key)
|
|
275
|
+
return null;
|
|
276
|
+
if (isBlocked(el, key))
|
|
277
|
+
return { blocked: true };
|
|
278
|
+
if (SKIP_RE.test(key))
|
|
279
|
+
return null;
|
|
280
|
+
return FIELD_MAP.find((f) => f.re.test(key)) ?? null;
|
|
281
|
+
};
|
|
282
|
+
const sha256hex = async (value) => {
|
|
283
|
+
try {
|
|
284
|
+
const enc = new TextEncoder().encode(String(value).trim().toLowerCase());
|
|
285
|
+
const buf = await win.crypto.subtle.digest("SHA-256", enc);
|
|
286
|
+
return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const resolveVisitorId = () => (this.identityManager ? this.identityManager.getVisitorId() : this.visitorId) || "";
|
|
293
|
+
const resolveSessionId = () => this.identityManager ? this.identityManager.getSessionId() : null;
|
|
294
|
+
// Posts the captured identity to the flowgrid-core identity store. Raw email
|
|
295
|
+
// stays server-side (CRM enrichment + dashboard display); the analytics
|
|
296
|
+
// event store only ever sees hashes (the `field_captured` mirror below).
|
|
297
|
+
const sendIdentity = () => {
|
|
298
|
+
const visitorId = resolveVisitorId();
|
|
299
|
+
if (!this.webId || !visitorId)
|
|
300
|
+
return;
|
|
301
|
+
if (!captured.email && !captured.emailHash && !captured.firstName && !captured.lastName)
|
|
302
|
+
return;
|
|
303
|
+
const traits = {
|
|
304
|
+
email: captured.email ?? null,
|
|
305
|
+
email_hash: captured.emailHash ?? null,
|
|
306
|
+
phone_hash: captured.phoneHash ?? null,
|
|
307
|
+
first_name: captured.firstName ?? null,
|
|
308
|
+
last_name: captured.lastName ?? null,
|
|
309
|
+
company: captured.company ?? null,
|
|
310
|
+
address: captured.address ?? null,
|
|
311
|
+
city: captured.city ?? null,
|
|
312
|
+
postcode: captured.postcode ?? null,
|
|
313
|
+
};
|
|
314
|
+
const url = `${this.endpoint}/api/v1/identity/identify`;
|
|
315
|
+
const payload = JSON.stringify({
|
|
316
|
+
web_id: this.webId,
|
|
317
|
+
visitor_id: visitorId,
|
|
318
|
+
session_id: resolveSessionId(),
|
|
319
|
+
path_name: win.location.pathname,
|
|
320
|
+
traits,
|
|
321
|
+
});
|
|
322
|
+
try {
|
|
323
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon &&
|
|
324
|
+
navigator.sendBeacon(url, new Blob([payload], { type: "application/json" }))) {
|
|
325
|
+
// sent via beacon
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
void fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: payload, keepalive: true }).catch(() => { });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
catch { /* swallow */ }
|
|
332
|
+
};
|
|
333
|
+
const captureField = (el) => {
|
|
334
|
+
const cls = classify(el);
|
|
335
|
+
if (!cls || "blocked" in cls)
|
|
336
|
+
return;
|
|
337
|
+
const value = (fieldEl(el).value || "").trim();
|
|
338
|
+
if (!value)
|
|
339
|
+
return;
|
|
340
|
+
if (cls.pii) {
|
|
341
|
+
const toHash = cls.trait === "phone" ? value.replace(/[^\d+]/g, "") : value;
|
|
342
|
+
void sha256hex(toHash).then((hash) => {
|
|
343
|
+
if (cls.trait === "email") {
|
|
344
|
+
captured.email = value;
|
|
345
|
+
if (hash)
|
|
346
|
+
captured.emailHash = hash;
|
|
347
|
+
}
|
|
348
|
+
else if (cls.trait === "phone" && hash) {
|
|
349
|
+
captured.phoneHash = hash;
|
|
350
|
+
}
|
|
351
|
+
if (cls.identify || captured.email)
|
|
352
|
+
sendIdentity();
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
captured[cls.trait] = value;
|
|
357
|
+
if (captured.email)
|
|
358
|
+
sendIdentity();
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
const attach = (el) => {
|
|
362
|
+
if (seen.has(el))
|
|
363
|
+
return;
|
|
364
|
+
const cls = classify(el);
|
|
365
|
+
if (!cls || "blocked" in cls)
|
|
366
|
+
return; // never listen to blocked / unknown fields
|
|
367
|
+
seen.add(el);
|
|
368
|
+
const onBlur = () => captureField(el);
|
|
369
|
+
const onInput = () => {
|
|
370
|
+
clearTimeout(debounceTimers.get(el));
|
|
371
|
+
debounceTimers.set(el, setTimeout(() => captureField(el), debounceMs));
|
|
372
|
+
};
|
|
373
|
+
el.addEventListener("blur", onBlur, { passive: true });
|
|
374
|
+
el.addEventListener("input", onInput, { passive: true });
|
|
375
|
+
teardowns.push(() => {
|
|
376
|
+
el.removeEventListener("blur", onBlur);
|
|
377
|
+
el.removeEventListener("input", onInput);
|
|
378
|
+
});
|
|
379
|
+
};
|
|
380
|
+
const scanPage = () => {
|
|
381
|
+
if (EXCLUDED_PATH_RE.test(win.location.pathname))
|
|
382
|
+
return;
|
|
383
|
+
doc.querySelectorAll("input, textarea, select").forEach(attach);
|
|
384
|
+
};
|
|
385
|
+
scanPage();
|
|
386
|
+
let mo = null;
|
|
387
|
+
try {
|
|
388
|
+
mo = new MutationObserver(() => scanPage());
|
|
389
|
+
if (doc.body)
|
|
390
|
+
mo.observe(doc.body, { childList: true, subtree: true });
|
|
391
|
+
}
|
|
392
|
+
catch { /* MutationObserver unavailable */ }
|
|
393
|
+
return () => {
|
|
394
|
+
if (mo)
|
|
395
|
+
try {
|
|
396
|
+
mo.disconnect();
|
|
397
|
+
}
|
|
398
|
+
catch { /* noop */ }
|
|
399
|
+
for (const t of teardowns) {
|
|
400
|
+
try {
|
|
401
|
+
t();
|
|
402
|
+
}
|
|
403
|
+
catch { /* noop */ }
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
214
407
|
}
|
|
215
408
|
exports.Identify = Identify;
|
|
216
409
|
exports.default = Identify;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identify.js","sourceRoot":"","sources":["../../../../src/lib/features/analytics/identify.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;;;AAEH,sDAA2D;
|
|
1
|
+
{"version":3,"file":"identify.js","sourceRoot":"","sources":["../../../../src/lib/features/analytics/identify.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,7 +16,7 @@ export type { ReplayPrivacyMetadata, SessionReplayDropMetadata, SessionReplayErr
|
|
|
16
16
|
export type { PageViewConfig, ScrollDepthConfig, TimeOnPageConfig, PageViewResponse, PageAnalyticsData } from './page-views';
|
|
17
17
|
export type { SessionStartConfig, SessionResponse, SessionEndResponse, SessionAnalyticsData, RealTimeSessionData } from './sessions';
|
|
18
18
|
export type { EventTrackConfig, BatchEventConfig, EventResponse, BatchEventResponse, EventAnalyticsData } from './events';
|
|
19
|
-
export type { IdentifyConfig, AliasConfig, GroupConfig, IdentifyResponse, UserAnalyticsData } from './identify';
|
|
19
|
+
export type { IdentifyConfig, AliasConfig, GroupConfig, IdentifyResponse, UserAnalyticsData, PassiveIdentityOptions } from './identify';
|
|
20
20
|
export type { FunnelDefinition, FunnelStepConfig, FunnelStepResponse, FunnelAnalyticsData, FunnelComparisonData } from './funnels';
|
|
21
21
|
export type { ActivityConfig, CohortAnalysisConfig, ActivityResponse, CohortAnalysisData, StickinessData, ChurnAnalysisData } from './retention';
|
|
22
22
|
export type { AttributionModel, TouchpointConfig, ConversionEvent, TouchpointResponse, AttributionReportData } from './attribution';
|
|
@@ -7,6 +7,6 @@ export { TrackFeature } from './track-feature';
|
|
|
7
7
|
export { TrackPrompt } from './track-prompt';
|
|
8
8
|
export { Experiment } from './experiment';
|
|
9
9
|
export type { ActivationConfig, OnboardingStepConfig, FirstActionConfig, ActivationResponse, ActivationAnalyticsData } from './activation';
|
|
10
|
-
export type { FeatureConfig, FeatureUsageConfig, FeatureDiscoveryConfig, FeatureResponse, FeatureAnalyticsData } from './track-feature';
|
|
10
|
+
export type { FeatureConfig, FeatureUsageConfig, FeatureDiscoveryConfig, FeatureResponse, FeatureAnalyticsData, SmartFeatureOptions, FeatureStage } from './track-feature';
|
|
11
11
|
export type { PromptConfig, PromptSubmissionConfig, PromptResponseConfig, PromptFeedbackConfig, PromptResponse, PromptAnalyticsData } from './track-prompt';
|
|
12
12
|
export type { ExperimentConfig, ExperimentDefinition, ExperimentAssignmentConfig, ExperimentConversionConfig, ExperimentResponse, ExperimentResultsData, ExperimentAnalyticsData, ExperimentVariant, ExperimentInitConfig, VariantResponse } from './experiment';
|
|
@@ -39,6 +39,25 @@
|
|
|
39
39
|
*/
|
|
40
40
|
import { FlowGridTransport } from "../../client/transport";
|
|
41
41
|
import { TimeSeriesData } from "../../types";
|
|
42
|
+
/** Lifecycle stage the dashboard's adoption funnel reads (event_subtype). */
|
|
43
|
+
export type FeatureStage = "discovery" | "viewed" | "adoption" | "completed" | "abandonment";
|
|
44
|
+
/** Options for the intelligent one-liner {@link TrackFeature.feature}. */
|
|
45
|
+
export interface SmartFeatureOptions {
|
|
46
|
+
/** Human label; defaults to a humanised `featureId`. */
|
|
47
|
+
name?: string;
|
|
48
|
+
/** Feature category (e.g. "Automation"). */
|
|
49
|
+
category?: string;
|
|
50
|
+
/** Mark this interaction as a successful completion of the feature's job. */
|
|
51
|
+
completed?: boolean;
|
|
52
|
+
/** Mark this interaction as abandoned (started but gave up). */
|
|
53
|
+
abandoned?: boolean;
|
|
54
|
+
/** Distinct days of use before a visitor counts as "adopted" (default 2). */
|
|
55
|
+
adoptionDays?: number;
|
|
56
|
+
/** Total uses before a visitor counts as "adopted" (default 5). */
|
|
57
|
+
adoptionUses?: number;
|
|
58
|
+
/** Extra properties to attach. */
|
|
59
|
+
properties?: Record<string, unknown>;
|
|
60
|
+
}
|
|
42
61
|
type FeatureConfigProperties = {
|
|
43
62
|
featureId?: string;
|
|
44
63
|
featureName: string;
|
|
@@ -199,4 +218,16 @@ export declare class TrackFeature extends FlowGridTransport {
|
|
|
199
218
|
*/
|
|
200
219
|
trackUsage(config: FeatureUsageConfig): Promise<FeatureResponse>;
|
|
201
220
|
}
|
|
221
|
+
/** Humanises a feature id → display name ("ai-agent-builder" → "Ai Agent Builder"). */
|
|
222
|
+
export declare function humaniseFeatureId(id: string): string;
|
|
223
|
+
/**
|
|
224
|
+
* Derives the lifecycle stage(s) a single `feature()` call should emit, from
|
|
225
|
+
* per-visitor local state. Pure of any transport — the caller emits each stage
|
|
226
|
+
* via `FlowGrid.track("feature_usage", …)`. Persists the updated state.
|
|
227
|
+
*
|
|
228
|
+
* - first-ever use → ["discovery", "viewed"]
|
|
229
|
+
* - subsequent use → ["viewed"] (+ "adoption" once past the threshold)
|
|
230
|
+
* - `{ completed }` → ["completed"] `{ abandoned }` → ["abandonment"]
|
|
231
|
+
*/
|
|
232
|
+
export declare function deriveFeatureStages(stateKey: string, opts?: SmartFeatureOptions): FeatureStage[];
|
|
202
233
|
export default TrackFeature;
|
|
@@ -40,7 +40,10 @@
|
|
|
40
40
|
*/
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
42
|
exports.TrackFeature = void 0;
|
|
43
|
+
exports.humaniseFeatureId = humaniseFeatureId;
|
|
44
|
+
exports.deriveFeatureStages = deriveFeatureStages;
|
|
43
45
|
const transport_1 = require("../../client/transport");
|
|
46
|
+
const browser_1 = require("../../utils/browser");
|
|
44
47
|
// ============================================================================
|
|
45
48
|
// TRACK FEATURE CLASS
|
|
46
49
|
// ============================================================================
|
|
@@ -119,5 +122,69 @@ class TrackFeature extends transport_1.FlowGridTransport {
|
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
exports.TrackFeature = TrackFeature;
|
|
125
|
+
/** Humanises a feature id → display name ("ai-agent-builder" → "Ai Agent Builder"). */
|
|
126
|
+
function humaniseFeatureId(id) {
|
|
127
|
+
return id
|
|
128
|
+
.replace(/[-_]+/g, " ")
|
|
129
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
130
|
+
.replace(/\s+/g, " ")
|
|
131
|
+
.trim()
|
|
132
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
133
|
+
}
|
|
134
|
+
function readSmartState(key) {
|
|
135
|
+
if (!(0, browser_1.isBrowser)())
|
|
136
|
+
return null;
|
|
137
|
+
try {
|
|
138
|
+
const raw = (0, browser_1.getWindow)().localStorage.getItem(key);
|
|
139
|
+
if (!raw)
|
|
140
|
+
return null;
|
|
141
|
+
const v = JSON.parse(raw);
|
|
142
|
+
return { uses: Number(v.uses) || 0, days: Array.isArray(v.days) ? v.days : [], adopted: Boolean(v.adopted) };
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function writeSmartState(key, state) {
|
|
149
|
+
if (!(0, browser_1.isBrowser)())
|
|
150
|
+
return;
|
|
151
|
+
try {
|
|
152
|
+
(0, browser_1.getWindow)().localStorage.setItem(key, JSON.stringify(state));
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
/* storage unavailable — capture still works, just not stage derivation */
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Derives the lifecycle stage(s) a single `feature()` call should emit, from
|
|
160
|
+
* per-visitor local state. Pure of any transport — the caller emits each stage
|
|
161
|
+
* via `FlowGrid.track("feature_usage", …)`. Persists the updated state.
|
|
162
|
+
*
|
|
163
|
+
* - first-ever use → ["discovery", "viewed"]
|
|
164
|
+
* - subsequent use → ["viewed"] (+ "adoption" once past the threshold)
|
|
165
|
+
* - `{ completed }` → ["completed"] `{ abandoned }` → ["abandonment"]
|
|
166
|
+
*/
|
|
167
|
+
function deriveFeatureStages(stateKey, opts = {}) {
|
|
168
|
+
if (opts.completed)
|
|
169
|
+
return ["completed"];
|
|
170
|
+
if (opts.abandoned)
|
|
171
|
+
return ["abandonment"];
|
|
172
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
173
|
+
const state = readSmartState(stateKey);
|
|
174
|
+
if (!state) {
|
|
175
|
+
writeSmartState(stateKey, { uses: 1, days: [today], adopted: false });
|
|
176
|
+
return ["discovery", "viewed"];
|
|
177
|
+
}
|
|
178
|
+
const stages = ["viewed"];
|
|
179
|
+
const days = state.days.includes(today) ? state.days : [...state.days, today];
|
|
180
|
+
const uses = state.uses + 1;
|
|
181
|
+
let adopted = state.adopted;
|
|
182
|
+
if (!adopted && (days.length >= (opts.adoptionDays ?? 2) || uses >= (opts.adoptionUses ?? 5))) {
|
|
183
|
+
stages.push("adoption");
|
|
184
|
+
adopted = true;
|
|
185
|
+
}
|
|
186
|
+
writeSmartState(stateKey, { uses, days, adopted });
|
|
187
|
+
return stages;
|
|
188
|
+
}
|
|
122
189
|
exports.default = TrackFeature;
|
|
123
190
|
//# sourceMappingURL=track-feature.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"track-feature.js","sourceRoot":"","sources":["../../../../src/lib/features/core/track-feature.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;;;
|
|
1
|
+
{"version":3,"file":"track-feature.js","sourceRoot":"","sources":["../../../../src/lib/features/core/track-feature.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/flowgrid.d.ts
CHANGED
|
@@ -946,6 +946,22 @@ export declare class FlowGrid {
|
|
|
946
946
|
* ```
|
|
947
947
|
*/
|
|
948
948
|
identifyUser(userId: string, traits?: Record<string, unknown>, anonymousId?: string): Promise<import("..").IdentifyResponse>;
|
|
949
|
+
/**
|
|
950
|
+
* Intelligent feature-usage one-liner. Call it wherever a feature is used —
|
|
951
|
+
* the SDK derives the discovered → viewed → adopted lifecycle per visitor
|
|
952
|
+
* (no need to hand-emit each stage). Pass `{ completed }` / `{ abandoned }`
|
|
953
|
+
* for terminal stages.
|
|
954
|
+
*
|
|
955
|
+
* Emission goes through the unified `.track()` router (`feature_usage`), so it
|
|
956
|
+
* shares the same routing/consent/transport as every other event.
|
|
957
|
+
*
|
|
958
|
+
* @example
|
|
959
|
+
* ```ts
|
|
960
|
+
* fg.feature("ai-agent-builder"); // used (auto stage)
|
|
961
|
+
* fg.feature("ai-agent-builder", { completed: true }); // finished the job
|
|
962
|
+
* ```
|
|
963
|
+
*/
|
|
964
|
+
feature(featureId: string, opts?: import("./features/core/track-feature").SmartFeatureOptions): void;
|
|
949
965
|
/**
|
|
950
966
|
* Track a page view. Auto-detects URL/path/title/referrer in the browser.
|
|
951
967
|
*
|
|
@@ -998,6 +1014,7 @@ export declare class FlowGrid {
|
|
|
998
1014
|
idleAfterMs?: number;
|
|
999
1015
|
pollMs?: number;
|
|
1000
1016
|
};
|
|
1017
|
+
passiveIdentity?: boolean | import("./features/analytics/identify").PassiveIdentityOptions;
|
|
1001
1018
|
}): () => void;
|
|
1002
1019
|
}
|
|
1003
1020
|
export default FlowGrid;
|
package/dist/lib/flowgrid.js
CHANGED
|
@@ -68,6 +68,7 @@ exports.FlowGrid = void 0;
|
|
|
68
68
|
const transport_1 = require("./client/transport");
|
|
69
69
|
const identity_1 = require("./utils/identity");
|
|
70
70
|
const core_1 = require("./features/core");
|
|
71
|
+
const track_feature_1 = require("./features/core/track-feature");
|
|
71
72
|
const analytics_1 = require("./features/analytics");
|
|
72
73
|
const ecommerce_1 = require("./features/ecommerce");
|
|
73
74
|
// Import directly from sub-barrels (not the top-level ./tracking barrel) so
|
|
@@ -955,6 +956,31 @@ class FlowGrid {
|
|
|
955
956
|
identifyUser(userId, traits, anonymousId) {
|
|
956
957
|
return this.identify.user(userId, traits, anonymousId);
|
|
957
958
|
}
|
|
959
|
+
/**
|
|
960
|
+
* Intelligent feature-usage one-liner. Call it wherever a feature is used —
|
|
961
|
+
* the SDK derives the discovered → viewed → adopted lifecycle per visitor
|
|
962
|
+
* (no need to hand-emit each stage). Pass `{ completed }` / `{ abandoned }`
|
|
963
|
+
* for terminal stages.
|
|
964
|
+
*
|
|
965
|
+
* Emission goes through the unified `.track()` router (`feature_usage`), so it
|
|
966
|
+
* shares the same routing/consent/transport as every other event.
|
|
967
|
+
*
|
|
968
|
+
* @example
|
|
969
|
+
* ```ts
|
|
970
|
+
* fg.feature("ai-agent-builder"); // used (auto stage)
|
|
971
|
+
* fg.feature("ai-agent-builder", { completed: true }); // finished the job
|
|
972
|
+
* ```
|
|
973
|
+
*/
|
|
974
|
+
feature(featureId, opts = {}) {
|
|
975
|
+
const featureName = opts.name ?? (0, track_feature_1.humaniseFeatureId)(featureId);
|
|
976
|
+
const visitorId = this.identityManager ? this.identityManager.getVisitorId() : (this.visitorId ?? "");
|
|
977
|
+
const stages = (0, track_feature_1.deriveFeatureStages)(`fg_feat:${visitorId}:${featureId}`, opts);
|
|
978
|
+
for (const stage of stages) {
|
|
979
|
+
// Only send what the caller provided — flowgrid-core fills userId
|
|
980
|
+
// (→ visitor_id) and a default category for anonymous feature usage.
|
|
981
|
+
this.track("feature_usage", { featureId, featureName, action: stage, ...(opts.category ? { category: opts.category } : {}), ...(opts.properties ?? {}) }, { domain: "feature" });
|
|
982
|
+
}
|
|
983
|
+
}
|
|
958
984
|
/**
|
|
959
985
|
* Track a page view. Auto-detects URL/path/title/referrer in the browser.
|
|
960
986
|
*
|
|
@@ -1026,6 +1052,12 @@ class FlowGrid {
|
|
|
1026
1052
|
if (options.attribution === true && ctx.userId) {
|
|
1027
1053
|
this.attribution.captureFromUrl(ctx.userId);
|
|
1028
1054
|
}
|
|
1055
|
+
// Passive identity capture is off by default — the flowgrid.js core script
|
|
1056
|
+
// already runs it for snippet installs; SDK-only sites opt in here.
|
|
1057
|
+
if (options.passiveIdentity === true || (options.passiveIdentity !== false && typeof options.passiveIdentity === "object")) {
|
|
1058
|
+
const piOpts = typeof options.passiveIdentity === "object" ? options.passiveIdentity : {};
|
|
1059
|
+
teardowns.push(this.identify.client.autoTrackForms(piOpts));
|
|
1060
|
+
}
|
|
1029
1061
|
if (options.replay !== false) {
|
|
1030
1062
|
const replayOpts = typeof options.replay === "object" ? options.replay : {};
|
|
1031
1063
|
// start() is async; track its resolved teardown so the outer stop()
|