@funnelsgrove/runtime 0.1.12 → 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.
- package/dist/components/FunnelContext.d.ts +1 -0
- package/dist/config/env.config.d.ts +4 -0
- package/dist/config/env.config.js +8 -0
- package/dist/config/funnel.experiments.types.d.ts +18 -0
- package/dist/config/funnel.experiments.types.js +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/runtime/funnel-attribution.js +1 -0
- package/dist/runtime/use-funnel-flow-controller.js +15 -15
- package/dist/services/project-env.d.ts +4 -0
- package/dist/services/project-env.js +8 -0
- package/package.json +1 -1
|
@@ -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';
|
|
@@ -10,7 +10,7 @@ import { usePreviewBridge } from './preview-bridge.js';
|
|
|
10
10
|
import { resolveExperimentAssignment } from './experiment-assignment.js';
|
|
11
11
|
import { collectCurrentFunnelAttribution } from './funnel-attribution.js';
|
|
12
12
|
import { resolveUrlUserAttributes } from './url-user-attributes.js';
|
|
13
|
-
import { bootstrapPostHog,
|
|
13
|
+
import { bootstrapPostHog, identifyPostHog, isPostHogReady, resolveExperimentVariant, } from './posthog-flags.js';
|
|
14
14
|
import { isEditorEnabled, useRuntimeMode } from '../services/runtime-mode.service.js';
|
|
15
15
|
import { isPreviewFrameRuntime } from '../services/preview-frame.service.js';
|
|
16
16
|
const FIRST_STEP_ENGAGEMENT_THRESHOLD_MS = 1000;
|
|
@@ -18,9 +18,6 @@ const buildFeatureFlagProperties = (featureFlags) => {
|
|
|
18
18
|
return Object.fromEntries(Object.entries(featureFlags).map(([key, value]) => [`$feature/${key}`, value]));
|
|
19
19
|
};
|
|
20
20
|
const capturePostHogStepEvent = (event, properties) => {
|
|
21
|
-
if (!getPostHog() || !POSTHOG_API_HOST || !POSTHOG_PROJECT_API_KEY || !PROJECT_ID) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
21
|
const payload = JSON.stringify({
|
|
25
22
|
api_key: POSTHOG_PROJECT_API_KEY,
|
|
26
23
|
batch: [
|
|
@@ -366,29 +363,29 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
|
|
|
366
363
|
return;
|
|
367
364
|
}
|
|
368
365
|
const prevId = prevStepIdRef.current;
|
|
369
|
-
if (prevId && prevId !==
|
|
366
|
+
if (prevId && prevId !== renderedStepId) {
|
|
370
367
|
recordStepCompletion(prevId);
|
|
371
368
|
}
|
|
372
369
|
const startedAt = new Date().toISOString();
|
|
373
|
-
const stepName = (
|
|
374
|
-
stepStartedAtByIdRef.current[
|
|
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;
|
|
375
372
|
if (!isPreviewRuntime) {
|
|
376
373
|
apiService.trackStepStarted({
|
|
377
374
|
userId: currentUserIdRef.current,
|
|
378
|
-
stepId:
|
|
375
|
+
stepId: renderedStepId,
|
|
379
376
|
stepName,
|
|
380
377
|
startedAt,
|
|
381
378
|
});
|
|
382
|
-
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:
|
|
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)));
|
|
383
380
|
}
|
|
384
381
|
attributesAtStepStart.current = Object.assign({}, attributesRef.current);
|
|
385
|
-
prevStepIdRef.current =
|
|
382
|
+
prevStepIdRef.current = renderedStepId;
|
|
386
383
|
if (isPreviewRuntime ||
|
|
387
|
-
|
|
388
|
-
engagedStepIdsRef.current.has(
|
|
384
|
+
renderedStepId !== defaultStepId ||
|
|
385
|
+
engagedStepIdsRef.current.has(renderedStepId)) {
|
|
389
386
|
return;
|
|
390
387
|
}
|
|
391
|
-
const engagedStepId =
|
|
388
|
+
const engagedStepId = renderedStepId;
|
|
392
389
|
const engagedStartedAt = startedAt;
|
|
393
390
|
const engagedStepName = stepName;
|
|
394
391
|
const timer = window.setTimeout(() => {
|
|
@@ -414,12 +411,13 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
|
|
|
414
411
|
return () => window.clearTimeout(timer);
|
|
415
412
|
}, [
|
|
416
413
|
activeStepId,
|
|
417
|
-
activeStepMeta === null || activeStepMeta === void 0 ? void 0 : activeStepMeta.name,
|
|
418
|
-
activeStepMeta === null || activeStepMeta === void 0 ? void 0 : activeStepMeta.title,
|
|
419
414
|
defaultStepId,
|
|
420
415
|
isPreviewRuntime,
|
|
421
416
|
recordStepCompletion,
|
|
422
417
|
renderSuspended,
|
|
418
|
+
renderedStepId,
|
|
419
|
+
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name,
|
|
420
|
+
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title,
|
|
423
421
|
safeActiveStepId,
|
|
424
422
|
]);
|
|
425
423
|
useEffect(() => {
|
|
@@ -535,6 +533,7 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
|
|
|
535
533
|
}, [editorVariantOverrides, postHogReady]);
|
|
536
534
|
const contextValue = useMemo(() => ({
|
|
537
535
|
activeStepId: safeActiveStepId,
|
|
536
|
+
featureFlags: postHogFeatureFlags,
|
|
538
537
|
isBuilder: isPreviewRuntime,
|
|
539
538
|
goToStep: goToStepFromContext,
|
|
540
539
|
goNext,
|
|
@@ -561,6 +560,7 @@ export function useFunnelFlowController({ initialStepId, lockToInitialStep = fal
|
|
|
561
560
|
goNext,
|
|
562
561
|
goToStepFromContext,
|
|
563
562
|
isPreviewRuntime,
|
|
563
|
+
postHogFeatureFlags,
|
|
564
564
|
resolveRenderableStepId,
|
|
565
565
|
safeActiveStepId,
|
|
566
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) => {
|