@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.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();
|
|
@@ -1563,6 +1564,10 @@ var TrackingConfigRuntime = class {
|
|
|
1563
1564
|
integrations() {
|
|
1564
1565
|
return this.current?.integrations ?? null;
|
|
1565
1566
|
}
|
|
1567
|
+
/** The lead-form contracts, from the cached object like `integrations()`. */
|
|
1568
|
+
leadForms() {
|
|
1569
|
+
return this.current?.lead_forms ?? null;
|
|
1570
|
+
}
|
|
1566
1571
|
async revalidateNow() {
|
|
1567
1572
|
try {
|
|
1568
1573
|
const headers = {};
|
|
@@ -2625,7 +2630,7 @@ function createTrackingClient(config) {
|
|
|
2625
2630
|
}
|
|
2626
2631
|
|
|
2627
2632
|
// ../tracking-core/src/ingest-typed.ts
|
|
2628
|
-
import { parse as parse2, safeParse } from "zod/mini";
|
|
2633
|
+
import { parse as parse2, safeParse as safeParse2 } from "zod/mini";
|
|
2629
2634
|
|
|
2630
2635
|
// ../tracking-core/src/events/cta-click.ts
|
|
2631
2636
|
import * as z3 from "zod/mini";
|
|
@@ -2715,7 +2720,19 @@ var jsonValueSchema = z7.lazy(
|
|
|
2715
2720
|
z7.record(z7.string(), jsonValueSchema)
|
|
2716
2721
|
])
|
|
2717
2722
|
).register(z7.globalRegistry, { id: "JsonSerializableValue" });
|
|
2718
|
-
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
|
+
});
|
|
2719
2736
|
var formSubmitMetadataSchema = z7.strictObject({
|
|
2720
2737
|
form: z7.strictObject({
|
|
2721
2738
|
id: z7.string(),
|
|
@@ -2744,6 +2761,9 @@ var formSubmitMetadataSchema = z7.strictObject({
|
|
|
2744
2761
|
})
|
|
2745
2762
|
).check(z7.maxLength(20))
|
|
2746
2763
|
)
|
|
2764
|
+
),
|
|
2765
|
+
contract_violations: z7.optional(
|
|
2766
|
+
z7.nullable(z7.array(contractViolationSchema).check(z7.maxLength(MAX_CONTRACT_VIOLATIONS)))
|
|
2747
2767
|
)
|
|
2748
2768
|
}),
|
|
2749
2769
|
page: z7.strictObject({
|
|
@@ -2977,24 +2997,88 @@ function getEventDefinition(name) {
|
|
|
2977
2997
|
return EVENT_REGISTRY[name];
|
|
2978
2998
|
}
|
|
2979
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
|
+
|
|
2980
3058
|
// ../tracking-core/src/ingest-typed.ts
|
|
3059
|
+
var clientSettings = /* @__PURE__ */ new WeakMap();
|
|
3060
|
+
function typedClientSettings(client) {
|
|
3061
|
+
return clientSettings.get(client) ?? {};
|
|
3062
|
+
}
|
|
2981
3063
|
function createTypedClient(raw, registry, options = {}) {
|
|
2982
3064
|
const debug = options.debug ?? false;
|
|
2983
3065
|
if (registry.manual?.form_submit) registerCapability("form_capture");
|
|
2984
|
-
|
|
3066
|
+
const client = {
|
|
2985
3067
|
trackEvent(eventType, metadata, opts) {
|
|
2986
3068
|
const def = getEventDefinition(eventType);
|
|
3069
|
+
let sent = metadata;
|
|
2987
3070
|
if (debug) {
|
|
2988
3071
|
parse2(def.metadataSchema, metadata);
|
|
2989
|
-
} else if (!
|
|
3072
|
+
} else if (!safeParse2(def.metadataSchema, metadata).success) {
|
|
3073
|
+
sent = eventType === "form_submit" ? salvageFormSubmit(metadata) : null;
|
|
2990
3074
|
console.warn(
|
|
2991
|
-
`[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`
|
|
2992
3076
|
);
|
|
2993
|
-
return;
|
|
3077
|
+
if (!sent) return;
|
|
2994
3078
|
}
|
|
2995
3079
|
raw.trackEvent({
|
|
2996
3080
|
eventType,
|
|
2997
|
-
metadata,
|
|
3081
|
+
metadata: sent,
|
|
2998
3082
|
pageUrl: opts?.pageUrl ?? null,
|
|
2999
3083
|
occurredAt: opts?.occurredAt ?? null
|
|
3000
3084
|
});
|
|
@@ -3003,6 +3087,8 @@ function createTypedClient(raw, registry, options = {}) {
|
|
|
3003
3087
|
getSessionId: raw.getSessionId.bind(raw),
|
|
3004
3088
|
getVisitorId: raw.getVisitorId.bind(raw)
|
|
3005
3089
|
};
|
|
3090
|
+
clientSettings.set(client, options);
|
|
3091
|
+
return client;
|
|
3006
3092
|
}
|
|
3007
3093
|
|
|
3008
3094
|
// ../tracking-core/src/triggers/time-on-site.ts
|
|
@@ -3073,7 +3159,7 @@ function attachSpecificPageVisit(client, config) {
|
|
|
3073
3159
|
}
|
|
3074
3160
|
const { pages } = config;
|
|
3075
3161
|
const firedSet = /* @__PURE__ */ new Set();
|
|
3076
|
-
function
|
|
3162
|
+
function check2() {
|
|
3077
3163
|
const path = window.location.pathname;
|
|
3078
3164
|
for (const { name, pathPattern } of pages) {
|
|
3079
3165
|
pathPattern.lastIndex = 0;
|
|
@@ -3093,20 +3179,20 @@ function attachSpecificPageVisit(client, config) {
|
|
|
3093
3179
|
const originalReplaceState = history.replaceState.bind(history);
|
|
3094
3180
|
function patchedPushState(...args) {
|
|
3095
3181
|
originalPushState(...args);
|
|
3096
|
-
setTimeout(
|
|
3182
|
+
setTimeout(check2, 0);
|
|
3097
3183
|
}
|
|
3098
3184
|
function patchedReplaceState(...args) {
|
|
3099
3185
|
originalReplaceState(...args);
|
|
3100
|
-
setTimeout(
|
|
3186
|
+
setTimeout(check2, 0);
|
|
3101
3187
|
}
|
|
3102
3188
|
history.pushState = patchedPushState;
|
|
3103
3189
|
history.replaceState = patchedReplaceState;
|
|
3104
|
-
window.addEventListener("popstate",
|
|
3105
|
-
|
|
3190
|
+
window.addEventListener("popstate", check2);
|
|
3191
|
+
check2();
|
|
3106
3192
|
return () => {
|
|
3107
3193
|
history.pushState = originalPushState;
|
|
3108
3194
|
history.replaceState = originalReplaceState;
|
|
3109
|
-
window.removeEventListener("popstate",
|
|
3195
|
+
window.removeEventListener("popstate", check2);
|
|
3110
3196
|
};
|
|
3111
3197
|
}
|
|
3112
3198
|
|
|
@@ -3209,7 +3295,7 @@ function attachScrollDepth(client, config) {
|
|
|
3209
3295
|
}
|
|
3210
3296
|
const thresholds = new Set(config.thresholds);
|
|
3211
3297
|
let firedForPath = /* @__PURE__ */ new Set();
|
|
3212
|
-
let
|
|
3298
|
+
let currentPath3 = window.location.pathname;
|
|
3213
3299
|
let rafId = null;
|
|
3214
3300
|
const gate = createBaselineGate();
|
|
3215
3301
|
function checkThresholds() {
|
|
@@ -3225,7 +3311,7 @@ function attachScrollDepth(client, config) {
|
|
|
3225
3311
|
eventType: "scroll_depth",
|
|
3226
3312
|
metadata: {
|
|
3227
3313
|
depth_percent: threshold,
|
|
3228
|
-
page: { path:
|
|
3314
|
+
page: { path: currentPath3 }
|
|
3229
3315
|
},
|
|
3230
3316
|
pageUrl: window.location.href,
|
|
3231
3317
|
occurredAt: null
|
|
@@ -3242,8 +3328,8 @@ function attachScrollDepth(client, config) {
|
|
|
3242
3328
|
}
|
|
3243
3329
|
function resetIfPathChanged() {
|
|
3244
3330
|
const newPath = window.location.pathname;
|
|
3245
|
-
if (newPath ===
|
|
3246
|
-
|
|
3331
|
+
if (newPath === currentPath3) return;
|
|
3332
|
+
currentPath3 = newPath;
|
|
3247
3333
|
firedForPath = /* @__PURE__ */ new Set();
|
|
3248
3334
|
gate.rebaseline();
|
|
3249
3335
|
}
|
|
@@ -3305,14 +3391,14 @@ function attachMultiPageSession(client, config) {
|
|
|
3305
3391
|
function hasFired() {
|
|
3306
3392
|
return storage.getItem(FIRED_KEY) === "1";
|
|
3307
3393
|
}
|
|
3308
|
-
function
|
|
3309
|
-
const
|
|
3310
|
-
if (
|
|
3311
|
-
lastCheckedPath =
|
|
3394
|
+
function check2() {
|
|
3395
|
+
const currentPath3 = window.location.pathname;
|
|
3396
|
+
if (currentPath3 === lastCheckedPath) return;
|
|
3397
|
+
lastCheckedPath = currentPath3;
|
|
3312
3398
|
resetIfSessionChanged();
|
|
3313
3399
|
if (hasFired()) return;
|
|
3314
3400
|
const paths = getDistinctPaths();
|
|
3315
|
-
paths.add(
|
|
3401
|
+
paths.add(currentPath3);
|
|
3316
3402
|
saveDistinctPaths(paths);
|
|
3317
3403
|
if (paths.size >= pageThreshold) {
|
|
3318
3404
|
storage.setItem(FIRED_KEY, "1");
|
|
@@ -3331,20 +3417,20 @@ function attachMultiPageSession(client, config) {
|
|
|
3331
3417
|
const originalReplaceState = history.replaceState.bind(history);
|
|
3332
3418
|
function patchedPushState(...args) {
|
|
3333
3419
|
originalPushState(...args);
|
|
3334
|
-
setTimeout(
|
|
3420
|
+
setTimeout(check2, 0);
|
|
3335
3421
|
}
|
|
3336
3422
|
function patchedReplaceState(...args) {
|
|
3337
3423
|
originalReplaceState(...args);
|
|
3338
|
-
setTimeout(
|
|
3424
|
+
setTimeout(check2, 0);
|
|
3339
3425
|
}
|
|
3340
3426
|
history.pushState = patchedPushState;
|
|
3341
3427
|
history.replaceState = patchedReplaceState;
|
|
3342
|
-
window.addEventListener("popstate",
|
|
3343
|
-
|
|
3428
|
+
window.addEventListener("popstate", check2);
|
|
3429
|
+
check2();
|
|
3344
3430
|
return () => {
|
|
3345
3431
|
history.pushState = originalPushState;
|
|
3346
3432
|
history.replaceState = originalReplaceState;
|
|
3347
|
-
window.removeEventListener("popstate",
|
|
3433
|
+
window.removeEventListener("popstate", check2);
|
|
3348
3434
|
};
|
|
3349
3435
|
}
|
|
3350
3436
|
|
|
@@ -3357,7 +3443,7 @@ function attachFormStart(client, config) {
|
|
|
3357
3443
|
}
|
|
3358
3444
|
const selector = config.selector ?? "form";
|
|
3359
3445
|
let firedForms = /* @__PURE__ */ new Set();
|
|
3360
|
-
let
|
|
3446
|
+
let currentPath3 = window.location.pathname;
|
|
3361
3447
|
let pendingTap = null;
|
|
3362
3448
|
function getFormKey(form) {
|
|
3363
3449
|
if (form.id) return `id:${form.id}`;
|
|
@@ -3408,8 +3494,8 @@ function attachFormStart(client, config) {
|
|
|
3408
3494
|
}
|
|
3409
3495
|
function resetIfPathChanged() {
|
|
3410
3496
|
const newPath = window.location.pathname;
|
|
3411
|
-
if (newPath ===
|
|
3412
|
-
|
|
3497
|
+
if (newPath === currentPath3) return;
|
|
3498
|
+
currentPath3 = newPath;
|
|
3413
3499
|
firedForms = /* @__PURE__ */ new Set();
|
|
3414
3500
|
}
|
|
3415
3501
|
const originalPushState = history.pushState.bind(history);
|
|
@@ -3450,7 +3536,7 @@ function attachPageExit(client) {
|
|
|
3450
3536
|
return () => {
|
|
3451
3537
|
};
|
|
3452
3538
|
}
|
|
3453
|
-
let
|
|
3539
|
+
let currentPath3 = window.location.pathname;
|
|
3454
3540
|
let activeSince = document.visibilityState === "visible" ? Date.now() : null;
|
|
3455
3541
|
let accumulatedMs = 0;
|
|
3456
3542
|
let maxScrollPercent = null;
|
|
@@ -3500,9 +3586,9 @@ function attachPageExit(client) {
|
|
|
3500
3586
|
}
|
|
3501
3587
|
function onNavigate() {
|
|
3502
3588
|
const newPath = window.location.pathname;
|
|
3503
|
-
if (newPath ===
|
|
3504
|
-
emitSegment(
|
|
3505
|
-
|
|
3589
|
+
if (newPath === currentPath3) return;
|
|
3590
|
+
emitSegment(currentPath3, false);
|
|
3591
|
+
currentPath3 = newPath;
|
|
3506
3592
|
maxScrollPercent = null;
|
|
3507
3593
|
gate.rebaseline();
|
|
3508
3594
|
accumulatedMs = 0;
|
|
@@ -3510,13 +3596,13 @@ function attachPageExit(client) {
|
|
|
3510
3596
|
}
|
|
3511
3597
|
function onVisibilityChange() {
|
|
3512
3598
|
if (document.visibilityState === "hidden") {
|
|
3513
|
-
emitSegment(
|
|
3599
|
+
emitSegment(currentPath3, true);
|
|
3514
3600
|
} else {
|
|
3515
3601
|
activeSince = Date.now();
|
|
3516
3602
|
}
|
|
3517
3603
|
}
|
|
3518
3604
|
function onPageHide() {
|
|
3519
|
-
emitSegment(
|
|
3605
|
+
emitSegment(currentPath3, true);
|
|
3520
3606
|
}
|
|
3521
3607
|
const unsubscribeNav = onHistoryChange(onNavigate);
|
|
3522
3608
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
@@ -3695,6 +3781,104 @@ function attachPhoneClickCapture(client, config) {
|
|
|
3695
3781
|
};
|
|
3696
3782
|
}
|
|
3697
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
|
+
|
|
3698
3882
|
// ../tracking-core/src/resources/sales/bookings.ts
|
|
3699
3883
|
var newBookingAttemptKey = uuidV4;
|
|
3700
3884
|
|
|
@@ -4029,7 +4213,7 @@ function GoogleAdsTracking(props) {
|
|
|
4029
4213
|
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useMemo as useMemo4 } from "react";
|
|
4030
4214
|
|
|
4031
4215
|
// package.json
|
|
4032
|
-
var version = "1.
|
|
4216
|
+
var version = "1.14.0";
|
|
4033
4217
|
|
|
4034
4218
|
// ../tracking-core/src/phone-react.tsx
|
|
4035
4219
|
import {
|
|
@@ -4207,7 +4391,11 @@ function createTracking(options) {
|
|
|
4207
4391
|
[rawClient, conversionStore]
|
|
4208
4392
|
);
|
|
4209
4393
|
const client = useMemo4(
|
|
4210
|
-
() => createTypedClient(conversionClient, triggers, {
|
|
4394
|
+
() => createTypedClient(conversionClient, triggers, {
|
|
4395
|
+
debug,
|
|
4396
|
+
environment,
|
|
4397
|
+
trackingConfig
|
|
4398
|
+
}),
|
|
4211
4399
|
[conversionClient]
|
|
4212
4400
|
);
|
|
4213
4401
|
useEffect5(() => {
|
|
@@ -4406,12 +4594,152 @@ function useBookingSubmit(bookings, options = {}) {
|
|
|
4406
4594
|
const status = submitting ? "submitting" : outcome?.status ?? "idle";
|
|
4407
4595
|
return { status, outcome, booked, submit, updateBooking, reset };
|
|
4408
4596
|
}
|
|
4597
|
+
|
|
4598
|
+
// ../tracking-core/src/lead-forms/submit.ts
|
|
4599
|
+
import { safeParse as safeParse3 } from "zod/mini";
|
|
4600
|
+
var KNOWN_ROLES = new Set(LEAD_FORM_ROLES);
|
|
4601
|
+
var ROLE_ORDER = new Map(LEAD_FORM_ROLES.map((role, index) => [role, index]));
|
|
4602
|
+
var ADD_ON = /^add_on:[a-z0-9_-]{1,48}$/;
|
|
4603
|
+
var CONTRACT_NAME = /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/;
|
|
4604
|
+
var INPUT_TYPES = {
|
|
4605
|
+
string: "text",
|
|
4606
|
+
text: "textarea",
|
|
4607
|
+
enum: "select",
|
|
4608
|
+
multi_enum: "select",
|
|
4609
|
+
bool: "checkbox",
|
|
4610
|
+
date: "date",
|
|
4611
|
+
time_window: "time",
|
|
4612
|
+
phone: "tel",
|
|
4613
|
+
email: "email",
|
|
4614
|
+
address: "address",
|
|
4615
|
+
number: "number"
|
|
4616
|
+
};
|
|
4617
|
+
function createLeadFormSubmit(tracking, contracts, options = {}) {
|
|
4618
|
+
registerCapability("typed_form_submit");
|
|
4619
|
+
const settings = { ...typedClientSettings(tracking), ...options };
|
|
4620
|
+
return {
|
|
4621
|
+
submit(input) {
|
|
4622
|
+
const generated = contracts.contracts[input.kind];
|
|
4623
|
+
const live = liveContract(settings.trackingConfig, input.kind);
|
|
4624
|
+
const fields = presentFields(input.fields);
|
|
4625
|
+
const violations = check(live === void 0 ? generated : fromLive(live), fields);
|
|
4626
|
+
const liveHash = live === void 0 ? void 0 : live?.contract_hash ?? null;
|
|
4627
|
+
if (liveHash !== void 0 && (generated?.hash ?? null) !== liveHash || contracts.registryVersion !== LEAD_FORM_REGISTRY_VERSION) {
|
|
4628
|
+
violations.push({ role: null, rule: "contract_behind" });
|
|
4629
|
+
}
|
|
4630
|
+
const hash = liveHash === void 0 ? generated?.hash ?? null : liveHash;
|
|
4631
|
+
const metadata = {
|
|
4632
|
+
form: {
|
|
4633
|
+
id: input.formId,
|
|
4634
|
+
action: input.action ?? null,
|
|
4635
|
+
fields: fields.map(([role, value]) => ({
|
|
4636
|
+
name: role,
|
|
4637
|
+
type: INPUT_TYPES[primitiveOf(role) ?? "string"],
|
|
4638
|
+
label: labelOf(role),
|
|
4639
|
+
value,
|
|
4640
|
+
role
|
|
4641
|
+
})),
|
|
4642
|
+
kind: input.kind,
|
|
4643
|
+
contract_version: LEAD_FORM_REGISTRY_VERSION,
|
|
4644
|
+
...hash ? { contract_hash: hash } : {},
|
|
4645
|
+
...input.extra?.length ? { extra: input.extra } : {},
|
|
4646
|
+
...violations.length > 0 ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) } : {}
|
|
4647
|
+
},
|
|
4648
|
+
page: { path: typeof window === "undefined" ? "/" : window.location.pathname }
|
|
4649
|
+
};
|
|
4650
|
+
if (violations.length > 0 && (settings.debug || settings.environment === "development")) {
|
|
4651
|
+
const message = `[AranovaTracking] lead form "${input.formId}" (${input.kind}) breaks its contract: ${violations.map((v) => `${v.rule}${v.role ? ` (${v.role})` : ""}`).join(", ")}`;
|
|
4652
|
+
console.error(message);
|
|
4653
|
+
if (settings.debug) throw new Error(message);
|
|
4654
|
+
}
|
|
4655
|
+
tracking.trackEvent("form_submit", metadata);
|
|
4656
|
+
return metadata;
|
|
4657
|
+
}
|
|
4658
|
+
};
|
|
4659
|
+
}
|
|
4660
|
+
function liveContract(ref, kind) {
|
|
4661
|
+
const leadForms = ref ? getTrackingConfigRuntime(ref).leadForms() : null;
|
|
4662
|
+
if (!leadForms) return void 0;
|
|
4663
|
+
return leadForms.contracts.find((c) => c.form_kind === kind) ?? null;
|
|
4664
|
+
}
|
|
4665
|
+
function fromLive(live) {
|
|
4666
|
+
if (!live) return void 0;
|
|
4667
|
+
return {
|
|
4668
|
+
requires: live.requires.map((r) => ({
|
|
4669
|
+
role: r.role,
|
|
4670
|
+
requiredWhen: r.required_when ? { role: r.required_when.role, anyOf: r.required_when.any_of } : null
|
|
4671
|
+
})),
|
|
4672
|
+
requiresAny: live.requires_any.map((g) => g.roles),
|
|
4673
|
+
// Deprecated keys still resolve: a cached form may send them (ADR-0024 §7).
|
|
4674
|
+
options: Object.fromEntries(live.options.map((o) => [o.role, o.options.map((x) => x.key)]))
|
|
4675
|
+
};
|
|
4676
|
+
}
|
|
4677
|
+
function presentFields(fields) {
|
|
4678
|
+
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));
|
|
4679
|
+
}
|
|
4680
|
+
function normalize(role, value) {
|
|
4681
|
+
if (typeof value !== "string") return value;
|
|
4682
|
+
const trimmed = value.trim();
|
|
4683
|
+
const primitive = primitiveOf(role);
|
|
4684
|
+
if (primitive === "phone") return toE164(trimmed) ?? trimmed;
|
|
4685
|
+
if (primitive === "email") return trimmed.toLowerCase();
|
|
4686
|
+
return primitive === "text" ? value : trimmed;
|
|
4687
|
+
}
|
|
4688
|
+
function isBlank(value) {
|
|
4689
|
+
return value === void 0 || value === null || typeof value === "string" && value.trim() === "" || Array.isArray(value) && value.length === 0;
|
|
4690
|
+
}
|
|
4691
|
+
function check(contract, fields) {
|
|
4692
|
+
const answered = new Map(fields);
|
|
4693
|
+
const violations = [];
|
|
4694
|
+
for (const [role, value] of fields) {
|
|
4695
|
+
const primitive = primitiveOf(role);
|
|
4696
|
+
if (!primitive) {
|
|
4697
|
+
violations.push({ role: CONTRACT_NAME.test(role) ? role : null, rule: "unknown_role" });
|
|
4698
|
+
continue;
|
|
4699
|
+
}
|
|
4700
|
+
if (!safeParse3(leadFormPrimitiveSchemas[primitive], value).success) {
|
|
4701
|
+
violations.push({ role, rule: "invalid_value" });
|
|
4702
|
+
continue;
|
|
4703
|
+
}
|
|
4704
|
+
const allowed = contract?.options[role] ?? registryOptions(role);
|
|
4705
|
+
if (allowed?.length && optionKeys(value).some((key) => !allowed.includes(key))) {
|
|
4706
|
+
violations.push({ role, rule: "unknown_option" });
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
for (const { role, requiredWhen } of contract?.requires ?? []) {
|
|
4710
|
+
const owing = requiredWhen === null || optionKeys(answered.get(requiredWhen.role)).some((key) => requiredWhen.anyOf.includes(key));
|
|
4711
|
+
if (owing && !answered.has(role)) violations.push({ role, rule: "missing_required" });
|
|
4712
|
+
}
|
|
4713
|
+
for (const group of contract?.requiresAny ?? []) {
|
|
4714
|
+
if (!group.some((role) => answered.has(role))) {
|
|
4715
|
+
violations.push({ role: group.join("|"), rule: "missing_any_of" });
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
return violations;
|
|
4719
|
+
}
|
|
4720
|
+
function primitiveOf(role) {
|
|
4721
|
+
if (ADD_ON.test(role)) return "bool";
|
|
4722
|
+
return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role].primitive : null;
|
|
4723
|
+
}
|
|
4724
|
+
function labelOf(role) {
|
|
4725
|
+
if (ADD_ON.test(role)) return role.slice("add_on:".length).replaceAll(/[_-]+/g, " ");
|
|
4726
|
+
return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role].smsLabel || null : null;
|
|
4727
|
+
}
|
|
4728
|
+
function registryOptions(role) {
|
|
4729
|
+
const spec = KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role] : void 0;
|
|
4730
|
+
return spec && "options" in spec ? spec.options.map((o) => o.key) : void 0;
|
|
4731
|
+
}
|
|
4732
|
+
function optionKeys(value) {
|
|
4733
|
+
if (typeof value === "string") return [value];
|
|
4734
|
+
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
4735
|
+
}
|
|
4409
4736
|
export {
|
|
4410
4737
|
AdPlatformTracking,
|
|
4411
4738
|
BookingHandoffLink,
|
|
4412
4739
|
ConsentBanner,
|
|
4413
4740
|
GoogleAdsTracking,
|
|
4414
4741
|
PhoneField,
|
|
4742
|
+
createLeadFormSubmit,
|
|
4415
4743
|
createTracking,
|
|
4416
4744
|
useBookingHandoffUrl,
|
|
4417
4745
|
useBookingIntegration,
|