@funnelsgrove/runtime 0.1.13 → 0.1.14

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.
@@ -18,6 +18,7 @@ export type FunnelUser = {
18
18
  };
19
19
  export type FunnelContextValue = {
20
20
  activeStepId: FunnelStepId;
21
+ featureFlags: Record<string, string>;
21
22
  isBuilder: boolean;
22
23
  goToStep: (stepId: string) => void;
23
24
  goNext: () => void;
@@ -33,6 +33,10 @@ export declare const RUNTIME_ENV_KEYS: {
33
33
  readonly stripeLivePublishableKey: "NEXT_PUBLIC_STRIPE_LIVE_PUBLISHABLE_KEY";
34
34
  readonly metaPixelEnabled: "NEXT_PUBLIC_META_PIXEL_ENABLED";
35
35
  readonly metaPixelId: "NEXT_PUBLIC_META_PIXEL_ID";
36
+ readonly googleTagEnabled: "NEXT_PUBLIC_GOOGLE_TAG_ENABLED";
37
+ readonly googleAnalyticsId: "NEXT_PUBLIC_GOOGLE_ANALYTICS_ID";
38
+ readonly googleTagManagerId: "NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID";
39
+ readonly googleTagEventTarget: "NEXT_PUBLIC_GOOGLE_TAG_EVENT_TARGET";
36
40
  readonly funnelCustomDomain: "FUNNEL_CUSTOM_DOMAIN";
37
41
  readonly claimbeeWeeklyPriceId: "NEXT_PUBLIC_CLAIMBEE_WEEKLY_PRICE_ID";
38
42
  readonly claimbeeMonthlyPriceId: "NEXT_PUBLIC_CLAIMBEE_MONTHLY_PRICE_ID";
@@ -44,6 +44,10 @@ export const RUNTIME_ENV_KEYS = {
44
44
  stripeLivePublishableKey: 'NEXT_PUBLIC_STRIPE_LIVE_PUBLISHABLE_KEY',
45
45
  metaPixelEnabled: 'NEXT_PUBLIC_META_PIXEL_ENABLED',
46
46
  metaPixelId: 'NEXT_PUBLIC_META_PIXEL_ID',
47
+ googleTagEnabled: 'NEXT_PUBLIC_GOOGLE_TAG_ENABLED',
48
+ googleAnalyticsId: 'NEXT_PUBLIC_GOOGLE_ANALYTICS_ID',
49
+ googleTagManagerId: 'NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID',
50
+ googleTagEventTarget: 'NEXT_PUBLIC_GOOGLE_TAG_EVENT_TARGET',
47
51
  funnelCustomDomain: 'FUNNEL_CUSTOM_DOMAIN',
48
52
  claimbeeWeeklyPriceId: 'NEXT_PUBLIC_CLAIMBEE_WEEKLY_PRICE_ID',
49
53
  claimbeeMonthlyPriceId: 'NEXT_PUBLIC_CLAIMBEE_MONTHLY_PRICE_ID',
@@ -97,6 +101,10 @@ const RUNTIME_ENV_VALUES = {
97
101
  NEXT_PUBLIC_STRIPE_LIVE_PUBLISHABLE_KEY: readRuntimeEnv(() => process.env.NEXT_PUBLIC_STRIPE_LIVE_PUBLISHABLE_KEY),
98
102
  NEXT_PUBLIC_META_PIXEL_ENABLED: readRuntimeEnv(() => process.env.NEXT_PUBLIC_META_PIXEL_ENABLED),
99
103
  NEXT_PUBLIC_META_PIXEL_ID: readRuntimeEnv(() => process.env.NEXT_PUBLIC_META_PIXEL_ID),
104
+ NEXT_PUBLIC_GOOGLE_TAG_ENABLED: readRuntimeEnv(() => process.env.NEXT_PUBLIC_GOOGLE_TAG_ENABLED),
105
+ NEXT_PUBLIC_GOOGLE_ANALYTICS_ID: readRuntimeEnv(() => process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID),
106
+ NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID: readRuntimeEnv(() => process.env.NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID),
107
+ NEXT_PUBLIC_GOOGLE_TAG_EVENT_TARGET: readRuntimeEnv(() => process.env.NEXT_PUBLIC_GOOGLE_TAG_EVENT_TARGET),
100
108
  NEXT_PUBLIC_CLAIMBEE_WEEKLY_PRICE_ID: readRuntimeEnv(() => process.env.NEXT_PUBLIC_CLAIMBEE_WEEKLY_PRICE_ID),
101
109
  NEXT_PUBLIC_CLAIMBEE_MONTHLY_PRICE_ID: readRuntimeEnv(() => process.env.NEXT_PUBLIC_CLAIMBEE_MONTHLY_PRICE_ID),
102
110
  NEXT_PUBLIC_CLAIMBEE_YEARLY_PRICE_ID: readRuntimeEnv(() => process.env.NEXT_PUBLIC_CLAIMBEE_YEARLY_PRICE_ID),
@@ -0,0 +1,18 @@
1
+ import type { FunnelManifestExperiment } from './funnel.manifest.types.js';
2
+ export type PaywallExperimentDefinition = {
3
+ id: string;
4
+ name: string;
5
+ type: 'paywall';
6
+ launchDate: string;
7
+ control: {
8
+ stepId: string;
9
+ trafficPercent: number;
10
+ };
11
+ variant: {
12
+ stepId: string;
13
+ trafficPercent: number;
14
+ };
15
+ };
16
+ export type FunnelExperimentDefinition = PaywallExperimentDefinition;
17
+ export declare const defineFunnelExperiments: <const T extends readonly FunnelExperimentDefinition[]>(experiments: T) => T;
18
+ export declare const toManifestExperiments: (experiments: readonly FunnelExperimentDefinition[]) => FunnelManifestExperiment[];
@@ -0,0 +1,23 @@
1
+ export const defineFunnelExperiments = (experiments) => {
2
+ for (const experiment of experiments) {
3
+ const trafficTotal = experiment.control.trafficPercent + experiment.variant.trafficPercent;
4
+ if (trafficTotal !== 100) {
5
+ throw new Error(`Experiment "${experiment.id}" traffic must sum to 100`);
6
+ }
7
+ }
8
+ return experiments;
9
+ };
10
+ export const toManifestExperiments = (experiments) => experiments.map((experiment) => ({
11
+ experimentId: experiment.id,
12
+ stepId: experiment.control.stepId,
13
+ variants: [
14
+ {
15
+ variantKey: 'control',
16
+ routeToStepId: experiment.control.stepId,
17
+ },
18
+ {
19
+ variantKey: 'variant_b',
20
+ routeToStepId: experiment.variant.stepId,
21
+ },
22
+ ],
23
+ }));
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './config/builder-preview.protocol.js';
2
2
  export * from './config/env.config.js';
3
3
  export * from './config/funnel.manifest.types.js';
4
+ export * from './config/funnel.experiments.types.js';
4
5
  export * from './config/funnel-theme.js';
5
6
  export * from './config/font-config.js';
6
7
  export * from './content/step-content.js';
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './config/builder-preview.protocol.js';
2
2
  export * from './config/env.config.js';
3
3
  export * from './config/funnel.manifest.types.js';
4
+ export * from './config/funnel.experiments.types.js';
4
5
  export * from './config/funnel-theme.js';
5
6
  export * from './config/font-config.js';
6
7
  export * from './content/step-content.js';
@@ -7,6 +7,7 @@ const INTERNAL_ATTRIBUTION_QUERY_KEYS = new Set([
7
7
  'user_id',
8
8
  ]);
9
9
  const POSTHOG_CONTEXT_MAPPINGS = {
10
+ $geoip_city_name: 'cityName',
10
11
  $geoip_country_code: 'countryCode',
11
12
  $geoip_country_name: 'countryName',
12
13
  $browser: 'browser',
@@ -363,29 +363,29 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
363
363
  return;
364
364
  }
365
365
  const prevId = prevStepIdRef.current;
366
- if (prevId && prevId !== safeActiveStepId) {
366
+ if (prevId && prevId !== renderedStepId) {
367
367
  recordStepCompletion(prevId);
368
368
  }
369
369
  const startedAt = new Date().toISOString();
370
- const stepName = (activeStepMeta === null || activeStepMeta === void 0 ? void 0 : activeStepMeta.name) || (activeStepMeta === null || activeStepMeta === void 0 ? void 0 : activeStepMeta.title) || safeActiveStepId;
371
- stepStartedAtByIdRef.current[safeActiveStepId] = startedAt;
370
+ const stepName = (renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name) || (renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title) || renderedStepId;
371
+ stepStartedAtByIdRef.current[renderedStepId] = startedAt;
372
372
  if (!isPreviewRuntime) {
373
373
  apiService.trackStepStarted({
374
374
  userId: currentUserIdRef.current,
375
- stepId: safeActiveStepId,
375
+ stepId: renderedStepId,
376
376
  stepName,
377
377
  startedAt,
378
378
  });
379
- 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, step_id: safeActiveStepId, step_name: stepName, started_at: startedAt }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
379
+ 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, step_id: renderedStepId, step_name: stepName, started_at: startedAt }, buildFeatureFlagProperties(postHogFeatureFlagsRef.current)));
380
380
  }
381
381
  attributesAtStepStart.current = Object.assign({}, attributesRef.current);
382
- prevStepIdRef.current = safeActiveStepId;
382
+ prevStepIdRef.current = renderedStepId;
383
383
  if (isPreviewRuntime ||
384
- safeActiveStepId !== defaultStepId ||
385
- engagedStepIdsRef.current.has(safeActiveStepId)) {
384
+ renderedStepId !== defaultStepId ||
385
+ engagedStepIdsRef.current.has(renderedStepId)) {
386
386
  return;
387
387
  }
388
- const engagedStepId = safeActiveStepId;
388
+ const engagedStepId = renderedStepId;
389
389
  const engagedStartedAt = startedAt;
390
390
  const engagedStepName = stepName;
391
391
  const timer = window.setTimeout(() => {
@@ -411,12 +411,13 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
411
411
  return () => window.clearTimeout(timer);
412
412
  }, [
413
413
  activeStepId,
414
- activeStepMeta === null || activeStepMeta === void 0 ? void 0 : activeStepMeta.name,
415
- activeStepMeta === null || activeStepMeta === void 0 ? void 0 : activeStepMeta.title,
416
414
  defaultStepId,
417
415
  isPreviewRuntime,
418
416
  recordStepCompletion,
419
417
  renderSuspended,
418
+ renderedStepId,
419
+ renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name,
420
+ renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title,
420
421
  safeActiveStepId,
421
422
  ]);
422
423
  useEffect(() => {
@@ -532,6 +533,7 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
532
533
  }, [editorVariantOverrides, postHogReady]);
533
534
  const contextValue = useMemo(() => ({
534
535
  activeStepId: safeActiveStepId,
536
+ featureFlags: postHogFeatureFlags,
535
537
  isBuilder: isPreviewRuntime,
536
538
  goToStep: goToStepFromContext,
537
539
  goNext,
@@ -558,6 +560,7 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
558
560
  goNext,
559
561
  goToStepFromContext,
560
562
  isPreviewRuntime,
563
+ postHogFeatureFlags,
561
564
  resolveRenderableStepId,
562
565
  safeActiveStepId,
563
566
  setAnswer,
@@ -33,6 +33,10 @@ export declare const PROJECT_ENV_STANDARD_KEYS: {
33
33
  readonly metaPixelId: "NEXT_PUBLIC_META_PIXEL_ID";
34
34
  readonly metaConversionsAccessToken: "META_CONVERSIONS_ACCESS_TOKEN";
35
35
  readonly metaTestEventCode: "META_TEST_EVENT_CODE";
36
+ readonly googleTagEnabled: "NEXT_PUBLIC_GOOGLE_TAG_ENABLED";
37
+ readonly googleAnalyticsId: "NEXT_PUBLIC_GOOGLE_ANALYTICS_ID";
38
+ readonly googleTagManagerId: "NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID";
39
+ readonly googleTagEventTarget: "NEXT_PUBLIC_GOOGLE_TAG_EVENT_TARGET";
36
40
  };
37
41
  export type ProjectEnvStandardKey = keyof typeof PROJECT_ENV_STANDARD_KEYS;
38
42
  export type ProjectEnvStandardValues = Record<ProjectEnvStandardKey, string>;
@@ -33,6 +33,10 @@ export const PROJECT_ENV_STANDARD_KEYS = {
33
33
  metaPixelId: 'NEXT_PUBLIC_META_PIXEL_ID',
34
34
  metaConversionsAccessToken: 'META_CONVERSIONS_ACCESS_TOKEN',
35
35
  metaTestEventCode: 'META_TEST_EVENT_CODE',
36
+ googleTagEnabled: 'NEXT_PUBLIC_GOOGLE_TAG_ENABLED',
37
+ googleAnalyticsId: 'NEXT_PUBLIC_GOOGLE_ANALYTICS_ID',
38
+ googleTagManagerId: 'NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID',
39
+ googleTagEventTarget: 'NEXT_PUBLIC_GOOGLE_TAG_EVENT_TARGET',
36
40
  };
37
41
  export const normalizeProjectEnvValue = (value) => {
38
42
  return (value === null || value === void 0 ? void 0 : value.trim()) || '';
@@ -100,6 +104,10 @@ const buildProjectStandardValues = (envMap) => {
100
104
  metaPixelId: getProjectEnvValue(envMap, PROJECT_ENV_STANDARD_KEYS.metaPixelId),
101
105
  metaConversionsAccessToken: getProjectEnvValue(envMap, PROJECT_ENV_STANDARD_KEYS.metaConversionsAccessToken),
102
106
  metaTestEventCode: getProjectEnvValue(envMap, PROJECT_ENV_STANDARD_KEYS.metaTestEventCode),
107
+ googleTagEnabled: getProjectEnvValue(envMap, PROJECT_ENV_STANDARD_KEYS.googleTagEnabled),
108
+ googleAnalyticsId: getProjectEnvValue(envMap, PROJECT_ENV_STANDARD_KEYS.googleAnalyticsId),
109
+ googleTagManagerId: getProjectEnvValue(envMap, PROJECT_ENV_STANDARD_KEYS.googleTagManagerId),
110
+ googleTagEventTarget: getProjectEnvValue(envMap, PROJECT_ENV_STANDARD_KEYS.googleTagEventTarget),
103
111
  };
104
112
  };
105
113
  export const parseProjectEnvVariables = (variables) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/runtime",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "main": "./dist/index.js",