@funnelsgrove/runtime 0.1.31 → 0.1.33

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.
@@ -1,5 +1,29 @@
1
1
  import type { FunnelStepKind, FunnelStepType } from '../steps/types.js';
2
2
  export type TemplateArchitectureVersion = number;
3
+ export type FunnelBreakpointName = 'small' | 'medium' | 'large' | 'desktop-small';
4
+ export type FunnelManifestBreakpoint = {
5
+ width: number;
6
+ height: number;
7
+ };
8
+ export type FunnelManifestBreakpoints = Record<FunnelBreakpointName, FunnelManifestBreakpoint>;
9
+ export declare const DEFAULT_FUNNEL_BREAKPOINTS: {
10
+ readonly small: {
11
+ readonly width: 375;
12
+ readonly height: 667;
13
+ };
14
+ readonly medium: {
15
+ readonly width: 393;
16
+ readonly height: 852;
17
+ };
18
+ readonly large: {
19
+ readonly width: 402;
20
+ readonly height: 874;
21
+ };
22
+ readonly 'desktop-small': {
23
+ readonly width: 1280;
24
+ readonly height: 800;
25
+ };
26
+ };
3
27
  export type FunnelManifestStep = {
4
28
  id: string;
5
29
  path: string;
@@ -59,6 +83,7 @@ export type FunnelManifest = {
59
83
  width: number;
60
84
  height: number;
61
85
  };
86
+ breakpoints?: FunnelManifestBreakpoints;
62
87
  assets: Record<string, FunnelManifestAsset>;
63
88
  steps: readonly FunnelManifestStep[];
64
89
  entryPoints?: readonly FunnelManifestEntryPoint[];
@@ -1 +1,6 @@
1
- export {};
1
+ export const DEFAULT_FUNNEL_BREAKPOINTS = {
2
+ small: { width: 375, height: 667 },
3
+ medium: { width: 393, height: 852 },
4
+ large: { width: 402, height: 874 },
5
+ 'desktop-small': { width: 1280, height: 800 },
6
+ };
@@ -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;
@@ -359,16 +360,27 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
359
360
  const durationMs = Math.max(0, new Date(record.completedAt).getTime() - new Date(startedAt).getTime());
360
361
  dispatchWindowCustomEvent('funnel:step-completed', record);
361
362
  if (!isPreviewRuntime) {
362
- apiService.trackStepCompleted({
363
- userId: currentUserIdRef.current,
363
+ (_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepCompleted) === null || _a === void 0 ? void 0 : _a.call(analytics, {
364
364
  stepId: record.stepId,
365
365
  stepName: record.stepName,
366
366
  stepType,
367
367
  startedAt,
368
368
  endedAt: record.completedAt,
369
369
  selected: record.choices,
370
+ featureFlags: postHogFeatureFlagsRef.current,
370
371
  });
371
- 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)));
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
+ }
372
384
  }
373
385
  setUser((prev) => {
374
386
  var _a, _b;
@@ -391,12 +403,12 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
391
403
  }
392
404
  return updatedUser;
393
405
  });
394
- }, [isPreviewRuntime, stepById]);
406
+ }, [analytics, isPreviewRuntime, runtimeMode, stepById]);
395
407
  const completeStep = useCallback((stepId, choices) => {
396
408
  recordStepCompletion(stepId, choices);
397
409
  }, [recordStepCompletion]);
398
410
  useEffect(() => {
399
- var _a;
411
+ var _a, _b, _c;
400
412
  if (safeActiveStepId !== activeStepId) {
401
413
  logger.warn(`[FunnelFlow] Unknown or non-renderable step "${activeStepId}", falling back to "${safeActiveStepId}".`);
402
414
  setActiveStepId(safeActiveStepId);
@@ -417,19 +429,36 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
417
429
  const stepType = renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.type;
418
430
  stepStartedAtByIdRef.current[renderedStepId] = startedAt;
419
431
  if (!isPreviewRuntime) {
420
- apiService.trackStepStarted({
421
- userId: currentUserIdRef.current,
432
+ (_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepStarted) === null || _a === void 0 ? void 0 : _a.call(analytics, {
422
433
  stepId: renderedStepId,
423
434
  stepName,
424
435
  stepType,
425
436
  startedAt,
437
+ featureFlags: postHogFeatureFlagsRef.current,
426
438
  });
427
- 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)));
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
+ }
428
449
  if (!firstStepViewedTrackedRef.current && safeActiveStepId === defaultStepId) {
429
450
  firstStepViewedTrackedRef.current = true;
430
- (_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, {
431
452
  stepId: renderedStepId,
432
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,
433
462
  occurredAt: startedAt,
434
463
  featureFlags: postHogFeatureFlagsRef.current,
435
464
  });
@@ -447,25 +476,38 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
447
476
  const engagedStepName = stepName;
448
477
  const engagedStepType = stepType;
449
478
  const timer = window.setTimeout(() => {
479
+ var _a;
450
480
  if (prevStepIdRef.current !== engagedStepId || engagedStepIdsRef.current.has(engagedStepId)) {
451
481
  return;
452
482
  }
453
483
  engagedStepIdsRef.current.add(engagedStepId);
454
484
  const engagedAt = new Date().toISOString();
455
- apiService.trackFunnelEvent({
456
- userId: currentUserIdRef.current,
457
- eventType: 'step_engaged',
485
+ (_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepEngaged) === null || _a === void 0 ? void 0 : _a.call(analytics, {
458
486
  stepId: engagedStepId,
459
487
  stepName: engagedStepName,
460
488
  stepType: engagedStepType,
461
489
  startedAt: engagedStartedAt,
462
- endedAt: engagedAt,
463
- metadata: {
464
- durationMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
465
- engagementThresholdMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
466
- },
490
+ engagedAt,
491
+ durationMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
492
+ engagementThresholdMs: FIRST_STEP_ENGAGEMENT_THRESHOLD_MS,
493
+ featureFlags: postHogFeatureFlagsRef.current,
467
494
  });
468
- 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)));
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
+ }
469
511
  }, FIRST_STEP_ENGAGEMENT_THRESHOLD_MS);
470
512
  return () => window.clearTimeout(timer);
471
513
  }, [
@@ -478,6 +520,8 @@ export function useFunnelFlowController({ analytics, initialStepId, lockToInitia
478
520
  renderedStepId,
479
521
  renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name,
480
522
  renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title,
523
+ renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.type,
524
+ runtimeMode,
481
525
  safeActiveStepId,
482
526
  ]);
483
527
  useEffect(() => {
@@ -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 || {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/runtime",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "main": "./dist/index.js",