@aranova/tracking-react 1.13.0 → 1.14.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/README.md +26 -0
- package/dist/{bookings-C1HMPjQl.d.ts → bookings-Bw2ImtPd.d.ts} +1 -1
- package/dist/{bookings-CHpplXU7.d.mts → bookings-CDGNZUaH.d.mts} +1 -1
- package/dist/client.d.mts +5 -4
- package/dist/client.d.ts +5 -4
- package/dist/client.js +365 -40
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +363 -39
- package/dist/client.mjs.map +1 -1
- package/dist/{form-submit-CX-CxJjQ.d.mts → form-submit-C_vJXCxh.d.mts} +11 -1
- package/dist/{form-submit-CX-CxJjQ.d.ts → form-submit-C_vJXCxh.d.ts} +11 -1
- package/dist/index.d.mts +9 -180
- package/dist/index.d.ts +9 -180
- package/dist/index.js +28 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +25 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{phone-utils-D12FW8tT.d.ts → phone-utils-Cfdx474X.d.mts} +178 -3
- package/dist/{phone-utils-BNiSqeF7.d.mts → phone-utils-D2TGPCQW.d.ts} +178 -3
- package/dist/phone.d.mts +2 -2
- package/dist/phone.d.ts +2 -2
- package/dist/phone.js +2 -2
- package/dist/phone.js.map +1 -1
- package/dist/phone.mjs +2 -2
- package/dist/phone.mjs.map +1 -1
- package/dist/{sales-YUH3bQl9.d.mts → sales-D4aU73mR.d.mts} +1 -1
- package/dist/{sales-CNvzyF5T.d.ts → sales-DkdrGIUS.d.ts} +1 -1
- package/dist/sales.d.mts +3 -3
- package/dist/sales.d.ts +3 -3
- package/dist/sales.js.map +1 -1
- package/dist/sales.mjs.map +1 -1
- package/dist/{ingest-typed-Ckt4R731.d.mts → submit-DuvP5AGN.d.ts} +161 -4
- package/dist/{ingest-typed-sCDoPInT.d.ts → submit-rqgXl-Wg.d.mts} +161 -4
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -36,6 +36,7 @@ __export(client_exports, {
|
|
|
36
36
|
ConsentBanner: () => ConsentBanner,
|
|
37
37
|
GoogleAdsTracking: () => GoogleAdsTracking,
|
|
38
38
|
PhoneField: () => PhoneField,
|
|
39
|
+
createLeadFormSubmit: () => createLeadFormSubmit,
|
|
39
40
|
createTracking: () => createTracking,
|
|
40
41
|
useBookingHandoffUrl: () => useBookingHandoffUrl,
|
|
41
42
|
useBookingIntegration: () => useBookingIntegration,
|
|
@@ -305,7 +306,8 @@ var TRACKING_CAPABILITIES = [
|
|
|
305
306
|
"cta_click_capture",
|
|
306
307
|
"form_capture",
|
|
307
308
|
"phone_click_capture",
|
|
308
|
-
"phone_fields"
|
|
309
|
+
"phone_fields",
|
|
310
|
+
"typed_form_submit"
|
|
309
311
|
];
|
|
310
312
|
var CAPABILITY_DOM_ATTRIBUTE = "data-aranova-capability";
|
|
311
313
|
var registered = /* @__PURE__ */ new Set();
|
|
@@ -2680,7 +2682,7 @@ function createTrackingClient(config) {
|
|
|
2680
2682
|
}
|
|
2681
2683
|
|
|
2682
2684
|
// ../tracking-core/src/ingest-typed.ts
|
|
2683
|
-
var
|
|
2685
|
+
var import_mini3 = require("zod/mini");
|
|
2684
2686
|
|
|
2685
2687
|
// ../tracking-core/src/events/cta-click.ts
|
|
2686
2688
|
var z3 = __toESM(require("zod/mini"));
|
|
@@ -2770,7 +2772,19 @@ var jsonValueSchema = z7.lazy(
|
|
|
2770
2772
|
z7.record(z7.string(), jsonValueSchema)
|
|
2771
2773
|
])
|
|
2772
2774
|
).register(z7.globalRegistry, { id: "JsonSerializableValue" });
|
|
2773
|
-
var
|
|
2775
|
+
var contractNameSchema = z7.string().check(z7.regex(/^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/));
|
|
2776
|
+
var contractName = contractNameSchema;
|
|
2777
|
+
var violationRole = z7.string().check(
|
|
2778
|
+
z7.maxLength(200),
|
|
2779
|
+
z7.regex(
|
|
2780
|
+
/^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?(\|[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?)*$/
|
|
2781
|
+
)
|
|
2782
|
+
);
|
|
2783
|
+
var MAX_CONTRACT_VIOLATIONS = 50;
|
|
2784
|
+
var contractViolationSchema = z7.strictObject({
|
|
2785
|
+
role: z7.nullable(violationRole),
|
|
2786
|
+
rule: z7.string().check(z7.regex(/^[a-z][a-z_]{0,47}$/))
|
|
2787
|
+
});
|
|
2774
2788
|
var formSubmitMetadataSchema = z7.strictObject({
|
|
2775
2789
|
form: z7.strictObject({
|
|
2776
2790
|
id: z7.string(),
|
|
@@ -2799,6 +2813,9 @@ var formSubmitMetadataSchema = z7.strictObject({
|
|
|
2799
2813
|
})
|
|
2800
2814
|
).check(z7.maxLength(20))
|
|
2801
2815
|
)
|
|
2816
|
+
),
|
|
2817
|
+
contract_violations: z7.optional(
|
|
2818
|
+
z7.nullable(z7.array(contractViolationSchema).check(z7.maxLength(MAX_CONTRACT_VIOLATIONS)))
|
|
2802
2819
|
)
|
|
2803
2820
|
}),
|
|
2804
2821
|
page: z7.strictObject({
|
|
@@ -3032,24 +3049,88 @@ function getEventDefinition(name) {
|
|
|
3032
3049
|
return EVENT_REGISTRY[name];
|
|
3033
3050
|
}
|
|
3034
3051
|
|
|
3052
|
+
// ../tracking-core/src/form-submit-salvage.ts
|
|
3053
|
+
var import_mini2 = require("zod/mini");
|
|
3054
|
+
var formShape = formSubmitMetadataSchema.shape.form.shape;
|
|
3055
|
+
var INVALID_SHAPE = { role: null, rule: "invalid_shape" };
|
|
3056
|
+
function salvageFormSubmit(metadata) {
|
|
3057
|
+
if (!isRecord2(metadata) || !isRecord2(metadata.form)) return null;
|
|
3058
|
+
const form = metadata.form;
|
|
3059
|
+
const page = isRecord2(metadata.page) ? metadata.page : {};
|
|
3060
|
+
const reported = Array.isArray(form.contract_violations) ? form.contract_violations.filter((v) => (0, import_mini2.safeParse)(contractViolationSchema, v).success) : [];
|
|
3061
|
+
const salvaged = {
|
|
3062
|
+
form: {
|
|
3063
|
+
id: typeof form.id === "string" ? form.id : String(form.id ?? ""),
|
|
3064
|
+
action: typeof form.action === "string" ? form.action : null,
|
|
3065
|
+
fields: Array.isArray(form.fields) ? form.fields.filter(isRecord2).map(salvageField) : [],
|
|
3066
|
+
...valid("kind", form.kind),
|
|
3067
|
+
...valid("contract_version", form.contract_version),
|
|
3068
|
+
...valid("contract_hash", form.contract_hash),
|
|
3069
|
+
...valid("extra", form.extra),
|
|
3070
|
+
contract_violations: [...reported, INVALID_SHAPE].slice(-MAX_CONTRACT_VIOLATIONS)
|
|
3071
|
+
},
|
|
3072
|
+
page: { path: typeof page.path === "string" ? page.path : currentPath2() }
|
|
3073
|
+
};
|
|
3074
|
+
const parsed = (0, import_mini2.safeParse)(formSubmitMetadataSchema, salvaged);
|
|
3075
|
+
return parsed.success ? parsed.data : null;
|
|
3076
|
+
}
|
|
3077
|
+
function salvageField(field) {
|
|
3078
|
+
return {
|
|
3079
|
+
name: String(field.name ?? ""),
|
|
3080
|
+
type: String(field.type ?? ""),
|
|
3081
|
+
label: typeof field.label === "string" ? field.label : null,
|
|
3082
|
+
value: toJson(field.value),
|
|
3083
|
+
...typeof field.role === "string" && (0, import_mini2.safeParse)(contractNameSchema, field.role).success ? { role: field.role } : {}
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
function valid(key, value) {
|
|
3087
|
+
if (value == null || !(0, import_mini2.safeParse)(formShape[key], value).success) return {};
|
|
3088
|
+
return { [key]: value };
|
|
3089
|
+
}
|
|
3090
|
+
function toJson(value) {
|
|
3091
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
3092
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
3093
|
+
if (typeof value === "bigint") return value.toString();
|
|
3094
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.toISOString();
|
|
3095
|
+
if (Array.isArray(value)) return value.map(toJson);
|
|
3096
|
+
if (isRecord2(value)) {
|
|
3097
|
+
return Object.fromEntries(
|
|
3098
|
+
Object.entries(value).filter(([, v]) => v !== void 0).map(([k, v]) => [k, toJson(v)])
|
|
3099
|
+
);
|
|
3100
|
+
}
|
|
3101
|
+
return null;
|
|
3102
|
+
}
|
|
3103
|
+
function isRecord2(value) {
|
|
3104
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
3105
|
+
}
|
|
3106
|
+
function currentPath2() {
|
|
3107
|
+
return typeof window === "undefined" ? "/" : window.location.pathname;
|
|
3108
|
+
}
|
|
3109
|
+
|
|
3035
3110
|
// ../tracking-core/src/ingest-typed.ts
|
|
3111
|
+
var clientSettings = /* @__PURE__ */ new WeakMap();
|
|
3112
|
+
function typedClientSettings(client) {
|
|
3113
|
+
return clientSettings.get(client) ?? {};
|
|
3114
|
+
}
|
|
3036
3115
|
function createTypedClient(raw, registry, options = {}) {
|
|
3037
3116
|
const debug = options.debug ?? false;
|
|
3038
3117
|
if (registry.manual?.form_submit) registerCapability("form_capture");
|
|
3039
|
-
|
|
3118
|
+
const client = {
|
|
3040
3119
|
trackEvent(eventType, metadata, opts) {
|
|
3041
3120
|
const def = getEventDefinition(eventType);
|
|
3121
|
+
let sent = metadata;
|
|
3042
3122
|
if (debug) {
|
|
3043
|
-
(0,
|
|
3044
|
-
} else if (!(0,
|
|
3123
|
+
(0, import_mini3.parse)(def.metadataSchema, metadata);
|
|
3124
|
+
} else if (!(0, import_mini3.safeParse)(def.metadataSchema, metadata).success) {
|
|
3125
|
+
sent = eventType === "form_submit" ? salvageFormSubmit(metadata) : null;
|
|
3045
3126
|
console.warn(
|
|
3046
|
-
`[AranovaTracking] trackEvent: metadata for "${eventType}" failed schema validation; event dropped`
|
|
3127
|
+
sent ? `[AranovaTracking] trackEvent: metadata for "form_submit" failed schema validation; sent salvaged` : `[AranovaTracking] trackEvent: metadata for "${eventType}" failed schema validation; event dropped`
|
|
3047
3128
|
);
|
|
3048
|
-
return;
|
|
3129
|
+
if (!sent) return;
|
|
3049
3130
|
}
|
|
3050
3131
|
raw.trackEvent({
|
|
3051
3132
|
eventType,
|
|
3052
|
-
metadata,
|
|
3133
|
+
metadata: sent,
|
|
3053
3134
|
pageUrl: opts?.pageUrl ?? null,
|
|
3054
3135
|
occurredAt: opts?.occurredAt ?? null
|
|
3055
3136
|
});
|
|
@@ -3058,6 +3139,8 @@ function createTypedClient(raw, registry, options = {}) {
|
|
|
3058
3139
|
getSessionId: raw.getSessionId.bind(raw),
|
|
3059
3140
|
getVisitorId: raw.getVisitorId.bind(raw)
|
|
3060
3141
|
};
|
|
3142
|
+
clientSettings.set(client, options);
|
|
3143
|
+
return client;
|
|
3061
3144
|
}
|
|
3062
3145
|
|
|
3063
3146
|
// ../tracking-core/src/triggers/time-on-site.ts
|
|
@@ -3128,7 +3211,7 @@ function attachSpecificPageVisit(client, config) {
|
|
|
3128
3211
|
}
|
|
3129
3212
|
const { pages } = config;
|
|
3130
3213
|
const firedSet = /* @__PURE__ */ new Set();
|
|
3131
|
-
function
|
|
3214
|
+
function check2() {
|
|
3132
3215
|
const path = window.location.pathname;
|
|
3133
3216
|
for (const { name, pathPattern } of pages) {
|
|
3134
3217
|
pathPattern.lastIndex = 0;
|
|
@@ -3148,20 +3231,20 @@ function attachSpecificPageVisit(client, config) {
|
|
|
3148
3231
|
const originalReplaceState = history.replaceState.bind(history);
|
|
3149
3232
|
function patchedPushState(...args) {
|
|
3150
3233
|
originalPushState(...args);
|
|
3151
|
-
setTimeout(
|
|
3234
|
+
setTimeout(check2, 0);
|
|
3152
3235
|
}
|
|
3153
3236
|
function patchedReplaceState(...args) {
|
|
3154
3237
|
originalReplaceState(...args);
|
|
3155
|
-
setTimeout(
|
|
3238
|
+
setTimeout(check2, 0);
|
|
3156
3239
|
}
|
|
3157
3240
|
history.pushState = patchedPushState;
|
|
3158
3241
|
history.replaceState = patchedReplaceState;
|
|
3159
|
-
window.addEventListener("popstate",
|
|
3160
|
-
|
|
3242
|
+
window.addEventListener("popstate", check2);
|
|
3243
|
+
check2();
|
|
3161
3244
|
return () => {
|
|
3162
3245
|
history.pushState = originalPushState;
|
|
3163
3246
|
history.replaceState = originalReplaceState;
|
|
3164
|
-
window.removeEventListener("popstate",
|
|
3247
|
+
window.removeEventListener("popstate", check2);
|
|
3165
3248
|
};
|
|
3166
3249
|
}
|
|
3167
3250
|
|
|
@@ -3264,7 +3347,7 @@ function attachScrollDepth(client, config) {
|
|
|
3264
3347
|
}
|
|
3265
3348
|
const thresholds = new Set(config.thresholds);
|
|
3266
3349
|
let firedForPath = /* @__PURE__ */ new Set();
|
|
3267
|
-
let
|
|
3350
|
+
let currentPath3 = window.location.pathname;
|
|
3268
3351
|
let rafId = null;
|
|
3269
3352
|
const gate = createBaselineGate();
|
|
3270
3353
|
function checkThresholds() {
|
|
@@ -3280,7 +3363,7 @@ function attachScrollDepth(client, config) {
|
|
|
3280
3363
|
eventType: "scroll_depth",
|
|
3281
3364
|
metadata: {
|
|
3282
3365
|
depth_percent: threshold,
|
|
3283
|
-
page: { path:
|
|
3366
|
+
page: { path: currentPath3 }
|
|
3284
3367
|
},
|
|
3285
3368
|
pageUrl: window.location.href,
|
|
3286
3369
|
occurredAt: null
|
|
@@ -3297,8 +3380,8 @@ function attachScrollDepth(client, config) {
|
|
|
3297
3380
|
}
|
|
3298
3381
|
function resetIfPathChanged() {
|
|
3299
3382
|
const newPath = window.location.pathname;
|
|
3300
|
-
if (newPath ===
|
|
3301
|
-
|
|
3383
|
+
if (newPath === currentPath3) return;
|
|
3384
|
+
currentPath3 = newPath;
|
|
3302
3385
|
firedForPath = /* @__PURE__ */ new Set();
|
|
3303
3386
|
gate.rebaseline();
|
|
3304
3387
|
}
|
|
@@ -3360,14 +3443,14 @@ function attachMultiPageSession(client, config) {
|
|
|
3360
3443
|
function hasFired() {
|
|
3361
3444
|
return storage.getItem(FIRED_KEY) === "1";
|
|
3362
3445
|
}
|
|
3363
|
-
function
|
|
3364
|
-
const
|
|
3365
|
-
if (
|
|
3366
|
-
lastCheckedPath =
|
|
3446
|
+
function check2() {
|
|
3447
|
+
const currentPath3 = window.location.pathname;
|
|
3448
|
+
if (currentPath3 === lastCheckedPath) return;
|
|
3449
|
+
lastCheckedPath = currentPath3;
|
|
3367
3450
|
resetIfSessionChanged();
|
|
3368
3451
|
if (hasFired()) return;
|
|
3369
3452
|
const paths = getDistinctPaths();
|
|
3370
|
-
paths.add(
|
|
3453
|
+
paths.add(currentPath3);
|
|
3371
3454
|
saveDistinctPaths(paths);
|
|
3372
3455
|
if (paths.size >= pageThreshold) {
|
|
3373
3456
|
storage.setItem(FIRED_KEY, "1");
|
|
@@ -3386,20 +3469,20 @@ function attachMultiPageSession(client, config) {
|
|
|
3386
3469
|
const originalReplaceState = history.replaceState.bind(history);
|
|
3387
3470
|
function patchedPushState(...args) {
|
|
3388
3471
|
originalPushState(...args);
|
|
3389
|
-
setTimeout(
|
|
3472
|
+
setTimeout(check2, 0);
|
|
3390
3473
|
}
|
|
3391
3474
|
function patchedReplaceState(...args) {
|
|
3392
3475
|
originalReplaceState(...args);
|
|
3393
|
-
setTimeout(
|
|
3476
|
+
setTimeout(check2, 0);
|
|
3394
3477
|
}
|
|
3395
3478
|
history.pushState = patchedPushState;
|
|
3396
3479
|
history.replaceState = patchedReplaceState;
|
|
3397
|
-
window.addEventListener("popstate",
|
|
3398
|
-
|
|
3480
|
+
window.addEventListener("popstate", check2);
|
|
3481
|
+
check2();
|
|
3399
3482
|
return () => {
|
|
3400
3483
|
history.pushState = originalPushState;
|
|
3401
3484
|
history.replaceState = originalReplaceState;
|
|
3402
|
-
window.removeEventListener("popstate",
|
|
3485
|
+
window.removeEventListener("popstate", check2);
|
|
3403
3486
|
};
|
|
3404
3487
|
}
|
|
3405
3488
|
|
|
@@ -3412,7 +3495,7 @@ function attachFormStart(client, config) {
|
|
|
3412
3495
|
}
|
|
3413
3496
|
const selector = config.selector ?? "form";
|
|
3414
3497
|
let firedForms = /* @__PURE__ */ new Set();
|
|
3415
|
-
let
|
|
3498
|
+
let currentPath3 = window.location.pathname;
|
|
3416
3499
|
let pendingTap = null;
|
|
3417
3500
|
function getFormKey(form) {
|
|
3418
3501
|
if (form.id) return `id:${form.id}`;
|
|
@@ -3463,8 +3546,8 @@ function attachFormStart(client, config) {
|
|
|
3463
3546
|
}
|
|
3464
3547
|
function resetIfPathChanged() {
|
|
3465
3548
|
const newPath = window.location.pathname;
|
|
3466
|
-
if (newPath ===
|
|
3467
|
-
|
|
3549
|
+
if (newPath === currentPath3) return;
|
|
3550
|
+
currentPath3 = newPath;
|
|
3468
3551
|
firedForms = /* @__PURE__ */ new Set();
|
|
3469
3552
|
}
|
|
3470
3553
|
const originalPushState = history.pushState.bind(history);
|
|
@@ -3505,7 +3588,7 @@ function attachPageExit(client) {
|
|
|
3505
3588
|
return () => {
|
|
3506
3589
|
};
|
|
3507
3590
|
}
|
|
3508
|
-
let
|
|
3591
|
+
let currentPath3 = window.location.pathname;
|
|
3509
3592
|
let activeSince = document.visibilityState === "visible" ? Date.now() : null;
|
|
3510
3593
|
let accumulatedMs = 0;
|
|
3511
3594
|
let maxScrollPercent = null;
|
|
@@ -3555,9 +3638,9 @@ function attachPageExit(client) {
|
|
|
3555
3638
|
}
|
|
3556
3639
|
function onNavigate() {
|
|
3557
3640
|
const newPath = window.location.pathname;
|
|
3558
|
-
if (newPath ===
|
|
3559
|
-
emitSegment(
|
|
3560
|
-
|
|
3641
|
+
if (newPath === currentPath3) return;
|
|
3642
|
+
emitSegment(currentPath3, false);
|
|
3643
|
+
currentPath3 = newPath;
|
|
3561
3644
|
maxScrollPercent = null;
|
|
3562
3645
|
gate.rebaseline();
|
|
3563
3646
|
accumulatedMs = 0;
|
|
@@ -3565,13 +3648,13 @@ function attachPageExit(client) {
|
|
|
3565
3648
|
}
|
|
3566
3649
|
function onVisibilityChange() {
|
|
3567
3650
|
if (document.visibilityState === "hidden") {
|
|
3568
|
-
emitSegment(
|
|
3651
|
+
emitSegment(currentPath3, true);
|
|
3569
3652
|
} else {
|
|
3570
3653
|
activeSince = Date.now();
|
|
3571
3654
|
}
|
|
3572
3655
|
}
|
|
3573
3656
|
function onPageHide() {
|
|
3574
|
-
emitSegment(
|
|
3657
|
+
emitSegment(currentPath3, true);
|
|
3575
3658
|
}
|
|
3576
3659
|
const unsubscribeNav = onHistoryChange(onNavigate);
|
|
3577
3660
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
@@ -3750,6 +3833,104 @@ function attachPhoneClickCapture(client, config) {
|
|
|
3750
3833
|
};
|
|
3751
3834
|
}
|
|
3752
3835
|
|
|
3836
|
+
// ../tracking-core/src/lead-forms/lead-forms.gen.ts
|
|
3837
|
+
var z15 = __toESM(require("zod/mini"));
|
|
3838
|
+
var LEAD_FORM_REGISTRY_VERSION = 2;
|
|
3839
|
+
var LEAD_FORM_ROLES = [
|
|
3840
|
+
"given_name",
|
|
3841
|
+
"family_name",
|
|
3842
|
+
"phone",
|
|
3843
|
+
"email",
|
|
3844
|
+
"service",
|
|
3845
|
+
"preferred_date",
|
|
3846
|
+
"preferred_time",
|
|
3847
|
+
"note",
|
|
3848
|
+
"visit",
|
|
3849
|
+
"intent",
|
|
3850
|
+
"vehicle",
|
|
3851
|
+
"drop_off",
|
|
3852
|
+
"address",
|
|
3853
|
+
"patient_type",
|
|
3854
|
+
"matter",
|
|
3855
|
+
"language",
|
|
3856
|
+
"products",
|
|
3857
|
+
"goal",
|
|
3858
|
+
"business_type",
|
|
3859
|
+
"lead_volume",
|
|
3860
|
+
"attribution"
|
|
3861
|
+
];
|
|
3862
|
+
var leadFormPrimitiveSchemas = /* @__PURE__ */ (() => ({
|
|
3863
|
+
string: z15.string().check(z15.maxLength(120)),
|
|
3864
|
+
text: z15.string().check(z15.maxLength(2e3)),
|
|
3865
|
+
enum: z15.string().check(z15.regex(/^[a-z0-9-]{1,48}$/)),
|
|
3866
|
+
multi_enum: z15.array(z15.string().check(z15.regex(/^[a-z0-9-]{1,48}$/))).check(z15.maxLength(10)),
|
|
3867
|
+
bool: z15.boolean(),
|
|
3868
|
+
date: z15.string().check(z15.regex(/^\d{4}-\d{2}-\d{2}$/)),
|
|
3869
|
+
time_window: z15.strictObject({
|
|
3870
|
+
start: z15.string().check(z15.regex(/^([01]\d|2[0-3]):[0-5]\d$/)),
|
|
3871
|
+
end: z15.nullable(z15.string().check(z15.regex(/^([01]\d|2[0-3]):[0-5]\d$/))),
|
|
3872
|
+
label: z15.optional(z15.string().check(z15.maxLength(60)))
|
|
3873
|
+
}),
|
|
3874
|
+
phone: z15.string().check(z15.regex(/^\+[1-9]\d{6,14}$/)),
|
|
3875
|
+
email: z15.string().check(z15.maxLength(320), z15.regex(/^[^\s@]+@[^\s@]+$/)),
|
|
3876
|
+
address: z15.strictObject({
|
|
3877
|
+
line1: z15.string().check(z15.maxLength(120)),
|
|
3878
|
+
line2: z15.optional(z15.string().check(z15.maxLength(120))),
|
|
3879
|
+
city: z15.optional(z15.string().check(z15.maxLength(120))),
|
|
3880
|
+
region: z15.optional(z15.string().check(z15.maxLength(120))),
|
|
3881
|
+
postal_code: z15.optional(z15.string().check(z15.maxLength(120))),
|
|
3882
|
+
country: z15.optional(z15.string().check(z15.maxLength(120)))
|
|
3883
|
+
}),
|
|
3884
|
+
number: z15.number()
|
|
3885
|
+
}))();
|
|
3886
|
+
var LEAD_FORM_ROLE_SPECS = {
|
|
3887
|
+
given_name: { primitive: "string", smsLabel: "", contactIdentity: true },
|
|
3888
|
+
family_name: { primitive: "string", smsLabel: "", contactIdentity: true },
|
|
3889
|
+
phone: { primitive: "phone", smsLabel: "", contactIdentity: true },
|
|
3890
|
+
email: { primitive: "email", smsLabel: "", contactIdentity: true },
|
|
3891
|
+
service: { primitive: "multi_enum", smsLabel: "Service" },
|
|
3892
|
+
preferred_date: { primitive: "date", smsLabel: "Date" },
|
|
3893
|
+
preferred_time: { primitive: "time_window", smsLabel: "Time" },
|
|
3894
|
+
note: { primitive: "text", smsLabel: "Note" },
|
|
3895
|
+
visit: {
|
|
3896
|
+
primitive: "enum",
|
|
3897
|
+
smsLabel: "Visit",
|
|
3898
|
+
options: [
|
|
3899
|
+
{ key: "in-shop", label: "In shop" },
|
|
3900
|
+
{ key: "in-home", label: "In home" },
|
|
3901
|
+
{ key: "remote", label: "Remote" },
|
|
3902
|
+
{ key: "on-site", label: "On site" }
|
|
3903
|
+
]
|
|
3904
|
+
},
|
|
3905
|
+
intent: {
|
|
3906
|
+
primitive: "enum",
|
|
3907
|
+
smsLabel: "Wants",
|
|
3908
|
+
options: [
|
|
3909
|
+
{ key: "book", label: "Book" },
|
|
3910
|
+
{ key: "quote", label: "Quote" }
|
|
3911
|
+
]
|
|
3912
|
+
},
|
|
3913
|
+
vehicle: { primitive: "string", smsLabel: "Vehicle" },
|
|
3914
|
+
drop_off: {
|
|
3915
|
+
primitive: "enum",
|
|
3916
|
+
smsLabel: "Drop-off",
|
|
3917
|
+
options: [
|
|
3918
|
+
{ key: "wait-for-it", label: "Waiting" },
|
|
3919
|
+
{ key: "drop-off", label: "Dropping off" }
|
|
3920
|
+
]
|
|
3921
|
+
},
|
|
3922
|
+
address: { primitive: "address", smsLabel: "Address" },
|
|
3923
|
+
patient_type: { primitive: "enum", smsLabel: "Patient" },
|
|
3924
|
+
matter: { primitive: "enum", smsLabel: "Matter" },
|
|
3925
|
+
language: { primitive: "enum", smsLabel: "Language" },
|
|
3926
|
+
products: { primitive: "multi_enum", smsLabel: "Products" },
|
|
3927
|
+
goal: { primitive: "enum", smsLabel: "Goal" },
|
|
3928
|
+
business_type: { primitive: "enum", smsLabel: "Business" },
|
|
3929
|
+
lead_volume: { primitive: "enum", smsLabel: "Lead volume" },
|
|
3930
|
+
add_on: { primitive: "bool", smsLabel: "" },
|
|
3931
|
+
attribution: { primitive: "enum", smsLabel: "", uninteresting: true }
|
|
3932
|
+
};
|
|
3933
|
+
|
|
3753
3934
|
// ../tracking-core/src/resources/sales/bookings.ts
|
|
3754
3935
|
var newBookingAttemptKey = uuidV4;
|
|
3755
3936
|
|
|
@@ -4084,7 +4265,7 @@ function GoogleAdsTracking(props) {
|
|
|
4084
4265
|
var import_react6 = require("react");
|
|
4085
4266
|
|
|
4086
4267
|
// package.json
|
|
4087
|
-
var version = "1.
|
|
4268
|
+
var version = "1.14.0";
|
|
4088
4269
|
|
|
4089
4270
|
// ../tracking-core/src/phone-react.tsx
|
|
4090
4271
|
var import_react5 = require("react");
|
|
@@ -4255,7 +4436,11 @@ function createTracking(options) {
|
|
|
4255
4436
|
[rawClient, conversionStore]
|
|
4256
4437
|
);
|
|
4257
4438
|
const client = (0, import_react6.useMemo)(
|
|
4258
|
-
() => createTypedClient(conversionClient, triggers, {
|
|
4439
|
+
() => createTypedClient(conversionClient, triggers, {
|
|
4440
|
+
debug,
|
|
4441
|
+
environment,
|
|
4442
|
+
trackingConfig
|
|
4443
|
+
}),
|
|
4259
4444
|
[conversionClient]
|
|
4260
4445
|
);
|
|
4261
4446
|
(0, import_react6.useEffect)(() => {
|
|
@@ -4454,6 +4639,145 @@ function useBookingSubmit(bookings, options = {}) {
|
|
|
4454
4639
|
const status = submitting ? "submitting" : outcome?.status ?? "idle";
|
|
4455
4640
|
return { status, outcome, booked, submit, updateBooking, reset };
|
|
4456
4641
|
}
|
|
4642
|
+
|
|
4643
|
+
// ../tracking-core/src/lead-forms/submit.ts
|
|
4644
|
+
var import_mini4 = require("zod/mini");
|
|
4645
|
+
var KNOWN_ROLES = new Set(LEAD_FORM_ROLES);
|
|
4646
|
+
var ROLE_ORDER = new Map(LEAD_FORM_ROLES.map((role, index) => [role, index]));
|
|
4647
|
+
var ADD_ON = /^add_on:[a-z0-9_-]{1,48}$/;
|
|
4648
|
+
var CONTRACT_NAME = /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/;
|
|
4649
|
+
var INPUT_TYPES = {
|
|
4650
|
+
string: "text",
|
|
4651
|
+
text: "textarea",
|
|
4652
|
+
enum: "select",
|
|
4653
|
+
multi_enum: "select",
|
|
4654
|
+
bool: "checkbox",
|
|
4655
|
+
date: "date",
|
|
4656
|
+
time_window: "time",
|
|
4657
|
+
phone: "tel",
|
|
4658
|
+
email: "email",
|
|
4659
|
+
address: "address",
|
|
4660
|
+
number: "number"
|
|
4661
|
+
};
|
|
4662
|
+
function createLeadFormSubmit(tracking, contracts, options = {}) {
|
|
4663
|
+
registerCapability("typed_form_submit");
|
|
4664
|
+
const settings = { ...typedClientSettings(tracking), ...options };
|
|
4665
|
+
return {
|
|
4666
|
+
submit(input) {
|
|
4667
|
+
const generated = contracts.contracts[input.kind];
|
|
4668
|
+
const live = liveContract(settings.trackingConfig, input.kind);
|
|
4669
|
+
const fields = presentFields(input.fields);
|
|
4670
|
+
const violations = check(live === void 0 ? generated : fromLive(live), fields);
|
|
4671
|
+
const liveHash = live === void 0 ? void 0 : live?.contract_hash ?? null;
|
|
4672
|
+
if (liveHash !== void 0 && (generated?.hash ?? null) !== liveHash || contracts.registryVersion !== LEAD_FORM_REGISTRY_VERSION) {
|
|
4673
|
+
violations.push({ role: null, rule: "contract_behind" });
|
|
4674
|
+
}
|
|
4675
|
+
const hash = liveHash === void 0 ? generated?.hash ?? null : liveHash;
|
|
4676
|
+
const metadata = {
|
|
4677
|
+
form: {
|
|
4678
|
+
id: input.formId,
|
|
4679
|
+
action: input.action ?? null,
|
|
4680
|
+
fields: fields.map(([role, value]) => ({
|
|
4681
|
+
name: role,
|
|
4682
|
+
type: INPUT_TYPES[primitiveOf(role) ?? "string"],
|
|
4683
|
+
label: labelOf(role),
|
|
4684
|
+
value,
|
|
4685
|
+
role
|
|
4686
|
+
})),
|
|
4687
|
+
kind: input.kind,
|
|
4688
|
+
contract_version: LEAD_FORM_REGISTRY_VERSION,
|
|
4689
|
+
...hash ? { contract_hash: hash } : {},
|
|
4690
|
+
...input.extra?.length ? { extra: input.extra } : {},
|
|
4691
|
+
...violations.length > 0 ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) } : {}
|
|
4692
|
+
},
|
|
4693
|
+
page: { path: typeof window === "undefined" ? "/" : window.location.pathname }
|
|
4694
|
+
};
|
|
4695
|
+
if (violations.length > 0 && (settings.debug || settings.environment === "development")) {
|
|
4696
|
+
const message = `[AranovaTracking] lead form "${input.formId}" (${input.kind}) breaks its contract: ${violations.map((v) => `${v.rule}${v.role ? ` (${v.role})` : ""}`).join(", ")}`;
|
|
4697
|
+
console.error(message);
|
|
4698
|
+
if (settings.debug) throw new Error(message);
|
|
4699
|
+
}
|
|
4700
|
+
tracking.trackEvent("form_submit", metadata);
|
|
4701
|
+
return metadata;
|
|
4702
|
+
}
|
|
4703
|
+
};
|
|
4704
|
+
}
|
|
4705
|
+
function liveContract(ref, kind) {
|
|
4706
|
+
const leadForms = ref ? getTrackingConfigRuntime(ref).leadForms() : null;
|
|
4707
|
+
if (!leadForms) return void 0;
|
|
4708
|
+
return leadForms.contracts.find((c) => c.form_kind === kind) ?? null;
|
|
4709
|
+
}
|
|
4710
|
+
function fromLive(live) {
|
|
4711
|
+
if (!live) return void 0;
|
|
4712
|
+
return {
|
|
4713
|
+
requires: live.requires.map((r) => ({
|
|
4714
|
+
role: r.role,
|
|
4715
|
+
requiredWhen: r.required_when ? { role: r.required_when.role, anyOf: r.required_when.any_of } : null
|
|
4716
|
+
})),
|
|
4717
|
+
requiresAny: live.requires_any.map((g) => g.roles),
|
|
4718
|
+
// Deprecated keys still resolve: a cached form may send them (ADR-0024 §7).
|
|
4719
|
+
options: Object.fromEntries(live.options.map((o) => [o.role, o.options.map((x) => x.key)]))
|
|
4720
|
+
};
|
|
4721
|
+
}
|
|
4722
|
+
function presentFields(fields) {
|
|
4723
|
+
return Object.entries(fields).map(([role, value]) => [role, normalize(role, value)]).filter((entry) => !isBlank(entry[1])).sort(([a], [b]) => (ROLE_ORDER.get(a) ?? 999) - (ROLE_ORDER.get(b) ?? 999));
|
|
4724
|
+
}
|
|
4725
|
+
function normalize(role, value) {
|
|
4726
|
+
if (typeof value !== "string") return value;
|
|
4727
|
+
const trimmed = value.trim();
|
|
4728
|
+
const primitive = primitiveOf(role);
|
|
4729
|
+
if (primitive === "phone") return toE164(trimmed) ?? trimmed;
|
|
4730
|
+
if (primitive === "email") return trimmed.toLowerCase();
|
|
4731
|
+
return primitive === "text" ? value : trimmed;
|
|
4732
|
+
}
|
|
4733
|
+
function isBlank(value) {
|
|
4734
|
+
return value === void 0 || value === null || typeof value === "string" && value.trim() === "" || Array.isArray(value) && value.length === 0;
|
|
4735
|
+
}
|
|
4736
|
+
function check(contract, fields) {
|
|
4737
|
+
const answered = new Map(fields);
|
|
4738
|
+
const violations = [];
|
|
4739
|
+
for (const [role, value] of fields) {
|
|
4740
|
+
const primitive = primitiveOf(role);
|
|
4741
|
+
if (!primitive) {
|
|
4742
|
+
violations.push({ role: CONTRACT_NAME.test(role) ? role : null, rule: "unknown_role" });
|
|
4743
|
+
continue;
|
|
4744
|
+
}
|
|
4745
|
+
if (!(0, import_mini4.safeParse)(leadFormPrimitiveSchemas[primitive], value).success) {
|
|
4746
|
+
violations.push({ role, rule: "invalid_value" });
|
|
4747
|
+
continue;
|
|
4748
|
+
}
|
|
4749
|
+
const allowed = contract?.options[role] ?? registryOptions(role);
|
|
4750
|
+
if (allowed?.length && optionKeys(value).some((key) => !allowed.includes(key))) {
|
|
4751
|
+
violations.push({ role, rule: "unknown_option" });
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
for (const { role, requiredWhen } of contract?.requires ?? []) {
|
|
4755
|
+
const owing = requiredWhen === null || optionKeys(answered.get(requiredWhen.role)).some((key) => requiredWhen.anyOf.includes(key));
|
|
4756
|
+
if (owing && !answered.has(role)) violations.push({ role, rule: "missing_required" });
|
|
4757
|
+
}
|
|
4758
|
+
for (const group of contract?.requiresAny ?? []) {
|
|
4759
|
+
if (!group.some((role) => answered.has(role))) {
|
|
4760
|
+
violations.push({ role: group.join("|"), rule: "missing_any_of" });
|
|
4761
|
+
}
|
|
4762
|
+
}
|
|
4763
|
+
return violations;
|
|
4764
|
+
}
|
|
4765
|
+
function primitiveOf(role) {
|
|
4766
|
+
if (ADD_ON.test(role)) return "bool";
|
|
4767
|
+
return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role].primitive : null;
|
|
4768
|
+
}
|
|
4769
|
+
function labelOf(role) {
|
|
4770
|
+
if (ADD_ON.test(role)) return role.slice("add_on:".length).replaceAll(/[_-]+/g, " ");
|
|
4771
|
+
return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role].smsLabel || null : null;
|
|
4772
|
+
}
|
|
4773
|
+
function registryOptions(role) {
|
|
4774
|
+
const spec = KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role] : void 0;
|
|
4775
|
+
return spec && "options" in spec ? spec.options.map((o) => o.key) : void 0;
|
|
4776
|
+
}
|
|
4777
|
+
function optionKeys(value) {
|
|
4778
|
+
if (typeof value === "string") return [value];
|
|
4779
|
+
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
4780
|
+
}
|
|
4457
4781
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4458
4782
|
0 && (module.exports = {
|
|
4459
4783
|
AdPlatformTracking,
|
|
@@ -4461,6 +4785,7 @@ function useBookingSubmit(bookings, options = {}) {
|
|
|
4461
4785
|
ConsentBanner,
|
|
4462
4786
|
GoogleAdsTracking,
|
|
4463
4787
|
PhoneField,
|
|
4788
|
+
createLeadFormSubmit,
|
|
4464
4789
|
createTracking,
|
|
4465
4790
|
useBookingHandoffUrl,
|
|
4466
4791
|
useBookingIntegration,
|