@funnelsgrove/runtime 0.1.30 → 0.1.32

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.
@@ -10,9 +10,43 @@ export type FunnelFlowControllerExperiment<StepId extends string> = FunnelManife
10
10
  };
11
11
  type StepComponentRegistry = Record<string, unknown>;
12
12
  type FunnelFlowAnalyticsAdapter = {
13
+ trackStepStarted?: (input: {
14
+ stepId: string;
15
+ stepName: string;
16
+ stepType?: string;
17
+ startedAt: string;
18
+ featureFlags?: Record<string, string>;
19
+ }) => string | null;
20
+ trackStepCompleted?: (input: {
21
+ stepId: string;
22
+ stepName: string;
23
+ stepType?: string;
24
+ startedAt: string;
25
+ endedAt: string;
26
+ selected?: Record<string, unknown>;
27
+ featureFlags?: Record<string, string>;
28
+ }) => string | null;
29
+ trackStepEngaged?: (input: {
30
+ stepId: string;
31
+ stepName: string;
32
+ stepType?: string;
33
+ startedAt: string;
34
+ engagedAt: string;
35
+ durationMs: number;
36
+ engagementThresholdMs: number;
37
+ featureFlags?: Record<string, string>;
38
+ }) => string | null;
39
+ trackFunnelStarted?: (input: {
40
+ stepId: string;
41
+ stepName: string;
42
+ stepType?: string;
43
+ occurredAt: string;
44
+ featureFlags?: Record<string, string>;
45
+ }) => string | null;
13
46
  trackFirstStepViewed?: (input: {
14
47
  stepId: string;
15
48
  stepName: string;
49
+ stepType?: string;
16
50
  occurredAt: string;
17
51
  featureFlags?: Record<string, string>;
18
52
  }) => string | null;
@@ -331,6 +331,7 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
331
331
  });
332
332
  }, [isPreviewRuntime]);
333
333
  const recordStepCompletion = useCallback((stepId, explicitChoices) => {
334
+ var _a;
334
335
  const meta = stepById[stepId];
335
336
  if (!meta) {
336
337
  return;
@@ -354,19 +355,32 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
354
355
  completedAt: new Date().toISOString(),
355
356
  choices,
356
357
  };
358
+ const stepType = meta.type;
357
359
  const startedAt = stepStartedAtByIdRef.current[meta.id] || record.completedAt;
358
360
  const durationMs = Math.max(0, new Date(record.completedAt).getTime() - new Date(startedAt).getTime());
359
361
  dispatchWindowCustomEvent('funnel:step-completed', record);
360
362
  if (!isPreviewRuntime) {
361
- apiService.trackStepCompleted({
362
- userId: currentUserIdRef.current,
363
+ (_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepCompleted) === null || _a === void 0 ? void 0 : _a.call(analytics, {
363
364
  stepId: record.stepId,
364
365
  stepName: record.stepName,
366
+ stepType,
365
367
  startedAt,
366
368
  endedAt: record.completedAt,
367
369
  selected: record.choices,
370
+ featureFlags: postHogFeatureFlagsRef.current,
368
371
  });
369
- capturePostHogStepEvent('step_completed', Object.assign({ distinct_id: currentUserIdRef.current, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, project_id: PROJECT_ID || undefined, environment: runtimeMode, step_id: record.stepId, step_name: record.stepName, started_at: startedAt, ended_at: record.completedAt, duration_ms: Number.isFinite(durationMs) ? durationMs : undefined, selected: record.choices }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
372
+ if (!(analytics === null || analytics === void 0 ? void 0 : analytics.trackStepCompleted)) {
373
+ apiService.trackStepCompleted({
374
+ userId: currentUserIdRef.current,
375
+ stepId: record.stepId,
376
+ stepName: record.stepName,
377
+ stepType,
378
+ startedAt,
379
+ endedAt: record.completedAt,
380
+ selected: record.choices,
381
+ });
382
+ capturePostHogStepEvent('step_completed', Object.assign({ distinct_id: currentUserIdRef.current, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, project_id: PROJECT_ID || undefined, environment: runtimeMode, step_id: record.stepId, step_name: record.stepName, step_type: stepType, started_at: startedAt, ended_at: record.completedAt, duration_ms: Number.isFinite(durationMs) ? durationMs : undefined, selected: record.choices }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
383
+ }
370
384
  }
371
385
  setUser((prev) => {
372
386
  var _a, _b;
@@ -389,12 +403,12 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
389
403
  }
390
404
  return updatedUser;
391
405
  });
392
- }, [isPreviewRuntime, stepById]);
406
+ }, [analytics, isPreviewRuntime, runtimeMode, stepById]);
393
407
  const completeStep = useCallback((stepId, choices) => {
394
408
  recordStepCompletion(stepId, choices);
395
409
  }, [recordStepCompletion]);
396
410
  useEffect(() => {
397
- var _a;
411
+ var _a, _b, _c;
398
412
  if (safeActiveStepId !== activeStepId) {
399
413
  logger.warn(`[FunnelFlow] Unknown or non-renderable step "${activeStepId}", falling back to "${safeActiveStepId}".`);
400
414
  setActiveStepId(safeActiveStepId);
@@ -412,20 +426,39 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
412
426
  }
413
427
  const startedAt = new Date().toISOString();
414
428
  const stepName = (renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name) || (renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title) || renderedStepId;
429
+ const stepType = renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.type;
415
430
  stepStartedAtByIdRef.current[renderedStepId] = startedAt;
416
431
  if (!isPreviewRuntime) {
417
- apiService.trackStepStarted({
418
- userId: currentUserIdRef.current,
432
+ (_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepStarted) === null || _a === void 0 ? void 0 : _a.call(analytics, {
419
433
  stepId: renderedStepId,
420
434
  stepName,
435
+ stepType,
421
436
  startedAt,
437
+ featureFlags: postHogFeatureFlagsRef.current,
422
438
  });
423
- capturePostHogStepEvent('step_started', Object.assign({ distinct_id: currentUserIdRef.current, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, project_id: PROJECT_ID || undefined, environment: runtimeMode, step_id: renderedStepId, step_name: stepName, started_at: startedAt }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
439
+ if (!(analytics === null || analytics === void 0 ? void 0 : analytics.trackStepStarted)) {
440
+ apiService.trackStepStarted({
441
+ userId: currentUserIdRef.current,
442
+ stepId: renderedStepId,
443
+ stepName,
444
+ stepType,
445
+ startedAt,
446
+ });
447
+ capturePostHogStepEvent('step_started', Object.assign({ distinct_id: currentUserIdRef.current, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, project_id: PROJECT_ID || undefined, environment: runtimeMode, step_id: renderedStepId, step_name: stepName, step_type: stepType, started_at: startedAt }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
448
+ }
424
449
  if (!firstStepViewedTrackedRef.current && safeActiveStepId === defaultStepId) {
425
450
  firstStepViewedTrackedRef.current = true;
426
- (_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackFirstStepViewed) === null || _a === void 0 ? void 0 : _a.call(analytics, {
451
+ (_b = analytics === null || analytics === void 0 ? void 0 : analytics.trackFunnelStarted) === null || _b === void 0 ? void 0 : _b.call(analytics, {
427
452
  stepId: renderedStepId,
428
453
  stepName,
454
+ stepType,
455
+ occurredAt: startedAt,
456
+ featureFlags: postHogFeatureFlagsRef.current,
457
+ });
458
+ (_c = analytics === null || analytics === void 0 ? void 0 : analytics.trackFirstStepViewed) === null || _c === void 0 ? void 0 : _c.call(analytics, {
459
+ stepId: renderedStepId,
460
+ stepName,
461
+ stepType,
429
462
  occurredAt: startedAt,
430
463
  featureFlags: postHogFeatureFlagsRef.current,
431
464
  });
@@ -441,25 +474,40 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
441
474
  const engagedStepId = renderedStepId;
442
475
  const engagedStartedAt = startedAt;
443
476
  const engagedStepName = stepName;
477
+ const engagedStepType = stepType;
444
478
  const timer = window.setTimeout(() => {
479
+ var _a;
445
480
  if (prevStepIdRef.current !== engagedStepId || engagedStepIdsRef.current.has(engagedStepId)) {
446
481
  return;
447
482
  }
448
483
  engagedStepIdsRef.current.add(engagedStepId);
449
484
  const engagedAt = new Date().toISOString();
450
- apiService.trackFunnelEvent({
451
- userId: currentUserIdRef.current,
452
- eventType: 'step_engaged',
485
+ (_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepEngaged) === null || _a === void 0 ? void 0 : _a.call(analytics, {
453
486
  stepId: engagedStepId,
454
487
  stepName: engagedStepName,
488
+ stepType: engagedStepType,
455
489
  startedAt: engagedStartedAt,
456
- endedAt: engagedAt,
457
- metadata: {
458
- durationMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
459
- engagementThresholdMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
460
- },
490
+ engagedAt,
491
+ durationMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
492
+ engagementThresholdMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
493
+ featureFlags: postHogFeatureFlagsRef.current,
461
494
  });
462
- capturePostHogStepEvent('step_engaged', Object.assign({ distinct_id: currentUserIdRef.current, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, project_id: PROJECT_ID || undefined, environment: runtimeMode, step_id: engagedStepId, step_name: engagedStepName, started_at: engagedStartedAt, ended_at: engagedAt, duration_ms: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS, engagement_threshold_ms: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
495
+ if (!(analytics === null || analytics === void 0 ? void 0 : analytics.trackStepEngaged)) {
496
+ apiService.trackFunnelEvent({
497
+ userId: currentUserIdRef.current,
498
+ eventType: 'step_engaged',
499
+ stepId: engagedStepId,
500
+ stepName: engagedStepName,
501
+ stepType: engagedStepType,
502
+ startedAt: engagedStartedAt,
503
+ endedAt: engagedAt,
504
+ metadata: {
505
+ durationMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
506
+ engagementThresholdMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
507
+ },
508
+ });
509
+ capturePostHogStepEvent('step_engaged', Object.assign({ distinct_id: currentUserIdRef.current, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, project_id: PROJECT_ID || undefined, environment: runtimeMode, step_id: engagedStepId, step_name: engagedStepName, step_type: engagedStepType, started_at: engagedStartedAt, ended_at: engagedAt, duration_ms: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS, engagement_threshold_ms: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
510
+ }
463
511
  }, FIRST_STEP_ENGAGEMENT_THRESHOLD_MS);
464
512
  return () => window.clearTimeout(timer);
465
513
  }, [
@@ -472,6 +520,8 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
472
520
  renderedStepId,
473
521
  renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name,
474
522
  renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title,
523
+ renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.type,
524
+ runtimeMode,
475
525
  safeActiveStepId,
476
526
  ]);
477
527
  useEffect(() => {
@@ -16,6 +16,7 @@ export type FunnelEvent = {
16
16
  eventType: string;
17
17
  stepId?: string;
18
18
  stepName?: string;
19
+ stepType?: string;
19
20
  startedAt?: string;
20
21
  endedAt?: string;
21
22
  selected?: Record<string, unknown>;
@@ -25,6 +26,7 @@ export type StepCompletionEvent = {
25
26
  userId: string;
26
27
  stepId: string;
27
28
  stepName: string;
29
+ stepType?: string;
28
30
  startedAt: string;
29
31
  endedAt: string;
30
32
  selected: Record<string, unknown>;
@@ -104,6 +106,7 @@ declare class ApiService {
104
106
  userId: string;
105
107
  stepId: string;
106
108
  stepName: string;
109
+ stepType?: string;
107
110
  startedAt: string;
108
111
  }): void;
109
112
  trackStepCompleted(event: StepCompletionEvent): void;
@@ -126,6 +126,7 @@ const buildSdkEvent = (event) => {
126
126
  eventType: event.eventType,
127
127
  stepId: event.stepId || null,
128
128
  stepName: event.stepName || null,
129
+ stepType: event.stepType || null,
129
130
  startedAt: event.startedAt || null,
130
131
  endedAt: event.endedAt || null,
131
132
  selected: event.selected || {},
@@ -493,6 +494,7 @@ class ApiService {
493
494
  eventType: 'step_start',
494
495
  stepId: input.stepId,
495
496
  stepName: input.stepName,
497
+ stepType: input.stepType,
496
498
  startedAt: input.startedAt,
497
499
  });
498
500
  }
@@ -502,6 +504,7 @@ class ApiService {
502
504
  eventType: 'step_end',
503
505
  stepId: event.stepId,
504
506
  stepName: event.stepName,
507
+ stepType: event.stepType,
505
508
  startedAt: event.startedAt,
506
509
  endedAt: event.endedAt,
507
510
  selected: event.selected,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/runtime",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "main": "./dist/index.js",