@aranova/tracking-react 1.13.0 → 1.14.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 +26 -0
- package/dist/{bookings-C1HMPjQl.d.ts → bookings-CkfA5XOM.d.ts} +283 -8
- package/dist/{bookings-CHpplXU7.d.mts → bookings-DFz3BUiP.d.mts} +283 -8
- package/dist/client.d.mts +10 -8
- package/dist/client.d.ts +10 -8
- package/dist/client.js +382 -41
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +380 -40
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.mts +8 -180
- package/dist/index.d.ts +8 -180
- package/dist/index.js +375 -189
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +372 -189
- package/dist/index.mjs.map +1 -1
- package/dist/{ingest-typed-sCDoPInT.d.ts → ingest-typed-BO6Arbw-.d.mts} +6 -99
- package/dist/{ingest-typed-Ckt4R731.d.mts → ingest-typed-D7I4aQ7p.d.ts} +6 -99
- package/dist/{phone-utils-BNiSqeF7.d.mts → phone-utils-CYr6AmEP.d.mts} +231 -3
- package/dist/{phone-utils-D12FW8tT.d.ts → phone-utils-CYr6AmEP.d.ts} +231 -3
- package/dist/phone.d.mts +1 -2
- package/dist/phone.d.ts +1 -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-CNvzyF5T.d.ts → sales-C0FMkN4m.d.ts} +1 -1
- package/dist/{sales-YUH3bQl9.d.mts → sales-KbTcowYa.d.mts} +1 -1
- package/dist/sales.d.mts +3 -3
- package/dist/sales.d.ts +3 -3
- package/dist/sales.js +1204 -170
- package/dist/sales.js.map +1 -1
- package/dist/sales.mjs +1204 -170
- package/dist/sales.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/form-submit-CX-CxJjQ.d.mts +0 -47
- package/dist/form-submit-CX-CxJjQ.d.ts +0 -47
package/dist/client.mjs
CHANGED
|
@@ -254,7 +254,8 @@ var TRACKING_CAPABILITIES = [
|
|
|
254
254
|
"cta_click_capture",
|
|
255
255
|
"form_capture",
|
|
256
256
|
"phone_click_capture",
|
|
257
|
-
"phone_fields"
|
|
257
|
+
"phone_fields",
|
|
258
|
+
"typed_form_submit"
|
|
258
259
|
];
|
|
259
260
|
var CAPABILITY_DOM_ATTRIBUTE = "data-aranova-capability";
|
|
260
261
|
var registered = /* @__PURE__ */ new Set();
|
|
@@ -2629,7 +2630,7 @@ function createTrackingClient(config) {
|
|
|
2629
2630
|
}
|
|
2630
2631
|
|
|
2631
2632
|
// ../tracking-core/src/ingest-typed.ts
|
|
2632
|
-
import { parse as parse2, safeParse } from "zod/mini";
|
|
2633
|
+
import { parse as parse2, safeParse as safeParse2 } from "zod/mini";
|
|
2633
2634
|
|
|
2634
2635
|
// ../tracking-core/src/events/cta-click.ts
|
|
2635
2636
|
import * as z3 from "zod/mini";
|
|
@@ -2719,7 +2720,19 @@ var jsonValueSchema = z7.lazy(
|
|
|
2719
2720
|
z7.record(z7.string(), jsonValueSchema)
|
|
2720
2721
|
])
|
|
2721
2722
|
).register(z7.globalRegistry, { id: "JsonSerializableValue" });
|
|
2722
|
-
var
|
|
2723
|
+
var contractNameSchema = z7.string().check(z7.regex(/^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/));
|
|
2724
|
+
var contractName = contractNameSchema;
|
|
2725
|
+
var violationRole = z7.string().check(
|
|
2726
|
+
z7.maxLength(200),
|
|
2727
|
+
z7.regex(
|
|
2728
|
+
/^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?(\|[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?)*$/
|
|
2729
|
+
)
|
|
2730
|
+
);
|
|
2731
|
+
var MAX_CONTRACT_VIOLATIONS = 50;
|
|
2732
|
+
var contractViolationSchema = z7.strictObject({
|
|
2733
|
+
role: z7.nullable(violationRole),
|
|
2734
|
+
rule: z7.string().check(z7.regex(/^[a-z][a-z_]{0,47}$/))
|
|
2735
|
+
});
|
|
2723
2736
|
var formSubmitMetadataSchema = z7.strictObject({
|
|
2724
2737
|
form: z7.strictObject({
|
|
2725
2738
|
id: z7.string(),
|
|
@@ -2748,6 +2761,9 @@ var formSubmitMetadataSchema = z7.strictObject({
|
|
|
2748
2761
|
})
|
|
2749
2762
|
).check(z7.maxLength(20))
|
|
2750
2763
|
)
|
|
2764
|
+
),
|
|
2765
|
+
contract_violations: z7.optional(
|
|
2766
|
+
z7.nullable(z7.array(contractViolationSchema).check(z7.maxLength(MAX_CONTRACT_VIOLATIONS)))
|
|
2751
2767
|
)
|
|
2752
2768
|
}),
|
|
2753
2769
|
page: z7.strictObject({
|
|
@@ -2981,24 +2997,88 @@ function getEventDefinition(name) {
|
|
|
2981
2997
|
return EVENT_REGISTRY[name];
|
|
2982
2998
|
}
|
|
2983
2999
|
|
|
3000
|
+
// ../tracking-core/src/form-submit-salvage.ts
|
|
3001
|
+
import { safeParse } from "zod/mini";
|
|
3002
|
+
var formShape = formSubmitMetadataSchema.shape.form.shape;
|
|
3003
|
+
var INVALID_SHAPE = { role: null, rule: "invalid_shape" };
|
|
3004
|
+
function salvageFormSubmit(metadata) {
|
|
3005
|
+
if (!isRecord2(metadata) || !isRecord2(metadata.form)) return null;
|
|
3006
|
+
const form = metadata.form;
|
|
3007
|
+
const page = isRecord2(metadata.page) ? metadata.page : {};
|
|
3008
|
+
const reported = Array.isArray(form.contract_violations) ? form.contract_violations.filter((v) => safeParse(contractViolationSchema, v).success) : [];
|
|
3009
|
+
const salvaged = {
|
|
3010
|
+
form: {
|
|
3011
|
+
id: typeof form.id === "string" ? form.id : String(form.id ?? ""),
|
|
3012
|
+
action: typeof form.action === "string" ? form.action : null,
|
|
3013
|
+
fields: Array.isArray(form.fields) ? form.fields.filter(isRecord2).map(salvageField) : [],
|
|
3014
|
+
...valid("kind", form.kind),
|
|
3015
|
+
...valid("contract_version", form.contract_version),
|
|
3016
|
+
...valid("contract_hash", form.contract_hash),
|
|
3017
|
+
...valid("extra", form.extra),
|
|
3018
|
+
contract_violations: [...reported, INVALID_SHAPE].slice(-MAX_CONTRACT_VIOLATIONS)
|
|
3019
|
+
},
|
|
3020
|
+
page: { path: typeof page.path === "string" ? page.path : currentPath2() }
|
|
3021
|
+
};
|
|
3022
|
+
const parsed = safeParse(formSubmitMetadataSchema, salvaged);
|
|
3023
|
+
return parsed.success ? parsed.data : null;
|
|
3024
|
+
}
|
|
3025
|
+
function salvageField(field) {
|
|
3026
|
+
return {
|
|
3027
|
+
name: String(field.name ?? ""),
|
|
3028
|
+
type: String(field.type ?? ""),
|
|
3029
|
+
label: typeof field.label === "string" ? field.label : null,
|
|
3030
|
+
value: toJson(field.value),
|
|
3031
|
+
...typeof field.role === "string" && safeParse(contractNameSchema, field.role).success ? { role: field.role } : {}
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
function valid(key, value) {
|
|
3035
|
+
if (value == null || !safeParse(formShape[key], value).success) return {};
|
|
3036
|
+
return { [key]: value };
|
|
3037
|
+
}
|
|
3038
|
+
function toJson(value) {
|
|
3039
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
3040
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
3041
|
+
if (typeof value === "bigint") return value.toString();
|
|
3042
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.toISOString();
|
|
3043
|
+
if (Array.isArray(value)) return value.map(toJson);
|
|
3044
|
+
if (isRecord2(value)) {
|
|
3045
|
+
return Object.fromEntries(
|
|
3046
|
+
Object.entries(value).filter(([, v]) => v !== void 0).map(([k, v]) => [k, toJson(v)])
|
|
3047
|
+
);
|
|
3048
|
+
}
|
|
3049
|
+
return null;
|
|
3050
|
+
}
|
|
3051
|
+
function isRecord2(value) {
|
|
3052
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
3053
|
+
}
|
|
3054
|
+
function currentPath2() {
|
|
3055
|
+
return typeof window === "undefined" ? "/" : window.location.pathname;
|
|
3056
|
+
}
|
|
3057
|
+
|
|
2984
3058
|
// ../tracking-core/src/ingest-typed.ts
|
|
3059
|
+
var clientSettings = /* @__PURE__ */ new WeakMap();
|
|
3060
|
+
function typedClientSettings(client) {
|
|
3061
|
+
return clientSettings.get(client) ?? {};
|
|
3062
|
+
}
|
|
2985
3063
|
function createTypedClient(raw, registry, options = {}) {
|
|
2986
3064
|
const debug = options.debug ?? false;
|
|
2987
3065
|
if (registry.manual?.form_submit) registerCapability("form_capture");
|
|
2988
|
-
|
|
3066
|
+
const client = {
|
|
2989
3067
|
trackEvent(eventType, metadata, opts) {
|
|
2990
3068
|
const def = getEventDefinition(eventType);
|
|
3069
|
+
let sent = metadata;
|
|
2991
3070
|
if (debug) {
|
|
2992
3071
|
parse2(def.metadataSchema, metadata);
|
|
2993
|
-
} else if (!
|
|
3072
|
+
} else if (!safeParse2(def.metadataSchema, metadata).success) {
|
|
3073
|
+
sent = eventType === "form_submit" ? salvageFormSubmit(metadata) : null;
|
|
2994
3074
|
console.warn(
|
|
2995
|
-
`[AranovaTracking] trackEvent: metadata for "${eventType}" failed schema validation; event dropped`
|
|
3075
|
+
sent ? `[AranovaTracking] trackEvent: metadata for "form_submit" failed schema validation; sent salvaged` : `[AranovaTracking] trackEvent: metadata for "${eventType}" failed schema validation; event dropped`
|
|
2996
3076
|
);
|
|
2997
|
-
return;
|
|
3077
|
+
if (!sent) return;
|
|
2998
3078
|
}
|
|
2999
3079
|
raw.trackEvent({
|
|
3000
3080
|
eventType,
|
|
3001
|
-
metadata,
|
|
3081
|
+
metadata: sent,
|
|
3002
3082
|
pageUrl: opts?.pageUrl ?? null,
|
|
3003
3083
|
occurredAt: opts?.occurredAt ?? null
|
|
3004
3084
|
});
|
|
@@ -3007,6 +3087,8 @@ function createTypedClient(raw, registry, options = {}) {
|
|
|
3007
3087
|
getSessionId: raw.getSessionId.bind(raw),
|
|
3008
3088
|
getVisitorId: raw.getVisitorId.bind(raw)
|
|
3009
3089
|
};
|
|
3090
|
+
clientSettings.set(client, options);
|
|
3091
|
+
return client;
|
|
3010
3092
|
}
|
|
3011
3093
|
|
|
3012
3094
|
// ../tracking-core/src/triggers/time-on-site.ts
|
|
@@ -3077,7 +3159,7 @@ function attachSpecificPageVisit(client, config) {
|
|
|
3077
3159
|
}
|
|
3078
3160
|
const { pages } = config;
|
|
3079
3161
|
const firedSet = /* @__PURE__ */ new Set();
|
|
3080
|
-
function
|
|
3162
|
+
function check2() {
|
|
3081
3163
|
const path = window.location.pathname;
|
|
3082
3164
|
for (const { name, pathPattern } of pages) {
|
|
3083
3165
|
pathPattern.lastIndex = 0;
|
|
@@ -3097,20 +3179,20 @@ function attachSpecificPageVisit(client, config) {
|
|
|
3097
3179
|
const originalReplaceState = history.replaceState.bind(history);
|
|
3098
3180
|
function patchedPushState(...args) {
|
|
3099
3181
|
originalPushState(...args);
|
|
3100
|
-
setTimeout(
|
|
3182
|
+
setTimeout(check2, 0);
|
|
3101
3183
|
}
|
|
3102
3184
|
function patchedReplaceState(...args) {
|
|
3103
3185
|
originalReplaceState(...args);
|
|
3104
|
-
setTimeout(
|
|
3186
|
+
setTimeout(check2, 0);
|
|
3105
3187
|
}
|
|
3106
3188
|
history.pushState = patchedPushState;
|
|
3107
3189
|
history.replaceState = patchedReplaceState;
|
|
3108
|
-
window.addEventListener("popstate",
|
|
3109
|
-
|
|
3190
|
+
window.addEventListener("popstate", check2);
|
|
3191
|
+
check2();
|
|
3110
3192
|
return () => {
|
|
3111
3193
|
history.pushState = originalPushState;
|
|
3112
3194
|
history.replaceState = originalReplaceState;
|
|
3113
|
-
window.removeEventListener("popstate",
|
|
3195
|
+
window.removeEventListener("popstate", check2);
|
|
3114
3196
|
};
|
|
3115
3197
|
}
|
|
3116
3198
|
|
|
@@ -3213,7 +3295,7 @@ function attachScrollDepth(client, config) {
|
|
|
3213
3295
|
}
|
|
3214
3296
|
const thresholds = new Set(config.thresholds);
|
|
3215
3297
|
let firedForPath = /* @__PURE__ */ new Set();
|
|
3216
|
-
let
|
|
3298
|
+
let currentPath3 = window.location.pathname;
|
|
3217
3299
|
let rafId = null;
|
|
3218
3300
|
const gate = createBaselineGate();
|
|
3219
3301
|
function checkThresholds() {
|
|
@@ -3229,7 +3311,7 @@ function attachScrollDepth(client, config) {
|
|
|
3229
3311
|
eventType: "scroll_depth",
|
|
3230
3312
|
metadata: {
|
|
3231
3313
|
depth_percent: threshold,
|
|
3232
|
-
page: { path:
|
|
3314
|
+
page: { path: currentPath3 }
|
|
3233
3315
|
},
|
|
3234
3316
|
pageUrl: window.location.href,
|
|
3235
3317
|
occurredAt: null
|
|
@@ -3246,8 +3328,8 @@ function attachScrollDepth(client, config) {
|
|
|
3246
3328
|
}
|
|
3247
3329
|
function resetIfPathChanged() {
|
|
3248
3330
|
const newPath = window.location.pathname;
|
|
3249
|
-
if (newPath ===
|
|
3250
|
-
|
|
3331
|
+
if (newPath === currentPath3) return;
|
|
3332
|
+
currentPath3 = newPath;
|
|
3251
3333
|
firedForPath = /* @__PURE__ */ new Set();
|
|
3252
3334
|
gate.rebaseline();
|
|
3253
3335
|
}
|
|
@@ -3309,14 +3391,14 @@ function attachMultiPageSession(client, config) {
|
|
|
3309
3391
|
function hasFired() {
|
|
3310
3392
|
return storage.getItem(FIRED_KEY) === "1";
|
|
3311
3393
|
}
|
|
3312
|
-
function
|
|
3313
|
-
const
|
|
3314
|
-
if (
|
|
3315
|
-
lastCheckedPath =
|
|
3394
|
+
function check2() {
|
|
3395
|
+
const currentPath3 = window.location.pathname;
|
|
3396
|
+
if (currentPath3 === lastCheckedPath) return;
|
|
3397
|
+
lastCheckedPath = currentPath3;
|
|
3316
3398
|
resetIfSessionChanged();
|
|
3317
3399
|
if (hasFired()) return;
|
|
3318
3400
|
const paths = getDistinctPaths();
|
|
3319
|
-
paths.add(
|
|
3401
|
+
paths.add(currentPath3);
|
|
3320
3402
|
saveDistinctPaths(paths);
|
|
3321
3403
|
if (paths.size >= pageThreshold) {
|
|
3322
3404
|
storage.setItem(FIRED_KEY, "1");
|
|
@@ -3335,20 +3417,20 @@ function attachMultiPageSession(client, config) {
|
|
|
3335
3417
|
const originalReplaceState = history.replaceState.bind(history);
|
|
3336
3418
|
function patchedPushState(...args) {
|
|
3337
3419
|
originalPushState(...args);
|
|
3338
|
-
setTimeout(
|
|
3420
|
+
setTimeout(check2, 0);
|
|
3339
3421
|
}
|
|
3340
3422
|
function patchedReplaceState(...args) {
|
|
3341
3423
|
originalReplaceState(...args);
|
|
3342
|
-
setTimeout(
|
|
3424
|
+
setTimeout(check2, 0);
|
|
3343
3425
|
}
|
|
3344
3426
|
history.pushState = patchedPushState;
|
|
3345
3427
|
history.replaceState = patchedReplaceState;
|
|
3346
|
-
window.addEventListener("popstate",
|
|
3347
|
-
|
|
3428
|
+
window.addEventListener("popstate", check2);
|
|
3429
|
+
check2();
|
|
3348
3430
|
return () => {
|
|
3349
3431
|
history.pushState = originalPushState;
|
|
3350
3432
|
history.replaceState = originalReplaceState;
|
|
3351
|
-
window.removeEventListener("popstate",
|
|
3433
|
+
window.removeEventListener("popstate", check2);
|
|
3352
3434
|
};
|
|
3353
3435
|
}
|
|
3354
3436
|
|
|
@@ -3361,7 +3443,7 @@ function attachFormStart(client, config) {
|
|
|
3361
3443
|
}
|
|
3362
3444
|
const selector = config.selector ?? "form";
|
|
3363
3445
|
let firedForms = /* @__PURE__ */ new Set();
|
|
3364
|
-
let
|
|
3446
|
+
let currentPath3 = window.location.pathname;
|
|
3365
3447
|
let pendingTap = null;
|
|
3366
3448
|
function getFormKey(form) {
|
|
3367
3449
|
if (form.id) return `id:${form.id}`;
|
|
@@ -3412,8 +3494,8 @@ function attachFormStart(client, config) {
|
|
|
3412
3494
|
}
|
|
3413
3495
|
function resetIfPathChanged() {
|
|
3414
3496
|
const newPath = window.location.pathname;
|
|
3415
|
-
if (newPath ===
|
|
3416
|
-
|
|
3497
|
+
if (newPath === currentPath3) return;
|
|
3498
|
+
currentPath3 = newPath;
|
|
3417
3499
|
firedForms = /* @__PURE__ */ new Set();
|
|
3418
3500
|
}
|
|
3419
3501
|
const originalPushState = history.pushState.bind(history);
|
|
@@ -3454,7 +3536,7 @@ function attachPageExit(client) {
|
|
|
3454
3536
|
return () => {
|
|
3455
3537
|
};
|
|
3456
3538
|
}
|
|
3457
|
-
let
|
|
3539
|
+
let currentPath3 = window.location.pathname;
|
|
3458
3540
|
let activeSince = document.visibilityState === "visible" ? Date.now() : null;
|
|
3459
3541
|
let accumulatedMs = 0;
|
|
3460
3542
|
let maxScrollPercent = null;
|
|
@@ -3504,9 +3586,9 @@ function attachPageExit(client) {
|
|
|
3504
3586
|
}
|
|
3505
3587
|
function onNavigate() {
|
|
3506
3588
|
const newPath = window.location.pathname;
|
|
3507
|
-
if (newPath ===
|
|
3508
|
-
emitSegment(
|
|
3509
|
-
|
|
3589
|
+
if (newPath === currentPath3) return;
|
|
3590
|
+
emitSegment(currentPath3, false);
|
|
3591
|
+
currentPath3 = newPath;
|
|
3510
3592
|
maxScrollPercent = null;
|
|
3511
3593
|
gate.rebaseline();
|
|
3512
3594
|
accumulatedMs = 0;
|
|
@@ -3514,13 +3596,13 @@ function attachPageExit(client) {
|
|
|
3514
3596
|
}
|
|
3515
3597
|
function onVisibilityChange() {
|
|
3516
3598
|
if (document.visibilityState === "hidden") {
|
|
3517
|
-
emitSegment(
|
|
3599
|
+
emitSegment(currentPath3, true);
|
|
3518
3600
|
} else {
|
|
3519
3601
|
activeSince = Date.now();
|
|
3520
3602
|
}
|
|
3521
3603
|
}
|
|
3522
3604
|
function onPageHide() {
|
|
3523
|
-
emitSegment(
|
|
3605
|
+
emitSegment(currentPath3, true);
|
|
3524
3606
|
}
|
|
3525
3607
|
const unsubscribeNav = onHistoryChange(onNavigate);
|
|
3526
3608
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
@@ -3699,6 +3781,255 @@ function attachPhoneClickCapture(client, config) {
|
|
|
3699
3781
|
};
|
|
3700
3782
|
}
|
|
3701
3783
|
|
|
3784
|
+
// ../tracking-core/src/lead-forms/lead-forms.gen.ts
|
|
3785
|
+
import * as z15 from "zod/mini";
|
|
3786
|
+
var LEAD_FORM_REGISTRY_VERSION = 2;
|
|
3787
|
+
var LEAD_FORM_ROLES = [
|
|
3788
|
+
"given_name",
|
|
3789
|
+
"family_name",
|
|
3790
|
+
"phone",
|
|
3791
|
+
"email",
|
|
3792
|
+
"service",
|
|
3793
|
+
"preferred_date",
|
|
3794
|
+
"preferred_time",
|
|
3795
|
+
"note",
|
|
3796
|
+
"visit",
|
|
3797
|
+
"intent",
|
|
3798
|
+
"vehicle",
|
|
3799
|
+
"drop_off",
|
|
3800
|
+
"address",
|
|
3801
|
+
"patient_type",
|
|
3802
|
+
"matter",
|
|
3803
|
+
"language",
|
|
3804
|
+
"products",
|
|
3805
|
+
"goal",
|
|
3806
|
+
"business_type",
|
|
3807
|
+
"lead_volume",
|
|
3808
|
+
"attribution"
|
|
3809
|
+
];
|
|
3810
|
+
var leadFormPrimitiveSchemas = /* @__PURE__ */ (() => ({
|
|
3811
|
+
string: z15.string().check(z15.maxLength(120)),
|
|
3812
|
+
text: z15.string().check(z15.maxLength(2e3)),
|
|
3813
|
+
enum: z15.string().check(z15.regex(/^[a-z0-9-]{1,48}$/)),
|
|
3814
|
+
multi_enum: z15.array(z15.string().check(z15.regex(/^[a-z0-9-]{1,48}$/))).check(z15.maxLength(10)),
|
|
3815
|
+
bool: z15.boolean(),
|
|
3816
|
+
date: z15.string().check(z15.regex(/^\d{4}-\d{2}-\d{2}$/)),
|
|
3817
|
+
time_window: z15.strictObject({
|
|
3818
|
+
start: z15.string().check(z15.regex(/^([01]\d|2[0-3]):[0-5]\d$/)),
|
|
3819
|
+
end: z15.nullable(z15.string().check(z15.regex(/^([01]\d|2[0-3]):[0-5]\d$/))),
|
|
3820
|
+
label: z15.optional(z15.string().check(z15.maxLength(60)))
|
|
3821
|
+
}),
|
|
3822
|
+
phone: z15.string().check(z15.regex(/^\+[1-9]\d{6,14}$/)),
|
|
3823
|
+
email: z15.string().check(z15.maxLength(320), z15.regex(/^[^\s@]+@[^\s@]+$/)),
|
|
3824
|
+
address: z15.strictObject({
|
|
3825
|
+
line1: z15.string().check(z15.maxLength(120)),
|
|
3826
|
+
line2: z15.optional(z15.string().check(z15.maxLength(120))),
|
|
3827
|
+
city: z15.optional(z15.string().check(z15.maxLength(120))),
|
|
3828
|
+
region: z15.optional(z15.string().check(z15.maxLength(120))),
|
|
3829
|
+
postal_code: z15.optional(z15.string().check(z15.maxLength(120))),
|
|
3830
|
+
country: z15.optional(z15.string().check(z15.maxLength(120)))
|
|
3831
|
+
}),
|
|
3832
|
+
number: z15.number()
|
|
3833
|
+
}))();
|
|
3834
|
+
var LEAD_FORM_ROLE_SPECS = {
|
|
3835
|
+
given_name: { primitive: "string", smsLabel: "", contactIdentity: true },
|
|
3836
|
+
family_name: { primitive: "string", smsLabel: "", contactIdentity: true },
|
|
3837
|
+
phone: { primitive: "phone", smsLabel: "", contactIdentity: true },
|
|
3838
|
+
email: { primitive: "email", smsLabel: "", contactIdentity: true },
|
|
3839
|
+
service: { primitive: "multi_enum", smsLabel: "Service" },
|
|
3840
|
+
preferred_date: { primitive: "date", smsLabel: "Date" },
|
|
3841
|
+
preferred_time: { primitive: "time_window", smsLabel: "Time" },
|
|
3842
|
+
note: { primitive: "text", smsLabel: "Note" },
|
|
3843
|
+
visit: {
|
|
3844
|
+
primitive: "enum",
|
|
3845
|
+
smsLabel: "Visit",
|
|
3846
|
+
options: [
|
|
3847
|
+
{ key: "in-shop", label: "In shop" },
|
|
3848
|
+
{ key: "in-home", label: "In home" },
|
|
3849
|
+
{ key: "remote", label: "Remote" },
|
|
3850
|
+
{ key: "on-site", label: "On site" }
|
|
3851
|
+
]
|
|
3852
|
+
},
|
|
3853
|
+
intent: {
|
|
3854
|
+
primitive: "enum",
|
|
3855
|
+
smsLabel: "Wants",
|
|
3856
|
+
options: [
|
|
3857
|
+
{ key: "book", label: "Book" },
|
|
3858
|
+
{ key: "quote", label: "Quote" }
|
|
3859
|
+
]
|
|
3860
|
+
},
|
|
3861
|
+
vehicle: { primitive: "string", smsLabel: "Vehicle" },
|
|
3862
|
+
drop_off: {
|
|
3863
|
+
primitive: "enum",
|
|
3864
|
+
smsLabel: "Drop-off",
|
|
3865
|
+
options: [
|
|
3866
|
+
{ key: "wait-for-it", label: "Waiting" },
|
|
3867
|
+
{ key: "drop-off", label: "Dropping off" }
|
|
3868
|
+
]
|
|
3869
|
+
},
|
|
3870
|
+
address: { primitive: "address", smsLabel: "Address" },
|
|
3871
|
+
patient_type: { primitive: "enum", smsLabel: "Patient" },
|
|
3872
|
+
matter: { primitive: "enum", smsLabel: "Matter" },
|
|
3873
|
+
language: { primitive: "enum", smsLabel: "Language" },
|
|
3874
|
+
products: { primitive: "multi_enum", smsLabel: "Products" },
|
|
3875
|
+
goal: { primitive: "enum", smsLabel: "Goal" },
|
|
3876
|
+
business_type: { primitive: "enum", smsLabel: "Business" },
|
|
3877
|
+
lead_volume: { primitive: "enum", smsLabel: "Lead volume" },
|
|
3878
|
+
add_on: { primitive: "bool", smsLabel: "" },
|
|
3879
|
+
attribution: { primitive: "enum", smsLabel: "", uninteresting: true }
|
|
3880
|
+
};
|
|
3881
|
+
|
|
3882
|
+
// ../tracking-core/src/lead-forms/submit.ts
|
|
3883
|
+
import { safeParse as safeParse3 } from "zod/mini";
|
|
3884
|
+
var KNOWN_ROLES = new Set(LEAD_FORM_ROLES);
|
|
3885
|
+
var ROLE_ORDER = new Map(LEAD_FORM_ROLES.map((role, index) => [role, index]));
|
|
3886
|
+
var ADD_ON = /^add_on:[a-z0-9_-]{1,48}$/;
|
|
3887
|
+
var CONTRACT_NAME = /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/;
|
|
3888
|
+
var INPUT_TYPES = {
|
|
3889
|
+
string: "text",
|
|
3890
|
+
text: "textarea",
|
|
3891
|
+
enum: "select",
|
|
3892
|
+
multi_enum: "select",
|
|
3893
|
+
bool: "checkbox",
|
|
3894
|
+
date: "date",
|
|
3895
|
+
time_window: "time",
|
|
3896
|
+
phone: "tel",
|
|
3897
|
+
email: "email",
|
|
3898
|
+
address: "address",
|
|
3899
|
+
number: "number"
|
|
3900
|
+
};
|
|
3901
|
+
function createLeadFormSubmit(tracking, contracts, options = {}) {
|
|
3902
|
+
registerCapability("typed_form_submit");
|
|
3903
|
+
const settings = { ...typedClientSettings(tracking), ...options };
|
|
3904
|
+
return {
|
|
3905
|
+
submit(input) {
|
|
3906
|
+
const fields = presentFields(input.fields);
|
|
3907
|
+
const { violations, hash } = judgeLeadForm(
|
|
3908
|
+
input.kind,
|
|
3909
|
+
input.formId,
|
|
3910
|
+
fields,
|
|
3911
|
+
contracts,
|
|
3912
|
+
settings
|
|
3913
|
+
);
|
|
3914
|
+
const metadata = {
|
|
3915
|
+
form: {
|
|
3916
|
+
id: input.formId,
|
|
3917
|
+
action: input.action ?? null,
|
|
3918
|
+
fields: fields.map(([role, value]) => ({
|
|
3919
|
+
name: role,
|
|
3920
|
+
type: INPUT_TYPES[primitiveOf(role) ?? "string"],
|
|
3921
|
+
label: labelOf(role),
|
|
3922
|
+
value,
|
|
3923
|
+
role
|
|
3924
|
+
})),
|
|
3925
|
+
kind: input.kind,
|
|
3926
|
+
contract_version: LEAD_FORM_REGISTRY_VERSION,
|
|
3927
|
+
...hash ? { contract_hash: hash } : {},
|
|
3928
|
+
...input.extra?.length ? { extra: input.extra } : {},
|
|
3929
|
+
...violations.length > 0 ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) } : {}
|
|
3930
|
+
},
|
|
3931
|
+
page: { path: typeof window === "undefined" ? "/" : window.location.pathname }
|
|
3932
|
+
};
|
|
3933
|
+
tracking.trackEvent("form_submit", metadata);
|
|
3934
|
+
return metadata;
|
|
3935
|
+
}
|
|
3936
|
+
};
|
|
3937
|
+
}
|
|
3938
|
+
function judgeLeadForm(kind, formId, fields, contracts, settings) {
|
|
3939
|
+
const generated = contracts?.contracts[kind];
|
|
3940
|
+
const live = liveContract(settings.trackingConfig, kind);
|
|
3941
|
+
const violations = check(live === void 0 ? generated : fromLive(live), fields);
|
|
3942
|
+
const liveHash = live === void 0 ? void 0 : live?.contract_hash ?? null;
|
|
3943
|
+
if (contracts && (liveHash !== void 0 && (generated?.hash ?? null) !== liveHash || contracts.registryVersion !== LEAD_FORM_REGISTRY_VERSION)) {
|
|
3944
|
+
violations.push({ role: null, rule: "contract_behind" });
|
|
3945
|
+
}
|
|
3946
|
+
if (violations.length > 0 && (settings.debug || settings.environment === "development")) {
|
|
3947
|
+
const message = `[AranovaTracking] lead form "${formId}" (${kind}) breaks its contract: ${violations.map((v) => `${v.rule}${v.role ? ` (${v.role})` : ""}`).join(", ")}`;
|
|
3948
|
+
console.error(message);
|
|
3949
|
+
if (settings.debug) throw new Error(message);
|
|
3950
|
+
}
|
|
3951
|
+
return {
|
|
3952
|
+
violations,
|
|
3953
|
+
hash: liveHash === void 0 ? generated?.hash ?? null : liveHash
|
|
3954
|
+
};
|
|
3955
|
+
}
|
|
3956
|
+
function liveContract(ref, kind) {
|
|
3957
|
+
const leadForms = ref ? getTrackingConfigRuntime(ref).leadForms() : null;
|
|
3958
|
+
if (!leadForms) return void 0;
|
|
3959
|
+
return leadForms.contracts.find((c) => c.form_kind === kind) ?? null;
|
|
3960
|
+
}
|
|
3961
|
+
function fromLive(live) {
|
|
3962
|
+
if (!live) return void 0;
|
|
3963
|
+
return {
|
|
3964
|
+
requires: live.requires.map((r) => ({
|
|
3965
|
+
role: r.role,
|
|
3966
|
+
requiredWhen: r.required_when ? { role: r.required_when.role, anyOf: r.required_when.any_of } : null
|
|
3967
|
+
})),
|
|
3968
|
+
requiresAny: live.requires_any.map((g) => g.roles),
|
|
3969
|
+
// Deprecated keys still resolve: a cached form may send them (ADR-0024 §7).
|
|
3970
|
+
options: Object.fromEntries(live.options.map((o) => [o.role, o.options.map((x) => x.key)]))
|
|
3971
|
+
};
|
|
3972
|
+
}
|
|
3973
|
+
function presentFields(fields) {
|
|
3974
|
+
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));
|
|
3975
|
+
}
|
|
3976
|
+
function normalize(role, value) {
|
|
3977
|
+
if (typeof value !== "string") return value;
|
|
3978
|
+
const trimmed = value.trim();
|
|
3979
|
+
const primitive = primitiveOf(role);
|
|
3980
|
+
if (primitive === "phone") return toE164(trimmed) ?? trimmed;
|
|
3981
|
+
if (primitive === "email") return trimmed.toLowerCase();
|
|
3982
|
+
return primitive === "text" ? value : trimmed;
|
|
3983
|
+
}
|
|
3984
|
+
function isBlank(value) {
|
|
3985
|
+
return value === void 0 || value === null || typeof value === "string" && value.trim() === "" || Array.isArray(value) && value.length === 0;
|
|
3986
|
+
}
|
|
3987
|
+
function check(contract, fields) {
|
|
3988
|
+
const answered = new Map(fields);
|
|
3989
|
+
const violations = [];
|
|
3990
|
+
for (const [role, value] of fields) {
|
|
3991
|
+
const primitive = primitiveOf(role);
|
|
3992
|
+
if (!primitive) {
|
|
3993
|
+
violations.push({ role: CONTRACT_NAME.test(role) ? role : null, rule: "unknown_role" });
|
|
3994
|
+
continue;
|
|
3995
|
+
}
|
|
3996
|
+
if (!safeParse3(leadFormPrimitiveSchemas[primitive], value).success) {
|
|
3997
|
+
violations.push({ role, rule: "invalid_value" });
|
|
3998
|
+
continue;
|
|
3999
|
+
}
|
|
4000
|
+
const allowed = contract?.options[role] ?? registryOptions(role);
|
|
4001
|
+
if (allowed?.length && optionKeys(value).some((key) => !allowed.includes(key))) {
|
|
4002
|
+
violations.push({ role, rule: "unknown_option" });
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
for (const { role, requiredWhen } of contract?.requires ?? []) {
|
|
4006
|
+
const owing = requiredWhen === null || optionKeys(answered.get(requiredWhen.role)).some((key) => requiredWhen.anyOf.includes(key));
|
|
4007
|
+
if (owing && !answered.has(role)) violations.push({ role, rule: "missing_required" });
|
|
4008
|
+
}
|
|
4009
|
+
for (const group of contract?.requiresAny ?? []) {
|
|
4010
|
+
if (!group.some((role) => answered.has(role))) {
|
|
4011
|
+
violations.push({ role: group.join("|"), rule: "missing_any_of" });
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
4014
|
+
return violations;
|
|
4015
|
+
}
|
|
4016
|
+
function primitiveOf(role) {
|
|
4017
|
+
if (ADD_ON.test(role)) return "bool";
|
|
4018
|
+
return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role].primitive : null;
|
|
4019
|
+
}
|
|
4020
|
+
function labelOf(role) {
|
|
4021
|
+
if (ADD_ON.test(role)) return role.slice("add_on:".length).replaceAll(/[_-]+/g, " ");
|
|
4022
|
+
return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role].smsLabel || null : null;
|
|
4023
|
+
}
|
|
4024
|
+
function registryOptions(role) {
|
|
4025
|
+
const spec = KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role] : void 0;
|
|
4026
|
+
return spec && "options" in spec ? spec.options.map((o) => o.key) : void 0;
|
|
4027
|
+
}
|
|
4028
|
+
function optionKeys(value) {
|
|
4029
|
+
if (typeof value === "string") return [value];
|
|
4030
|
+
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
4031
|
+
}
|
|
4032
|
+
|
|
3702
4033
|
// ../tracking-core/src/resources/sales/bookings.ts
|
|
3703
4034
|
var newBookingAttemptKey = uuidV4;
|
|
3704
4035
|
|
|
@@ -4033,7 +4364,7 @@ function GoogleAdsTracking(props) {
|
|
|
4033
4364
|
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useMemo as useMemo4 } from "react";
|
|
4034
4365
|
|
|
4035
4366
|
// package.json
|
|
4036
|
-
var version = "1.
|
|
4367
|
+
var version = "1.14.1";
|
|
4037
4368
|
|
|
4038
4369
|
// ../tracking-core/src/phone-react.tsx
|
|
4039
4370
|
import {
|
|
@@ -4211,7 +4542,11 @@ function createTracking(options) {
|
|
|
4211
4542
|
[rawClient, conversionStore]
|
|
4212
4543
|
);
|
|
4213
4544
|
const client = useMemo4(
|
|
4214
|
-
() => createTypedClient(conversionClient, triggers, {
|
|
4545
|
+
() => createTypedClient(conversionClient, triggers, {
|
|
4546
|
+
debug,
|
|
4547
|
+
environment,
|
|
4548
|
+
trackingConfig
|
|
4549
|
+
}),
|
|
4215
4550
|
[conversionClient]
|
|
4216
4551
|
);
|
|
4217
4552
|
useEffect5(() => {
|
|
@@ -4358,7 +4693,11 @@ function useBookingSubmit(bookings, options = {}) {
|
|
|
4358
4693
|
const attemptKey = useRef(null);
|
|
4359
4694
|
const [booked, setBooked] = useState4(null);
|
|
4360
4695
|
const inFlight = useRef(null);
|
|
4361
|
-
const {
|
|
4696
|
+
const { simulate, contracts } = options;
|
|
4697
|
+
const formSubmit = useMemo5(
|
|
4698
|
+
() => options.formSubmit && contracts ? { ...options.formSubmit, contracts } : options.formSubmit,
|
|
4699
|
+
[options.formSubmit, contracts]
|
|
4700
|
+
);
|
|
4362
4701
|
const submit = useCallback3(
|
|
4363
4702
|
(input) => {
|
|
4364
4703
|
if (inFlight.current) return inFlight.current;
|
|
@@ -4416,6 +4755,7 @@ export {
|
|
|
4416
4755
|
ConsentBanner,
|
|
4417
4756
|
GoogleAdsTracking,
|
|
4418
4757
|
PhoneField,
|
|
4758
|
+
createLeadFormSubmit,
|
|
4419
4759
|
createTracking,
|
|
4420
4760
|
useBookingHandoffUrl,
|
|
4421
4761
|
useBookingIntegration,
|