@aranova/tracking-react 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sales.mjs CHANGED
@@ -189,7 +189,8 @@ function getBookingIntegration(integrations) {
189
189
  timezone: booking.timezone,
190
190
  configVersion: booking.config_version,
191
191
  rules,
192
- options: booking.options.map((option) => ({ key: option.key, label: option.label }))
192
+ options: booking.options.map((option) => ({ key: option.key, label: option.label })),
193
+ mode: booking.mode ?? "live"
193
194
  };
194
195
  }
195
196
  function camelRules(rules) {
@@ -282,6 +283,55 @@ var AranovaApiError = class extends Error {
282
283
  }
283
284
  };
284
285
 
286
+ // ../tracking-core/src/resources/intl/date.ts
287
+ function formatDateInTz(iso3, timeZone, opts, locale) {
288
+ const date = new Date(iso3);
289
+ if (Number.isNaN(date.getTime())) return iso3;
290
+ return new Intl.DateTimeFormat(locale, {
291
+ year: "numeric",
292
+ month: "short",
293
+ day: "2-digit",
294
+ hour: "2-digit",
295
+ minute: "2-digit",
296
+ ...opts,
297
+ timeZone
298
+ }).format(date);
299
+ }
300
+
301
+ // ../tracking-core/src/resources/sales/money.ts
302
+ var MINOR_UNIT_EXPONENT = {
303
+ USD: 2,
304
+ CAD: 2
305
+ };
306
+ function exponentFor(currency) {
307
+ return MINOR_UNIT_EXPONENT[currency] ?? 2;
308
+ }
309
+ function toMinor(amount, currency) {
310
+ return Math.round(amount * 10 ** exponentFor(currency));
311
+ }
312
+ function fromMinor(cents, currency) {
313
+ return cents / 10 ** exponentFor(currency);
314
+ }
315
+ function formatMoney(cents, currency, locale) {
316
+ return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
317
+ fromMinor(cents, currency)
318
+ );
319
+ }
320
+
321
+ // ../tracking-core/src/resources/tracking-config-runtime.ts
322
+ var DEFAULT_CDN_BASE_URL = "https://demos.aranova.io";
323
+ function trackingConfigKey(ref) {
324
+ if (ref.siteId) return `tracking-config/v1/sites/${ref.siteId}-${ref.environment}.json`;
325
+ return `tracking-config/v1/${ref.businessId}-${ref.environment}.json`;
326
+ }
327
+ function resolveTrackingConfigUrl(ref) {
328
+ if (ref.cdnUrl) return ref.cdnUrl;
329
+ const configured = ref.cdnBaseUrl?.trim();
330
+ const base = (configured || DEFAULT_CDN_BASE_URL).replace(/\/+$/, "");
331
+ return `${base}/${trackingConfigKey(ref)}`;
332
+ }
333
+ var RETRY_MAX_MS = 5 * 6e4;
334
+
285
335
  // ../tracking-core/src/ingest.ts
286
336
  var API_KEY_HEADER = "X-Aranova-Api-Key";
287
337
  var SDK_VERSION_HEADER = "X-Aranova-Sdk-Version";
@@ -340,6 +390,7 @@ async function apiRequest(config, method, path, body, extraHeaders) {
340
390
  var salesRequest = apiRequest;
341
391
 
342
392
  // ../tracking-core/src/resources/sales/bookings.ts
393
+ var BOOKING_SIMULATE_HEADER = "X-Aranova-Simulate";
343
394
  var BOOKING_ERROR_CODES = {
344
395
  unavailable: "booking_unavailable",
345
396
  stale: "booking_config_stale",
@@ -354,10 +405,11 @@ function sleep(ms) {
354
405
  });
355
406
  }
356
407
  function outcomeOf(body) {
408
+ const mode = body.mode ?? "live";
357
409
  if (body.status === "created")
358
- return { status: "created", id: body.id, requestCode: body.request_code };
359
- if (body.status === "pending") return { status: "pending", id: body.id };
360
- return { status: "failed", id: body.id, fallbackUrl: body.fallback_url };
410
+ return { status: "created", id: body.id, requestCode: body.request_code, mode };
411
+ if (body.status === "pending") return { status: "pending", id: body.id, mode };
412
+ return { status: "failed", id: body.id, fallbackUrl: body.fallback_url, mode };
361
413
  }
362
414
  function formSubmitPayload(input, formId, labels) {
363
415
  const vehicle = input.vehicle ? `${input.vehicle.year} ${input.vehicle.make} ${input.vehicle.model}` : "";
@@ -399,9 +451,29 @@ function formSubmitPayload(input, formId, labels) {
399
451
  function runtimeOf(firing) {
400
452
  return firing && "integrations" in firing ? firing : null;
401
453
  }
454
+ function isLocalUrl(url) {
455
+ try {
456
+ return ["localhost", "127.0.0.1", "[::1]"].includes(new URL(url).hostname);
457
+ } catch {
458
+ return false;
459
+ }
460
+ }
461
+ function bookingSetupMismatch(endpoint, runtime) {
462
+ if (!runtime || !isLocalUrl(endpoint)) return null;
463
+ const configUrl = resolveTrackingConfigUrl(runtime.ref);
464
+ if (isLocalUrl(configUrl)) return null;
465
+ return `[aranova] Bookings go to the local API (${endpoint}) but the runtime config comes from ${configUrl}. Set NEXT_PUBLIC_ARANOVA_CDN_BASE_URL to the local bucket (http://localhost:9100/aranova-demos) so both come from one backend.`;
466
+ }
402
467
  function createSalesBookingsClient(config, firing) {
403
468
  const submittedForms = /* @__PURE__ */ new Set();
404
469
  const staleAttempts = /* @__PURE__ */ new Set();
470
+ let isSetupChecked = false;
471
+ function warnOnSetupMismatch(runtime) {
472
+ if (isSetupChecked) return;
473
+ isSetupChecked = true;
474
+ const warning = bookingSetupMismatch(config.endpoint, runtime);
475
+ if (warning) console.warn(warning);
476
+ }
405
477
  function handoff(input) {
406
478
  const booking = getBookingIntegration(runtimeOf(firing)?.integrations());
407
479
  if (!booking) return null;
@@ -412,14 +484,18 @@ function createSalesBookingsClient(config, firing) {
412
484
  email: input.email
413
485
  });
414
486
  }
487
+ function failedBeforeSend(input) {
488
+ const mode = getBookingIntegration(runtimeOf(firing)?.integrations())?.mode ?? "live";
489
+ return { status: "failed", id: null, fallbackUrl: handoff(input), mode };
490
+ }
415
491
  function withHandoff(outcome, input) {
416
492
  if (outcome.status === "failed" && !outcome.fallbackUrl) {
417
493
  return { ...outcome, fallbackUrl: handoff(input) };
418
494
  }
419
495
  return outcome;
420
496
  }
421
- async function poll(id) {
422
- let outcome = { status: "pending", id };
497
+ async function poll(id, mode) {
498
+ let outcome = { status: "pending", id, mode };
423
499
  for (const delay of POLL_DELAYS_MS) {
424
500
  await sleep(delay);
425
501
  try {
@@ -434,12 +510,10 @@ function createSalesBookingsClient(config, firing) {
434
510
  }
435
511
  async function rejected(error, input, attemptKey, runtime) {
436
512
  if (!(error instanceof AranovaApiError)) {
437
- return { status: "failed", id: null, fallbackUrl: handoff(input) };
513
+ return failedBeforeSend(input);
438
514
  }
439
515
  if (error.code === BOOKING_ERROR_CODES.stale) {
440
- if (staleAttempts.has(attemptKey)) {
441
- return { status: "failed", id: null, fallbackUrl: handoff(input) };
442
- }
516
+ if (staleAttempts.has(attemptKey)) return failedBeforeSend(input);
443
517
  staleAttempts.add(attemptKey);
444
518
  await runtime?.revalidate();
445
519
  return { status: "stale", message: error.message };
@@ -455,15 +529,16 @@ function createSalesBookingsClient(config, firing) {
455
529
  if (error.code === "validation_error") {
456
530
  return { status: "invalid", message: INVALID_DETAILS, reasons: [] };
457
531
  }
458
- return { status: "failed", id: null, fallbackUrl: handoff(input) };
532
+ return failedBeforeSend(input);
459
533
  }
460
534
  return {
461
- async submit(input, { attemptKey, formSubmit }) {
535
+ async submit(input, { attemptKey, formSubmit, simulate }) {
462
536
  registerCapability("booking_integration");
463
537
  const runtime = runtimeOf(firing);
538
+ warnOnSetupMismatch(runtime);
464
539
  const booking = getBookingIntegration(runtime?.integrations());
465
540
  if (!booking?.acceptsBookings || booking.configVersion === null) {
466
- return { status: "failed", id: null, fallbackUrl: handoff(input) };
541
+ return failedBeforeSend(input);
467
542
  }
468
543
  if (formSubmit && !submittedForms.has(attemptKey)) {
469
544
  submittedForms.add(attemptKey);
@@ -493,12 +568,22 @@ function createSalesBookingsClient(config, firing) {
493
568
  };
494
569
  let response;
495
570
  try {
496
- response = await salesRequest(config, "POST", "/bookings", body);
571
+ const headers = simulate ? { [BOOKING_SIMULATE_HEADER]: simulate } : void 0;
572
+ response = await salesRequest(
573
+ config,
574
+ "POST",
575
+ "/bookings",
576
+ body,
577
+ headers
578
+ );
497
579
  } catch (error) {
498
580
  return rejected(error, input, attemptKey, runtime);
499
581
  }
500
582
  const outcome = outcomeOf(response);
501
- return withHandoff(outcome.status === "pending" ? await poll(outcome.id) : outcome, input);
583
+ return withHandoff(
584
+ outcome.status === "pending" ? await poll(outcome.id, outcome.mode) : outcome,
585
+ input
586
+ );
502
587
  },
503
588
  async status(id) {
504
589
  return outcomeOf(
@@ -512,41 +597,6 @@ function createSalesBookingsClient(config, firing) {
512
597
  };
513
598
  }
514
599
 
515
- // ../tracking-core/src/resources/intl/date.ts
516
- function formatDateInTz(iso3, timeZone, opts, locale) {
517
- const date = new Date(iso3);
518
- if (Number.isNaN(date.getTime())) return iso3;
519
- return new Intl.DateTimeFormat(locale, {
520
- year: "numeric",
521
- month: "short",
522
- day: "2-digit",
523
- hour: "2-digit",
524
- minute: "2-digit",
525
- ...opts,
526
- timeZone
527
- }).format(date);
528
- }
529
-
530
- // ../tracking-core/src/resources/sales/money.ts
531
- var MINOR_UNIT_EXPONENT = {
532
- USD: 2,
533
- CAD: 2
534
- };
535
- function exponentFor(currency) {
536
- return MINOR_UNIT_EXPONENT[currency] ?? 2;
537
- }
538
- function toMinor(amount, currency) {
539
- return Math.round(amount * 10 ** exponentFor(currency));
540
- }
541
- function fromMinor(cents, currency) {
542
- return cents / 10 ** exponentFor(currency);
543
- }
544
- function formatMoney(cents, currency, locale) {
545
- return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
546
- fromMinor(cents, currency)
547
- );
548
- }
549
-
550
600
  // ../tracking-core/src/resources/sales/client.ts
551
601
  function fireRecordedConversions(firing, input, recorded, sale, currency) {
552
602
  if (!firing) return;