@aranova/tracking-react 1.12.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 +31 -3
- package/dist/{bookings-v_ucn93l.d.ts → bookings-Bw2ImtPd.d.ts} +3 -1
- package/dist/{bookings-Lmi0o_PA.d.mts → bookings-CDGNZUaH.d.mts} +3 -1
- package/dist/client.d.mts +5 -4
- package/dist/client.d.ts +5 -4
- package/dist/client.js +369 -40
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +367 -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 +332 -285
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +329 -285
- 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-6vMEcopj.d.mts → sales-D4aU73mR.d.mts} +1 -1
- package/dist/{sales-DXLGjJQ6.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 +193 -171
- package/dist/sales.js.map +1 -1
- package/dist/sales.mjs +193 -171
- package/dist/sales.mjs.map +1 -1
- package/dist/{ingest-typed-B9I1c7jr.d.ts → submit-DuvP5AGN.d.ts} +161 -4
- package/dist/{ingest-typed-PKFEuJ8E.d.mts → 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();
|
|
@@ -1614,6 +1616,10 @@ var TrackingConfigRuntime = class {
|
|
|
1614
1616
|
integrations() {
|
|
1615
1617
|
return this.current?.integrations ?? null;
|
|
1616
1618
|
}
|
|
1619
|
+
/** The lead-form contracts, from the cached object like `integrations()`. */
|
|
1620
|
+
leadForms() {
|
|
1621
|
+
return this.current?.lead_forms ?? null;
|
|
1622
|
+
}
|
|
1617
1623
|
async revalidateNow() {
|
|
1618
1624
|
try {
|
|
1619
1625
|
const headers = {};
|
|
@@ -2676,7 +2682,7 @@ function createTrackingClient(config) {
|
|
|
2676
2682
|
}
|
|
2677
2683
|
|
|
2678
2684
|
// ../tracking-core/src/ingest-typed.ts
|
|
2679
|
-
var
|
|
2685
|
+
var import_mini3 = require("zod/mini");
|
|
2680
2686
|
|
|
2681
2687
|
// ../tracking-core/src/events/cta-click.ts
|
|
2682
2688
|
var z3 = __toESM(require("zod/mini"));
|
|
@@ -2766,7 +2772,19 @@ var jsonValueSchema = z7.lazy(
|
|
|
2766
2772
|
z7.record(z7.string(), jsonValueSchema)
|
|
2767
2773
|
])
|
|
2768
2774
|
).register(z7.globalRegistry, { id: "JsonSerializableValue" });
|
|
2769
|
-
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
|
+
});
|
|
2770
2788
|
var formSubmitMetadataSchema = z7.strictObject({
|
|
2771
2789
|
form: z7.strictObject({
|
|
2772
2790
|
id: z7.string(),
|
|
@@ -2795,6 +2813,9 @@ var formSubmitMetadataSchema = z7.strictObject({
|
|
|
2795
2813
|
})
|
|
2796
2814
|
).check(z7.maxLength(20))
|
|
2797
2815
|
)
|
|
2816
|
+
),
|
|
2817
|
+
contract_violations: z7.optional(
|
|
2818
|
+
z7.nullable(z7.array(contractViolationSchema).check(z7.maxLength(MAX_CONTRACT_VIOLATIONS)))
|
|
2798
2819
|
)
|
|
2799
2820
|
}),
|
|
2800
2821
|
page: z7.strictObject({
|
|
@@ -3028,24 +3049,88 @@ function getEventDefinition(name) {
|
|
|
3028
3049
|
return EVENT_REGISTRY[name];
|
|
3029
3050
|
}
|
|
3030
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
|
+
|
|
3031
3110
|
// ../tracking-core/src/ingest-typed.ts
|
|
3111
|
+
var clientSettings = /* @__PURE__ */ new WeakMap();
|
|
3112
|
+
function typedClientSettings(client) {
|
|
3113
|
+
return clientSettings.get(client) ?? {};
|
|
3114
|
+
}
|
|
3032
3115
|
function createTypedClient(raw, registry, options = {}) {
|
|
3033
3116
|
const debug = options.debug ?? false;
|
|
3034
3117
|
if (registry.manual?.form_submit) registerCapability("form_capture");
|
|
3035
|
-
|
|
3118
|
+
const client = {
|
|
3036
3119
|
trackEvent(eventType, metadata, opts) {
|
|
3037
3120
|
const def = getEventDefinition(eventType);
|
|
3121
|
+
let sent = metadata;
|
|
3038
3122
|
if (debug) {
|
|
3039
|
-
(0,
|
|
3040
|
-
} 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;
|
|
3041
3126
|
console.warn(
|
|
3042
|
-
`[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`
|
|
3043
3128
|
);
|
|
3044
|
-
return;
|
|
3129
|
+
if (!sent) return;
|
|
3045
3130
|
}
|
|
3046
3131
|
raw.trackEvent({
|
|
3047
3132
|
eventType,
|
|
3048
|
-
metadata,
|
|
3133
|
+
metadata: sent,
|
|
3049
3134
|
pageUrl: opts?.pageUrl ?? null,
|
|
3050
3135
|
occurredAt: opts?.occurredAt ?? null
|
|
3051
3136
|
});
|
|
@@ -3054,6 +3139,8 @@ function createTypedClient(raw, registry, options = {}) {
|
|
|
3054
3139
|
getSessionId: raw.getSessionId.bind(raw),
|
|
3055
3140
|
getVisitorId: raw.getVisitorId.bind(raw)
|
|
3056
3141
|
};
|
|
3142
|
+
clientSettings.set(client, options);
|
|
3143
|
+
return client;
|
|
3057
3144
|
}
|
|
3058
3145
|
|
|
3059
3146
|
// ../tracking-core/src/triggers/time-on-site.ts
|
|
@@ -3124,7 +3211,7 @@ function attachSpecificPageVisit(client, config) {
|
|
|
3124
3211
|
}
|
|
3125
3212
|
const { pages } = config;
|
|
3126
3213
|
const firedSet = /* @__PURE__ */ new Set();
|
|
3127
|
-
function
|
|
3214
|
+
function check2() {
|
|
3128
3215
|
const path = window.location.pathname;
|
|
3129
3216
|
for (const { name, pathPattern } of pages) {
|
|
3130
3217
|
pathPattern.lastIndex = 0;
|
|
@@ -3144,20 +3231,20 @@ function attachSpecificPageVisit(client, config) {
|
|
|
3144
3231
|
const originalReplaceState = history.replaceState.bind(history);
|
|
3145
3232
|
function patchedPushState(...args) {
|
|
3146
3233
|
originalPushState(...args);
|
|
3147
|
-
setTimeout(
|
|
3234
|
+
setTimeout(check2, 0);
|
|
3148
3235
|
}
|
|
3149
3236
|
function patchedReplaceState(...args) {
|
|
3150
3237
|
originalReplaceState(...args);
|
|
3151
|
-
setTimeout(
|
|
3238
|
+
setTimeout(check2, 0);
|
|
3152
3239
|
}
|
|
3153
3240
|
history.pushState = patchedPushState;
|
|
3154
3241
|
history.replaceState = patchedReplaceState;
|
|
3155
|
-
window.addEventListener("popstate",
|
|
3156
|
-
|
|
3242
|
+
window.addEventListener("popstate", check2);
|
|
3243
|
+
check2();
|
|
3157
3244
|
return () => {
|
|
3158
3245
|
history.pushState = originalPushState;
|
|
3159
3246
|
history.replaceState = originalReplaceState;
|
|
3160
|
-
window.removeEventListener("popstate",
|
|
3247
|
+
window.removeEventListener("popstate", check2);
|
|
3161
3248
|
};
|
|
3162
3249
|
}
|
|
3163
3250
|
|
|
@@ -3260,7 +3347,7 @@ function attachScrollDepth(client, config) {
|
|
|
3260
3347
|
}
|
|
3261
3348
|
const thresholds = new Set(config.thresholds);
|
|
3262
3349
|
let firedForPath = /* @__PURE__ */ new Set();
|
|
3263
|
-
let
|
|
3350
|
+
let currentPath3 = window.location.pathname;
|
|
3264
3351
|
let rafId = null;
|
|
3265
3352
|
const gate = createBaselineGate();
|
|
3266
3353
|
function checkThresholds() {
|
|
@@ -3276,7 +3363,7 @@ function attachScrollDepth(client, config) {
|
|
|
3276
3363
|
eventType: "scroll_depth",
|
|
3277
3364
|
metadata: {
|
|
3278
3365
|
depth_percent: threshold,
|
|
3279
|
-
page: { path:
|
|
3366
|
+
page: { path: currentPath3 }
|
|
3280
3367
|
},
|
|
3281
3368
|
pageUrl: window.location.href,
|
|
3282
3369
|
occurredAt: null
|
|
@@ -3293,8 +3380,8 @@ function attachScrollDepth(client, config) {
|
|
|
3293
3380
|
}
|
|
3294
3381
|
function resetIfPathChanged() {
|
|
3295
3382
|
const newPath = window.location.pathname;
|
|
3296
|
-
if (newPath ===
|
|
3297
|
-
|
|
3383
|
+
if (newPath === currentPath3) return;
|
|
3384
|
+
currentPath3 = newPath;
|
|
3298
3385
|
firedForPath = /* @__PURE__ */ new Set();
|
|
3299
3386
|
gate.rebaseline();
|
|
3300
3387
|
}
|
|
@@ -3356,14 +3443,14 @@ function attachMultiPageSession(client, config) {
|
|
|
3356
3443
|
function hasFired() {
|
|
3357
3444
|
return storage.getItem(FIRED_KEY) === "1";
|
|
3358
3445
|
}
|
|
3359
|
-
function
|
|
3360
|
-
const
|
|
3361
|
-
if (
|
|
3362
|
-
lastCheckedPath =
|
|
3446
|
+
function check2() {
|
|
3447
|
+
const currentPath3 = window.location.pathname;
|
|
3448
|
+
if (currentPath3 === lastCheckedPath) return;
|
|
3449
|
+
lastCheckedPath = currentPath3;
|
|
3363
3450
|
resetIfSessionChanged();
|
|
3364
3451
|
if (hasFired()) return;
|
|
3365
3452
|
const paths = getDistinctPaths();
|
|
3366
|
-
paths.add(
|
|
3453
|
+
paths.add(currentPath3);
|
|
3367
3454
|
saveDistinctPaths(paths);
|
|
3368
3455
|
if (paths.size >= pageThreshold) {
|
|
3369
3456
|
storage.setItem(FIRED_KEY, "1");
|
|
@@ -3382,20 +3469,20 @@ function attachMultiPageSession(client, config) {
|
|
|
3382
3469
|
const originalReplaceState = history.replaceState.bind(history);
|
|
3383
3470
|
function patchedPushState(...args) {
|
|
3384
3471
|
originalPushState(...args);
|
|
3385
|
-
setTimeout(
|
|
3472
|
+
setTimeout(check2, 0);
|
|
3386
3473
|
}
|
|
3387
3474
|
function patchedReplaceState(...args) {
|
|
3388
3475
|
originalReplaceState(...args);
|
|
3389
|
-
setTimeout(
|
|
3476
|
+
setTimeout(check2, 0);
|
|
3390
3477
|
}
|
|
3391
3478
|
history.pushState = patchedPushState;
|
|
3392
3479
|
history.replaceState = patchedReplaceState;
|
|
3393
|
-
window.addEventListener("popstate",
|
|
3394
|
-
|
|
3480
|
+
window.addEventListener("popstate", check2);
|
|
3481
|
+
check2();
|
|
3395
3482
|
return () => {
|
|
3396
3483
|
history.pushState = originalPushState;
|
|
3397
3484
|
history.replaceState = originalReplaceState;
|
|
3398
|
-
window.removeEventListener("popstate",
|
|
3485
|
+
window.removeEventListener("popstate", check2);
|
|
3399
3486
|
};
|
|
3400
3487
|
}
|
|
3401
3488
|
|
|
@@ -3408,7 +3495,7 @@ function attachFormStart(client, config) {
|
|
|
3408
3495
|
}
|
|
3409
3496
|
const selector = config.selector ?? "form";
|
|
3410
3497
|
let firedForms = /* @__PURE__ */ new Set();
|
|
3411
|
-
let
|
|
3498
|
+
let currentPath3 = window.location.pathname;
|
|
3412
3499
|
let pendingTap = null;
|
|
3413
3500
|
function getFormKey(form) {
|
|
3414
3501
|
if (form.id) return `id:${form.id}`;
|
|
@@ -3459,8 +3546,8 @@ function attachFormStart(client, config) {
|
|
|
3459
3546
|
}
|
|
3460
3547
|
function resetIfPathChanged() {
|
|
3461
3548
|
const newPath = window.location.pathname;
|
|
3462
|
-
if (newPath ===
|
|
3463
|
-
|
|
3549
|
+
if (newPath === currentPath3) return;
|
|
3550
|
+
currentPath3 = newPath;
|
|
3464
3551
|
firedForms = /* @__PURE__ */ new Set();
|
|
3465
3552
|
}
|
|
3466
3553
|
const originalPushState = history.pushState.bind(history);
|
|
@@ -3501,7 +3588,7 @@ function attachPageExit(client) {
|
|
|
3501
3588
|
return () => {
|
|
3502
3589
|
};
|
|
3503
3590
|
}
|
|
3504
|
-
let
|
|
3591
|
+
let currentPath3 = window.location.pathname;
|
|
3505
3592
|
let activeSince = document.visibilityState === "visible" ? Date.now() : null;
|
|
3506
3593
|
let accumulatedMs = 0;
|
|
3507
3594
|
let maxScrollPercent = null;
|
|
@@ -3551,9 +3638,9 @@ function attachPageExit(client) {
|
|
|
3551
3638
|
}
|
|
3552
3639
|
function onNavigate() {
|
|
3553
3640
|
const newPath = window.location.pathname;
|
|
3554
|
-
if (newPath ===
|
|
3555
|
-
emitSegment(
|
|
3556
|
-
|
|
3641
|
+
if (newPath === currentPath3) return;
|
|
3642
|
+
emitSegment(currentPath3, false);
|
|
3643
|
+
currentPath3 = newPath;
|
|
3557
3644
|
maxScrollPercent = null;
|
|
3558
3645
|
gate.rebaseline();
|
|
3559
3646
|
accumulatedMs = 0;
|
|
@@ -3561,13 +3648,13 @@ function attachPageExit(client) {
|
|
|
3561
3648
|
}
|
|
3562
3649
|
function onVisibilityChange() {
|
|
3563
3650
|
if (document.visibilityState === "hidden") {
|
|
3564
|
-
emitSegment(
|
|
3651
|
+
emitSegment(currentPath3, true);
|
|
3565
3652
|
} else {
|
|
3566
3653
|
activeSince = Date.now();
|
|
3567
3654
|
}
|
|
3568
3655
|
}
|
|
3569
3656
|
function onPageHide() {
|
|
3570
|
-
emitSegment(
|
|
3657
|
+
emitSegment(currentPath3, true);
|
|
3571
3658
|
}
|
|
3572
3659
|
const unsubscribeNav = onHistoryChange(onNavigate);
|
|
3573
3660
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
@@ -3746,6 +3833,104 @@ function attachPhoneClickCapture(client, config) {
|
|
|
3746
3833
|
};
|
|
3747
3834
|
}
|
|
3748
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
|
+
|
|
3749
3934
|
// ../tracking-core/src/resources/sales/bookings.ts
|
|
3750
3935
|
var newBookingAttemptKey = uuidV4;
|
|
3751
3936
|
|
|
@@ -4080,7 +4265,7 @@ function GoogleAdsTracking(props) {
|
|
|
4080
4265
|
var import_react6 = require("react");
|
|
4081
4266
|
|
|
4082
4267
|
// package.json
|
|
4083
|
-
var version = "1.
|
|
4268
|
+
var version = "1.14.0";
|
|
4084
4269
|
|
|
4085
4270
|
// ../tracking-core/src/phone-react.tsx
|
|
4086
4271
|
var import_react5 = require("react");
|
|
@@ -4251,7 +4436,11 @@ function createTracking(options) {
|
|
|
4251
4436
|
[rawClient, conversionStore]
|
|
4252
4437
|
);
|
|
4253
4438
|
const client = (0, import_react6.useMemo)(
|
|
4254
|
-
() => createTypedClient(conversionClient, triggers, {
|
|
4439
|
+
() => createTypedClient(conversionClient, triggers, {
|
|
4440
|
+
debug,
|
|
4441
|
+
environment,
|
|
4442
|
+
trackingConfig
|
|
4443
|
+
}),
|
|
4255
4444
|
[conversionClient]
|
|
4256
4445
|
);
|
|
4257
4446
|
(0, import_react6.useEffect)(() => {
|
|
@@ -4450,6 +4639,145 @@ function useBookingSubmit(bookings, options = {}) {
|
|
|
4450
4639
|
const status = submitting ? "submitting" : outcome?.status ?? "idle";
|
|
4451
4640
|
return { status, outcome, booked, submit, updateBooking, reset };
|
|
4452
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
|
+
}
|
|
4453
4781
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4454
4782
|
0 && (module.exports = {
|
|
4455
4783
|
AdPlatformTracking,
|
|
@@ -4457,6 +4785,7 @@ function useBookingSubmit(bookings, options = {}) {
|
|
|
4457
4785
|
ConsentBanner,
|
|
4458
4786
|
GoogleAdsTracking,
|
|
4459
4787
|
PhoneField,
|
|
4788
|
+
createLeadFormSubmit,
|
|
4460
4789
|
createTracking,
|
|
4461
4790
|
useBookingHandoffUrl,
|
|
4462
4791
|
useBookingIntegration,
|