@funnelsgrove/runtime 0.1.0 → 0.1.1

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 +13 -3
  40. package/dist/runtime/preview-bridge.js +96 -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
@@ -0,0 +1,363 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useMemo, useState } from 'react';
4
+ import { clearPaywallStateValue } from '../services/funnel-state.service.js';
5
+ import { RUNTIME_PUBLIC_ENV_KEYS, runtimePublicConfig, } from '../services/public-env.js';
6
+ const normalizeDisplayValue = (value) => {
7
+ if (value === null || typeof value === 'undefined' || value === '') {
8
+ return '(empty)';
9
+ }
10
+ return String(value);
11
+ };
12
+ const getCanonicalRuntimeEnvKey = (envKeys) => {
13
+ var _a, _b;
14
+ return (_b = (_a = envKeys.find((envKey) => envKey.startsWith('NEXT_PUBLIC_'))) !== null && _a !== void 0 ? _a : envKeys[0]) !== null && _b !== void 0 ? _b : '';
15
+ };
16
+ const runtimePublicConfigEntries = () => {
17
+ return Object.entries(RUNTIME_PUBLIC_ENV_KEYS).map(([configKey, envKey]) => ({
18
+ label: configKey,
19
+ source: getCanonicalRuntimeEnvKey(envKey),
20
+ value: runtimePublicConfig[configKey],
21
+ }));
22
+ };
23
+ const canUseDom = () => {
24
+ return typeof window !== 'undefined';
25
+ };
26
+ export const copyRuntimeDevInfoValue = async (value) => {
27
+ var _a, _b;
28
+ if (!value || value === '(empty)') {
29
+ return false;
30
+ }
31
+ if (typeof document !== 'undefined' && document.body) {
32
+ const textarea = document.createElement('textarea');
33
+ textarea.value = value;
34
+ textarea.setAttribute('readonly', '');
35
+ textarea.style.position = 'fixed';
36
+ textarea.style.left = '-9999px';
37
+ textarea.style.top = '0';
38
+ document.body.appendChild(textarea);
39
+ (_a = textarea.focus) === null || _a === void 0 ? void 0 : _a.call(textarea);
40
+ textarea.select();
41
+ (_b = textarea.setSelectionRange) === null || _b === void 0 ? void 0 : _b.call(textarea, 0, value.length);
42
+ try {
43
+ if (document.execCommand('copy')) {
44
+ return true;
45
+ }
46
+ }
47
+ catch (_c) {
48
+ // fall through to async clipboard
49
+ }
50
+ finally {
51
+ document.body.removeChild(textarea);
52
+ }
53
+ }
54
+ const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;
55
+ if (!(clipboard === null || clipboard === void 0 ? void 0 : clipboard.writeText)) {
56
+ return false;
57
+ }
58
+ try {
59
+ await clipboard.writeText(value);
60
+ return true;
61
+ }
62
+ catch (_d) {
63
+ return false;
64
+ }
65
+ };
66
+ export function RuntimeDevInfoBox({ runtimeMode, user, onSwitchToLiveMode, configValues = [], initialConfigOpen = false, initialExpanded = false, paywallStateOptions, reloadOnReset = true, title = 'Funnel sandbox', }) {
67
+ const [expanded, setExpanded] = useState(initialExpanded);
68
+ const [configOpen, setConfigOpen] = useState(initialConfigOpen);
69
+ const [copiedKey, setCopiedKey] = useState(null);
70
+ const [resetDone, setResetDone] = useState(false);
71
+ const configItems = useMemo(() => [...configValues, ...runtimePublicConfigEntries()], [configValues]);
72
+ if (runtimeMode !== 'test') {
73
+ return null;
74
+ }
75
+ const markCopied = (key) => {
76
+ setCopiedKey(key);
77
+ if (canUseDom()) {
78
+ window.setTimeout(() => setCopiedKey(null), 1200);
79
+ }
80
+ };
81
+ const copyValue = (value, key) => {
82
+ void copyRuntimeDevInfoValue(value).then((copied) => {
83
+ if (copied) {
84
+ markCopied(key);
85
+ }
86
+ });
87
+ };
88
+ const handleResetState = () => {
89
+ clearPaywallStateValue(paywallStateOptions);
90
+ setResetDone(true);
91
+ if (reloadOnReset && canUseDom()) {
92
+ window.location.reload();
93
+ }
94
+ };
95
+ const userRows = [
96
+ {
97
+ key: 'user-id',
98
+ label: 'Funnel user ID',
99
+ value: normalizeDisplayValue(user.id),
100
+ },
101
+ {
102
+ key: 'user-email',
103
+ label: 'Email',
104
+ value: normalizeDisplayValue(user.email),
105
+ },
106
+ ];
107
+ return (_jsxs("aside", { className: `runtime-dev-info ${expanded ? 'is-expanded' : 'is-collapsed'}`, children: [_jsxs("button", { type: 'button', className: 'runtime-dev-info-pill', onClick: () => setExpanded((current) => !current), "aria-expanded": expanded, children: [_jsx("span", { className: 'runtime-dev-info-wordmark', children: "funnelgrove" }), _jsx("span", { className: 'runtime-dev-info-chevron', "aria-hidden": 'true', children: expanded ? 'v' : '>' })] }), expanded ? (_jsxs("div", { className: 'runtime-dev-info-panel', children: [_jsxs("div", { className: 'runtime-dev-info-status-row', children: [_jsx("p", { className: 'runtime-dev-info-title', children: title }), _jsx("span", { className: 'runtime-dev-info-badge', children: "Test" })] }), _jsx("div", { className: 'runtime-dev-info-identities', children: userRows.map((row) => (_jsxs("div", { className: 'runtime-dev-info-identity-card', children: [_jsxs("div", { className: 'runtime-dev-info-identity-main', children: [_jsx("code", { className: 'runtime-dev-info-identity-value', children: row.value }), _jsx("button", { type: 'button', onClick: () => copyValue(row.value, row.key), children: copiedKey === row.key ? 'Copied' : 'Copy' })] }), _jsx("span", { className: 'runtime-dev-info-identity-label', children: row.label })] }, row.key))) }), _jsxs("div", { className: 'runtime-dev-info-actions', children: [_jsx("button", { type: 'button', onClick: () => setConfigOpen((current) => !current), children: configOpen ? 'Hide config' : 'View config' }), _jsx("button", { type: 'button', onClick: onSwitchToLiveMode, children: "Switch to live mode" }), _jsx("button", { type: 'button', onClick: handleResetState, children: resetDone ? 'Timers reset' : 'Reset timers' })] }), configOpen ? (_jsxs("div", { className: 'runtime-dev-info-config', children: [_jsxs("div", { className: 'runtime-dev-info-config-head', children: [_jsx("p", { children: "Runtime config" }), _jsx("button", { type: 'button', onClick: () => {
108
+ const value = configItems
109
+ .map((item) => `${item.source || item.label}=${normalizeDisplayValue(item.value)}`)
110
+ .join('\n');
111
+ copyValue(value, 'config');
112
+ }, children: copiedKey === 'config' ? 'Copied' : 'Copy all' })] }), _jsx("div", { className: 'runtime-dev-info-config-list', children: configItems.map((item) => (_jsxs("div", { className: 'runtime-dev-info-config-row', children: [_jsx("code", { className: 'runtime-dev-info-config-value', children: normalizeDisplayValue(item.value) }), _jsx("span", { className: 'runtime-dev-info-config-name', children: item.source || item.label })] }, `${item.source || item.label}:${item.label}`))) })] })) : null, _jsxs("div", { className: 'runtime-dev-info-help', children: [_jsxs("p", { children: ["Use ", _jsx("code", { children: "?mode=test" }), " to force test mode."] }), _jsxs("p", { children: ["Use ", _jsx("code", { children: "?mode=prod" }), " or ", _jsx("code", { children: "?mode=production" }), " for live mode."] })] })] })) : null] }));
113
+ }
114
+ export const runtimeDevInfoBoxStyles = `
115
+ .runtime-dev-info {
116
+ position: fixed;
117
+ right: 16px;
118
+ bottom: 16px;
119
+ z-index: 1400;
120
+ width: min(392px, calc(100vw - 32px));
121
+ color: #f8fafc;
122
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
123
+ pointer-events: none;
124
+ }
125
+
126
+ .runtime-dev-info * {
127
+ box-sizing: border-box;
128
+ }
129
+
130
+ .runtime-dev-info-pill,
131
+ .runtime-dev-info-panel {
132
+ pointer-events: auto;
133
+ }
134
+
135
+ .runtime-dev-info.is-collapsed {
136
+ width: auto;
137
+ }
138
+
139
+ .runtime-dev-info-pill {
140
+ min-height: 56px;
141
+ display: inline-flex;
142
+ align-items: center;
143
+ gap: 10px;
144
+ border: 1px solid rgb(255 255 255 / 12%);
145
+ border-radius: 28px;
146
+ background: #11161d;
147
+ color: inherit;
148
+ padding: 0 20px;
149
+ box-shadow: 0 18px 42px rgb(15 23 42 / 24%);
150
+ cursor: pointer;
151
+ }
152
+
153
+ .runtime-dev-info-wordmark {
154
+ font-size: 17px;
155
+ font-weight: 800;
156
+ letter-spacing: 0;
157
+ }
158
+
159
+ .runtime-dev-info-chevron {
160
+ color: #cbd5e1;
161
+ font-size: 18px;
162
+ font-weight: 700;
163
+ line-height: 1;
164
+ }
165
+
166
+ .runtime-dev-info-panel {
167
+ margin-top: 10px;
168
+ border: 1px solid rgb(255 255 255 / 12%);
169
+ border-radius: 20px;
170
+ background: #11161d;
171
+ box-shadow: 0 22px 50px rgb(15 23 42 / 30%);
172
+ overflow: hidden;
173
+ }
174
+
175
+ .runtime-dev-info-status-row,
176
+ .runtime-dev-info-config-head {
177
+ display: flex;
178
+ align-items: center;
179
+ justify-content: space-between;
180
+ gap: 12px;
181
+ }
182
+
183
+ .runtime-dev-info-status-row {
184
+ padding: 18px 20px 14px;
185
+ }
186
+
187
+ .runtime-dev-info-title,
188
+ .runtime-dev-info-config-head p,
189
+ .runtime-dev-info-help p {
190
+ margin: 0;
191
+ }
192
+
193
+ .runtime-dev-info-title {
194
+ color: #e5e7eb;
195
+ font-size: 16px;
196
+ font-weight: 750;
197
+ line-height: 1.2;
198
+ }
199
+
200
+ .runtime-dev-info-badge {
201
+ border: 1px solid #3b82f6;
202
+ border-radius: 999px;
203
+ color: #60a5fa;
204
+ font-size: 12px;
205
+ font-weight: 750;
206
+ line-height: 1;
207
+ padding: 7px 10px;
208
+ }
209
+
210
+ .runtime-dev-info-identities {
211
+ display: grid;
212
+ gap: 10px;
213
+ padding: 0 20px 18px;
214
+ }
215
+
216
+ .runtime-dev-info-identity-card {
217
+ min-width: 0;
218
+ border: 1px solid rgb(255 255 255 / 10%);
219
+ border-radius: 10px;
220
+ background: rgb(255 255 255 / 4%);
221
+ padding: 10px;
222
+ }
223
+
224
+ .runtime-dev-info-identity-main {
225
+ display: flex;
226
+ align-items: center;
227
+ gap: 8px;
228
+ }
229
+
230
+ .runtime-dev-info-identity-value {
231
+ min-width: 0;
232
+ flex: 1;
233
+ color: #f8fafc;
234
+ overflow-wrap: anywhere;
235
+ white-space: normal;
236
+ }
237
+
238
+ .runtime-dev-info-identity-label,
239
+ .runtime-dev-info-config-name {
240
+ display: block;
241
+ margin-top: 5px;
242
+ color: #94a3b8;
243
+ font-size: 12px;
244
+ line-height: 1.25;
245
+ }
246
+
247
+ .runtime-dev-info-identity-value,
248
+ .runtime-dev-info-config-value,
249
+ .runtime-dev-info-help code {
250
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
251
+ font-size: 12px;
252
+ letter-spacing: 0;
253
+ }
254
+
255
+ .runtime-dev-info-config-value,
256
+ .runtime-dev-info-help code {
257
+ color: #dbeafe;
258
+ }
259
+
260
+ .runtime-dev-info-identity-main button,
261
+ .runtime-dev-info-actions button,
262
+ .runtime-dev-info-config-head button {
263
+ min-height: 30px;
264
+ border: 1px solid rgb(255 255 255 / 14%);
265
+ border-radius: 8px;
266
+ background: rgb(255 255 255 / 6%);
267
+ color: #e5e7eb;
268
+ font: inherit;
269
+ font-size: 12px;
270
+ font-weight: 700;
271
+ cursor: pointer;
272
+ }
273
+
274
+ .runtime-dev-info-identity-main button {
275
+ flex: 0 0 auto;
276
+ padding: 0 9px;
277
+ }
278
+
279
+ .runtime-dev-info-actions {
280
+ display: grid;
281
+ grid-template-columns: repeat(3, minmax(0, 1fr));
282
+ gap: 8px;
283
+ border-top: 1px solid rgb(255 255 255 / 10%);
284
+ padding: 14px 20px;
285
+ }
286
+
287
+ .runtime-dev-info-actions button {
288
+ width: 100%;
289
+ padding: 0 8px;
290
+ }
291
+
292
+ .runtime-dev-info-actions button:hover,
293
+ .runtime-dev-info-identity-main button:hover,
294
+ .runtime-dev-info-config-head button:hover {
295
+ background: rgb(255 255 255 / 12%);
296
+ }
297
+
298
+ .runtime-dev-info-config {
299
+ border-top: 1px solid rgb(255 255 255 / 10%);
300
+ padding: 14px 20px;
301
+ }
302
+
303
+ .runtime-dev-info-config-head {
304
+ margin-bottom: 10px;
305
+ color: #e5e7eb;
306
+ font-size: 13px;
307
+ font-weight: 750;
308
+ }
309
+
310
+ .runtime-dev-info-config-head button {
311
+ padding: 0 10px;
312
+ }
313
+
314
+ .runtime-dev-info-config-list {
315
+ display: grid;
316
+ gap: 5px;
317
+ max-height: 220px;
318
+ overflow: auto;
319
+ }
320
+
321
+ .runtime-dev-info-config-row {
322
+ min-width: 0;
323
+ border-bottom: 1px solid rgb(255 255 255 / 8%);
324
+ padding: 7px 0;
325
+ }
326
+
327
+ .runtime-dev-info-config-value {
328
+ display: block;
329
+ min-width: 0;
330
+ overflow-wrap: anywhere;
331
+ white-space: normal;
332
+ }
333
+
334
+ .runtime-dev-info-config-name {
335
+ font-size: 11px;
336
+ }
337
+
338
+ .runtime-dev-info-help {
339
+ display: grid;
340
+ gap: 6px;
341
+ border-top: 1px solid rgb(255 255 255 / 10%);
342
+ padding: 14px 20px 18px;
343
+ color: #94a3b8;
344
+ font-size: 12px;
345
+ line-height: 1.45;
346
+ }
347
+
348
+ @media (max-width: 430px) {
349
+ .runtime-dev-info {
350
+ right: 10px;
351
+ bottom: 10px;
352
+ width: min(360px, calc(100vw - 20px));
353
+ }
354
+
355
+ .runtime-dev-info-actions {
356
+ grid-template-columns: 1fr;
357
+ }
358
+
359
+ .runtime-dev-info-identity-main {
360
+ align-items: flex-start;
361
+ }
362
+ }
363
+ `;
@@ -0,0 +1,31 @@
1
+ import type { StoreLinkItem } from '../content/step-content.js';
2
+ export type SubscriptionHandoffContent = {
3
+ kicker: string;
4
+ title: string;
5
+ copyWithQr: string;
6
+ copyWithoutQr: string;
7
+ note: string;
8
+ openAppLabel: string;
9
+ emailButtonLabel: string;
10
+ emailSubject: string;
11
+ emailIntro: string;
12
+ emailOpenAppLabel: string;
13
+ emailIosDeepLinkLabel: string;
14
+ emailAndroidDeepLinkLabel: string;
15
+ emailStoreLabels: {
16
+ ios: string;
17
+ android: string;
18
+ };
19
+ storeLinks: readonly StoreLinkItem[];
20
+ emailSentStatus: string;
21
+ autoOpenStatus: string;
22
+ qrOpenLabel: string;
23
+ };
24
+ type SubscriptionHandoffScreenProps = {
25
+ stepId: string;
26
+ content: SubscriptionHandoffContent;
27
+ confirmationUrl?: string | null;
28
+ nodeId?: string;
29
+ };
30
+ export declare function SubscriptionHandoffScreen({ stepId, content, confirmationUrl, nodeId, }: SubscriptionHandoffScreenProps): import("react/jsx-runtime").JSX.Element;
31
+ export {};