@aranova/tracking-react 1.3.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -342,6 +342,13 @@ await submit({
342
342
  your own), `pending` (same success; still being sent, the owner is alerted if it fails),
343
343
  `failed` (success copy plus `outcome.fallbackUrl`, the prefilled shop page), `invalid`
344
344
  (`outcome.message`, nothing sent), `stale` (the times changed; the config is refetched).
345
+ - `outcome.mode` (and `booking.mode` before submitting): `live` for every real customer;
346
+ `test` (sent to the shop's test lead source) or `simulated` (never sent) on a test visit or a
347
+ non-production API. Show a small "test booking" note when it isn't `live`.
348
+ - Development only: `useBookingSubmit(bookings, { simulate: "failed" })` forces a simulated
349
+ outcome (`created`, `rejected`, `unknown`, `failed`) to exercise each screen. The production
350
+ API ignores it. Test locally against the local API **and** the local CDN; the SDK warns in the
351
+ console when the endpoint is localhost but the config comes from production.
345
352
  - `useBookingSlots` / `bookableSlots(rules, { timeZone })` offer exactly the portal's start hours,
346
353
  judged by the shop's date. `useBookingSubmit` reuses one attempt key across a double-click or
347
354
  a retry, and sends the visit's `form_submit` once: don't send your own for this form.
@@ -70,6 +70,13 @@ interface BookingOptionConfig {
70
70
  key: string;
71
71
  label: string;
72
72
  }
73
+ /** Where a booking went; decided by the API. A real customer always gets `live`. */
74
+ declare const BOOKING_MODES: readonly ["live", "test", "simulated"];
75
+ /**
76
+ * `live`: the shop's real queue. `test`: a real request on the shop's test lead source, never
77
+ * counted. `simulated`: never sent; nothing reached the shop or the customer.
78
+ */
79
+ type BookingMode = (typeof BOOKING_MODES)[number];
73
80
  /** `integrations.autoleap.booking` — never carries a secret. */
74
81
  interface AutoLeapBookingConfig {
75
82
  /** False: show only `fallback_url`. The site never blocks the customer on it. */
@@ -83,6 +90,8 @@ interface AutoLeapBookingConfig {
83
90
  accepts_bookings: boolean;
84
91
  rules: BookingRulesConfig | null;
85
92
  options: BookingOptionConfig[];
93
+ /** Development objects only: `test` or `simulated`, so a dev page can say so up front. */
94
+ mode?: BookingMode;
86
95
  }
87
96
  interface ConversionIntegrations {
88
97
  autoleap?: {
@@ -254,8 +263,11 @@ declare function getTrackingConfigRuntime(ref: TrackingConfigReference, fetchImp
254
263
  /** A UUID v4 from Web Crypto, with a `getRandomValues` (then `Math.random`) fallback. */
255
264
  declare function uuidV4(): string;
256
265
 
266
+ /** Developer-only outcomes for a simulated create; the production API ignores them. */
267
+ declare const BOOKING_SIMULATIONS: readonly ["created", "rejected", "unknown", "failed"];
257
268
  declare const BOOKING_DROP_OFF_OPTIONS: readonly ["waitForIt", "dropOff"];
258
269
  type BookingDropOff = (typeof BOOKING_DROP_OFF_OPTIONS)[number];
270
+ type BookingSimulation = (typeof BOOKING_SIMULATIONS)[number];
259
271
  /** What the booking form collected. Dates and hours are the shop's wall time. */
260
272
  interface BookingInput {
261
273
  /** `YYYY-MM-DD`, one of `bookableSlots(...)`'s dates. */
@@ -300,11 +312,13 @@ type BookingOutcome =
300
312
  status: "created";
301
313
  id: string;
302
314
  requestCode: string | null;
315
+ mode: BookingMode;
303
316
  }
304
317
  /** Received and still being sent; the shop confirms. Show the same success. */
305
318
  | {
306
319
  status: "pending";
307
320
  id: string;
321
+ mode: BookingMode;
308
322
  }
309
323
  /**
310
324
  * Not created. Still a success for the customer: show the prefilled shop booking link.
@@ -314,6 +328,7 @@ type BookingOutcome =
314
328
  status: "failed";
315
329
  id: string | null;
316
330
  fallbackUrl: string | null;
331
+ mode: BookingMode;
317
332
  }
318
333
  /** The booking breaks the shop's rules; `message` is customer-safe. Nothing was sent. */
319
334
  | {
@@ -326,20 +341,26 @@ type BookingOutcome =
326
341
  status: "stale";
327
342
  message: string;
328
343
  };
344
+ interface BookingSubmitOptions {
345
+ attemptKey: string;
346
+ formSubmit?: BookingFormSubmit;
347
+ /**
348
+ * Development only: force a simulated create's outcome to exercise the success, pending and
349
+ * fallback screens. The production API ignores it, so it can never change a real booking.
350
+ */
351
+ simulate?: BookingSimulation;
352
+ }
329
353
  interface SalesBookingsClient {
330
354
  /**
331
355
  * Send one booking. Never rejects for an API outcome: every answer is a {@link BookingOutcome}.
332
356
  * Pass the same `attemptKey` (a UUID, `newBookingAttemptKey()`) on a retry of the same submit,
333
357
  * so a double-click or a network retry can never book twice.
334
358
  */
335
- submit(input: BookingInput, options: {
336
- attemptKey: string;
337
- formSubmit?: BookingFormSubmit;
338
- }): Promise<BookingOutcome>;
359
+ submit(input: BookingInput, options: BookingSubmitOptions): Promise<BookingOutcome>;
339
360
  /** A booking's current status by the id `submit` returned. */
340
361
  status(id: string): Promise<BookingOutcome>;
341
362
  }
342
363
  /** A fresh per-submit key. Keep it for retries of the same submit; mint a new one after. */
343
364
  declare const newBookingAttemptKey: typeof uuidV4;
344
365
 
345
- export { type BookingDropOff as B, type ConversionConfig as C, type SalesBookingsClient as S, type TrackingConfigReference as T, type BookingFormSubmit as a, type BookingInput as b, type BookingOutcome as c, type ConversionConfigStore as d, resolveTrackingConfigUrl as e, type BookingDayWindows as f, getTrackingConfigRuntime as g, type ConversionIntegrations as h, TrackingConfigRuntime as i, newBookingAttemptKey as n, resolveConversionConfig as r };
366
+ export { type BookingDropOff as B, type ConversionConfig as C, type SalesBookingsClient as S, type TrackingConfigReference as T, type BookingFormSubmit as a, type BookingInput as b, type BookingMode as c, type BookingOutcome as d, type BookingSimulation as e, type BookingSubmitOptions as f, type ConversionConfigStore as g, getTrackingConfigRuntime as h, resolveTrackingConfigUrl as i, type BookingDayWindows as j, type ConversionIntegrations as k, TrackingConfigRuntime as l, newBookingAttemptKey as n, resolveConversionConfig as r };
@@ -70,6 +70,13 @@ interface BookingOptionConfig {
70
70
  key: string;
71
71
  label: string;
72
72
  }
73
+ /** Where a booking went; decided by the API. A real customer always gets `live`. */
74
+ declare const BOOKING_MODES: readonly ["live", "test", "simulated"];
75
+ /**
76
+ * `live`: the shop's real queue. `test`: a real request on the shop's test lead source, never
77
+ * counted. `simulated`: never sent; nothing reached the shop or the customer.
78
+ */
79
+ type BookingMode = (typeof BOOKING_MODES)[number];
73
80
  /** `integrations.autoleap.booking` — never carries a secret. */
74
81
  interface AutoLeapBookingConfig {
75
82
  /** False: show only `fallback_url`. The site never blocks the customer on it. */
@@ -83,6 +90,8 @@ interface AutoLeapBookingConfig {
83
90
  accepts_bookings: boolean;
84
91
  rules: BookingRulesConfig | null;
85
92
  options: BookingOptionConfig[];
93
+ /** Development objects only: `test` or `simulated`, so a dev page can say so up front. */
94
+ mode?: BookingMode;
86
95
  }
87
96
  interface ConversionIntegrations {
88
97
  autoleap?: {
@@ -254,8 +263,11 @@ declare function getTrackingConfigRuntime(ref: TrackingConfigReference, fetchImp
254
263
  /** A UUID v4 from Web Crypto, with a `getRandomValues` (then `Math.random`) fallback. */
255
264
  declare function uuidV4(): string;
256
265
 
266
+ /** Developer-only outcomes for a simulated create; the production API ignores them. */
267
+ declare const BOOKING_SIMULATIONS: readonly ["created", "rejected", "unknown", "failed"];
257
268
  declare const BOOKING_DROP_OFF_OPTIONS: readonly ["waitForIt", "dropOff"];
258
269
  type BookingDropOff = (typeof BOOKING_DROP_OFF_OPTIONS)[number];
270
+ type BookingSimulation = (typeof BOOKING_SIMULATIONS)[number];
259
271
  /** What the booking form collected. Dates and hours are the shop's wall time. */
260
272
  interface BookingInput {
261
273
  /** `YYYY-MM-DD`, one of `bookableSlots(...)`'s dates. */
@@ -300,11 +312,13 @@ type BookingOutcome =
300
312
  status: "created";
301
313
  id: string;
302
314
  requestCode: string | null;
315
+ mode: BookingMode;
303
316
  }
304
317
  /** Received and still being sent; the shop confirms. Show the same success. */
305
318
  | {
306
319
  status: "pending";
307
320
  id: string;
321
+ mode: BookingMode;
308
322
  }
309
323
  /**
310
324
  * Not created. Still a success for the customer: show the prefilled shop booking link.
@@ -314,6 +328,7 @@ type BookingOutcome =
314
328
  status: "failed";
315
329
  id: string | null;
316
330
  fallbackUrl: string | null;
331
+ mode: BookingMode;
317
332
  }
318
333
  /** The booking breaks the shop's rules; `message` is customer-safe. Nothing was sent. */
319
334
  | {
@@ -326,20 +341,26 @@ type BookingOutcome =
326
341
  status: "stale";
327
342
  message: string;
328
343
  };
344
+ interface BookingSubmitOptions {
345
+ attemptKey: string;
346
+ formSubmit?: BookingFormSubmit;
347
+ /**
348
+ * Development only: force a simulated create's outcome to exercise the success, pending and
349
+ * fallback screens. The production API ignores it, so it can never change a real booking.
350
+ */
351
+ simulate?: BookingSimulation;
352
+ }
329
353
  interface SalesBookingsClient {
330
354
  /**
331
355
  * Send one booking. Never rejects for an API outcome: every answer is a {@link BookingOutcome}.
332
356
  * Pass the same `attemptKey` (a UUID, `newBookingAttemptKey()`) on a retry of the same submit,
333
357
  * so a double-click or a network retry can never book twice.
334
358
  */
335
- submit(input: BookingInput, options: {
336
- attemptKey: string;
337
- formSubmit?: BookingFormSubmit;
338
- }): Promise<BookingOutcome>;
359
+ submit(input: BookingInput, options: BookingSubmitOptions): Promise<BookingOutcome>;
339
360
  /** A booking's current status by the id `submit` returned. */
340
361
  status(id: string): Promise<BookingOutcome>;
341
362
  }
342
363
  /** A fresh per-submit key. Keep it for retries of the same submit; mint a new one after. */
343
364
  declare const newBookingAttemptKey: typeof uuidV4;
344
365
 
345
- export { type BookingDropOff as B, type ConversionConfig as C, type SalesBookingsClient as S, type TrackingConfigReference as T, type BookingFormSubmit as a, type BookingInput as b, type BookingOutcome as c, type ConversionConfigStore as d, resolveTrackingConfigUrl as e, type BookingDayWindows as f, getTrackingConfigRuntime as g, type ConversionIntegrations as h, TrackingConfigRuntime as i, newBookingAttemptKey as n, resolveConversionConfig as r };
366
+ export { type BookingDropOff as B, type ConversionConfig as C, type SalesBookingsClient as S, type TrackingConfigReference as T, type BookingFormSubmit as a, type BookingInput as b, type BookingMode as c, type BookingOutcome as d, type BookingSimulation as e, type BookingSubmitOptions as f, type ConversionConfigStore as g, getTrackingConfigRuntime as h, resolveTrackingConfigUrl as i, type BookingDayWindows as j, type ConversionIntegrations as k, TrackingConfigRuntime as l, newBookingAttemptKey as n, resolveConversionConfig as r };
package/dist/client.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, CSSProperties, InputHTMLAttributes, ChangeEvent, FocusEvent, AnchorHTMLAttributes } from 'react';
3
3
  import { G as GtagEnvironmentMap, M as MetaPixelEnvironmentMap, h as ConsentState, f as ConsentChoiceState, g as ConsentSource, T as TrackingParams, b as TrackingEnvironment, j as PhoneConfig, P as ParsedPhone, k as PhoneDisplayFormat } from './phone-utils-CM8eUPz8.mjs';
4
- import { T as TrackingConfigReference, C as ConversionConfig, c as BookingOutcome, b as BookingInput, S as SalesBookingsClient, a as BookingFormSubmit } from './bookings-VsQdopTt.mjs';
5
- import { W as TriggerRegistryConfig, Y as TypedTrackingClient, a as BookingHandoffPrefill, b as BookingIntegration, B as BookableDay } from './ingest-typed-BCUv93ax.mjs';
4
+ import { T as TrackingConfigReference, C as ConversionConfig, d as BookingOutcome, b as BookingInput, S as SalesBookingsClient, a as BookingFormSubmit, e as BookingSimulation } from './bookings-CqCuuyP6.mjs';
5
+ import { W as TriggerRegistryConfig, Y as TypedTrackingClient, a as BookingHandoffPrefill, b as BookingIntegration, B as BookableDay } from './ingest-typed-D7UZr0EM.mjs';
6
6
  import { CountryCode } from 'libphonenumber-js';
7
7
  import './form-submit-BLb28aCk.mjs';
8
8
  import 'zod/mini';
@@ -418,6 +418,8 @@ interface BookingSubmitState {
418
418
  */
419
419
  declare function useBookingSubmit(bookings: SalesBookingsClient, options?: {
420
420
  formSubmit?: BookingFormSubmit;
421
+ /** Development only: a simulated create's outcome. The production API ignores it. */
422
+ simulate?: BookingSimulation;
421
423
  }): BookingSubmitState;
422
424
 
423
425
  export { AdPlatformTracking, type AdPlatformTrackingProps, BookingHandoffLink, type BookingHandoffLinkProps, type BookingHandoffOptions, type BookingSubmitState, type BookingSubmitStatus, ConsentBanner, type ConsentBannerProps, type CreateTrackingOptions, type CreateTrackingResult, GoogleAdsTracking, type GoogleAdsTrackingProps, PhoneField, type PhoneFieldApi, type PhoneFieldProps, type PhoneInputProps, type TrackingProviderProps, type UseConsentResult, type UseCookiePreferencesOptions, type UseCookiePreferencesResult, type UsePhoneFieldOptions, createTracking, useBookingHandoffUrl, useBookingIntegration, useBookingSlots, useBookingSubmit, useConsent, useConsentState, useCookiePreferences, useGclid, usePhoneConfig, usePhoneField, useTrackingParams };
package/dist/client.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, CSSProperties, InputHTMLAttributes, ChangeEvent, FocusEvent, AnchorHTMLAttributes } from 'react';
3
3
  import { G as GtagEnvironmentMap, M as MetaPixelEnvironmentMap, h as ConsentState, f as ConsentChoiceState, g as ConsentSource, T as TrackingParams, b as TrackingEnvironment, j as PhoneConfig, P as ParsedPhone, k as PhoneDisplayFormat } from './phone-utils-l8ey3Zdm.js';
4
- import { T as TrackingConfigReference, C as ConversionConfig, c as BookingOutcome, b as BookingInput, S as SalesBookingsClient, a as BookingFormSubmit } from './bookings-CqdjLMkv.js';
5
- import { W as TriggerRegistryConfig, Y as TypedTrackingClient, a as BookingHandoffPrefill, b as BookingIntegration, B as BookableDay } from './ingest-typed-DGqzbteM.js';
4
+ import { T as TrackingConfigReference, C as ConversionConfig, d as BookingOutcome, b as BookingInput, S as SalesBookingsClient, a as BookingFormSubmit, e as BookingSimulation } from './bookings-DIu1ho6v.js';
5
+ import { W as TriggerRegistryConfig, Y as TypedTrackingClient, a as BookingHandoffPrefill, b as BookingIntegration, B as BookableDay } from './ingest-typed-D2S3Kb8m.js';
6
6
  import { CountryCode } from 'libphonenumber-js';
7
7
  import './form-submit-BLb28aCk.js';
8
8
  import 'zod/mini';
@@ -418,6 +418,8 @@ interface BookingSubmitState {
418
418
  */
419
419
  declare function useBookingSubmit(bookings: SalesBookingsClient, options?: {
420
420
  formSubmit?: BookingFormSubmit;
421
+ /** Development only: a simulated create's outcome. The production API ignores it. */
422
+ simulate?: BookingSimulation;
421
423
  }): BookingSubmitState;
422
424
 
423
425
  export { AdPlatformTracking, type AdPlatformTrackingProps, BookingHandoffLink, type BookingHandoffLinkProps, type BookingHandoffOptions, type BookingSubmitState, type BookingSubmitStatus, ConsentBanner, type ConsentBannerProps, type CreateTrackingOptions, type CreateTrackingResult, GoogleAdsTracking, type GoogleAdsTrackingProps, PhoneField, type PhoneFieldApi, type PhoneFieldProps, type PhoneInputProps, type TrackingProviderProps, type UseConsentResult, type UseCookiePreferencesOptions, type UseCookiePreferencesResult, type UsePhoneFieldOptions, createTracking, useBookingHandoffUrl, useBookingIntegration, useBookingSlots, useBookingSubmit, useConsent, useConsentState, useCookiePreferences, useGclid, usePhoneConfig, usePhoneField, useTrackingParams };
package/dist/client.js CHANGED
@@ -979,6 +979,10 @@ function fireConversionWithConsent(input, options) {
979
979
  }
980
980
 
981
981
  // ../tracking-core/src/resources/conversion-config.ts
982
+ var BOOKING_MODES = ["live", "test", "simulated"];
983
+ function isBookingMode(value) {
984
+ return BOOKING_MODES.includes(value);
985
+ }
982
986
  function isStringMap(value) {
983
987
  return typeof value === "object" && value !== null && Object.values(value).every((v) => typeof v === "string");
984
988
  }
@@ -1050,7 +1054,8 @@ function parseIntegrations(value) {
1050
1054
  config_version: typeof b.config_version === "number" ? b.config_version : null,
1051
1055
  accepts_bookings: b.accepts_bookings === true,
1052
1056
  rules: parseBookingRules(b.rules),
1053
- options: Array.isArray(b.options) ? b.options.flatMap(parseBookingOption) : []
1057
+ options: Array.isArray(b.options) ? b.options.flatMap(parseBookingOption) : [],
1058
+ ...isBookingMode(b.mode) ? { mode: b.mode } : {}
1054
1059
  }
1055
1060
  }
1056
1061
  };
@@ -1734,7 +1739,8 @@ function getBookingIntegration(integrations) {
1734
1739
  timezone: booking.timezone,
1735
1740
  configVersion: booking.config_version,
1736
1741
  rules,
1737
- options: booking.options.map((option) => ({ key: option.key, label: option.label }))
1742
+ options: booking.options.map((option) => ({ key: option.key, label: option.label })),
1743
+ mode: booking.mode ?? "live"
1738
1744
  };
1739
1745
  }
1740
1746
  function camelRules(rules) {
@@ -3967,7 +3973,7 @@ function GoogleAdsTracking(props) {
3967
3973
  var import_react6 = require("react");
3968
3974
 
3969
3975
  // package.json
3970
- var version = "1.3.0";
3976
+ var version = "1.4.1";
3971
3977
 
3972
3978
  // ../tracking-core/src/phone-react.tsx
3973
3979
  var import_react5 = require("react");
@@ -4284,13 +4290,13 @@ function useBookingSubmit(bookings, options = {}) {
4284
4290
  const [submitting, setSubmitting] = (0, import_react7.useState)(false);
4285
4291
  const attemptKey = (0, import_react7.useRef)(null);
4286
4292
  const inFlight = (0, import_react7.useRef)(null);
4287
- const { formSubmit } = options;
4293
+ const { formSubmit, simulate } = options;
4288
4294
  const submit = (0, import_react7.useCallback)(
4289
4295
  (input) => {
4290
4296
  if (inFlight.current) return inFlight.current;
4291
4297
  attemptKey.current ?? (attemptKey.current = newBookingAttemptKey());
4292
4298
  setSubmitting(true);
4293
- const run = bookings.submit(input, { attemptKey: attemptKey.current, formSubmit }).then((result) => {
4299
+ const run = bookings.submit(input, { attemptKey: attemptKey.current, formSubmit, simulate }).then((result) => {
4294
4300
  const unrecorded = result.status === "invalid" || result.status === "stale" || result.status === "failed" && result.id === null;
4295
4301
  if (!unrecorded) attemptKey.current = null;
4296
4302
  setOutcome(result);
@@ -4302,7 +4308,7 @@ function useBookingSubmit(bookings, options = {}) {
4302
4308
  inFlight.current = run;
4303
4309
  return run;
4304
4310
  },
4305
- [bookings, formSubmit]
4311
+ [bookings, formSubmit, simulate]
4306
4312
  );
4307
4313
  const reset = (0, import_react7.useCallback)(() => {
4308
4314
  attemptKey.current = null;