@funnelsgrove/runtime 0.1.0 → 0.1.2

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.
Files changed (62) hide show
  1. package/README.md +20 -1
  2. package/dist/components/FunnelContext.d.ts +5 -2
  3. package/dist/components/FunnelContext.js +3 -0
  4. package/dist/components/FunnelEditorPanel.d.ts +3 -5
  5. package/dist/components/FunnelEditorPanel.js +3 -3
  6. package/dist/components/ManageSubscriptionScreen.d.ts +51 -0
  7. package/dist/components/ManageSubscriptionScreen.js +349 -0
  8. package/dist/components/RuntimeDevInfoBox.d.ts +23 -0
  9. package/dist/components/RuntimeDevInfoBox.js +363 -0
  10. package/dist/components/SubscriptionHandoffScreen.d.ts +31 -0
  11. package/dist/components/SubscriptionHandoffScreen.js +338 -0
  12. package/dist/config/builder-preview.protocol.d.ts +73 -0
  13. package/dist/config/builder-preview.protocol.js +3 -0
  14. package/dist/config/env.config.d.ts +44 -0
  15. package/dist/config/env.config.js +161 -0
  16. package/dist/config/font-config.d.ts +14 -0
  17. package/dist/config/font-config.js +101 -0
  18. package/dist/config/funnel-theme.d.ts +61 -10
  19. package/dist/config/funnel-theme.js +355 -35
  20. package/dist/config/funnel.manifest.types.d.ts +13 -7
  21. package/dist/content/step-content.d.ts +130 -0
  22. package/dist/content/step-content.js +381 -0
  23. package/dist/index.d.ts +33 -21
  24. package/dist/index.js +33 -21
  25. package/dist/runtime/browser-helpers.d.ts +1 -0
  26. package/dist/runtime/browser-helpers.js +14 -0
  27. package/dist/runtime/experiment-assignment.d.ts +13 -4
  28. package/dist/runtime/experiment-assignment.js +9 -27
  29. package/dist/runtime/funnel-attribution.d.ts +18 -0
  30. package/dist/runtime/funnel-attribution.js +226 -0
  31. package/dist/runtime/funnel-flow.d.ts +9 -10
  32. package/dist/runtime/funnel-flow.js +4 -18
  33. package/dist/runtime/funnel-manifest.validation.d.ts +1 -1
  34. package/dist/runtime/funnel-manifest.validation.js +2 -6
  35. package/dist/runtime/funnel-runtime.d.ts +2 -3
  36. package/dist/runtime/funnel-runtime.js +6 -13
  37. package/dist/runtime/posthog-flags.d.ts +30 -0
  38. package/dist/runtime/posthog-flags.js +71 -0
  39. package/dist/runtime/preview-bridge.d.ts +12 -2
  40. package/dist/runtime/preview-bridge.js +95 -4
  41. package/dist/runtime/preview-definition-overrides.d.ts +20 -0
  42. package/dist/runtime/preview-definition-overrides.js +148 -0
  43. package/dist/runtime/route-resolver.d.ts +2 -3
  44. package/dist/runtime/route-resolver.js +15 -26
  45. package/dist/runtime/subscription-handoff.d.ts +32 -0
  46. package/dist/runtime/subscription-handoff.js +113 -0
  47. package/dist/runtime/use-funnel-flow-controller.d.ts +19 -10
  48. package/dist/runtime/use-funnel-flow-controller.js +190 -159
  49. package/dist/sdk/userAnswers.d.ts +2 -2
  50. package/dist/services/api.service.d.ts +21 -4
  51. package/dist/services/api.service.js +165 -35
  52. package/dist/services/funnel-state.service.d.ts +8 -0
  53. package/dist/services/funnel-state.service.js +44 -0
  54. package/dist/services/preview-frame.service.d.ts +2 -2
  55. package/dist/services/preview-frame.service.js +2 -2
  56. package/dist/services/public-env.d.ts +69 -0
  57. package/dist/services/public-env.js +105 -0
  58. package/dist/services/runtime-api.config.d.ts +5 -0
  59. package/dist/services/runtime-api.config.js +12 -7
  60. package/dist/services/runtime-mode.service.d.ts +3 -0
  61. package/dist/services/runtime-mode.service.js +142 -4
  62. package/package.json +8 -2
@@ -1,5 +1,8 @@
1
1
  export type RuntimeMode = 'test' | 'live';
2
+ export type FunnelMode = 'test' | 'production';
2
3
  export declare const isEditorEnabled: () => boolean;
4
+ export declare const getFunnelMode: () => FunnelMode;
3
5
  export declare const getRuntimeMode: () => RuntimeMode;
6
+ export declare const setFunnelMode: (mode: FunnelMode) => void;
4
7
  export declare const setRuntimeMode: (mode: RuntimeMode) => void;
5
8
  export declare const useRuntimeMode: () => [RuntimeMode, (mode: RuntimeMode) => void];
@@ -1,12 +1,48 @@
1
1
  'use client';
2
2
  import { useCallback, useEffect, useState } from 'react';
3
3
  const EDITOR_MODE_STORAGE_KEY = 'funnel:editor:mode';
4
+ const FUNNEL_MODE_STORAGE_KEY = 'funnel-mode';
5
+ const FUNNEL_MODE_COOKIE_NAME = 'funnel-mode';
4
6
  const MODE_CHANGE_EVENT = 'funnel:editor-mode-changed';
7
+ const DEFAULT_FUNNEL_DOMAIN_SIGNAL = 'funnelsgrove';
8
+ const FUNNEL_MODE_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
5
9
  const canUseDom = () => {
6
10
  return typeof window !== 'undefined';
7
11
  };
12
+ const toFunnelMode = (value) => {
13
+ if (typeof value !== 'string') {
14
+ return null;
15
+ }
16
+ const normalized = value.trim().toLowerCase();
17
+ if (normalized === 'test') {
18
+ return 'test';
19
+ }
20
+ if (normalized === 'prod' || normalized === 'production' || normalized === 'live') {
21
+ return 'production';
22
+ }
23
+ return null;
24
+ };
8
25
  const toRuntimeMode = (value) => {
9
- return value === 'live' ? 'live' : 'test';
26
+ return toFunnelMode(value) === 'production' ? 'live' : 'test';
27
+ };
28
+ const toRuntimeModeFromFunnelMode = (mode) => {
29
+ return mode === 'production' ? 'live' : 'test';
30
+ };
31
+ const toFunnelModeFromRuntimeMode = (mode) => {
32
+ return mode === 'live' ? 'production' : 'test';
33
+ };
34
+ const normalizeHostname = (value) => {
35
+ return value.trim().toLowerCase().replace(/\.+$/, '');
36
+ };
37
+ const isDefaultPublishedDomain = () => {
38
+ if (!canUseDom()) {
39
+ return false;
40
+ }
41
+ const hostname = normalizeHostname(window.location.hostname || '');
42
+ if (!hostname) {
43
+ return false;
44
+ }
45
+ return hostname.includes(DEFAULT_FUNNEL_DOMAIN_SIGNAL);
10
46
  };
11
47
  export const isEditorEnabled = () => {
12
48
  if (!canUseDom()) {
@@ -34,6 +70,79 @@ const readStoredEditorMode = () => {
34
70
  return null;
35
71
  }
36
72
  };
73
+ const readCookieValue = (name) => {
74
+ if (typeof document === 'undefined') {
75
+ return null;
76
+ }
77
+ try {
78
+ const target = `${encodeURIComponent(name)}=`;
79
+ const entry = document.cookie
80
+ .split(';')
81
+ .map((item) => item.trim())
82
+ .find((item) => item.startsWith(target));
83
+ return entry ? decodeURIComponent(entry.slice(target.length)) : null;
84
+ }
85
+ catch (_a) {
86
+ return null;
87
+ }
88
+ };
89
+ const writeFunnelModeCookie = (mode) => {
90
+ if (typeof document === 'undefined') {
91
+ return;
92
+ }
93
+ try {
94
+ document.cookie = [
95
+ `${encodeURIComponent(FUNNEL_MODE_COOKIE_NAME)}=${encodeURIComponent(mode)}`,
96
+ 'Path=/',
97
+ `Max-Age=${FUNNEL_MODE_COOKIE_MAX_AGE_SECONDS}`,
98
+ 'SameSite=Lax',
99
+ ].join('; ');
100
+ }
101
+ catch (_a) {
102
+ // ignore cookie failures
103
+ }
104
+ };
105
+ const readFunnelModeCookie = () => {
106
+ return toFunnelMode(readCookieValue(FUNNEL_MODE_COOKIE_NAME));
107
+ };
108
+ const readFunnelModeStorage = () => {
109
+ if (!canUseDom()) {
110
+ return null;
111
+ }
112
+ try {
113
+ return toFunnelMode(window.localStorage.getItem(FUNNEL_MODE_STORAGE_KEY));
114
+ }
115
+ catch (_a) {
116
+ return null;
117
+ }
118
+ };
119
+ const persistFunnelMode = (mode, options = {}) => {
120
+ if (!canUseDom()) {
121
+ return;
122
+ }
123
+ try {
124
+ window.localStorage.setItem(FUNNEL_MODE_STORAGE_KEY, mode);
125
+ }
126
+ catch (_a) {
127
+ // ignore storage failures
128
+ }
129
+ writeFunnelModeCookie(mode);
130
+ if (options.emit === false) {
131
+ return;
132
+ }
133
+ emitModeChange(toRuntimeModeFromFunnelMode(mode));
134
+ };
135
+ const readQueryFunnelMode = () => {
136
+ if (!canUseDom()) {
137
+ return null;
138
+ }
139
+ try {
140
+ return toFunnelMode(new URLSearchParams(window.location.search).get('mode'));
141
+ }
142
+ catch (_a) {
143
+ return null;
144
+ }
145
+ };
37
146
  const emitModeChange = (mode) => {
38
147
  if (!canUseDom()) {
39
148
  return;
@@ -44,15 +153,44 @@ const emitModeChange = (mode) => {
44
153
  },
45
154
  }));
46
155
  };
156
+ export const getFunnelMode = () => {
157
+ if (!canUseDom()) {
158
+ return 'production';
159
+ }
160
+ const queryMode = readQueryFunnelMode();
161
+ if (queryMode) {
162
+ persistFunnelMode(queryMode, { emit: false });
163
+ return queryMode;
164
+ }
165
+ const cookieMode = readFunnelModeCookie();
166
+ if (cookieMode) {
167
+ return cookieMode;
168
+ }
169
+ if (isDefaultPublishedDomain()) {
170
+ return 'test';
171
+ }
172
+ return readFunnelModeStorage() || 'production';
173
+ };
47
174
  export const getRuntimeMode = () => {
48
175
  if (!canUseDom()) {
49
176
  return 'live';
50
177
  }
178
+ const queryMode = readQueryFunnelMode();
179
+ if (queryMode) {
180
+ persistFunnelMode(queryMode, { emit: false });
181
+ return toRuntimeModeFromFunnelMode(queryMode);
182
+ }
51
183
  if (!isEditorEnabled()) {
52
- return 'live';
184
+ return toRuntimeModeFromFunnelMode(getFunnelMode());
53
185
  }
54
186
  return readStoredEditorMode() || 'test';
55
187
  };
188
+ export const setFunnelMode = (mode) => {
189
+ if (!canUseDom()) {
190
+ return;
191
+ }
192
+ persistFunnelMode(mode);
193
+ };
56
194
  export const setRuntimeMode = (mode) => {
57
195
  if (!canUseDom()) {
58
196
  return;
@@ -64,7 +202,7 @@ export const setRuntimeMode = (mode) => {
64
202
  catch (_a) {
65
203
  // ignore storage failures
66
204
  }
67
- emitModeChange(normalizedMode);
205
+ persistFunnelMode(toFunnelModeFromRuntimeMode(normalizedMode));
68
206
  };
69
207
  const subscribeRuntimeMode = (listener) => {
70
208
  if (!canUseDom()) {
@@ -76,7 +214,7 @@ const subscribeRuntimeMode = (listener) => {
76
214
  listener(toRuntimeMode((_a = customEvent.detail) === null || _a === void 0 ? void 0 : _a.mode));
77
215
  };
78
216
  const onStorageChanged = (event) => {
79
- if (event.key !== EDITOR_MODE_STORAGE_KEY) {
217
+ if (event.key !== EDITOR_MODE_STORAGE_KEY && event.key !== FUNNEL_MODE_STORAGE_KEY) {
80
218
  return;
81
219
  }
82
220
  listener(getRuntimeMode());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/runtime",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "main": "./dist/index.js",
@@ -11,6 +11,11 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js",
13
13
  "default": "./dist/index.js"
14
+ },
15
+ "./config/env.config": {
16
+ "types": "./dist/config/env.config.d.ts",
17
+ "import": "./dist/config/env.config.js",
18
+ "default": "./dist/config/env.config.js"
14
19
  }
15
20
  },
16
21
  "files": [
@@ -20,12 +25,13 @@
20
25
  "access": "public"
21
26
  },
22
27
  "scripts": {
23
- "build": "rm -rf dist && tsc -p tsconfig.build.json",
28
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/fix-esm-relative-imports.mjs dist",
24
29
  "lint": "eslint .",
25
30
  "prepublishOnly": "npm run build",
26
31
  "test:run": "vitest run --passWithNoTests"
27
32
  },
28
33
  "dependencies": {
34
+ "posthog-js": "^1.369.2",
29
35
  "react": "19.2.3",
30
36
  "react-dom": "19.2.3"
31
37
  },