@tagadapay/plugin-sdk 3.1.2 → 3.1.8

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 (56) hide show
  1. package/README.md +1129 -1129
  2. package/build-cdn.js +113 -113
  3. package/dist/external-tracker.js +1104 -491
  4. package/dist/external-tracker.min.js +2 -2
  5. package/dist/external-tracker.min.js.map +4 -4
  6. package/dist/react/hooks/useApplePay.js +25 -36
  7. package/dist/react/hooks/usePaymentPolling.d.ts +9 -3
  8. package/dist/react/providers/TagadaProvider.js +5 -5
  9. package/dist/react/utils/money.d.ts +4 -3
  10. package/dist/react/utils/money.js +39 -6
  11. package/dist/react/utils/trackingUtils.js +1 -0
  12. package/dist/v2/core/client.js +34 -2
  13. package/dist/v2/core/config/environment.js +9 -2
  14. package/dist/v2/core/funnelClient.d.ts +92 -1
  15. package/dist/v2/core/funnelClient.js +247 -3
  16. package/dist/v2/core/resources/apiClient.js +1 -1
  17. package/dist/v2/core/resources/checkout.d.ts +68 -0
  18. package/dist/v2/core/resources/funnel.d.ts +15 -0
  19. package/dist/v2/core/resources/payments.d.ts +50 -3
  20. package/dist/v2/core/resources/payments.js +38 -7
  21. package/dist/v2/core/utils/pluginConfig.js +40 -5
  22. package/dist/v2/core/utils/previewMode.d.ts +3 -0
  23. package/dist/v2/core/utils/previewMode.js +44 -14
  24. package/dist/v2/core/utils/previewModeIndicator.d.ts +19 -0
  25. package/dist/v2/core/utils/previewModeIndicator.js +414 -0
  26. package/dist/v2/core/utils/tokenStorage.d.ts +4 -0
  27. package/dist/v2/core/utils/tokenStorage.js +15 -1
  28. package/dist/v2/index.d.ts +6 -1
  29. package/dist/v2/index.js +6 -1
  30. package/dist/v2/react/components/ApplePayButton.d.ts +21 -121
  31. package/dist/v2/react/components/ApplePayButton.js +221 -290
  32. package/dist/v2/react/components/FunnelScriptInjector.d.ts +3 -1
  33. package/dist/v2/react/components/FunnelScriptInjector.js +128 -24
  34. package/dist/v2/react/components/PreviewModeIndicator.d.ts +46 -0
  35. package/dist/v2/react/components/PreviewModeIndicator.js +113 -0
  36. package/dist/v2/react/hooks/useApplePayCheckout.d.ts +16 -0
  37. package/dist/v2/react/hooks/useApplePayCheckout.js +193 -0
  38. package/dist/v2/react/hooks/useFunnel.d.ts +42 -6
  39. package/dist/v2/react/hooks/useFunnel.js +25 -5
  40. package/dist/v2/react/hooks/usePaymentPolling.d.ts +9 -3
  41. package/dist/v2/react/hooks/usePaymentPolling.js +31 -9
  42. package/dist/v2/react/hooks/usePaymentQuery.d.ts +32 -2
  43. package/dist/v2/react/hooks/usePaymentQuery.js +304 -7
  44. package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +26 -0
  45. package/dist/v2/react/hooks/usePaymentRetrieve.js +175 -0
  46. package/dist/v2/react/hooks/useStepConfig.d.ts +62 -0
  47. package/dist/v2/react/hooks/useStepConfig.js +52 -0
  48. package/dist/v2/react/index.d.ts +9 -3
  49. package/dist/v2/react/index.js +5 -1
  50. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +27 -19
  51. package/dist/v2/react/providers/TagadaProvider.js +7 -7
  52. package/dist/v2/standalone/external-tracker.d.ts +2 -0
  53. package/dist/v2/standalone/external-tracker.js +6 -3
  54. package/package.json +112 -112
  55. package/dist/v2/react/hooks/useApplePay.d.ts +0 -16
  56. package/dist/v2/react/hooks/useApplePay.js +0 -247
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TagadaPay External Tracker v3.1.2
2
+ * TagadaPay External Tracker v3.1.8
3
3
  * CDN Bundle - Standalone tracking for external pages (Debug Build)
4
4
  * @license MIT
5
5
  */
@@ -30,6 +30,9 @@ var TagadaTrackerBundle = (() => {
30
30
  return a;
31
31
  };
32
32
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
33
+ var __esm = (fn, res) => function __init() {
34
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
35
+ };
33
36
  var __export = (target, all3) => {
34
37
  for (var name in all3)
35
38
  __defProp(target, name, { get: all3[name], enumerable: true });
@@ -88,13 +91,6 @@ var TagadaTrackerBundle = (() => {
88
91
  };
89
92
  var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
90
93
 
91
- // src/v2/standalone/external-tracker.ts
92
- var external_tracker_exports = {};
93
- __export(external_tracker_exports, {
94
- TagadaExternalTracker: () => TagadaExternalTracker,
95
- TagadaTracker: () => TagadaTracker
96
- });
97
-
98
94
  // src/v2/core/config/environment.ts
99
95
  function getCookie(name) {
100
96
  var _a;
@@ -104,62 +100,6 @@ var TagadaTrackerBundle = (() => {
104
100
  if (parts.length === 2) return ((_a = parts.pop()) == null ? void 0 : _a.split(";").shift()) || null;
105
101
  return null;
106
102
  }
107
- var ENVIRONMENT_CONFIGS = {
108
- production: {
109
- baseUrl: "https://app.tagadapay.com",
110
- endpoints: {
111
- checkout: {
112
- sessionInit: "/api/v1/checkout/session/init",
113
- sessionInitAsync: "/api/v1/checkout/session/init-async",
114
- sessionStatus: "/api/v1/checkout/session/status",
115
- asyncStatus: "/api/public/v1/checkout/async-status"
116
- },
117
- customer: {
118
- profile: "/api/v1/customer/profile",
119
- session: "/api/v1/customer/session"
120
- },
121
- store: {
122
- config: "/api/v1/store/config"
123
- }
124
- }
125
- },
126
- development: {
127
- baseUrl: "https://app.tagadapay.dev",
128
- endpoints: {
129
- checkout: {
130
- sessionInit: "/api/v1/checkout/session/init",
131
- sessionInitAsync: "/api/v1/checkout/session/init-async",
132
- sessionStatus: "/api/v1/checkout/session/status",
133
- asyncStatus: "/api/public/v1/checkout/async-status"
134
- },
135
- customer: {
136
- profile: "/api/v1/customer/profile",
137
- session: "/api/v1/customer/session"
138
- },
139
- store: {
140
- config: "/api/v1/store/config"
141
- }
142
- }
143
- },
144
- local: {
145
- baseUrl: "http://app.localhost:3000",
146
- endpoints: {
147
- checkout: {
148
- sessionInit: "/api/v1/checkout/session/init",
149
- sessionInitAsync: "/api/v1/checkout/session/init-async",
150
- sessionStatus: "/api/v1/checkout/session/status",
151
- asyncStatus: "/api/public/v1/checkout/async-status"
152
- },
153
- customer: {
154
- profile: "/api/v1/customer/profile",
155
- session: "/api/v1/customer/session"
156
- },
157
- store: {
158
- config: "/api/v1/store/config"
159
- }
160
- }
161
- }
162
- };
163
103
  function getEnvironmentConfig(environment = "local") {
164
104
  const apiConfig = ENVIRONMENT_CONFIGS[environment];
165
105
  if (!apiConfig) {
@@ -196,6 +136,7 @@ var TagadaTrackerBundle = (() => {
196
136
  }
197
137
  function detectEnvironment() {
198
138
  var _a;
139
+ console.log("[SDK] detectEnvironment() called");
199
140
  if (typeof window === "undefined") {
200
141
  return "local";
201
142
  }
@@ -215,7 +156,9 @@ var TagadaTrackerBundle = (() => {
215
156
  }
216
157
  const hostname = window.location.hostname;
217
158
  const href = window.location.href;
218
- if (hostname === "localhost" || hostname.startsWith("127.") || hostname.startsWith("192.168.") || hostname.startsWith("10.") || hostname.includes(".local") || hostname === "" || hostname === "0.0.0.0") {
159
+ console.log('[SDK] detectEnvironment() - hostname: "'.concat(hostname, '"'));
160
+ if (hostname === "localhost" || hostname.startsWith("127.") || hostname.startsWith("192.168.") || hostname.startsWith("10.") || hostname.includes(".local") || hostname === "" || hostname === "0.0.0.0" || hostname.includes("ngrok-free.dev") || hostname.includes("ngrok-free.app") || hostname.includes("ngrok.io") || hostname.includes("ngrok.app")) {
161
+ console.log("[SDK] detectEnvironment() - returning LOCAL");
219
162
  if (typeof window !== "undefined" && ((_a = window == null ? void 0 : window.__TAGADA_ENV__) == null ? void 0 : _a.TAGADA_ENVIRONMENT)) {
220
163
  const override = window.__TAGADA_ENV__.TAGADA_ENVIRONMENT.toLowerCase();
221
164
  if (override === "production" || override === "development" || override === "local") {
@@ -244,82 +187,154 @@ var TagadaTrackerBundle = (() => {
244
187
  }
245
188
  return true;
246
189
  }
190
+ var ENVIRONMENT_CONFIGS;
191
+ var init_environment = __esm({
192
+ "src/v2/core/config/environment.ts"() {
193
+ "use strict";
194
+ ENVIRONMENT_CONFIGS = {
195
+ production: {
196
+ baseUrl: "https://app.tagadapay.com",
197
+ endpoints: {
198
+ checkout: {
199
+ sessionInit: "/api/v1/checkout/session/init",
200
+ sessionInitAsync: "/api/v1/checkout/session/init-async",
201
+ sessionStatus: "/api/v1/checkout/session/status",
202
+ asyncStatus: "/api/public/v1/checkout/async-status"
203
+ },
204
+ customer: {
205
+ profile: "/api/v1/customer/profile",
206
+ session: "/api/v1/customer/session"
207
+ },
208
+ store: {
209
+ config: "/api/v1/store/config"
210
+ }
211
+ }
212
+ },
213
+ development: {
214
+ baseUrl: "https://app.tagadapay.dev",
215
+ endpoints: {
216
+ checkout: {
217
+ sessionInit: "/api/v1/checkout/session/init",
218
+ sessionInitAsync: "/api/v1/checkout/session/init-async",
219
+ sessionStatus: "/api/v1/checkout/session/status",
220
+ asyncStatus: "/api/public/v1/checkout/async-status"
221
+ },
222
+ customer: {
223
+ profile: "/api/v1/customer/profile",
224
+ session: "/api/v1/customer/session"
225
+ },
226
+ store: {
227
+ config: "/api/v1/store/config"
228
+ }
229
+ }
230
+ },
231
+ local: {
232
+ baseUrl: "http://app.localhost:3000",
233
+ endpoints: {
234
+ checkout: {
235
+ sessionInit: "/api/v1/checkout/session/init",
236
+ sessionInitAsync: "/api/v1/checkout/session/init-async",
237
+ sessionStatus: "/api/v1/checkout/session/status",
238
+ asyncStatus: "/api/public/v1/checkout/async-status"
239
+ },
240
+ customer: {
241
+ profile: "/api/v1/customer/profile",
242
+ session: "/api/v1/customer/session"
243
+ },
244
+ store: {
245
+ config: "/api/v1/store/config"
246
+ }
247
+ }
248
+ }
249
+ };
250
+ }
251
+ });
247
252
 
248
253
  // src/v2/core/resources/funnel.ts
249
- var FunnelResource = class {
250
- constructor(apiClient) {
251
- this.apiClient = apiClient;
252
- }
253
- /**
254
- * Initialize a funnel session
255
- */
256
- async initialize(request) {
257
- return this.apiClient.post("/api/v1/funnel/initialize", request);
258
- }
259
- /**
260
- * Navigate to next step in funnel
261
- */
262
- async navigate(request) {
263
- return this.apiClient.post("/api/v1/funnel/navigate", request);
264
- }
265
- /**
266
- * Update funnel context
267
- */
268
- async updateContext(sessionId, request) {
269
- return this.apiClient.patch("/api/v1/funnel/context/".concat(sessionId), request);
270
- }
271
- /**
272
- * End funnel session
273
- */
274
- async endSession(sessionId) {
275
- return this.apiClient.delete("/api/v1/funnel/session/".concat(sessionId));
276
- }
277
- /**
278
- * Get funnel session by ID
279
- * @param sessionId - The session ID to fetch
280
- * @param currentUrl - Optional current URL for session synchronization on page load
281
- */
282
- async getSession(sessionId, currentUrl, includeDebugData) {
283
- const params = new URLSearchParams();
284
- if (currentUrl) {
285
- params.append("currentUrl", currentUrl);
286
- }
287
- if (includeDebugData) {
288
- params.append("includeDebugData", "true");
289
- }
290
- const url = "/api/v1/funnel/session/".concat(sessionId).concat(params.toString() ? "?".concat(params) : "");
291
- return this.apiClient.get(url);
254
+ var FunnelResource;
255
+ var init_funnel = __esm({
256
+ "src/v2/core/resources/funnel.ts"() {
257
+ "use strict";
258
+ FunnelResource = class {
259
+ constructor(apiClient) {
260
+ this.apiClient = apiClient;
261
+ }
262
+ /**
263
+ * Initialize a funnel session
264
+ */
265
+ async initialize(request) {
266
+ return this.apiClient.post("/api/v1/funnel/initialize", request);
267
+ }
268
+ /**
269
+ * Navigate to next step in funnel
270
+ */
271
+ async navigate(request) {
272
+ return this.apiClient.post("/api/v1/funnel/navigate", request);
273
+ }
274
+ /**
275
+ * Update funnel context
276
+ */
277
+ async updateContext(sessionId, request) {
278
+ return this.apiClient.patch("/api/v1/funnel/context/".concat(sessionId), request);
279
+ }
280
+ /**
281
+ * End funnel session
282
+ */
283
+ async endSession(sessionId) {
284
+ return this.apiClient.delete("/api/v1/funnel/session/".concat(sessionId));
285
+ }
286
+ /**
287
+ * Get funnel session by ID
288
+ * @param sessionId - The session ID to fetch
289
+ * @param currentUrl - Optional current URL for session synchronization on page load
290
+ */
291
+ async getSession(sessionId, currentUrl, includeDebugData) {
292
+ const params = new URLSearchParams();
293
+ if (currentUrl) {
294
+ params.append("currentUrl", currentUrl);
295
+ }
296
+ if (includeDebugData) {
297
+ params.append("includeDebugData", "true");
298
+ }
299
+ const url = "/api/v1/funnel/session/".concat(sessionId).concat(params.toString() ? "?".concat(params) : "");
300
+ return this.apiClient.get(url);
301
+ }
302
+ };
292
303
  }
293
- };
304
+ });
294
305
 
295
306
  // src/v2/core/utils/eventDispatcher.ts
296
- var EventDispatcher = class {
297
- constructor() {
298
- this.listeners = /* @__PURE__ */ new Set();
299
- }
300
- subscribe(listener) {
301
- this.listeners.add(listener);
302
- return () => {
303
- this.listeners.delete(listener);
304
- };
305
- }
306
- notify(data) {
307
- this.listeners.forEach((listener) => {
308
- try {
309
- listener(data);
310
- } catch (error) {
311
- console.error("Error in event listener:", error);
307
+ var EventDispatcher;
308
+ var init_eventDispatcher = __esm({
309
+ "src/v2/core/utils/eventDispatcher.ts"() {
310
+ "use strict";
311
+ EventDispatcher = class {
312
+ constructor() {
313
+ this.listeners = /* @__PURE__ */ new Set();
314
+ }
315
+ subscribe(listener) {
316
+ this.listeners.add(listener);
317
+ return () => {
318
+ this.listeners.delete(listener);
319
+ };
312
320
  }
313
- });
314
- }
315
- clear() {
316
- this.listeners.clear();
321
+ notify(data) {
322
+ this.listeners.forEach((listener) => {
323
+ try {
324
+ listener(data);
325
+ } catch (error) {
326
+ console.error("Error in event listener:", error);
327
+ }
328
+ });
329
+ }
330
+ clear() {
331
+ this.listeners.clear();
332
+ }
333
+ };
317
334
  }
318
- };
335
+ });
319
336
 
320
337
  // src/v2/core/utils/sessionStorage.ts
321
- var FUNNEL_SESSION_COOKIE_NAME = "tgd-funnel-session-id";
322
- var SESSION_MAX_AGE = 30 * 24 * 60 * 60;
323
338
  function setFunnelSessionCookie(sessionId) {
324
339
  if (typeof document === "undefined") return;
325
340
  document.cookie = "".concat(FUNNEL_SESSION_COOKIE_NAME, "=").concat(sessionId, "; path=/; max-age=").concat(SESSION_MAX_AGE, "; SameSite=Lax");
@@ -333,14 +348,25 @@ var TagadaTrackerBundle = (() => {
333
348
  if (typeof document === "undefined") return;
334
349
  document.cookie = "".concat(FUNNEL_SESSION_COOKIE_NAME, "=; path=/; max-age=0");
335
350
  }
351
+ var FUNNEL_SESSION_COOKIE_NAME, SESSION_MAX_AGE;
352
+ var init_sessionStorage = __esm({
353
+ "src/v2/core/utils/sessionStorage.ts"() {
354
+ "use strict";
355
+ FUNNEL_SESSION_COOKIE_NAME = "tgd-funnel-session-id";
356
+ SESSION_MAX_AGE = 30 * 24 * 60 * 60;
357
+ }
358
+ });
336
359
 
337
360
  // src/v2/core/utils/tokenStorage.ts
338
- var TOKEN_KEY = "cms_token";
339
361
  function setClientToken(token) {
340
362
  if (typeof window !== "undefined") {
341
363
  try {
364
+ const currentToken = localStorage.getItem(TOKEN_KEY);
365
+ const tokenChanged = currentToken !== token;
342
366
  localStorage.setItem(TOKEN_KEY, token);
343
- window.dispatchEvent(new Event("storage"));
367
+ if (tokenChanged) {
368
+ window.dispatchEvent(new Event("storage"));
369
+ }
344
370
  } catch (error) {
345
371
  console.error("Failed to save token to localStorage:", error);
346
372
  }
@@ -366,15 +392,15 @@ var TagadaTrackerBundle = (() => {
366
392
  }
367
393
  }
368
394
  }
395
+ var TOKEN_KEY;
396
+ var init_tokenStorage = __esm({
397
+ "src/v2/core/utils/tokenStorage.ts"() {
398
+ "use strict";
399
+ TOKEN_KEY = "cms_token";
400
+ }
401
+ });
369
402
 
370
403
  // src/v2/core/utils/previewMode.ts
371
- var STORAGE_KEYS = {
372
- DRAFT: "tgd_draft",
373
- FUNNEL_TRACKING: "tgd_funnel_tracking",
374
- FORCE_RESET: "tgd_force_reset",
375
- CLIENT_ENV: "tgd_client_env",
376
- CLIENT_BASE_URL: "tgd_client_base_url"
377
- };
378
404
  function getFromStorage(key) {
379
405
  if (typeof window === "undefined") return null;
380
406
  try {
@@ -399,6 +425,10 @@ var TagadaTrackerBundle = (() => {
399
425
  }
400
426
  function setInCookie(key, value, maxAge = 86400) {
401
427
  if (typeof document === "undefined") return;
428
+ if (key === "tgd_draft" || key === "tgd-draft") {
429
+ console.warn("\u{1F6E1}\uFE0F [SDK] Blocked attempt to set ".concat(key, " as cookie - this should only be in localStorage"));
430
+ return;
431
+ }
402
432
  document.cookie = "".concat(key, "=").concat(value, "; path=/; max-age=").concat(maxAge);
403
433
  }
404
434
  function clearFromCookie(key) {
@@ -415,7 +445,7 @@ var TagadaTrackerBundle = (() => {
415
445
  if (urlDraft !== null) {
416
446
  draft = urlDraft === "true";
417
447
  } else {
418
- const storageDraft = getFromStorage(STORAGE_KEYS.DRAFT) || getFromCookie(STORAGE_KEYS.DRAFT);
448
+ const storageDraft = getFromStorage(STORAGE_KEYS.DRAFT);
419
449
  if (storageDraft !== null) {
420
450
  draft = storageDraft === "true";
421
451
  }
@@ -433,6 +463,11 @@ var TagadaTrackerBundle = (() => {
433
463
  const token = urlParams.get("token") || getClientToken() || null;
434
464
  const funnelSessionId = urlParams.get("funnelSessionId") || null;
435
465
  const funnelId = urlParams.get("funnelId") || null;
466
+ let funnelEnv;
467
+ const urlFunnelEnv = urlParams.get("funnelEnv");
468
+ if (urlFunnelEnv && (urlFunnelEnv === "staging" || urlFunnelEnv === "production")) {
469
+ funnelEnv = urlFunnelEnv;
470
+ }
436
471
  const forceReset = urlParams.get("forceReset") === "true";
437
472
  let tagadaClientEnv;
438
473
  const urlEnv = urlParams.get("tagadaClientEnv");
@@ -461,6 +496,7 @@ var TagadaTrackerBundle = (() => {
461
496
  funnelId,
462
497
  draft,
463
498
  funnelTracking,
499
+ funnelEnv,
464
500
  tagadaClientEnv,
465
501
  tagadaClientBaseUrl
466
502
  };
@@ -474,13 +510,19 @@ var TagadaTrackerBundle = (() => {
474
510
  if (typeof window === "undefined") return;
475
511
  if (draft) {
476
512
  setInStorage(STORAGE_KEYS.DRAFT, "true");
477
- setInCookie(STORAGE_KEYS.DRAFT, "true", 86400);
478
513
  } else {
479
- setInStorage(STORAGE_KEYS.DRAFT, "false");
480
- clearFromCookie(STORAGE_KEYS.DRAFT);
514
+ try {
515
+ localStorage.removeItem(STORAGE_KEYS.DRAFT);
516
+ } catch (e) {
517
+ }
481
518
  }
482
519
  }
483
520
  function handlePreviewMode(debugMode = false) {
521
+ let urlToken = null;
522
+ if (typeof window !== "undefined") {
523
+ const urlParams = new URLSearchParams(window.location.search);
524
+ urlToken = urlParams.get("token");
525
+ }
484
526
  const params = getSDKParams();
485
527
  const shouldReset = params.forceReset || false;
486
528
  if (!shouldReset && !params.token) {
@@ -489,6 +531,7 @@ var TagadaTrackerBundle = (() => {
489
531
  }
490
532
  if (debugMode) {
491
533
  console.log("[SDK] Detected params:", params);
534
+ console.log("[SDK] URL token (direct read):", urlToken ? urlToken.substring(0, 20) + "..." : "none");
492
535
  }
493
536
  if (shouldReset) {
494
537
  if (debugMode) {
@@ -508,18 +551,22 @@ var TagadaTrackerBundle = (() => {
508
551
  });
509
552
  }
510
553
  }
511
- if (params.token !== null && params.token !== void 0) {
554
+ const tokenToSet = urlToken || params.token;
555
+ if (tokenToSet !== null && tokenToSet !== void 0) {
512
556
  if (debugMode) {
513
- console.log("[SDK] Using token from URL:", params.token.substring(0, 20) + "...");
557
+ console.log("[SDK] Setting token from URL:", tokenToSet.substring(0, 20) + "...");
514
558
  }
515
- if (params.token === "" || params.token === "null") {
559
+ if (tokenToSet === "" || tokenToSet === "null") {
516
560
  clearClientToken();
517
561
  } else {
518
- setClientToken(params.token);
562
+ setClientToken(tokenToSet);
563
+ if (debugMode) {
564
+ console.log("[SDK] \u2705 Token set in localStorage immediately after clear");
565
+ }
519
566
  }
520
567
  } else if (shouldReset) {
521
568
  if (debugMode) {
522
- console.log("[SDK] Force reset mode (no token)");
569
+ console.log("[SDK] Force reset mode (no token in URL)");
523
570
  }
524
571
  clearClientToken();
525
572
  }
@@ -560,13 +607,327 @@ var TagadaTrackerBundle = (() => {
560
607
  setInStorage(STORAGE_KEYS.CLIENT_ENV, env);
561
608
  setInCookie(STORAGE_KEYS.CLIENT_ENV, env, 86400);
562
609
  }
610
+ function clearClientEnvironment() {
611
+ if (typeof window === "undefined") return;
612
+ try {
613
+ localStorage.removeItem(STORAGE_KEYS.CLIENT_ENV);
614
+ clearFromCookie(STORAGE_KEYS.CLIENT_ENV);
615
+ } catch (e) {
616
+ }
617
+ }
563
618
  function setClientBaseUrl(baseUrl) {
564
619
  if (typeof window === "undefined") return;
565
620
  setInStorage(STORAGE_KEYS.CLIENT_BASE_URL, baseUrl);
566
621
  setInCookie(STORAGE_KEYS.CLIENT_BASE_URL, baseUrl, 86400);
567
622
  }
623
+ function clearClientBaseUrl() {
624
+ if (typeof window === "undefined") return;
625
+ try {
626
+ localStorage.removeItem(STORAGE_KEYS.CLIENT_BASE_URL);
627
+ clearFromCookie(STORAGE_KEYS.CLIENT_BASE_URL);
628
+ } catch (e) {
629
+ }
630
+ }
631
+ function isFunnelTrackingEnabled() {
632
+ var _a;
633
+ const params = getSDKParams();
634
+ return (_a = params.funnelTracking) != null ? _a : true;
635
+ }
636
+ var STORAGE_KEYS;
637
+ var init_previewMode = __esm({
638
+ "src/v2/core/utils/previewMode.ts"() {
639
+ "use strict";
640
+ init_tokenStorage();
641
+ init_sessionStorage();
642
+ STORAGE_KEYS = {
643
+ DRAFT: "tgd_draft",
644
+ FUNNEL_TRACKING: "tgd_funnel_tracking",
645
+ FORCE_RESET: "tgd_force_reset",
646
+ CLIENT_ENV: "tgd_client_env",
647
+ CLIENT_BASE_URL: "tgd_client_base_url"
648
+ };
649
+ }
650
+ });
651
+
652
+ // src/v2/core/utils/previewModeIndicator.ts
653
+ function clearSpecificCookie(cookieName) {
654
+ if (typeof window === "undefined" || typeof document === "undefined") return;
655
+ const hostname = window.location.hostname;
656
+ const parts = hostname.split(".");
657
+ const domains = [
658
+ "",
659
+ // No domain (current)
660
+ hostname,
661
+ // Full hostname
662
+ "." + hostname
663
+ // Wildcard current
664
+ ];
665
+ for (let i = 1; i < parts.length; i++) {
666
+ const domain = parts.slice(i).join(".");
667
+ domains.push(domain);
668
+ domains.push("." + domain);
669
+ }
670
+ const pathParts = window.location.pathname.split("/").filter((p) => p);
671
+ const paths = ["/"];
672
+ let currentPath = "";
673
+ pathParts.forEach((part) => {
674
+ currentPath += "/" + part;
675
+ paths.push(currentPath);
676
+ });
677
+ domains.forEach((domain) => {
678
+ paths.forEach((path) => {
679
+ const baseDelete = "".concat(cookieName, "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=").concat(path);
680
+ const domainPart = domain ? "; domain=".concat(domain) : "";
681
+ document.cookie = baseDelete + domainPart;
682
+ document.cookie = baseDelete + domainPart + "; secure";
683
+ document.cookie = baseDelete + domainPart + "; SameSite=None; secure";
684
+ document.cookie = baseDelete + domainPart + "; SameSite=Lax";
685
+ document.cookie = baseDelete + domainPart + "; SameSite=Strict";
686
+ });
687
+ });
688
+ }
689
+ function leavePreviewMode() {
690
+ if (typeof window === "undefined") return;
691
+ const confirmed = confirm(
692
+ "\u{1F6AA} Leave Preview Mode?\n\nThis will clear ALL cookies and localStorage (including Shopify session, cart, and preview settings) and reload the page.\n\nAre you sure?"
693
+ );
694
+ if (!confirmed) return;
695
+ const url = new URL(window.location.href);
696
+ const previewParams = [
697
+ "draft",
698
+ "funnelEnv",
699
+ "funnelTracking",
700
+ "tagadaClientEnv",
701
+ "tagadaClientBaseUrl",
702
+ "forceReset",
703
+ "funnelId",
704
+ "funnelSessionId",
705
+ "token"
706
+ ];
707
+ previewParams.forEach((param) => url.searchParams.delete(param));
708
+ window.history.replaceState({}, "", url.href);
709
+ try {
710
+ const cookieDescriptor = Object.getOwnPropertyDescriptor(Document.prototype, "cookie") || Object.getOwnPropertyDescriptor(HTMLDocument.prototype, "cookie");
711
+ if (cookieDescriptor && cookieDescriptor.set) {
712
+ Object.defineProperty(document, "cookie", {
713
+ configurable: true,
714
+ enumerable: true,
715
+ get: function() {
716
+ return cookieDescriptor.get ? cookieDescriptor.get.call(this) : "";
717
+ },
718
+ set: function(val) {
719
+ if (typeof val === "string") {
720
+ const normalizedVal = val.toLowerCase();
721
+ if (normalizedVal.includes("tgd_draft") || normalizedVal.includes("tgd-draft") || normalizedVal.includes("tgd.draft") || normalizedVal.includes("tgddraft")) {
722
+ if (normalizedVal.includes("max-age=0") || normalizedVal.includes("expires=thu, 01 jan 1970")) {
723
+ if (cookieDescriptor.set) {
724
+ cookieDescriptor.set.call(this, val);
725
+ }
726
+ return;
727
+ }
728
+ console.warn("\u{1F6E1}\uFE0F [TagadaPay] BLOCKED: tgd_draft should never be a cookie");
729
+ return;
730
+ }
731
+ }
732
+ if (cookieDescriptor.set) {
733
+ cookieDescriptor.set.call(this, val);
734
+ }
735
+ }
736
+ });
737
+ }
738
+ } catch (e) {
739
+ console.warn("[TagadaPay] Could not install cookie blocker:", e);
740
+ }
741
+ if (window.localStorage) {
742
+ try {
743
+ localStorage.removeItem("tgd_draft");
744
+ localStorage.removeItem("tgd_funnel_tracking");
745
+ localStorage.removeItem("tgd_client_env");
746
+ localStorage.removeItem("tgd_client_base_url");
747
+ localStorage.removeItem("cms_token");
748
+ } catch (e) {
749
+ console.warn("[TagadaPay] Failed to clear some localStorage keys:", e);
750
+ }
751
+ }
752
+ clearClientToken();
753
+ clearFunnelSessionCookie();
754
+ clearClientEnvironment();
755
+ clearClientBaseUrl();
756
+ if (window.localStorage) {
757
+ localStorage.clear();
758
+ }
759
+ if (window.sessionStorage) {
760
+ sessionStorage.clear();
761
+ }
762
+ const tagadaCookies = [
763
+ "tgd-funnel-session-id",
764
+ "tgd_draft",
765
+ "tgd_funnel_tracking",
766
+ "tgd_client_env",
767
+ "tgd_client_base_url",
768
+ "cms_token",
769
+ "tagadapay_session"
770
+ ];
771
+ tagadaCookies.forEach((cookieName) => clearSpecificCookie(cookieName));
772
+ const alternativeNames = ["tgd-draft", "tgd_draft", "tgd.draft", "tgddraft"];
773
+ alternativeNames.forEach((cookieName) => clearSpecificCookie(cookieName));
774
+ if (document.cookie) {
775
+ const cookies = document.cookie.split(";");
776
+ cookies.forEach((cookie) => {
777
+ const cookieName = cookie.split("=")[0].trim();
778
+ if (!cookieName) return;
779
+ const hostname = window.location.hostname;
780
+ const parts = hostname.split(".");
781
+ const domains = ["", hostname, "." + hostname];
782
+ for (let i = 1; i < parts.length; i++) {
783
+ const domain = parts.slice(i).join(".");
784
+ domains.push(domain);
785
+ domains.push("." + domain);
786
+ }
787
+ const pathParts = window.location.pathname.split("/").filter((p) => p);
788
+ const paths = ["/"];
789
+ let currentPath = "";
790
+ pathParts.forEach((part) => {
791
+ currentPath += "/" + part;
792
+ paths.push(currentPath);
793
+ });
794
+ domains.forEach((domain) => {
795
+ paths.forEach((path) => {
796
+ const baseDelete = "".concat(cookieName, "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=").concat(path);
797
+ const domainPart = domain ? "; domain=".concat(domain) : "";
798
+ document.cookie = baseDelete + domainPart;
799
+ document.cookie = baseDelete + domainPart + "; secure";
800
+ document.cookie = baseDelete + domainPart + "; SameSite=None; secure";
801
+ document.cookie = baseDelete + domainPart + "; SameSite=Lax";
802
+ document.cookie = baseDelete + domainPart + "; SameSite=Strict";
803
+ document.cookie = baseDelete + domainPart + "; secure; SameSite=None";
804
+ document.cookie = baseDelete + domainPart + "; secure; SameSite=Lax";
805
+ document.cookie = baseDelete + domainPart + "; secure; SameSite=Strict";
806
+ });
807
+ });
808
+ });
809
+ }
810
+ setTimeout(() => {
811
+ if (window.localStorage && localStorage.length > 0) {
812
+ localStorage.clear();
813
+ }
814
+ clearSpecificCookie("tgd_draft");
815
+ if (typeof window.stop === "function") {
816
+ window.stop();
817
+ }
818
+ window.location.replace(url.href);
819
+ }, 10);
820
+ }
821
+ function injectPreviewModeIndicator() {
822
+ if (isInjected || typeof window === "undefined" || typeof document === "undefined") {
823
+ return;
824
+ }
825
+ const params = getSDKParams();
826
+ const draftMode = isDraftMode();
827
+ const trackingDisabled = !isFunnelTrackingEnabled();
828
+ const hasCustomEnv = !!(params.tagadaClientEnv || params.tagadaClientBaseUrl);
829
+ if (!draftMode && !trackingDisabled && !hasCustomEnv) {
830
+ return;
831
+ }
832
+ const container = document.createElement("div");
833
+ container.id = "tgd-preview-indicator";
834
+ container.style.cssText = '\n position: fixed;\n bottom: 16px;\n right: 16px;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\n ';
835
+ const badge = document.createElement("div");
836
+ badge.style.cssText = "\n background: ".concat(draftMode ? "#ff9500" : "#007aff", ";\n color: white;\n padding: 8px 12px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 600;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 6px;\n ");
837
+ badge.innerHTML = '\n <span style="font-size: 16px;">\u{1F50D}</span>\n <span>'.concat(draftMode ? "Preview Mode" : "Dev Mode", "</span>\n ");
838
+ const details = document.createElement("div");
839
+ details.style.cssText = "\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n background: white;\n border: 1px solid #e5e5e5;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n padding: 12px;\n min-width: 250px;\n font-size: 12px;\n line-height: 1.5;\n display: none;\n ";
840
+ details.style.paddingTop = "20px";
841
+ const bridge = document.createElement("div");
842
+ bridge.style.cssText = "\n position: absolute;\n bottom: 100%;\n left: 0;\n right: 0;\n height: 8px;\n display: none;\n ";
843
+ let detailsHTML = '<div style="margin-bottom: 8px; font-weight: 600; color: #1d1d1f;">Current Environment</div>';
844
+ detailsHTML += '<div style="display: flex; flex-direction: column; gap: 6px;">';
845
+ if (draftMode) {
846
+ detailsHTML += '\n <div style="display: flex; justify-content: space-between; color: #86868b;">\n <span>Draft Mode:</span>\n <span style="color: #ff9500; font-weight: 600;">ON</span>\n </div>\n ';
847
+ }
848
+ if (trackingDisabled) {
849
+ detailsHTML += '\n <div style="display: flex; justify-content: space-between; color: #86868b;">\n <span>Tracking:</span>\n <span style="color: #ff3b30; font-weight: 600;">DISABLED</span>\n </div>\n ';
850
+ }
851
+ if (params.funnelEnv) {
852
+ detailsHTML += '\n <div style="display: flex; justify-content: space-between; color: #86868b;">\n <span>Funnel Env:</span>\n <span style="color: #1d1d1f; font-weight: 600; font-family: monospace; font-size: 11px;">\n '.concat(params.funnelEnv, "\n </span>\n </div>\n ");
853
+ }
854
+ if (params.tagadaClientEnv) {
855
+ detailsHTML += '\n <div style="display: flex; justify-content: space-between; color: #86868b;">\n <span>API Env:</span>\n <span style="color: #1d1d1f; font-weight: 600; font-family: monospace; font-size: 11px;">\n '.concat(params.tagadaClientEnv, "\n </span>\n </div>\n ");
856
+ }
857
+ if (params.tagadaClientBaseUrl) {
858
+ detailsHTML += '\n <div style="color: #86868b;">\n <div style="margin-bottom: 4px;">API URL:</div>\n <div style="color: #1d1d1f; font-weight: 600; font-family: monospace; font-size: 10px; word-break: break-all; background: #f5f5f7; padding: 4px 6px; border-radius: 4px;">\n '.concat(params.tagadaClientBaseUrl, "\n </div>\n </div>\n ");
859
+ }
860
+ if (params.funnelId) {
861
+ detailsHTML += '\n <div style="color: #86868b; margin-top: 4px; padding-top: 8px; border-top: 1px solid #e5e5e5;">\n <div style="margin-bottom: 4px;">Funnel ID:</div>\n <div style="color: #1d1d1f; font-family: monospace; font-size: 10px; word-break: break-all; background: #f5f5f7; padding: 4px 6px; border-radius: 4px;">\n '.concat(params.funnelId, "\n </div>\n </div>\n ");
862
+ }
863
+ detailsHTML += "</div>";
864
+ detailsHTML += '\n <div style="margin-top: 12px; padding-top: 8px; border-top: 1px solid #e5e5e5; font-size: 11px; color: #86868b; text-align: center;">\n Add <code style="background: #f5f5f7; padding: 2px 4px; border-radius: 3px;">?forceReset=true</code> to reset\n </div>\n ';
865
+ detailsHTML += '\n <div style="margin-top: 12px; padding-top: 8px; border-top: 1px solid #e5e5e5;">\n <button id="tgd-leave-preview" style="\n background: #ff3b30;\n color: white;\n border: none;\n border-radius: 6px;\n padding: 10px 12px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: opacity 0.2s;\n width: 100%;\n ">\n \u{1F6AA} Leave Preview Mode\n </button>\n </div>\n ';
866
+ details.innerHTML = detailsHTML;
867
+ let isHovering = false;
868
+ const showDetails = () => {
869
+ isHovering = true;
870
+ details.style.display = "block";
871
+ bridge.style.display = "block";
872
+ };
873
+ const hideDetails = () => {
874
+ isHovering = false;
875
+ setTimeout(() => {
876
+ if (!isHovering) {
877
+ details.style.display = "none";
878
+ bridge.style.display = "none";
879
+ }
880
+ }, 100);
881
+ };
882
+ badge.addEventListener("mouseenter", showDetails);
883
+ badge.addEventListener("mouseleave", hideDetails);
884
+ bridge.addEventListener("mouseenter", showDetails);
885
+ bridge.addEventListener("mouseleave", hideDetails);
886
+ details.addEventListener("mouseenter", showDetails);
887
+ details.addEventListener("mouseleave", hideDetails);
888
+ const leavePreviewBtn = details.querySelector("#tgd-leave-preview");
889
+ if (leavePreviewBtn) {
890
+ leavePreviewBtn.addEventListener("mouseenter", () => {
891
+ leavePreviewBtn.style.opacity = "0.8";
892
+ });
893
+ leavePreviewBtn.addEventListener("mouseleave", () => {
894
+ leavePreviewBtn.style.opacity = "1";
895
+ });
896
+ leavePreviewBtn.addEventListener("click", (e) => {
897
+ e.stopPropagation();
898
+ leavePreviewMode();
899
+ });
900
+ }
901
+ container.appendChild(badge);
902
+ container.appendChild(bridge);
903
+ container.appendChild(details);
904
+ document.body.appendChild(container);
905
+ indicatorElement = container;
906
+ isInjected = true;
907
+ }
908
+ var indicatorElement, isInjected;
909
+ var init_previewModeIndicator = __esm({
910
+ "src/v2/core/utils/previewModeIndicator.ts"() {
911
+ "use strict";
912
+ init_previewMode();
913
+ init_tokenStorage();
914
+ init_sessionStorage();
915
+ indicatorElement = null;
916
+ isInjected = false;
917
+ }
918
+ });
568
919
 
569
920
  // src/v2/core/funnelClient.ts
921
+ var funnelClient_exports = {};
922
+ __export(funnelClient_exports, {
923
+ FunnelClient: () => FunnelClient,
924
+ getAssignedPaymentFlowId: () => getAssignedPaymentFlowId,
925
+ getAssignedScripts: () => getAssignedScripts,
926
+ getAssignedStaticResources: () => getAssignedStaticResources,
927
+ getAssignedStepConfig: () => getAssignedStepConfig,
928
+ getLocalFunnelConfig: () => getLocalFunnelConfig,
929
+ loadLocalFunnelConfig: () => loadLocalFunnelConfig
930
+ });
570
931
  function getAssignedFunnelId() {
571
932
  if (typeof window === "undefined") return void 0;
572
933
  if (window.__TGD_FUNNEL_ID__) {
@@ -600,367 +961,551 @@ var TagadaTrackerBundle = (() => {
600
961
  }
601
962
  return void 0;
602
963
  }
603
- var FunnelClient = class {
604
- constructor(config) {
605
- this.eventDispatcher = new EventDispatcher();
606
- // Guards
607
- this.isInitializing = false;
608
- this.initializationAttempted = false;
609
- this.config = config;
610
- this.resource = new FunnelResource(config.apiClient);
611
- this.state = {
612
- context: null,
613
- isLoading: false,
614
- isInitialized: false,
615
- isNavigating: false,
616
- error: null,
617
- sessionError: null
618
- };
964
+ function parseStepConfig(value) {
965
+ if (!value || typeof value !== "string") return void 0;
966
+ const trimmed = value.trim();
967
+ if (!trimmed) return void 0;
968
+ const strategies = [
969
+ // Strategy 1: Direct JSON parse (for properly escaped window variable)
970
+ () => JSON.parse(trimmed),
971
+ // Strategy 2: URL decode then JSON parse (for meta tag)
972
+ () => JSON.parse(decodeURIComponent(trimmed)),
973
+ // Strategy 3: Double URL decode (edge case: double-encoded)
974
+ () => JSON.parse(decodeURIComponent(decodeURIComponent(trimmed)))
975
+ ];
976
+ for (const strategy of strategies) {
977
+ try {
978
+ const result = strategy();
979
+ if (result && typeof result === "object") {
980
+ return result;
981
+ }
982
+ } catch (e) {
983
+ }
619
984
  }
620
- /**
621
- * Update configuration (e.g. when plugin config loads)
622
- */
623
- setConfig(config) {
624
- this.config = __spreadValues(__spreadValues({}, this.config), config);
985
+ if (typeof console !== "undefined") {
986
+ console.warn("[SDK] Failed to parse stepConfig:", trimmed.substring(0, 100));
625
987
  }
626
- /**
627
- * Subscribe to state changes
628
- */
629
- subscribe(listener) {
630
- return this.eventDispatcher.subscribe(listener);
988
+ return void 0;
989
+ }
990
+ function isLocalDevelopment() {
991
+ if (typeof window === "undefined") return false;
992
+ const hostname = window.location.hostname;
993
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".localhost") && !hostname.includes(".cdn.");
994
+ }
995
+ async function loadLocalFunnelConfig() {
996
+ if (!isLocalDevelopment()) return null;
997
+ if (localFunnelConfigCache !== void 0) {
998
+ return localFunnelConfigCache;
631
999
  }
632
- /**
633
- * Get current state
634
- */
635
- getState() {
636
- return this.state;
1000
+ if (localFunnelConfigLoading) {
1001
+ await new Promise((resolve) => setTimeout(resolve, 100));
1002
+ return localFunnelConfigCache != null ? localFunnelConfigCache : null;
637
1003
  }
638
- /**
639
- * Get the session ID that would be used for initialization (URL params or cookie)
640
- * This allows getting the session ID even before the client is fully initialized.
641
- */
642
- getDetectedSessionId() {
643
- var _a;
644
- if ((_a = this.state.context) == null ? void 0 : _a.sessionId) {
645
- return this.state.context.sessionId;
1004
+ localFunnelConfigLoading = true;
1005
+ try {
1006
+ console.log("\u{1F6E0}\uFE0F [SDK] Loading local funnel config from /config/funnel.local.json...");
1007
+ const response = await fetch("/config/funnel.local.json");
1008
+ if (!response.ok) {
1009
+ console.log("\u{1F6E0}\uFE0F [SDK] funnel.local.json not found (this is fine in production)");
1010
+ localFunnelConfigCache = null;
1011
+ return null;
646
1012
  }
647
- if (typeof window === "undefined") return null;
648
- const params = new URLSearchParams(window.location.search);
649
- const urlSessionId = params.get("funnelSessionId");
650
- if (urlSessionId) return urlSessionId;
651
- return getFunnelSessionCookie() || null;
1013
+ const config = await response.json();
1014
+ console.log("\u{1F6E0}\uFE0F [SDK] \u2705 Loaded local funnel config:", config);
1015
+ localFunnelConfigCache = config;
1016
+ return config;
1017
+ } catch (error) {
1018
+ console.log("\u{1F6E0}\uFE0F [SDK] funnel.local.json not available:", error);
1019
+ localFunnelConfigCache = null;
1020
+ return null;
1021
+ } finally {
1022
+ localFunnelConfigLoading = false;
652
1023
  }
653
- /**
654
- * Reset initialization state (used for back-button restores)
655
- */
656
- resetInitialization() {
657
- this.initializationAttempted = false;
658
- this.isInitializing = false;
659
- this.updateState({
660
- context: null,
661
- isInitialized: false
662
- });
1024
+ }
1025
+ function getLocalFunnelConfig() {
1026
+ return localFunnelConfigCache != null ? localFunnelConfigCache : null;
1027
+ }
1028
+ function localConfigToStepConfig(local) {
1029
+ return {
1030
+ payment: local.paymentFlowId ? { paymentFlowId: local.paymentFlowId } : void 0,
1031
+ staticResources: local.staticResources,
1032
+ scripts: local.scripts,
1033
+ pixels: local.pixels
1034
+ };
1035
+ }
1036
+ function getAssignedStepConfig() {
1037
+ if (typeof window === "undefined") return void 0;
1038
+ const localConfig = getLocalFunnelConfig();
1039
+ if (localConfig) {
1040
+ console.log("\u{1F6E0}\uFE0F [SDK] Using local funnel.local.json (overrides injected)");
1041
+ return localConfigToStepConfig(localConfig);
663
1042
  }
664
- /**
665
- * Initialize session with automatic detection (cookies, URL, etc.)
666
- */
667
- async autoInitialize(authSession, store, funnelId) {
668
- if (this.state.context) return this.state.context;
669
- if (this.isInitializing) return null;
670
- if (this.initializationAttempted) return null;
671
- this.initializationAttempted = true;
672
- this.isInitializing = true;
673
- this.updateState({ isLoading: true, error: null });
674
- try {
675
- const existingSessionId = this.getDetectedSessionId();
676
- const params = new URLSearchParams(typeof window !== "undefined" ? window.location.search : "");
677
- const urlFunnelId = params.get("funnelId");
678
- const effectiveFunnelId = urlFunnelId || funnelId;
679
- const injectedFunnelId = getAssignedFunnelId();
680
- const funnelVariantId = getAssignedFunnelVariant();
681
- const injectedStepId = getAssignedFunnelStep();
682
- const sdkParams = getSDKParams();
683
- const finalFunnelId = this.config.funnelId || injectedFunnelId || effectiveFunnelId;
684
- const finalStepId = this.config.stepId || injectedStepId;
685
- if (this.config.debugMode) {
686
- console.log("\u{1F680} [FunnelClient] Auto-initializing...", {
687
- existingSessionId,
688
- effectiveFunnelId: finalFunnelId,
689
- funnelVariantId,
690
- // 🎯 Log variant ID for debugging
691
- funnelStepId: finalStepId,
692
- // 🎯 Log step ID for debugging
693
- draft: sdkParams.draft,
694
- // 🎯 Log draft mode
695
- funnelTracking: sdkParams.funnelTracking,
696
- // 🎯 Log tracking flag
697
- source: {
698
- funnelId: this.config.funnelId ? "config" : injectedFunnelId ? "injected" : effectiveFunnelId ? "url/prop" : "none",
699
- stepId: this.config.stepId ? "config" : injectedStepId ? "injected" : "none"
700
- }
701
- });
702
- }
703
- const response = await this.resource.initialize({
704
- cmsSession: {
705
- customerId: authSession.customerId,
706
- sessionId: authSession.sessionId,
707
- storeId: store.id,
708
- accountId: store.accountId
709
- },
710
- funnelId: finalFunnelId,
711
- existingSessionId: existingSessionId || void 0,
712
- currentUrl: typeof window !== "undefined" ? window.location.href : void 0,
713
- funnelVariantId,
714
- // 🎯 Pass A/B test variant ID to backend
715
- funnelStepId: finalStepId,
716
- // 🎯 Pass step ID to backend (with config override)
717
- draft: sdkParams.draft,
718
- // 🎯 Pass draft mode explicitly (more robust than URL parsing)
719
- funnelTracking: sdkParams.funnelTracking
720
- // 🎯 Pass funnel tracking flag explicitly
721
- });
722
- if (response.success && response.context) {
723
- const enriched = this.enrichContext(response.context);
724
- this.handleSessionSuccess(enriched);
725
- return enriched;
726
- } else {
727
- throw new Error(response.error || "Failed to initialize funnel session");
728
- }
729
- } catch (error) {
730
- const err = error instanceof Error ? error : new Error(String(error));
731
- this.updateState({ error: err, isLoading: false });
732
- if (this.config.debugMode) {
733
- console.error("\u274C [FunnelClient] Init failed:", err);
734
- }
735
- throw err;
736
- } finally {
737
- this.isInitializing = false;
1043
+ const windowValue = window.__TGD_STEP_CONFIG__;
1044
+ if (windowValue) {
1045
+ const parsed = parseStepConfig(windowValue);
1046
+ if (parsed) return parsed;
1047
+ }
1048
+ if (typeof document !== "undefined") {
1049
+ const meta = document.querySelector('meta[name="x-step-config"]');
1050
+ const content = meta == null ? void 0 : meta.getAttribute("content");
1051
+ if (content) {
1052
+ const parsed = parseStepConfig(content);
1053
+ if (parsed) return parsed;
738
1054
  }
739
1055
  }
740
- /**
741
- * Manual initialization
742
- */
743
- async initialize(authSession, store, funnelId, entryStepId) {
744
- this.updateState({ isLoading: true, error: null });
745
- try {
746
- const funnelVariantId = getAssignedFunnelVariant();
747
- const funnelStepId = getAssignedFunnelStep();
748
- if (this.config.debugMode) {
749
- if (funnelVariantId) console.log("\u{1F3AF} [FunnelClient] Detected A/B test variant:", funnelVariantId);
750
- if (funnelStepId) console.log("\u{1F3AF} [FunnelClient] Detected step ID:", funnelStepId);
751
- }
752
- const response = await this.resource.initialize({
753
- cmsSession: {
754
- customerId: authSession.customerId,
755
- sessionId: authSession.sessionId,
756
- storeId: store.id,
757
- accountId: store.accountId
758
- },
759
- funnelId,
760
- entryStepId,
761
- currentUrl: typeof window !== "undefined" ? window.location.href : void 0,
762
- funnelVariantId,
763
- // 🎯 Pass A/B test variant ID to backend
764
- funnelStepId
765
- // 🎯 Pass step ID to backend
766
- });
767
- if (response.success && response.context) {
768
- const enriched = this.enrichContext(response.context);
769
- this.handleSessionSuccess(enriched);
770
- return enriched;
771
- } else {
772
- throw new Error(response.error || "Failed to initialize");
773
- }
774
- } catch (error) {
775
- const err = error instanceof Error ? error : new Error(String(error));
776
- this.updateState({ error: err, isLoading: false });
777
- throw err;
1056
+ return void 0;
1057
+ }
1058
+ function getAssignedPaymentFlowId() {
1059
+ var _a;
1060
+ const stepConfig = getAssignedStepConfig();
1061
+ if ((_a = stepConfig == null ? void 0 : stepConfig.payment) == null ? void 0 : _a.paymentFlowId) {
1062
+ return stepConfig.payment.paymentFlowId;
1063
+ }
1064
+ if (typeof window !== "undefined") {
1065
+ if (window.__TGD_PAYMENT_FLOW_ID__) {
1066
+ return window.__TGD_PAYMENT_FLOW_ID__;
1067
+ }
1068
+ if (typeof document !== "undefined") {
1069
+ const meta = document.querySelector('meta[name="x-payment-flow-id"]');
1070
+ return (meta == null ? void 0 : meta.getAttribute("content")) || void 0;
778
1071
  }
779
1072
  }
780
- /**
781
- * Navigate
782
- * @param event - Navigation event/action
783
- * @param options - Navigation options
784
- * @param options.fireAndForget - If true, queues navigation to QStash and returns immediately without waiting for result
785
- * @param options.customerTags - Customer tags to set (merged with existing customer tags)
786
- * @param options.deviceId - Device ID for geo/device tag enrichment (optional, rarely needed)
787
- */
788
- async navigate(event, options) {
789
- var _a, _b, _c;
790
- if (!((_a = this.state.context) == null ? void 0 : _a.sessionId)) throw new Error("No active session");
791
- this.updateState({ isNavigating: true, isLoading: true });
792
- try {
793
- let funnelVariantId = getAssignedFunnelVariant();
794
- let funnelStepId = getAssignedFunnelStep();
795
- const currentUrl = typeof window !== "undefined" ? window.location.href : void 0;
796
- if (!funnelStepId && this.config.stepId) {
797
- funnelStepId = this.config.stepId;
798
- if (this.config.debugMode) {
799
- console.log("\u{1F50D} [FunnelClient.navigate] Using stepId from config (no injection):", funnelStepId);
800
- }
1073
+ return void 0;
1074
+ }
1075
+ function getAssignedStaticResources() {
1076
+ const stepConfig = getAssignedStepConfig();
1077
+ return stepConfig == null ? void 0 : stepConfig.staticResources;
1078
+ }
1079
+ function getAssignedScripts(position) {
1080
+ const stepConfig = getAssignedStepConfig();
1081
+ if (!(stepConfig == null ? void 0 : stepConfig.scripts)) return void 0;
1082
+ let scripts = stepConfig.scripts.filter((s) => s.enabled);
1083
+ if (position) {
1084
+ scripts = scripts.filter((s) => s.position === position || !s.position && position === "head-end");
1085
+ }
1086
+ return scripts.length > 0 ? scripts : void 0;
1087
+ }
1088
+ var localFunnelConfigCache, localFunnelConfigLoading, FunnelClient;
1089
+ var init_funnelClient = __esm({
1090
+ "src/v2/core/funnelClient.ts"() {
1091
+ "use strict";
1092
+ init_funnel();
1093
+ init_eventDispatcher();
1094
+ init_sessionStorage();
1095
+ init_environment();
1096
+ init_previewMode();
1097
+ init_previewModeIndicator();
1098
+ localFunnelConfigCache = void 0;
1099
+ localFunnelConfigLoading = false;
1100
+ FunnelClient = class {
1101
+ constructor(config) {
1102
+ this.eventDispatcher = new EventDispatcher();
1103
+ // Guards
1104
+ this.isInitializing = false;
1105
+ this.initializationAttempted = false;
1106
+ this.config = config;
1107
+ this.resource = new FunnelResource(config.apiClient);
1108
+ this.state = {
1109
+ context: null,
1110
+ isLoading: false,
1111
+ isInitialized: false,
1112
+ isNavigating: false,
1113
+ error: null,
1114
+ sessionError: null
1115
+ };
801
1116
  }
802
- if (!funnelVariantId && this.config.variantId) {
803
- funnelVariantId = this.config.variantId;
804
- if (this.config.debugMode) {
805
- console.log("\u{1F50D} [FunnelClient.navigate] Using variantId from config (no injection):", funnelVariantId);
1117
+ /**
1118
+ * Update configuration (e.g. when plugin config loads)
1119
+ */
1120
+ setConfig(config) {
1121
+ this.config = __spreadValues(__spreadValues({}, this.config), config);
1122
+ }
1123
+ /**
1124
+ * Subscribe to state changes
1125
+ */
1126
+ subscribe(listener) {
1127
+ return this.eventDispatcher.subscribe(listener);
1128
+ }
1129
+ /**
1130
+ * Get current state
1131
+ */
1132
+ getState() {
1133
+ return this.state;
1134
+ }
1135
+ /**
1136
+ * Get the session ID that would be used for initialization (URL params or cookie)
1137
+ * This allows getting the session ID even before the client is fully initialized.
1138
+ */
1139
+ getDetectedSessionId() {
1140
+ var _a;
1141
+ if ((_a = this.state.context) == null ? void 0 : _a.sessionId) {
1142
+ return this.state.context.sessionId;
806
1143
  }
807
- }
808
- if (this.config.debugMode) {
809
- console.log("\u{1F50D} [FunnelClient.navigate] Sending to backend:", {
810
- sessionId: this.state.context.sessionId,
811
- currentUrl,
812
- funnelStepId: funnelStepId || "(not found)",
813
- funnelVariantId: funnelVariantId || "(not found)",
814
- hasInjectedStepId: !!getAssignedFunnelStep(),
815
- hasInjectedVariantId: !!getAssignedFunnelVariant(),
816
- usedConfigFallback: !getAssignedFunnelStep() && !!this.config.stepId,
817
- customerTags: (options == null ? void 0 : options.customerTags) || "(none)",
818
- deviceId: (options == null ? void 0 : options.deviceId) || "(none)"
1144
+ if (typeof window === "undefined") return null;
1145
+ const params = new URLSearchParams(window.location.search);
1146
+ const urlSessionId = params.get("funnelSessionId");
1147
+ if (urlSessionId) return urlSessionId;
1148
+ return getFunnelSessionCookie() || null;
1149
+ }
1150
+ /**
1151
+ * Reset initialization state (used for back-button restores)
1152
+ */
1153
+ resetInitialization() {
1154
+ this.initializationAttempted = false;
1155
+ this.isInitializing = false;
1156
+ this.updateState({
1157
+ context: null,
1158
+ isInitialized: false
819
1159
  });
820
1160
  }
821
- const fireAndForget = (options == null ? void 0 : options.fireAndForget) || false;
822
- const response = await this.resource.navigate({
823
- sessionId: this.state.context.sessionId,
824
- event,
825
- currentUrl,
826
- funnelStepId,
827
- funnelVariantId,
828
- fireAndForget,
829
- customerTags: options == null ? void 0 : options.customerTags,
830
- deviceId: options == null ? void 0 : options.deviceId
831
- });
832
- if (!response.success || !response.result) {
833
- throw new Error(response.error || "Navigation failed");
1161
+ /**
1162
+ * Initialize session with automatic detection (cookies, URL, etc.)
1163
+ */
1164
+ async autoInitialize(authSession, store, funnelId) {
1165
+ if (this.state.context) return this.state.context;
1166
+ if (this.isInitializing) return null;
1167
+ if (this.initializationAttempted) return null;
1168
+ this.initializationAttempted = true;
1169
+ this.isInitializing = true;
1170
+ this.updateState({ isLoading: true, error: null });
1171
+ try {
1172
+ const existingSessionId = this.getDetectedSessionId();
1173
+ const params = new URLSearchParams(typeof window !== "undefined" ? window.location.search : "");
1174
+ const urlFunnelId = params.get("funnelId");
1175
+ const effectiveFunnelId = urlFunnelId || funnelId;
1176
+ const injectedFunnelId = getAssignedFunnelId();
1177
+ const funnelVariantId = getAssignedFunnelVariant();
1178
+ const injectedStepId = getAssignedFunnelStep();
1179
+ const sdkParams = getSDKParams();
1180
+ const finalFunnelId = this.config.funnelId || injectedFunnelId || effectiveFunnelId;
1181
+ const finalStepId = this.config.stepId || injectedStepId;
1182
+ const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
1183
+ const funnelEnv = urlParams == null ? void 0 : urlParams.get("funnelEnv");
1184
+ if (this.config.debugMode) {
1185
+ console.log("\u{1F680} [FunnelClient] Auto-initializing...", {
1186
+ existingSessionId,
1187
+ effectiveFunnelId: finalFunnelId,
1188
+ funnelVariantId,
1189
+ // 🎯 Log variant ID for debugging
1190
+ funnelStepId: finalStepId,
1191
+ // 🎯 Log step ID for debugging
1192
+ draft: sdkParams.draft,
1193
+ // 🎯 Log draft mode
1194
+ funnelTracking: sdkParams.funnelTracking,
1195
+ // 🎯 Log tracking flag
1196
+ funnelEnv,
1197
+ // 🎯 Log funnel environment
1198
+ tagadaClientEnv: sdkParams.tagadaClientEnv,
1199
+ // 🎯 Log client environment
1200
+ tagadaClientBaseUrl: sdkParams.tagadaClientBaseUrl,
1201
+ // 🎯 Log custom API URL
1202
+ source: {
1203
+ funnelId: this.config.funnelId ? "config" : injectedFunnelId ? "injected" : effectiveFunnelId ? "url/prop" : "none",
1204
+ stepId: this.config.stepId ? "config" : injectedStepId ? "injected" : "none"
1205
+ }
1206
+ });
1207
+ }
1208
+ const response = await this.resource.initialize({
1209
+ cmsSession: {
1210
+ customerId: authSession.customerId,
1211
+ sessionId: authSession.sessionId,
1212
+ storeId: store.id,
1213
+ accountId: store.accountId
1214
+ },
1215
+ funnelId: finalFunnelId,
1216
+ existingSessionId: existingSessionId || void 0,
1217
+ currentUrl: typeof window !== "undefined" ? window.location.href : void 0,
1218
+ funnelVariantId,
1219
+ // 🎯 Pass A/B test variant ID to backend
1220
+ funnelStepId: finalStepId,
1221
+ // 🎯 Pass step ID to backend (with config override)
1222
+ draft: sdkParams.draft,
1223
+ // 🎯 Pass draft mode explicitly (more robust than URL parsing)
1224
+ funnelTracking: sdkParams.funnelTracking,
1225
+ // 🎯 Pass funnel tracking flag explicitly
1226
+ funnelEnv: funnelEnv || void 0,
1227
+ // 🎯 Pass funnel environment (staging/production)
1228
+ tagadaClientEnv: sdkParams.tagadaClientEnv,
1229
+ // 🎯 Pass client environment override
1230
+ tagadaClientBaseUrl: sdkParams.tagadaClientBaseUrl
1231
+ // 🎯 Pass custom API base URL
1232
+ });
1233
+ if (response.success && response.context) {
1234
+ const enriched = this.enrichContext(response.context);
1235
+ this.handleSessionSuccess(enriched);
1236
+ injectPreviewModeIndicator();
1237
+ return enriched;
1238
+ } else {
1239
+ throw new Error(response.error || "Failed to initialize funnel session");
1240
+ }
1241
+ } catch (error) {
1242
+ const err = error instanceof Error ? error : new Error(String(error));
1243
+ this.updateState({ error: err, isLoading: false });
1244
+ if (this.config.debugMode) {
1245
+ console.error("\u274C [FunnelClient] Init failed:", err);
1246
+ }
1247
+ throw err;
1248
+ } finally {
1249
+ this.isInitializing = false;
1250
+ }
834
1251
  }
835
- const result = response.result;
836
- if (result.queued) {
837
- this.updateState({ isNavigating: false, isLoading: false });
838
- if (result.sessionId && result.sessionId !== ((_b = this.state.context) == null ? void 0 : _b.sessionId)) {
1252
+ /**
1253
+ * Manual initialization
1254
+ */
1255
+ async initialize(authSession, store, funnelId, entryStepId) {
1256
+ this.updateState({ isLoading: true, error: null });
1257
+ try {
1258
+ const funnelVariantId = getAssignedFunnelVariant();
1259
+ const funnelStepId = getAssignedFunnelStep();
839
1260
  if (this.config.debugMode) {
840
- console.log("\u{1F525} [FunnelClient] Session ID updated: ".concat((_c = this.state.context) == null ? void 0 : _c.sessionId, " \u2192 ").concat(result.sessionId));
1261
+ if (funnelVariantId) console.log("\u{1F3AF} [FunnelClient] Detected A/B test variant:", funnelVariantId);
1262
+ if (funnelStepId) console.log("\u{1F3AF} [FunnelClient] Detected step ID:", funnelStepId);
841
1263
  }
842
- if (this.state.context) {
843
- this.state.context.sessionId = result.sessionId;
1264
+ const response = await this.resource.initialize({
1265
+ cmsSession: {
1266
+ customerId: authSession.customerId,
1267
+ sessionId: authSession.sessionId,
1268
+ storeId: store.id,
1269
+ accountId: store.accountId
1270
+ },
1271
+ funnelId,
1272
+ entryStepId,
1273
+ currentUrl: typeof window !== "undefined" ? window.location.href : void 0,
1274
+ funnelVariantId,
1275
+ // 🎯 Pass A/B test variant ID to backend
1276
+ funnelStepId
1277
+ // 🎯 Pass step ID to backend
1278
+ });
1279
+ if (response.success && response.context) {
1280
+ const enriched = this.enrichContext(response.context);
1281
+ this.handleSessionSuccess(enriched);
1282
+ injectPreviewModeIndicator();
1283
+ return enriched;
1284
+ } else {
1285
+ throw new Error(response.error || "Failed to initialize");
844
1286
  }
1287
+ } catch (error) {
1288
+ const err = error instanceof Error ? error : new Error(String(error));
1289
+ this.updateState({ error: err, isLoading: false });
1290
+ throw err;
845
1291
  }
846
- if (this.config.debugMode) {
847
- console.log("\u{1F525} [FunnelClient] Navigation queued (fire-and-forget mode)");
1292
+ }
1293
+ /**
1294
+ * Navigate
1295
+ * @param event - Navigation event/action
1296
+ * @param options - Navigation options
1297
+ * @param options.fireAndForget - If true, queues navigation to QStash and returns immediately without waiting for result
1298
+ * @param options.customerTags - Customer tags to set (merged with existing customer tags)
1299
+ * @param options.deviceId - Device ID for geo/device tag enrichment (optional, rarely needed)
1300
+ * @param options.autoRedirect - Override global autoRedirect setting for this specific call (default: use config)
1301
+ */
1302
+ async navigate(event, options) {
1303
+ var _a, _b, _c, _d, _e, _f;
1304
+ if ((options == null ? void 0 : options.waitForSession) && !((_a = this.state.context) == null ? void 0 : _a.sessionId)) {
1305
+ if (this.config.debugMode) {
1306
+ console.log("\u23F3 [FunnelClient] Waiting for session before navigation...");
1307
+ }
1308
+ const maxWaitTime = 5e3;
1309
+ const startTime = Date.now();
1310
+ while (!((_b = this.state.context) == null ? void 0 : _b.sessionId) && Date.now() - startTime < maxWaitTime) {
1311
+ await new Promise((resolve) => setTimeout(resolve, 100));
1312
+ }
1313
+ if (((_c = this.state.context) == null ? void 0 : _c.sessionId) && this.config.debugMode) {
1314
+ console.log("\u2705 [FunnelClient] Session ready, proceeding with navigation");
1315
+ }
848
1316
  }
849
- return result;
1317
+ if (!((_d = this.state.context) == null ? void 0 : _d.sessionId)) throw new Error("No active session");
1318
+ this.updateState({ isNavigating: true, isLoading: true });
1319
+ try {
1320
+ let funnelVariantId = getAssignedFunnelVariant();
1321
+ let funnelStepId = getAssignedFunnelStep();
1322
+ const currentUrl = typeof window !== "undefined" ? window.location.href : void 0;
1323
+ if (!funnelStepId && this.config.stepId) {
1324
+ funnelStepId = this.config.stepId;
1325
+ if (this.config.debugMode) {
1326
+ console.log("\u{1F50D} [FunnelClient.navigate] Using stepId from config (no injection):", funnelStepId);
1327
+ }
1328
+ }
1329
+ if (!funnelVariantId && this.config.variantId) {
1330
+ funnelVariantId = this.config.variantId;
1331
+ if (this.config.debugMode) {
1332
+ console.log("\u{1F50D} [FunnelClient.navigate] Using variantId from config (no injection):", funnelVariantId);
1333
+ }
1334
+ }
1335
+ if (this.config.debugMode) {
1336
+ console.log("\u{1F50D} [FunnelClient.navigate] Sending to backend:", {
1337
+ sessionId: this.state.context.sessionId,
1338
+ currentUrl,
1339
+ funnelStepId: funnelStepId || "(not found)",
1340
+ funnelVariantId: funnelVariantId || "(not found)",
1341
+ hasInjectedStepId: !!getAssignedFunnelStep(),
1342
+ hasInjectedVariantId: !!getAssignedFunnelVariant(),
1343
+ usedConfigFallback: !getAssignedFunnelStep() && !!this.config.stepId,
1344
+ customerTags: (options == null ? void 0 : options.customerTags) || "(none)",
1345
+ deviceId: (options == null ? void 0 : options.deviceId) || "(none)"
1346
+ });
1347
+ }
1348
+ const fireAndForget = (options == null ? void 0 : options.fireAndForget) || false;
1349
+ const response = await this.resource.navigate({
1350
+ sessionId: this.state.context.sessionId,
1351
+ event,
1352
+ currentUrl,
1353
+ funnelStepId,
1354
+ funnelVariantId,
1355
+ fireAndForget,
1356
+ customerTags: options == null ? void 0 : options.customerTags,
1357
+ deviceId: options == null ? void 0 : options.deviceId
1358
+ });
1359
+ if (!response.success || !response.result) {
1360
+ throw new Error(response.error || "Navigation failed");
1361
+ }
1362
+ const result = response.result;
1363
+ if (result.queued) {
1364
+ this.updateState({ isNavigating: false, isLoading: false });
1365
+ if (result.sessionId && result.sessionId !== ((_e = this.state.context) == null ? void 0 : _e.sessionId)) {
1366
+ if (this.config.debugMode) {
1367
+ console.log("\u{1F525} [FunnelClient] Session ID updated: ".concat((_f = this.state.context) == null ? void 0 : _f.sessionId, " \u2192 ").concat(result.sessionId));
1368
+ }
1369
+ if (this.state.context) {
1370
+ this.state.context.sessionId = result.sessionId;
1371
+ }
1372
+ }
1373
+ if (this.config.debugMode) {
1374
+ console.log("\u{1F525} [FunnelClient] Navigation queued (fire-and-forget mode)");
1375
+ }
1376
+ return result;
1377
+ }
1378
+ const shouldAutoRedirect = (options == null ? void 0 : options.autoRedirect) !== void 0 ? options.autoRedirect : this.config.autoRedirect !== false;
1379
+ if (!shouldAutoRedirect) {
1380
+ if (this.config.debugMode) {
1381
+ console.log("\u{1F504} [FunnelClient] Refreshing session (no auto-redirect)");
1382
+ }
1383
+ await this.refreshSession();
1384
+ }
1385
+ this.updateState({ isNavigating: false, isLoading: false });
1386
+ if (shouldAutoRedirect && (result == null ? void 0 : result.url) && typeof window !== "undefined") {
1387
+ if (this.config.debugMode) {
1388
+ console.log("\u{1F680} [FunnelClient] Auto-redirecting to:", result.url, "(skipped session refresh - next page will initialize)");
1389
+ }
1390
+ window.location.href = result.url;
1391
+ }
1392
+ return result;
1393
+ } catch (error) {
1394
+ const err = error instanceof Error ? error : new Error(String(error));
1395
+ this.updateState({ error: err, isNavigating: false, isLoading: false });
1396
+ throw err;
1397
+ }
1398
+ }
1399
+ /**
1400
+ * Go to a specific step (direct navigation)
1401
+ * @param stepId - Target step ID
1402
+ * @param options - Navigation options (autoRedirect, etc.)
1403
+ */
1404
+ async goToStep(stepId, options) {
1405
+ return this.navigate(
1406
+ {
1407
+ type: "direct_navigation" /* DIRECT_NAVIGATION */,
1408
+ data: { targetStepId: stepId }
1409
+ },
1410
+ options
1411
+ );
850
1412
  }
851
- const shouldAutoRedirect = this.config.autoRedirect !== false;
852
- if (!shouldAutoRedirect) {
853
- if (this.config.debugMode) {
854
- console.log("\u{1F504} [FunnelClient] Refreshing session (no auto-redirect)");
1413
+ /**
1414
+ * Refresh session data
1415
+ */
1416
+ async refreshSession() {
1417
+ var _a;
1418
+ if (!((_a = this.state.context) == null ? void 0 : _a.sessionId)) return;
1419
+ try {
1420
+ const response = await this.resource.getSession(this.state.context.sessionId);
1421
+ if (response.success && response.context) {
1422
+ const enriched = this.enrichContext(response.context);
1423
+ this.updateState({ context: enriched, sessionError: null });
1424
+ return enriched;
1425
+ }
1426
+ } catch (error) {
1427
+ this.updateState({ sessionError: error instanceof Error ? error : new Error(String(error)) });
855
1428
  }
856
- await this.refreshSession();
857
1429
  }
858
- this.updateState({ isNavigating: false, isLoading: false });
859
- if (shouldAutoRedirect && (result == null ? void 0 : result.url) && typeof window !== "undefined") {
860
- if (this.config.debugMode) {
861
- console.log("\u{1F680} [FunnelClient] Auto-redirecting to:", result.url, "(skipped session refresh - next page will initialize)");
1430
+ /**
1431
+ * Update context data
1432
+ */
1433
+ async updateContext(updates) {
1434
+ var _a;
1435
+ if (!((_a = this.state.context) == null ? void 0 : _a.sessionId)) throw new Error("No active session");
1436
+ this.updateState({ isLoading: true });
1437
+ try {
1438
+ const response = await this.resource.updateContext(this.state.context.sessionId, { contextUpdates: updates });
1439
+ if (response.success) {
1440
+ await this.refreshSession();
1441
+ } else {
1442
+ throw new Error(response.error || "Failed to update context");
1443
+ }
1444
+ } finally {
1445
+ this.updateState({ isLoading: false });
862
1446
  }
863
- window.location.href = result.url;
864
1447
  }
865
- return result;
866
- } catch (error) {
867
- const err = error instanceof Error ? error : new Error(String(error));
868
- this.updateState({ error: err, isNavigating: false, isLoading: false });
869
- throw err;
870
- }
871
- }
872
- /**
873
- * Go to a specific step (direct navigation)
874
- */
875
- async goToStep(stepId) {
876
- return this.navigate({
877
- type: "direct_navigation" /* DIRECT_NAVIGATION */,
878
- data: { targetStepId: stepId }
879
- });
880
- }
881
- /**
882
- * Refresh session data
883
- */
884
- async refreshSession() {
885
- var _a;
886
- if (!((_a = this.state.context) == null ? void 0 : _a.sessionId)) return;
887
- try {
888
- const response = await this.resource.getSession(this.state.context.sessionId);
889
- if (response.success && response.context) {
890
- const enriched = this.enrichContext(response.context);
891
- this.updateState({ context: enriched, sessionError: null });
892
- return enriched;
1448
+ /**
1449
+ * End session
1450
+ */
1451
+ async endSession() {
1452
+ var _a;
1453
+ if (!((_a = this.state.context) == null ? void 0 : _a.sessionId)) return;
1454
+ try {
1455
+ await this.resource.endSession(this.state.context.sessionId);
1456
+ } finally {
1457
+ this.state.context = null;
1458
+ this.updateState({ context: null, isInitialized: false });
1459
+ if (typeof document !== "undefined") {
1460
+ }
1461
+ }
893
1462
  }
894
- } catch (error) {
895
- this.updateState({ sessionError: error instanceof Error ? error : new Error(String(error)) });
896
- }
897
- }
898
- /**
899
- * Update context data
900
- */
901
- async updateContext(updates) {
902
- var _a;
903
- if (!((_a = this.state.context) == null ? void 0 : _a.sessionId)) throw new Error("No active session");
904
- this.updateState({ isLoading: true });
905
- try {
906
- const response = await this.resource.updateContext(this.state.context.sessionId, { contextUpdates: updates });
907
- if (response.success) {
908
- await this.refreshSession();
909
- } else {
910
- throw new Error(response.error || "Failed to update context");
1463
+ // Private helpers
1464
+ updateState(updates) {
1465
+ this.state = __spreadValues(__spreadValues({}, this.state), updates);
1466
+ this.eventDispatcher.notify(this.state);
1467
+ }
1468
+ handleSessionSuccess(context) {
1469
+ setFunnelSessionCookie(context.sessionId);
1470
+ this.updateState({
1471
+ context,
1472
+ isLoading: false,
1473
+ isInitialized: true,
1474
+ error: null,
1475
+ sessionError: null
1476
+ });
911
1477
  }
912
- } finally {
913
- this.updateState({ isLoading: false });
914
- }
915
- }
916
- /**
917
- * End session
918
- */
919
- async endSession() {
920
- var _a;
921
- if (!((_a = this.state.context) == null ? void 0 : _a.sessionId)) return;
922
- try {
923
- await this.resource.endSession(this.state.context.sessionId);
924
- } finally {
925
- this.state.context = null;
926
- this.updateState({ context: null, isInitialized: false });
927
- if (typeof document !== "undefined") {
1478
+ enrichContext(ctx) {
1479
+ var _a, _b;
1480
+ const env = ((_a = this.config.environment) == null ? void 0 : _a.environment) || detectEnvironment();
1481
+ if (env !== "local") return ctx;
1482
+ const localResources = ((_b = this.config.pluginConfig) == null ? void 0 : _b.staticResources) || {};
1483
+ if (Object.keys(localResources).length === 0) return ctx;
1484
+ const existingStatic = ctx.static || {};
1485
+ const hasAllResources = Object.keys(localResources).every(
1486
+ (key) => existingStatic[key] === localResources[key]
1487
+ );
1488
+ if (hasAllResources && Object.keys(existingStatic).length === Object.keys(localResources).length) {
1489
+ return ctx;
1490
+ }
1491
+ return __spreadProps(__spreadValues({}, ctx), {
1492
+ static: __spreadValues(__spreadValues({}, localResources), existingStatic)
1493
+ });
928
1494
  }
929
- }
930
- }
931
- // Private helpers
932
- updateState(updates) {
933
- this.state = __spreadValues(__spreadValues({}, this.state), updates);
934
- this.eventDispatcher.notify(this.state);
935
- }
936
- handleSessionSuccess(context) {
937
- setFunnelSessionCookie(context.sessionId);
938
- this.updateState({
939
- context,
940
- isLoading: false,
941
- isInitialized: true,
942
- error: null,
943
- sessionError: null
944
- });
945
- }
946
- enrichContext(ctx) {
947
- var _a, _b;
948
- const env = ((_a = this.config.environment) == null ? void 0 : _a.environment) || detectEnvironment();
949
- if (env !== "local") return ctx;
950
- const localResources = ((_b = this.config.pluginConfig) == null ? void 0 : _b.staticResources) || {};
951
- if (Object.keys(localResources).length === 0) return ctx;
952
- const existingStatic = ctx.static || {};
953
- const hasAllResources = Object.keys(localResources).every(
954
- (key) => existingStatic[key] === localResources[key]
955
- );
956
- if (hasAllResources && Object.keys(existingStatic).length === Object.keys(localResources).length) {
957
- return ctx;
958
- }
959
- return __spreadProps(__spreadValues({}, ctx), {
960
- static: __spreadValues(__spreadValues({}, localResources), existingStatic)
961
- });
1495
+ };
962
1496
  }
963
- };
1497
+ });
1498
+
1499
+ // src/v2/standalone/external-tracker.ts
1500
+ var external_tracker_exports = {};
1501
+ __export(external_tracker_exports, {
1502
+ TagadaExternalTracker: () => TagadaExternalTracker,
1503
+ TagadaTracker: () => TagadaTracker
1504
+ });
1505
+
1506
+ // src/v2/core/client.ts
1507
+ init_environment();
1508
+ init_funnelClient();
964
1509
 
965
1510
  // ../../node_modules/.pnpm/axios@1.13.0/node_modules/axios/lib/helpers/bind.js
966
1511
  function bind(fn, thisArg) {
@@ -3592,7 +4137,8 @@ var TagadaTrackerBundle = (() => {
3592
4137
  this.MAX_REQUESTS = 30;
3593
4138
  this.axios = axios_default.create({
3594
4139
  baseURL: config.baseURL,
3595
- timeout: config.timeout || 3e4,
4140
+ timeout: config.timeout || 6e4,
4141
+ // 60 seconds for payment operations
3596
4142
  headers: __spreadValues({
3597
4143
  "Content-Type": "application/json"
3598
4144
  }, config.headers)
@@ -7108,6 +7654,9 @@ var TagadaTrackerBundle = (() => {
7108
7654
  }
7109
7655
  };
7110
7656
 
7657
+ // src/v2/core/client.ts
7658
+ init_eventDispatcher();
7659
+
7111
7660
  // src/v2/core/utils/jwtDecoder.ts
7112
7661
  function decodeJWTClient(token) {
7113
7662
  try {
@@ -7156,6 +7705,9 @@ var TagadaTrackerBundle = (() => {
7156
7705
  }
7157
7706
  }
7158
7707
 
7708
+ // src/v2/core/utils/pluginConfig.ts
7709
+ init_environment();
7710
+
7159
7711
  // src/v2/core/utils/env.ts
7160
7712
  var import_meta = {};
7161
7713
  var DEFAULT_PREFIXES = ["", "VITE_", "REACT_APP_", "NEXT_PUBLIC_"];
@@ -7250,14 +7802,34 @@ var TagadaTrackerBundle = (() => {
7250
7802
  if (!isLocalEnvironment(true)) {
7251
7803
  return null;
7252
7804
  }
7253
- console.log("\u{1F6E0}\uFE0F [V2] Attempting to load /config/resources.static.json...");
7805
+ try {
7806
+ console.log("\u{1F6E0}\uFE0F [V2] Attempting to load /config/funnel.local.json...");
7807
+ const funnelResponse = await fetch("/config/funnel.local.json");
7808
+ if (funnelResponse.ok) {
7809
+ const funnelConfig = await funnelResponse.json();
7810
+ console.log("\u{1F6E0}\uFE0F [V2] \u2705 Loaded local funnel config (NEW format):", funnelConfig);
7811
+ const { loadLocalFunnelConfig: loadLocalFunnelConfig2 } = await Promise.resolve().then(() => (init_funnelClient(), funnelClient_exports));
7812
+ await loadLocalFunnelConfig2();
7813
+ if (funnelConfig.staticResources) {
7814
+ const transformed = {};
7815
+ for (const [key, value] of Object.entries(funnelConfig.staticResources)) {
7816
+ transformed[key] = { id: value };
7817
+ }
7818
+ return transformed;
7819
+ }
7820
+ return null;
7821
+ }
7822
+ } catch (e) {
7823
+ console.log("\u{1F6E0}\uFE0F [V2] funnel.local.json not found, trying legacy format...");
7824
+ }
7825
+ console.log("\u{1F6E0}\uFE0F [V2] Attempting to load /config/resources.static.json (legacy)...");
7254
7826
  const response = await fetch("/config/resources.static.json");
7255
7827
  if (!response.ok) {
7256
- console.log("\u{1F6E0}\uFE0F [V2] resources.static.json not found or failed to load");
7828
+ console.log("\u{1F6E0}\uFE0F [V2] No local static resources found");
7257
7829
  return null;
7258
7830
  }
7259
7831
  const staticResources = await response.json();
7260
- console.log("\u{1F6E0}\uFE0F [V2] \u2705 Loaded local static resources:", staticResources);
7832
+ console.log("\u{1F6E0}\uFE0F [V2] \u2705 Loaded legacy static resources:", staticResources);
7261
7833
  return staticResources;
7262
7834
  } catch (error) {
7263
7835
  console.error("\u{1F6E0}\uFE0F [V2] \u274C Error loading static resources:", error);
@@ -7418,7 +7990,12 @@ var TagadaTrackerBundle = (() => {
7418
7990
  }
7419
7991
  }
7420
7992
 
7993
+ // src/v2/core/client.ts
7994
+ init_previewMode();
7995
+ init_tokenStorage();
7996
+
7421
7997
  // src/v2/core/utils/authHandoff.ts
7998
+ init_tokenStorage();
7422
7999
  var resolutionCache = /* @__PURE__ */ new Map();
7423
8000
  var resolvedCodes = /* @__PURE__ */ new Set();
7424
8001
  function getAuthCode() {
@@ -7803,9 +8380,16 @@ var TagadaTrackerBundle = (() => {
7803
8380
  * Normal token initialization flow (no cross-domain handoff)
7804
8381
  */
7805
8382
  async fallbackToNormalFlow() {
7806
- const existingToken = getClientToken();
7807
8383
  const urlParams = new URLSearchParams(typeof window !== "undefined" ? window.location.search : "");
7808
8384
  const queryToken = urlParams.get("token");
8385
+ if (queryToken) {
8386
+ if (this.state.debugMode) {
8387
+ console.log("[TagadaClient ".concat(this.instanceId, "] \u{1F512} URL token detected, setting immediately to prevent race condition"));
8388
+ }
8389
+ this.apiClient.updateToken(queryToken);
8390
+ setClientToken(queryToken);
8391
+ }
8392
+ const existingToken = getClientToken();
7809
8393
  console.log("[TagadaClient ".concat(this.instanceId, "] Initializing token (normal flow)..."), {
7810
8394
  hasExistingToken: !!existingToken,
7811
8395
  hasQueryToken: !!queryToken,
@@ -7880,6 +8464,23 @@ var TagadaTrackerBundle = (() => {
7880
8464
  * Create anonymous token
7881
8465
  */
7882
8466
  async createAnonymousToken(storeId) {
8467
+ if (typeof window !== "undefined") {
8468
+ const urlParams = new URLSearchParams(window.location.search);
8469
+ const urlToken = urlParams.get("token");
8470
+ if (urlToken) {
8471
+ if (this.state.debugMode) {
8472
+ console.log("[TagadaClient ".concat(this.instanceId, "] \u{1F512} URL has token, skipping anonymous token creation"));
8473
+ }
8474
+ this.setToken(urlToken);
8475
+ setClientToken(urlToken);
8476
+ const decodedSession = decodeJWTClient(urlToken);
8477
+ if (decodedSession) {
8478
+ this.updateState({ session: decodedSession });
8479
+ await this.initializeSession(decodedSession);
8480
+ }
8481
+ return;
8482
+ }
8483
+ }
7883
8484
  if (this.isInitializingSession) {
7884
8485
  if (this.state.debugMode) {
7885
8486
  console.log("[TagadaClient ".concat(this.instanceId, "] Session initialization in progress, skipping anonymous token creation"));
@@ -8362,12 +8963,19 @@ var TagadaTrackerBundle = (() => {
8362
8963
  }
8363
8964
  };
8364
8965
 
8966
+ // src/v2/standalone/index.ts
8967
+ init_funnel();
8968
+
8969
+ // src/v2/core/utils/index.ts
8970
+ init_sessionStorage();
8971
+
8365
8972
  // src/v2/standalone/index.ts
8366
8973
  function createTagadaClient(config = {}) {
8367
8974
  return new TagadaClient(config);
8368
8975
  }
8369
8976
 
8370
8977
  // src/v2/standalone/external-tracker.ts
8978
+ init_tokenStorage();
8371
8979
  function getUrlParam(name) {
8372
8980
  if (typeof window === "undefined") return null;
8373
8981
  const params = new URLSearchParams(window.location.search);
@@ -8458,14 +9066,19 @@ var TagadaTrackerBundle = (() => {
8458
9066
  throw new Error("Tracker not initialized. Call init() first.");
8459
9067
  }
8460
9068
  log(this.config.debug, "\u{1F680} Navigating:", options);
9069
+ const shouldAutoRedirect = options.autoRedirect !== false;
8461
9070
  try {
8462
- const result = await this.client.funnel.navigate({
8463
- type: options.eventType,
8464
- data: options.eventData || {}
8465
- });
9071
+ const result = await this.client.funnel.navigate(
9072
+ {
9073
+ type: options.eventType,
9074
+ data: options.eventData || {}
9075
+ },
9076
+ { autoRedirect: false }
9077
+ // Always disable SDK auto-redirect, we handle it here
9078
+ );
8466
9079
  if (result == null ? void 0 : result.url) {
8467
9080
  log(this.config.debug, "\u2705 Navigation result:", result.url);
8468
- if (typeof window !== "undefined") {
9081
+ if (shouldAutoRedirect && typeof window !== "undefined") {
8469
9082
  window.location.href = options.returnUrl || result.url;
8470
9083
  }
8471
9084
  return { url: result.url };