@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
- import { buildMainApiUrl, buildSdkHeaders, FUNNEL_ID, getFunnelSdkPublishableKey, } from './runtime-api.config';
2
- const USER_ID_STORAGE_KEY = 'funnel:external-user-id';
1
+ import { buildMainApiUrl, buildSdkHeaders, FUNNEL_ID, FUNNEL_VERSION_ID, getFunnelSdkPublishableKey, } from './runtime-api.config.js';
2
+ import { mergeFunnelUserAttribution, } from '../runtime/funnel-attribution.js';
3
+ const DEFAULT_USER_ID_STORAGE_KEY = 'funnel:user-id';
4
+ const LOCATION_USER_ID_QUERY_KEYS = ['user_id'];
5
+ const LOCATION_STRIPE_CUSTOMER_ID_QUERY_KEYS = ['stripe_customer_id'];
3
6
  const canUseDom = () => {
4
7
  return typeof window !== 'undefined';
5
8
  };
@@ -29,26 +32,72 @@ const normalizeUserId = (value) => {
29
32
  const trimmed = value.trim();
30
33
  return trimmed || null;
31
34
  };
35
+ const normalizeFunnelId = (value) => {
36
+ return asString(value);
37
+ };
38
+ const buildUserIdStorageKey = (funnelId = FUNNEL_ID) => {
39
+ const normalizedFunnelId = normalizeFunnelId(funnelId);
40
+ if (!normalizedFunnelId) {
41
+ return DEFAULT_USER_ID_STORAGE_KEY;
42
+ }
43
+ return `funnel:${normalizedFunnelId}:user-id`;
44
+ };
32
45
  const generateUserId = () => {
33
46
  var _a;
34
47
  if (canUseDom() && typeof ((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.randomUUID) === 'function') {
35
- return `sdk_user_${window.crypto.randomUUID().replace(/-/g, '')}`;
48
+ return `u_${window.crypto.randomUUID().replace(/-/g, '')}`;
36
49
  }
37
- return `sdk_user_${Date.now()}_${Math.random().toString(16).slice(2, 10)}`;
50
+ return `u_${Date.now()}_${Math.random().toString(16).slice(2, 10)}`;
38
51
  };
39
- const readStoredUserId = () => {
52
+ const readStoredUserId = (funnelId = FUNNEL_ID) => {
40
53
  if (!canUseDom()) {
41
54
  return null;
42
55
  }
43
- return normalizeUserId(window.localStorage.getItem(USER_ID_STORAGE_KEY));
56
+ return normalizeUserId(window.localStorage.getItem(buildUserIdStorageKey(funnelId)));
44
57
  };
45
- const persistUserId = (value) => {
58
+ const persistUserId = (value, funnelId = FUNNEL_ID) => {
46
59
  const normalized = normalizeUserId(value) || generateUserId();
47
60
  if (canUseDom()) {
48
- window.localStorage.setItem(USER_ID_STORAGE_KEY, normalized);
61
+ window.localStorage.setItem(buildUserIdStorageKey(funnelId), normalized);
49
62
  }
50
63
  return normalized;
51
64
  };
65
+ const readLocationUserId = () => {
66
+ if (!canUseDom()) {
67
+ return null;
68
+ }
69
+ try {
70
+ const searchParams = new URLSearchParams(window.location.search);
71
+ for (const key of LOCATION_USER_ID_QUERY_KEYS) {
72
+ const candidate = normalizeUserId(searchParams.get(key));
73
+ if (candidate) {
74
+ return candidate;
75
+ }
76
+ }
77
+ }
78
+ catch (_a) {
79
+ return null;
80
+ }
81
+ return null;
82
+ };
83
+ const readLocationStripeCustomerId = () => {
84
+ if (!canUseDom()) {
85
+ return null;
86
+ }
87
+ try {
88
+ const searchParams = new URLSearchParams(window.location.search);
89
+ for (const key of LOCATION_STRIPE_CUSTOMER_ID_QUERY_KEYS) {
90
+ const candidate = asString(searchParams.get(key));
91
+ if (candidate) {
92
+ return candidate;
93
+ }
94
+ }
95
+ }
96
+ catch (_a) {
97
+ return null;
98
+ }
99
+ return null;
100
+ };
52
101
  const buildSdkEvent = (event) => {
53
102
  return {
54
103
  eventType: event.eventType,
@@ -60,19 +109,27 @@ const buildSdkEvent = (event) => {
60
109
  metadata: event.metadata || {},
61
110
  };
62
111
  };
112
+ const withMergedAttributionDocument = (document, attribution) => {
113
+ const normalizedDocument = asRecord(document);
114
+ if (!attribution) {
115
+ return normalizedDocument;
116
+ }
117
+ return Object.assign(Object.assign({}, normalizedDocument), { attribution: mergeFunnelUserAttribution(normalizedDocument.attribution, attribution) });
118
+ };
63
119
  const toAppUser = (input) => {
64
- var _a;
120
+ var _a, _b;
65
121
  const apiUser = input.apiUser;
66
122
  const apiDocument = asRecord(apiUser === null || apiUser === void 0 ? void 0 : apiUser.document);
67
123
  const apiProgress = asRecord(apiDocument.progress);
68
124
  const apiAttributes = asRecord(apiProgress.attributes);
69
125
  return {
70
- id: input.externalUserId,
126
+ id: input.userId,
71
127
  name: asString(apiUser === null || apiUser === void 0 ? void 0 : apiUser.fullName) || input.fallbackName || '',
72
128
  email: asString(apiUser === null || apiUser === void 0 ? void 0 : apiUser.email) || input.fallbackEmail || '',
73
129
  attributes: Object.keys(apiAttributes).length > 0
74
130
  ? apiAttributes
75
131
  : ((_a = input.fallbackAttributes) !== null && _a !== void 0 ? _a : {}),
132
+ document: Object.keys(apiDocument).length > 0 ? apiDocument : ((_b = input.fallbackDocument) !== null && _b !== void 0 ? _b : {}),
76
133
  };
77
134
  };
78
135
  const parseErrorMessage = async (response, fallbackMessage) => {
@@ -113,21 +170,79 @@ class ApiService {
113
170
  });
114
171
  }
115
172
  getOrCreateClientUserId() {
116
- const existing = readStoredUserId();
173
+ const existing = readStoredUserId(FUNNEL_ID);
117
174
  if (existing) {
118
175
  return existing;
119
176
  }
120
- return persistUserId(generateUserId());
177
+ return persistUserId(generateUserId(), FUNNEL_ID);
178
+ }
179
+ getSubscriptionManagementUserId() {
180
+ const locationUserId = readLocationUserId();
181
+ if (locationUserId) {
182
+ return persistUserId(locationUserId, FUNNEL_ID);
183
+ }
184
+ if (this.getSubscriptionManagementStripeCustomerId()) {
185
+ return null;
186
+ }
187
+ return this.getOrCreateClientUserId();
188
+ }
189
+ getSubscriptionManagementStripeCustomerId() {
190
+ return readLocationStripeCustomerId();
191
+ }
192
+ getBootstrapCandidateUserId(fallbackUserId) {
193
+ return readLocationUserId() || normalizeUserId(fallbackUserId);
194
+ }
195
+ async fetchCurrentUserState(userId) {
196
+ const publishableKey = getFunnelSdkPublishableKey();
197
+ if (!publishableKey || !FUNNEL_ID) {
198
+ return null;
199
+ }
200
+ const url = new URL(buildMainApiUrl('/sdk/public/users/me'));
201
+ url.searchParams.set('funnelId', FUNNEL_ID);
202
+ url.searchParams.set('user_id', userId);
203
+ try {
204
+ const response = await fetch(url.toString(), {
205
+ method: 'GET',
206
+ headers: buildSdkHeaders({}),
207
+ });
208
+ if (!response.ok) {
209
+ return null;
210
+ }
211
+ return (await response.json());
212
+ }
213
+ catch (_a) {
214
+ return null;
215
+ }
216
+ }
217
+ async resolveBootstrapUserId(candidateUserId) {
218
+ var _a;
219
+ const storedUserId = readStoredUserId(FUNNEL_ID);
220
+ const fallbackUserId = storedUserId || generateUserId();
221
+ const normalizedCandidateUserId = normalizeUserId(candidateUserId);
222
+ const publishableKey = getFunnelSdkPublishableKey();
223
+ if (!normalizedCandidateUserId) {
224
+ return persistUserId(fallbackUserId, FUNNEL_ID);
225
+ }
226
+ if (normalizedCandidateUserId === storedUserId) {
227
+ return persistUserId(normalizedCandidateUserId, FUNNEL_ID);
228
+ }
229
+ if (!publishableKey || !FUNNEL_ID) {
230
+ return persistUserId(fallbackUserId, FUNNEL_ID);
231
+ }
232
+ const existingUser = await this.fetchCurrentUserState(normalizedCandidateUserId);
233
+ const resolvedCandidateUserId = normalizeUserId(asString((_a = existingUser === null || existingUser === void 0 ? void 0 : existingUser.user) === null || _a === void 0 ? void 0 : _a.user_id) || asString(existingUser === null || existingUser === void 0 ? void 0 : existingUser.user_id));
234
+ return persistUserId(resolvedCandidateUserId || fallbackUserId, FUNNEL_ID);
121
235
  }
122
236
  async bootstrapSession(input) {
123
237
  var _a;
124
- const externalUserId = persistUserId((input === null || input === void 0 ? void 0 : input.userId) || this.getOrCreateClientUserId());
238
+ const userId = await this.resolveBootstrapUserId(input === null || input === void 0 ? void 0 : input.userId);
125
239
  const publishableKey = getFunnelSdkPublishableKey();
126
240
  if (!publishableKey) {
127
241
  return toAppUser({
128
- externalUserId,
242
+ userId,
129
243
  fallbackName: input === null || input === void 0 ? void 0 : input.name,
130
244
  fallbackEmail: input === null || input === void 0 ? void 0 : input.email,
245
+ fallbackDocument: withMergedAttributionDocument({}, input === null || input === void 0 ? void 0 : input.attribution),
131
246
  });
132
247
  }
133
248
  const response = await fetch(buildMainApiUrl('/sdk/public/users/bootstrap'), {
@@ -138,12 +253,14 @@ class ApiService {
138
253
  body: JSON.stringify({
139
254
  publishableKey,
140
255
  funnelId: FUNNEL_ID || undefined,
141
- externalUserId,
256
+ funnelVersionId: FUNNEL_VERSION_ID || undefined,
257
+ user_id: userId,
142
258
  email: (input === null || input === void 0 ? void 0 : input.email) || undefined,
143
259
  fullName: (input === null || input === void 0 ? void 0 : input.name) || undefined,
144
260
  metadata: {
145
261
  source: 'funnel-session',
146
262
  },
263
+ attribution: input === null || input === void 0 ? void 0 : input.attribution,
147
264
  }),
148
265
  });
149
266
  if (!response.ok) {
@@ -151,25 +268,27 @@ class ApiService {
151
268
  throw new Error(errorMessage);
152
269
  }
153
270
  const payload = (await response.json());
154
- const persistedUserId = persistUserId(asString((_a = payload.user) === null || _a === void 0 ? void 0 : _a.externalUserId) ||
155
- asString(payload.externalUserId) ||
156
- externalUserId) || externalUserId;
271
+ const persistedUserId = persistUserId(asString((_a = payload.user) === null || _a === void 0 ? void 0 : _a.user_id) ||
272
+ asString(payload.user_id) ||
273
+ userId, FUNNEL_ID) || userId;
157
274
  return toAppUser({
158
275
  apiUser: payload.user,
159
- externalUserId: persistedUserId,
276
+ userId: persistedUserId,
160
277
  fallbackName: input === null || input === void 0 ? void 0 : input.name,
161
278
  fallbackEmail: input === null || input === void 0 ? void 0 : input.email,
279
+ fallbackDocument: withMergedAttributionDocument({}, input === null || input === void 0 ? void 0 : input.attribution),
162
280
  });
163
281
  }
164
- async updateUser(user) {
165
- const persistedUserId = persistUserId(user.id || this.getOrCreateClientUserId());
282
+ async updateUser(user, options) {
283
+ const persistedUserId = persistUserId(user.id || this.getOrCreateClientUserId(), FUNNEL_ID);
166
284
  const publishableKey = getFunnelSdkPublishableKey();
167
285
  if (!publishableKey) {
168
286
  return toAppUser({
169
- externalUserId: persistedUserId,
287
+ userId: persistedUserId,
170
288
  fallbackName: user.name,
171
289
  fallbackEmail: user.email,
172
290
  fallbackAttributes: user.attributes,
291
+ fallbackDocument: withMergedAttributionDocument(user.document, options === null || options === void 0 ? void 0 : options.attribution),
173
292
  });
174
293
  }
175
294
  const response = await fetch(buildMainApiUrl('/public/funnel-users/upsert'), {
@@ -180,7 +299,8 @@ class ApiService {
180
299
  body: JSON.stringify({
181
300
  publishableKey,
182
301
  funnelId: FUNNEL_ID || undefined,
183
- externalUserId: persistedUserId,
302
+ funnelVersionId: FUNNEL_VERSION_ID || undefined,
303
+ user_id: persistedUserId,
184
304
  email: user.email || undefined,
185
305
  fullName: user.name || undefined,
186
306
  progress: {
@@ -189,6 +309,7 @@ class ApiService {
189
309
  metadata: {
190
310
  source: 'funnel',
191
311
  },
312
+ attribution: options === null || options === void 0 ? void 0 : options.attribution,
192
313
  }),
193
314
  });
194
315
  if (!response.ok) {
@@ -198,19 +319,20 @@ class ApiService {
198
319
  const payload = (await response.json());
199
320
  return toAppUser({
200
321
  apiUser: payload.user,
201
- externalUserId: persistedUserId,
322
+ userId: persistedUserId,
202
323
  fallbackName: user.name,
203
324
  fallbackEmail: user.email,
204
325
  fallbackAttributes: user.attributes,
326
+ fallbackDocument: withMergedAttributionDocument(user.document, options === null || options === void 0 ? void 0 : options.attribution),
205
327
  });
206
328
  }
207
329
  async uploadTempPhoto(input) {
208
- const externalUserId = persistUserId(input.externalUserId || this.getOrCreateClientUserId());
330
+ const userId = persistUserId(input.userId || this.getOrCreateClientUserId(), FUNNEL_ID);
209
331
  const publishableKey = getFunnelSdkPublishableKey();
210
332
  const imageDataUrl = await this.readFileAsDataUrl(input.file);
211
333
  if (!publishableKey) {
212
334
  return {
213
- key: `local_${externalUserId}_${Date.now()}`,
335
+ key: `local_${userId}_${Date.now()}`,
214
336
  publicUrl: imageDataUrl,
215
337
  contentType: input.file.type || 'image/jpeg',
216
338
  sizeBytes: input.file.size || 0,
@@ -225,7 +347,7 @@ class ApiService {
225
347
  body: JSON.stringify({
226
348
  publishableKey,
227
349
  funnelId: FUNNEL_ID || undefined,
228
- externalUserId,
350
+ user_id: userId,
229
351
  fileName: input.file.name || 'capture.jpg',
230
352
  mimeType: input.file.type || undefined,
231
353
  imageDataUrl,
@@ -261,7 +383,8 @@ class ApiService {
261
383
  const payload = JSON.stringify({
262
384
  publishableKey,
263
385
  funnelId: FUNNEL_ID || undefined,
264
- externalUserId: event.userId,
386
+ funnelVersionId: FUNNEL_VERSION_ID || undefined,
387
+ user_id: event.userId,
265
388
  events: [buildSdkEvent(event)],
266
389
  });
267
390
  const endpoint = buildMainApiUrl('/sdk/public/events');
@@ -282,7 +405,7 @@ class ApiService {
282
405
  });
283
406
  }
284
407
  trackFunnelEvent(event) {
285
- const userId = persistUserId(event.userId || this.getOrCreateClientUserId());
408
+ const userId = persistUserId(event.userId || this.getOrCreateClientUserId(), FUNNEL_ID);
286
409
  this.sendEventAsync(Object.assign(Object.assign({}, event), { userId }));
287
410
  }
288
411
  trackStepStarted(input) {
@@ -306,10 +429,15 @@ class ApiService {
306
429
  });
307
430
  }
308
431
  async getManageSubscriptions() {
309
- const externalUserId = this.getOrCreateClientUserId();
310
- const query = new URLSearchParams({
311
- externalUserId,
312
- });
432
+ const userId = this.getSubscriptionManagementUserId();
433
+ const stripeCustomerId = this.getSubscriptionManagementStripeCustomerId();
434
+ const query = new URLSearchParams();
435
+ if (userId) {
436
+ query.set('user_id', userId);
437
+ }
438
+ if (stripeCustomerId) {
439
+ query.set('stripe_customer_id', stripeCustomerId);
440
+ }
313
441
  if (FUNNEL_ID) {
314
442
  query.set('funnelId', FUNNEL_ID);
315
443
  }
@@ -325,14 +453,16 @@ class ApiService {
325
453
  return (await response.json());
326
454
  }
327
455
  async updateSubscription(input) {
328
- const externalUserId = this.getOrCreateClientUserId();
456
+ const userId = this.getSubscriptionManagementUserId();
457
+ const stripeCustomerId = this.getSubscriptionManagementStripeCustomerId();
329
458
  const response = await fetch(buildMainApiUrl(`/sdk/public/subscriptions/${encodeURIComponent(input.subscriptionId)}/${input.action}`), {
330
459
  method: 'POST',
331
460
  headers: buildSdkHeaders({
332
461
  'Content-Type': 'application/json',
333
462
  }),
334
463
  body: JSON.stringify({
335
- externalUserId,
464
+ user_id: userId || undefined,
465
+ stripe_customer_id: stripeCustomerId || undefined,
336
466
  funnelId: FUNNEL_ID || undefined,
337
467
  }),
338
468
  });
@@ -0,0 +1,8 @@
1
+ export type PaywallStateOptions = {
2
+ funnelId?: string | null;
3
+ legacyKeys?: readonly string[];
4
+ };
5
+ export declare const buildPaywallStateStorageKey: (funnelId?: string | null | undefined) => string;
6
+ export declare const readPaywallStateValue: (options?: PaywallStateOptions) => string | null;
7
+ export declare const updatePaywallStateValue: (updater: (currentValue: string | null) => string, options?: PaywallStateOptions) => string;
8
+ export declare const clearPaywallStateValue: (options?: PaywallStateOptions) => void;
@@ -0,0 +1,44 @@
1
+ import { readWindowStorageValue, removeWindowStorageValue, writeWindowStorageValue, } from '../runtime/browser-helpers.js';
2
+ import { FUNNEL_ID } from './runtime-api.config.js';
3
+ const PAYWALL_STATE_STORAGE_PREFIX = 'fg_state';
4
+ const GLOBAL_FUNNEL_STATE_ID = 'global';
5
+ const normalizeStorageSegment = (value) => {
6
+ if (typeof value !== 'string') {
7
+ return null;
8
+ }
9
+ const trimmed = value.trim();
10
+ return trimmed || null;
11
+ };
12
+ export const buildPaywallStateStorageKey = (funnelId = FUNNEL_ID) => {
13
+ var _a;
14
+ const normalizedFunnelId = (_a = normalizeStorageSegment(funnelId)) !== null && _a !== void 0 ? _a : GLOBAL_FUNNEL_STATE_ID;
15
+ return `${PAYWALL_STATE_STORAGE_PREFIX}:${normalizedFunnelId}`;
16
+ };
17
+ const readLegacyPaywallStateValue = (legacyKeys) => {
18
+ for (const legacyKey of legacyKeys !== null && legacyKeys !== void 0 ? legacyKeys : []) {
19
+ const value = readWindowStorageValue(legacyKey);
20
+ if (value !== null) {
21
+ return value;
22
+ }
23
+ }
24
+ return null;
25
+ };
26
+ export const readPaywallStateValue = (options = {}) => {
27
+ const primaryValue = readWindowStorageValue(buildPaywallStateStorageKey(options.funnelId));
28
+ if (primaryValue !== null) {
29
+ return primaryValue;
30
+ }
31
+ return readLegacyPaywallStateValue(options.legacyKeys);
32
+ };
33
+ export const updatePaywallStateValue = (updater, options = {}) => {
34
+ const nextValue = updater(readPaywallStateValue(options));
35
+ writeWindowStorageValue(buildPaywallStateStorageKey(options.funnelId), nextValue);
36
+ return nextValue;
37
+ };
38
+ export const clearPaywallStateValue = (options = {}) => {
39
+ var _a;
40
+ removeWindowStorageValue(buildPaywallStateStorageKey(options.funnelId));
41
+ for (const legacyKey of (_a = options.legacyKeys) !== null && _a !== void 0 ? _a : []) {
42
+ removeWindowStorageValue(legacyKey);
43
+ }
44
+ };
@@ -1,4 +1,4 @@
1
- import type { ManageSubscriptionsResponse } from './api.service';
1
+ import type { ManageSubscriptionsResponse } from './api.service.js';
2
2
  export declare const isPreviewFrameRuntimeSearch: (search: string) => boolean;
3
3
  export declare const isPreviewFrameRuntime: () => boolean;
4
- export declare const buildPreviewManageSubscriptionsFallback: (externalUserId: string) => ManageSubscriptionsResponse;
4
+ export declare const buildPreviewManageSubscriptionsFallback: (userId: string) => ManageSubscriptionsResponse;
@@ -16,11 +16,11 @@ export const isPreviewFrameRuntime = () => {
16
16
  }
17
17
  return isPreviewFrameRuntimeSearch(window.location.search);
18
18
  };
19
- export const buildPreviewManageSubscriptionsFallback = (externalUserId) => {
19
+ export const buildPreviewManageSubscriptionsFallback = (userId) => {
20
20
  return {
21
21
  user: {
22
22
  id: 'preview-user',
23
- externalUserId,
23
+ user_id: userId,
24
24
  email: null,
25
25
  fullName: 'Preview user',
26
26
  subscriptionStatus: null,
@@ -0,0 +1,69 @@
1
+ import { isDevelopmentRuntime, readRuntimeEnv } from '../config/env.config.js';
2
+ export { isDevelopmentRuntime, readRuntimeEnv };
3
+ export declare const RUNTIME_PUBLIC_ENV_KEYS: {
4
+ readonly apiBaseUrl: readonly ["NEXT_PUBLIC_APP_DEALS_API_BASE_URL", "APP_DEALS_API_BASE_URL"];
5
+ readonly funnelSdkPublishableKey: readonly ["NEXT_PUBLIC_FUNNEL_SDK_PUBLISHABLE_KEY", "NEXT_PUBLIC_FUNNEL_PUBLISHABLE_KEY", "FUNNEL_SDK_PUBLISHABLE_KEY"];
6
+ readonly funnelId: readonly ["NEXT_PUBLIC_FUNNEL_ID", "FUNNEL_ID", "NEXT_PUBLIC_RAG_FUNNEL_ID", "RAG_FUNNEL_ID", "NEXT_PUBLIC_PREVIEW_TEST_FUNNEL_ID", "PREVIEW_TEST_FUNNEL_ID"];
7
+ readonly funnelVersionId: readonly ["NEXT_PUBLIC_FUNNEL_VERSION_ID", "FUNNEL_VERSION_ID"];
8
+ readonly projectId: readonly ["NEXT_PUBLIC_PROJECT_ID", "PROJECT_ID"];
9
+ readonly posthogProjectApiKey: readonly ["NEXT_PUBLIC_POSTHOG_PROJECT_API_KEY", "POSTHOG_PROJECT_API_KEY"];
10
+ readonly supportEmail: readonly ["NEXT_PUBLIC_FUNNEL_SUPPORT_EMAIL", "FUNNEL_SUPPORT_EMAIL"];
11
+ readonly iosAppStoreUrl: readonly ["NEXT_PUBLIC_IOS_APP_STORE_URL", "IOS_APP_STORE_URL"];
12
+ readonly androidPlayStoreUrl: readonly ["NEXT_PUBLIC_ANDROID_PLAY_STORE_URL", "ANDROID_PLAY_STORE_URL", "GOOGLE_PLAY_URL"];
13
+ readonly appStoreId: readonly ["NEXT_PUBLIC_FUNNEL_APP_STORE_ID", "FUNNEL_APP_STORE_ID"];
14
+ readonly googleStoreId: readonly ["NEXT_PUBLIC_FUNNEL_GOOGLE_STORE_ID", "FUNNEL_GOOGLE_STORE_ID"];
15
+ readonly timezone: readonly ["NEXT_PUBLIC_FUNNEL_TIMEZONE", "FUNNEL_TIMEZONE"];
16
+ readonly faviconUrl: readonly ["NEXT_PUBLIC_FUNNEL_FAVICON_URL", "FUNNEL_FAVICON_URL"];
17
+ readonly pageTitle: readonly ["NEXT_PUBLIC_FUNNEL_PAGE_TITLE", "FUNNEL_PAGE_TITLE"];
18
+ readonly pageDescription: readonly ["NEXT_PUBLIC_FUNNEL_PAGE_DESCRIPTION", "FUNNEL_PAGE_DESCRIPTION"];
19
+ readonly pagePreviewImageUrl: readonly ["NEXT_PUBLIC_FUNNEL_PAGE_PREVIEW_IMAGE_URL", "FUNNEL_PAGE_PREVIEW_IMAGE_URL"];
20
+ readonly companyName: readonly ["NEXT_PUBLIC_FUNNEL_COMPANY_NAME", "FUNNEL_COMPANY_NAME"];
21
+ readonly companyAddress: readonly ["NEXT_PUBLIC_FUNNEL_COMPANY_ADDRESS", "FUNNEL_COMPANY_ADDRESS"];
22
+ readonly companyCountry: readonly ["NEXT_PUBLIC_FUNNEL_COMPANY_COUNTRY", "FUNNEL_COMPANY_COUNTRY"];
23
+ readonly privacyUrl: readonly ["NEXT_PUBLIC_FUNNEL_PRIVACY_URL", "FUNNEL_PRIVACY_URL"];
24
+ readonly termsUrl: readonly ["NEXT_PUBLIC_FUNNEL_TERMS_URL", "FUNNEL_TERMS_URL"];
25
+ readonly universalLink: readonly ["NEXT_PUBLIC_FUNNEL_UNIVERSAL_LINK", "WEB_LANDING_URL", "FUNNEL_UNIVERSAL_LINK"];
26
+ readonly iosDeepLink: readonly ["NEXT_PUBLIC_FUNNEL_IOS_DEEP_LINK", "APPSTORE_DEEPLINK_URL", "FUNNEL_IOS_DEEP_LINK"];
27
+ readonly androidDeepLink: readonly ["NEXT_PUBLIC_FUNNEL_ANDROID_DEEP_LINK", "FUNNEL_ANDROID_DEEP_LINK"];
28
+ readonly qrTarget: readonly ["NEXT_PUBLIC_FUNNEL_QR_TARGET", "FUNNEL_QR_TARGET"];
29
+ };
30
+ export type RuntimePublicConfig = {
31
+ apiBaseUrl: string;
32
+ funnelSdkPublishableKey: string;
33
+ funnelId: string;
34
+ funnelVersionId: string;
35
+ projectId: string;
36
+ posthogProjectApiKey: string;
37
+ supportEmail: string;
38
+ iosAppStoreUrl: string | null;
39
+ androidPlayStoreUrl: string | null;
40
+ appStoreId: string | null;
41
+ googleStoreId: string | null;
42
+ timezone: string;
43
+ faviconUrl: string | null;
44
+ pageTitle: string | null;
45
+ pageDescription: string | null;
46
+ pagePreviewImageUrl: string | null;
47
+ companyName: string | null;
48
+ companyAddress: string | null;
49
+ companyCountry: string | null;
50
+ privacyUrl: string | null;
51
+ termsUrl: string | null;
52
+ universalLink: string | null;
53
+ iosDeepLink: string | null;
54
+ androidDeepLink: string | null;
55
+ qrTarget: string | null;
56
+ };
57
+ export declare const runtimePublicConfig: RuntimePublicConfig;
58
+ export declare const RUNTIME_PUBLIC_DEFAULTS: {
59
+ readonly apiBaseUrl: "https://sdk-api.funnelsgrove.com";
60
+ readonly funnelSdkPublishableKey: "pk_test_app_deals_seed_public";
61
+ readonly supportEmail: "support@example.com";
62
+ readonly timezone: "UTC";
63
+ };
64
+ export declare const RUNTIME_PREVIEW_PAYMENT_DEFAULTS: {
65
+ readonly funnelSdkPublishableKey: "pk_test_51SopXWPEfkEVo5kh6foOs9zVyKsVSKLhlQw8YsAfMMymRlGH3Qp8U5I7j5bHivmOB232oJiQZi8cEv1wkmV80QGg00vPcI1DZI";
66
+ };
67
+ export declare const isSeedFunnelSdkPublishableKey: (value: string | null | undefined) => boolean;
68
+ export declare const isPreviewPaymentPublishableKey: (value: string | null | undefined) => boolean;
69
+ export declare const resolvePreviewPaymentPublishableKey: (value: string | null | undefined) => string;
@@ -0,0 +1,105 @@
1
+ import { RUNTIME_ENV_KEYS, isDevelopmentRuntime, readRuntimeEnv, runtimeEnvConfig, } from '../config/env.config.js';
2
+ export { isDevelopmentRuntime, readRuntimeEnv };
3
+ const DEFAULT_API_BASE_URL = 'https://sdk-api.funnelsgrove.com';
4
+ const DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY = 'pk_test_app_deals_seed_public';
5
+ const DEFAULT_SUPPORT_EMAIL = 'support@example.com';
6
+ const DEFAULT_TIMEZONE = 'UTC';
7
+ const DEFAULT_PREVIEW_PAYMENT_PUBLISHABLE_KEY = 'pk_test_51SopXWPEfkEVo5kh6foOs9zVyKsVSKLhlQw8YsAfMMymRlGH3Qp8U5I7j5bHivmOB232oJiQZi8cEv1wkmV80QGg00vPcI1DZI';
8
+ const trimTrailingSlash = (value) => {
9
+ return value.replace(/\/+$/, '');
10
+ };
11
+ const toOptionalEnv = (value) => {
12
+ const normalized = (value === null || value === void 0 ? void 0 : value.trim()) || '';
13
+ return normalized || null;
14
+ };
15
+ const toIosAppStoreUrlFromId = (value) => {
16
+ const normalized = (value === null || value === void 0 ? void 0 : value.trim().replace(/^id/i, '')) || '';
17
+ return normalized ? `https://apps.apple.com/app/id${normalized}` : null;
18
+ };
19
+ const toAndroidPlayStoreUrlFromId = (value) => {
20
+ const normalized = (value === null || value === void 0 ? void 0 : value.trim()) || '';
21
+ return normalized ? `https://play.google.com/store/apps/details?id=${normalized}` : null;
22
+ };
23
+ const readRequiredRuntimeValue = (value, fallback) => {
24
+ return (value !== null && value !== void 0 ? value : fallback).trim() || fallback;
25
+ };
26
+ export const RUNTIME_PUBLIC_ENV_KEYS = {
27
+ apiBaseUrl: RUNTIME_ENV_KEYS.appDealsApiBaseUrl,
28
+ funnelSdkPublishableKey: RUNTIME_ENV_KEYS.funnelSdkPublishableKey,
29
+ funnelId: RUNTIME_ENV_KEYS.funnelId,
30
+ funnelVersionId: RUNTIME_ENV_KEYS.funnelVersionId,
31
+ projectId: RUNTIME_ENV_KEYS.projectId,
32
+ posthogProjectApiKey: RUNTIME_ENV_KEYS.posthogProjectApiKey,
33
+ supportEmail: RUNTIME_ENV_KEYS.supportEmail,
34
+ iosAppStoreUrl: RUNTIME_ENV_KEYS.iosAppStoreUrl,
35
+ androidPlayStoreUrl: RUNTIME_ENV_KEYS.androidPlayStoreUrl,
36
+ appStoreId: RUNTIME_ENV_KEYS.appStoreId,
37
+ googleStoreId: RUNTIME_ENV_KEYS.googleStoreId,
38
+ timezone: RUNTIME_ENV_KEYS.timezone,
39
+ faviconUrl: RUNTIME_ENV_KEYS.faviconUrl,
40
+ pageTitle: RUNTIME_ENV_KEYS.pageTitle,
41
+ pageDescription: RUNTIME_ENV_KEYS.pageDescription,
42
+ pagePreviewImageUrl: RUNTIME_ENV_KEYS.pagePreviewImageUrl,
43
+ companyName: RUNTIME_ENV_KEYS.companyName,
44
+ companyAddress: RUNTIME_ENV_KEYS.companyAddress,
45
+ companyCountry: RUNTIME_ENV_KEYS.companyCountry,
46
+ privacyUrl: RUNTIME_ENV_KEYS.privacyUrl,
47
+ termsUrl: RUNTIME_ENV_KEYS.termsUrl,
48
+ universalLink: RUNTIME_ENV_KEYS.universalLink,
49
+ iosDeepLink: RUNTIME_ENV_KEYS.iosDeepLink,
50
+ androidDeepLink: RUNTIME_ENV_KEYS.androidDeepLink,
51
+ qrTarget: RUNTIME_ENV_KEYS.qrTarget,
52
+ };
53
+ const appStoreId = toOptionalEnv(runtimeEnvConfig.appStoreId);
54
+ const googleStoreId = toOptionalEnv(runtimeEnvConfig.googleStoreId);
55
+ export const runtimePublicConfig = Object.freeze({
56
+ apiBaseUrl: trimTrailingSlash(readRequiredRuntimeValue(runtimeEnvConfig.appDealsApiBaseUrl, DEFAULT_API_BASE_URL)),
57
+ funnelSdkPublishableKey: readRequiredRuntimeValue(runtimeEnvConfig.funnelSdkPublishableKey, DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY),
58
+ funnelId: readRequiredRuntimeValue(runtimeEnvConfig.funnelId, ''),
59
+ funnelVersionId: readRequiredRuntimeValue(runtimeEnvConfig.funnelVersionId, ''),
60
+ projectId: readRequiredRuntimeValue(runtimeEnvConfig.projectId, ''),
61
+ posthogProjectApiKey: readRequiredRuntimeValue(runtimeEnvConfig.posthogProjectApiKey, ''),
62
+ supportEmail: readRequiredRuntimeValue(runtimeEnvConfig.supportEmail, DEFAULT_SUPPORT_EMAIL),
63
+ iosAppStoreUrl: toOptionalEnv(runtimeEnvConfig.iosAppStoreUrl) || toIosAppStoreUrlFromId(appStoreId),
64
+ androidPlayStoreUrl: toOptionalEnv(runtimeEnvConfig.androidPlayStoreUrl) ||
65
+ toAndroidPlayStoreUrlFromId(googleStoreId),
66
+ appStoreId,
67
+ googleStoreId,
68
+ timezone: readRequiredRuntimeValue(runtimeEnvConfig.timezone, DEFAULT_TIMEZONE),
69
+ faviconUrl: toOptionalEnv(runtimeEnvConfig.faviconUrl),
70
+ pageTitle: toOptionalEnv(runtimeEnvConfig.pageTitle),
71
+ pageDescription: toOptionalEnv(runtimeEnvConfig.pageDescription),
72
+ pagePreviewImageUrl: toOptionalEnv(runtimeEnvConfig.pagePreviewImageUrl),
73
+ companyName: toOptionalEnv(runtimeEnvConfig.companyName),
74
+ companyAddress: toOptionalEnv(runtimeEnvConfig.companyAddress),
75
+ companyCountry: toOptionalEnv(runtimeEnvConfig.companyCountry),
76
+ privacyUrl: toOptionalEnv(runtimeEnvConfig.privacyUrl),
77
+ termsUrl: toOptionalEnv(runtimeEnvConfig.termsUrl),
78
+ universalLink: toOptionalEnv(runtimeEnvConfig.universalLink),
79
+ iosDeepLink: toOptionalEnv(runtimeEnvConfig.iosDeepLink),
80
+ androidDeepLink: toOptionalEnv(runtimeEnvConfig.androidDeepLink),
81
+ qrTarget: toOptionalEnv(runtimeEnvConfig.qrTarget),
82
+ });
83
+ export const RUNTIME_PUBLIC_DEFAULTS = {
84
+ apiBaseUrl: DEFAULT_API_BASE_URL,
85
+ funnelSdkPublishableKey: DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY,
86
+ supportEmail: DEFAULT_SUPPORT_EMAIL,
87
+ timezone: DEFAULT_TIMEZONE,
88
+ };
89
+ export const RUNTIME_PREVIEW_PAYMENT_DEFAULTS = {
90
+ funnelSdkPublishableKey: DEFAULT_PREVIEW_PAYMENT_PUBLISHABLE_KEY,
91
+ };
92
+ export const isSeedFunnelSdkPublishableKey = (value) => {
93
+ const normalized = (value === null || value === void 0 ? void 0 : value.trim()) || '';
94
+ return !normalized || normalized === DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY;
95
+ };
96
+ export const isPreviewPaymentPublishableKey = (value) => {
97
+ return ((value === null || value === void 0 ? void 0 : value.trim()) || '') === DEFAULT_PREVIEW_PAYMENT_PUBLISHABLE_KEY;
98
+ };
99
+ export const resolvePreviewPaymentPublishableKey = (value) => {
100
+ const normalized = (value === null || value === void 0 ? void 0 : value.trim()) || '';
101
+ if (isSeedFunnelSdkPublishableKey(normalized)) {
102
+ return DEFAULT_PREVIEW_PAYMENT_PUBLISHABLE_KEY;
103
+ }
104
+ return normalized;
105
+ };
@@ -1,6 +1,11 @@
1
1
  export declare const APP_DEALS_API_BASE_URL: string;
2
2
  export declare const FUNNEL_SDK_PUBLISHABLE_KEY: string;
3
3
  export declare const FUNNEL_ID: string;
4
+ export declare const FUNNEL_VERSION_ID: string;
5
+ export declare const PROJECT_ID: string;
6
+ export declare const POSTHOG_API_HOST = "https://us.i.posthog.com";
7
+ export declare const POSTHOG_PROJECT_API_KEY: string;
8
+ export declare const hasPostHogRuntimeConfig: () => boolean;
4
9
  export declare const hasRealFunnelSdkPublishableKey: (value?: string) => boolean;
5
10
  export declare const getFunnelSdkPublishableKey: () => string | null;
6
11
  export declare const buildMainApiUrl: (path: string) => string;
@@ -1,6 +1,4 @@
1
- var _a, _b, _c, _d;
2
- const DEFAULT_API_BASE_URL = 'https://sdk-api.funnelsgrove.com';
3
- const DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY = 'pk_test_app_deals_seed_public';
1
+ import { RUNTIME_PUBLIC_DEFAULTS, runtimePublicConfig } from './public-env.js';
4
2
  const trimTrailingSlash = (value) => {
5
3
  return value.replace(/\/+$/, '');
6
4
  };
@@ -18,12 +16,19 @@ const isPreviewFrameRuntime = () => {
18
16
  return false;
19
17
  }
20
18
  };
21
- export const APP_DEALS_API_BASE_URL = trimTrailingSlash((_a = process.env.NEXT_PUBLIC_APP_DEALS_API_BASE_URL) !== null && _a !== void 0 ? _a : DEFAULT_API_BASE_URL);
22
- export const FUNNEL_SDK_PUBLISHABLE_KEY = ((_c = (_b = process.env.NEXT_PUBLIC_FUNNEL_SDK_PUBLISHABLE_KEY) !== null && _b !== void 0 ? _b : process.env.NEXT_PUBLIC_FUNNEL_PUBLISHABLE_KEY) !== null && _c !== void 0 ? _c : DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY).trim();
23
- export const FUNNEL_ID = ((_d = process.env.NEXT_PUBLIC_FUNNEL_ID) !== null && _d !== void 0 ? _d : '').trim();
19
+ export const APP_DEALS_API_BASE_URL = trimTrailingSlash(runtimePublicConfig.apiBaseUrl);
20
+ export const FUNNEL_SDK_PUBLISHABLE_KEY = runtimePublicConfig.funnelSdkPublishableKey;
21
+ export const FUNNEL_ID = runtimePublicConfig.funnelId;
22
+ export const FUNNEL_VERSION_ID = runtimePublicConfig.funnelVersionId;
23
+ export const PROJECT_ID = runtimePublicConfig.projectId;
24
+ export const POSTHOG_API_HOST = 'https://us.i.posthog.com';
25
+ export const POSTHOG_PROJECT_API_KEY = runtimePublicConfig.posthogProjectApiKey;
26
+ export const hasPostHogRuntimeConfig = () => {
27
+ return Boolean(POSTHOG_API_HOST && POSTHOG_PROJECT_API_KEY && PROJECT_ID);
28
+ };
24
29
  export const hasRealFunnelSdkPublishableKey = (value = FUNNEL_SDK_PUBLISHABLE_KEY) => {
25
30
  const normalized = value.trim();
26
- return Boolean(normalized) && normalized !== DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY;
31
+ return Boolean(normalized) && normalized !== RUNTIME_PUBLIC_DEFAULTS.funnelSdkPublishableKey;
27
32
  };
28
33
  export const getFunnelSdkPublishableKey = () => {
29
34
  if (isPreviewFrameRuntime()) {