@aranova/tracking-next 0.5.1 → 0.6.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.
@@ -57,7 +57,7 @@ var pageViewMetadataSchema = import_zod.z.object({
57
57
  path: import_zod.z.string(),
58
58
  search: import_zod.z.string(),
59
59
  hash: import_zod.z.string()
60
- }),
60
+ }).strict(),
61
61
  referrer: import_zod.z.string().nullable(),
62
62
  // `.nullable().optional()` — absent (undefined) OR explicit null OR a
63
63
  // real viewport object. Mirrors Pydantic's `_Viewport | None = None`
@@ -65,7 +65,7 @@ var pageViewMetadataSchema = import_zod.z.object({
65
65
  viewport: import_zod.z.object({
66
66
  w: import_zod.z.number(),
67
67
  h: import_zod.z.number()
68
- }).nullable().optional()
68
+ }).strict().nullable().optional()
69
69
  }).strict();
70
70
  var pageViewConfigSchema = import_zod.z.object({}).strict();
71
71
 
@@ -75,7 +75,7 @@ var ctaClickMetadataSchema = import_zod2.z.object({
75
75
  cta_name: import_zod2.z.string(),
76
76
  page: import_zod2.z.object({
77
77
  path: import_zod2.z.string()
78
- }),
78
+ }).strict(),
79
79
  section: import_zod2.z.string().nullable().optional(),
80
80
  destination_url: import_zod2.z.string().nullable().optional()
81
81
  }).strict();
@@ -102,10 +102,10 @@ var formStartMetadataSchema = import_zod4.z.object({
102
102
  form: import_zod4.z.object({
103
103
  id: import_zod4.z.string(),
104
104
  action: import_zod4.z.string().nullable()
105
- }),
105
+ }).strict(),
106
106
  page: import_zod4.z.object({
107
107
  path: import_zod4.z.string()
108
- })
108
+ }).strict()
109
109
  }).strict();
110
110
  var formStartConfigSchema = import_zod4.z.object({
111
111
  selector: import_zod4.z.string().optional()
@@ -113,6 +113,16 @@ var formStartConfigSchema = import_zod4.z.object({
113
113
 
114
114
  // ../tracking-core/src/events/form-submit.ts
115
115
  var import_zod5 = require("zod");
116
+ var jsonValueSchema = import_zod5.z.lazy(
117
+ () => import_zod5.z.union([
118
+ import_zod5.z.string(),
119
+ import_zod5.z.number().finite(),
120
+ import_zod5.z.boolean(),
121
+ import_zod5.z.null(),
122
+ import_zod5.z.array(jsonValueSchema),
123
+ import_zod5.z.record(jsonValueSchema)
124
+ ])
125
+ );
116
126
  var formSubmitMetadataSchema = import_zod5.z.object({
117
127
  form: import_zod5.z.object({
118
128
  id: import_zod5.z.string(),
@@ -122,13 +132,13 @@ var formSubmitMetadataSchema = import_zod5.z.object({
122
132
  name: import_zod5.z.string(),
123
133
  type: import_zod5.z.string(),
124
134
  label: import_zod5.z.string().nullable(),
125
- has_value: import_zod5.z.boolean()
126
- })
135
+ value: jsonValueSchema
136
+ }).strict()
127
137
  ).optional()
128
- }),
138
+ }).strict(),
129
139
  page: import_zod5.z.object({
130
140
  path: import_zod5.z.string()
131
- })
141
+ }).strict()
132
142
  }).strict();
133
143
  var formSubmitConfigSchema = import_zod5.z.object({}).strict();
134
144
 
@@ -138,7 +148,7 @@ var multiPageSessionMetadataSchema = import_zod6.z.object({
138
148
  page_count: import_zod6.z.number().int().min(2),
139
149
  page: import_zod6.z.object({
140
150
  path: import_zod6.z.string()
141
- })
151
+ }).strict()
142
152
  }).strict();
143
153
  var multiPageSessionConfigSchema = import_zod6.z.object({
144
154
  pageThreshold: import_zod6.z.number().int().min(2)
@@ -150,7 +160,7 @@ var phoneClickMetadataSchema = import_zod7.z.object({
150
160
  phone_number: import_zod7.z.string(),
151
161
  page: import_zod7.z.object({
152
162
  path: import_zod7.z.string()
153
- }),
163
+ }).strict(),
154
164
  section: import_zod7.z.string().nullable().optional()
155
165
  }).strict();
156
166
  var phoneClickConfigSchema = import_zod7.z.object({}).strict();
@@ -161,7 +171,7 @@ var scrollDepthMetadataSchema = import_zod8.z.object({
161
171
  depth_percent: import_zod8.z.number().int().min(1).max(100),
162
172
  page: import_zod8.z.object({
163
173
  path: import_zod8.z.string()
164
- })
174
+ }).strict()
165
175
  }).strict();
166
176
  var scrollDepthConfigSchema = import_zod8.z.object({
167
177
  thresholds: import_zod8.z.array(import_zod8.z.number().int().min(1).max(100)).min(1)
@@ -184,7 +194,7 @@ var specificPageVisitMetadataSchema = import_zod9.z.object({
184
194
  page_name: specificPageNameSchema,
185
195
  page: import_zod9.z.object({
186
196
  path: import_zod9.z.string()
187
- })
197
+ }).strict()
188
198
  }).strict();
189
199
  var specificPageVisitConfigSchema = import_zod9.z.object({
190
200
  pages: import_zod9.z.array(
@@ -193,7 +203,7 @@ var specificPageVisitConfigSchema = import_zod9.z.object({
193
203
  pathPattern: import_zod9.z.custom((value) => value instanceof RegExp, {
194
204
  message: "pathPattern must be a RegExp"
195
205
  })
196
- })
206
+ }).strict()
197
207
  ).min(1)
198
208
  }).strict();
199
209
 
@@ -203,7 +213,7 @@ var timeOnSiteMetadataSchema = import_zod10.z.object({
203
213
  duration_ms: import_zod10.z.number().int().nonnegative(),
204
214
  page: import_zod10.z.object({
205
215
  path: import_zod10.z.string()
206
- })
216
+ }).strict()
207
217
  }).strict();
208
218
  var timeOnSiteConfigSchema = import_zod10.z.object({
209
219
  thresholdSeconds: import_zod10.z.number().int().positive()
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware.ts","../../tracking-core/src/tracking.ts","../../tracking-core/src/session.ts","../../tracking-core/src/events/page-view.ts","../../tracking-core/src/events/cta-click.ts","../../tracking-core/src/events/sdk-heartbeat.ts","../../tracking-core/src/events/form-start.ts","../../tracking-core/src/events/form-submit.ts","../../tracking-core/src/events/multi-page-session.ts","../../tracking-core/src/events/phone-click.ts","../../tracking-core/src/events/scroll-depth.ts","../../tracking-core/src/events/specific-page-visit.ts","../../tracking-core/src/events/time-on-site.ts","../../tracking-core/src/events/registry.ts"],"sourcesContent":["import 'server-only';\n\nimport { NextResponse } from 'next/server';\nimport type { NextMiddleware, NextRequest } from 'next/server';\n\nimport { getTrackingQueryValues } from '../../tracking-core/src/index';\n\nexport function createTrackingMiddleware(): NextMiddleware {\n return (request: NextRequest) => {\n const response = NextResponse.next();\n const trackingValues = getTrackingQueryValues(request.nextUrl.searchParams);\n\n Object.entries(trackingValues).forEach(([param, value]) => {\n response.cookies.set(param, value, {\n maxAge: 7776000,\n path: '/',\n httpOnly: false,\n });\n });\n\n return response;\n };\n}\n","import type { TrackingParams } from './types';\n\nexport const TRACKING_COOKIE_MAX_AGE_SECONDS = 7_776_000;\n\nexport const TRACKING_PARAM_KEYS = [\n 'gclid',\n 'fbclid',\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n] as const;\n\nexport type TrackingParamKey = (typeof TRACKING_PARAM_KEYS)[number];\n\nexport function createEmptyTrackingParams(): TrackingParams {\n return {\n gclid: null,\n fbclid: null,\n utm_source: null,\n utm_medium: null,\n utm_campaign: null,\n utm_term: null,\n utm_content: null,\n };\n}\n\nexport function normalizeTrackingCookieValue(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nexport function getTrackingParamsFromCookieReader(\n readCookie: (key: TrackingParamKey) => unknown,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((params, key) => {\n params[key] = normalizeTrackingCookieValue(readCookie(key));\n return params;\n }, createEmptyTrackingParams());\n}\n\nexport function getTrackingQueryValues(\n searchParams: Pick<URLSearchParams, 'get'>,\n): Partial<Record<TrackingParamKey, string>> {\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const value = searchParams.get(key);\n\n if (typeof value === 'string' && value.trim().length > 0) {\n params[key] = value;\n }\n\n return params;\n }, {});\n}\n\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n if (typeof document === 'undefined')\n return null;\n\n const cookies = document.cookie ? document.cookie.split('; ') : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${key}=`));\n\n if (!match)\n return null;\n\n const [, rawValue = ''] = match.split('=');\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n}\n\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n if (typeof document === 'undefined')\n return;\n\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${key}=${encodedValue}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n}\n\nexport function persistTrackingParamsFromSearchParams(\n searchParams: Pick<URLSearchParams, 'get'>,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): Partial<Record<TrackingParamKey, string>> {\n const trackingValues = getTrackingQueryValues(searchParams);\n\n Object.entries(trackingValues).forEach(([key, value]) => {\n setTrackingCookie(key as TrackingParamKey, value, maxAgeSeconds);\n });\n\n return trackingValues;\n}\n\nexport function captureTrackingParamsFromLocation(\n url = typeof window === 'undefined' ? '' : window.location.href,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): TrackingParams {\n const resolvedUrl = typeof window === 'undefined'\n ? new URL(url || 'https://example.invalid')\n : new URL(url, window.location.origin);\n persistTrackingParamsFromSearchParams(resolvedUrl.searchParams, maxAgeSeconds);\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n","// Visitor + session identity for the tracking SDK.\n//\n// Visitor: persistent localStorage UUID, never expires until the user clears\n// browser storage. Used for cross-session correlation.\n//\n// Session: rolling 30-minute idle window. Regenerated when more than\n// SESSION_IDLE_MS has passed since the last event. Matches the behavior of\n// GA4, PostHog, Mixpanel, etc., so analytics is comparable.\n\nexport const VISITOR_STORAGE_KEY = 'aranova_tracking_visitor';\nexport const SESSION_STORAGE_KEY = 'aranova_tracking_session';\nexport const SESSION_IDLE_MS = 30 * 60 * 1000;\n\nexport interface StoredSession {\n id: string;\n last_event_at: number;\n}\n\nfunction safeUuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')\n return crypto.randomUUID();\n // Fallback for ancient browsers — not cryptographically perfect but unique enough.\n return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction readLocalStorage(key: string): string | null {\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value);\n } catch {\n // Storage may be denied (private mode, blocked cookies, quota). Caller is\n // responsible for degrading gracefully.\n }\n}\n\nexport function getVisitorId(): string {\n if (typeof window === 'undefined')\n return safeUuid();\n\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0)\n return existing;\n\n const fresh = safeUuid();\n writeLocalStorage(VISITOR_STORAGE_KEY, fresh);\n return fresh;\n}\n\nexport interface SessionIdResult {\n id: string;\n isNew: boolean;\n}\n\nexport function getOrRotateSessionId(now: number = Date.now()): SessionIdResult {\n if (typeof window === 'undefined')\n return { id: safeUuid(), isNew: true };\n\n const raw = readLocalStorage(SESSION_STORAGE_KEY);\n if (raw) {\n try {\n const parsed = JSON.parse(raw) as Partial<StoredSession>;\n if (typeof parsed.id === 'string' && typeof parsed.last_event_at === 'number') {\n if (now - parsed.last_event_at <= SESSION_IDLE_MS) {\n const refreshed: StoredSession = { id: parsed.id, last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));\n return { id: parsed.id, isNew: false };\n }\n }\n } catch {\n // Fall through to a fresh session.\n }\n }\n\n const fresh: StoredSession = { id: safeUuid(), last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));\n return { id: fresh.id, isNew: true };\n}\n\n/** Test/debug helper — wipes both ids. */\nexport function resetTrackingIdentity(): void {\n if (typeof window === 'undefined')\n return;\n try {\n window.localStorage.removeItem(VISITOR_STORAGE_KEY);\n window.localStorage.removeItem(SESSION_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","import { z } from 'zod';\n\n// page_view is automatic and auto-emitted by attachAutoPageView() on initial\n// load, SPA route changes (pushState / replaceState / popstate), and bfcache\n// restore (pageshow with event.persisted === true).\n\nexport const pageViewMetadataSchema = z\n .object({\n page: z.object({\n title: z.string().nullable(),\n path: z.string(),\n search: z.string(),\n hash: z.string(),\n }),\n referrer: z.string().nullable(),\n // `.nullable().optional()` — absent (undefined) OR explicit null OR a\n // real viewport object. Mirrors Pydantic's `_Viewport | None = None`\n // on the backend side so the drift test stays clean.\n viewport: z\n .object({\n w: z.number(),\n h: z.number(),\n })\n .nullable()\n .optional(),\n })\n .strict();\n\nexport type PageViewMetadata = z.infer<typeof pageViewMetadataSchema>;\n\n// No configurable options today, but must be registered for the SDK to attach\n// the auto-page-view handler. Reserved for future options like `throttleMs`\n// or `includeHash`.\nexport const pageViewConfigSchema = z.object({}).strict();\nexport type PageViewConfig = z.infer<typeof pageViewConfigSchema>;\n","import { z } from 'zod';\n\n// cta_click is manual. Consumer code calls trackEvent('cta_click', ...) from\n// click handlers on CTAs like \"Get Directions\", \"Book Appointment\", etc.\n// The cta_name is a free-form string chosen by the consumer to identify the\n// action (e.g. \"get_directions\", \"book_appointment\", \"download_brochure\").\n\nexport const ctaClickMetadataSchema = z\n .object({\n cta_name: z.string(),\n page: z.object({\n path: z.string(),\n }),\n section: z.string().nullable().optional(),\n destination_url: z.string().nullable().optional(),\n })\n .strict();\n\nexport type CtaClickMetadata = z.infer<typeof ctaClickMetadataSchema>;\n\nexport const ctaClickConfigSchema = z.object({}).strict();\nexport type CtaClickConfig = z.infer<typeof ctaClickConfigSchema>;\n","import { z } from 'zod';\n\n// sdk_heartbeat is an SDK-internal automatic event fired exactly once at the\n// start of each new session. It carries the SDK version and the complete\n// trigger registry the consumer registered, so the backend knows which\n// events a client site is configured to capture without requiring that\n// information on every single event payload.\n\nexport const sdkHeartbeatTriggersSchema = z.object({\n automatic: z.array(z.string()),\n manual: z.array(z.string()),\n}).strict();\n\nexport const sdkHeartbeatMetadataSchema = z\n .object({\n sdk_version: z.string(),\n package_name: z.string().nullable(),\n surface: z.enum(['next', 'react', 'script']),\n triggers: sdkHeartbeatTriggersSchema,\n trigger_config: z.record(z.string(), z.record(z.string(), z.unknown())).nullable().optional(),\n })\n .strict();\n\nexport type SdkHeartbeatMetadata = z.infer<typeof sdkHeartbeatMetadataSchema>;\n\n// No consumer-facing config — heartbeat is SDK-internal.\nexport const sdkHeartbeatConfigSchema = z.object({}).strict();\nexport type SdkHeartbeatConfig = z.infer<typeof sdkHeartbeatConfigSchema>;\n","import { z } from 'zod';\n\n// form_start is automatic. The SDK uses focusin event delegation on document\n// to detect the first field interaction inside a <form>. Fires once per form\n// per page lifecycle. Resets on SPA navigation.\n\nexport const formStartMetadataSchema = z\n .object({\n form: z.object({\n id: z.string(),\n action: z.string().nullable(),\n }),\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type FormStartMetadata = z.infer<typeof formStartMetadataSchema>;\n\nexport const formStartConfigSchema = z\n .object({\n selector: z.string().optional(),\n })\n .strict();\n\nexport type FormStartConfig = z.infer<typeof formStartConfigSchema>;\n","import { z } from 'zod';\n\n// form_submit is manual — the SDK never auto-fires this. Consumer code calls\n// `tracking.trackEvent('form_submit', { form, page })` from their own submit\n// handler. Registering it enables the type-level permission; omitting it\n// turns manual calls into a compile error.\n//\n// The optional `fields` array captures structural metadata about the form\n// fields (name, type, label, whether the field was filled). It NEVER captures\n// actual field values — this is a hard PII boundary. Consumers explicitly\n// build the fields array themselves so they control exactly what is sent.\n\nexport const formSubmitMetadataSchema = z\n .object({\n form: z.object({\n id: z.string(),\n action: z.string().nullable(),\n fields: z\n .array(\n z.object({\n name: z.string(),\n type: z.string(),\n label: z.string().nullable(),\n has_value: z.boolean(),\n }),\n )\n .optional(),\n }),\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type FormSubmitMetadata = z.infer<typeof formSubmitMetadataSchema>;\n\nexport const formSubmitConfigSchema = z.object({}).strict();\nexport type FormSubmitConfig = z.infer<typeof formSubmitConfigSchema>;\n","import { z } from 'zod';\n\n// multi_page_session is automatic. The SDK tracks distinct page paths visited\n// in the current session via sessionStorage. When the count reaches the\n// configured threshold, fires once. Resets on session rotation.\n\nexport const multiPageSessionMetadataSchema = z\n .object({\n page_count: z.number().int().min(2),\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type MultiPageSessionMetadata = z.infer<typeof multiPageSessionMetadataSchema>;\n\nexport const multiPageSessionConfigSchema = z\n .object({\n pageThreshold: z.number().int().min(2),\n })\n .strict();\n\nexport type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;\n","import { z } from 'zod';\n\n// phone_click is manual. Consumer code calls trackEvent('phone_click', ...)\n// from a click handler on a tel: anchor or similar. The phone_number is the\n// business's number (extracted from the href), not the visitor's. The optional\n// section helps distinguish multiple phone links on the same page.\n\nexport const phoneClickMetadataSchema = z\n .object({\n phone_number: z.string(),\n page: z.object({\n path: z.string(),\n }),\n section: z.string().nullable().optional(),\n })\n .strict();\n\nexport type PhoneClickMetadata = z.infer<typeof phoneClickMetadataSchema>;\n\nexport const phoneClickConfigSchema = z.object({}).strict();\nexport type PhoneClickConfig = z.infer<typeof phoneClickConfigSchema>;\n","import { z } from 'zod';\n\n// scroll_depth is automatic. The SDK installs a requestAnimationFrame-throttled\n// scroll listener and fires once per configured threshold per page. Resets on\n// SPA navigation.\n\nexport const scrollDepthMetadataSchema = z\n .object({\n depth_percent: z.number().int().min(1).max(100),\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type ScrollDepthMetadata = z.infer<typeof scrollDepthMetadataSchema>;\n\nexport const scrollDepthConfigSchema = z\n .object({\n thresholds: z.array(z.number().int().min(1).max(100)).min(1),\n })\n .strict();\n\nexport type ScrollDepthConfig = z.infer<typeof scrollDepthConfigSchema>;\n","import { z } from 'zod';\n\n// specific_page_visit is automatic. The SDK observes every navigation and\n// emits a specific_page_visit event when the current pathname matches one of\n// the consumer-provided patterns. Each pattern has a strongly typed name\n// (contact_page, services_page, etc.) so the event stream is self-describing\n// and downstream analytics can group by page intent without re-applying regexes.\n\nexport const SPECIFIC_PAGE_NAMES = [\n 'contact_page',\n 'about_page',\n 'services_page',\n 'booking_page',\n 'location_page',\n 'pricing_page',\n 'faq_page',\n 'testimonials_page',\n] as const;\n\nexport type SpecificPageName = (typeof SPECIFIC_PAGE_NAMES)[number];\n\nexport const specificPageNameSchema = z.enum(SPECIFIC_PAGE_NAMES);\n\nexport const specificPageVisitMetadataSchema = z\n .object({\n page_name: specificPageNameSchema,\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type SpecificPageVisitMetadata = z.infer<typeof specificPageVisitMetadataSchema>;\n\nexport const specificPageVisitConfigSchema = z\n .object({\n pages: z\n .array(\n z.object({\n name: specificPageNameSchema,\n pathPattern: z.custom<RegExp>((value) => value instanceof RegExp, {\n message: 'pathPattern must be a RegExp',\n }),\n }),\n )\n .min(1),\n })\n .strict();\n\nexport type SpecificPageVisitConfig = z.infer<typeof specificPageVisitConfigSchema>;\n","import { z } from 'zod';\n\n// time_on_site is automatic. The SDK starts a visibility-aware timer on page\n// load and fires once when the visible-time threshold is crossed. The timer\n// pauses when the tab goes hidden so a user who walks away from a tab\n// doesn't generate fake engagement.\n\nexport const timeOnSiteMetadataSchema = z\n .object({\n duration_ms: z.number().int().nonnegative(),\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type TimeOnSiteMetadata = z.infer<typeof timeOnSiteMetadataSchema>;\n\nexport const timeOnSiteConfigSchema = z\n .object({\n thresholdSeconds: z.number().int().positive(),\n })\n .strict();\nexport type TimeOnSiteConfig = z.infer<typeof timeOnSiteConfigSchema>;\n","import type { z } from 'zod';\n\nimport {\n ctaClickConfigSchema,\n ctaClickMetadataSchema,\n type CtaClickConfig,\n type CtaClickMetadata,\n} from './cta-click';\nimport {\n sdkHeartbeatConfigSchema,\n sdkHeartbeatMetadataSchema,\n type SdkHeartbeatConfig,\n type SdkHeartbeatMetadata,\n} from './sdk-heartbeat';\nimport {\n formStartConfigSchema,\n formStartMetadataSchema,\n type FormStartConfig,\n type FormStartMetadata,\n} from './form-start';\nimport {\n formSubmitConfigSchema,\n formSubmitMetadataSchema,\n type FormSubmitConfig,\n type FormSubmitMetadata,\n} from './form-submit';\nimport {\n multiPageSessionConfigSchema,\n multiPageSessionMetadataSchema,\n type MultiPageSessionConfig,\n type MultiPageSessionMetadata,\n} from './multi-page-session';\nimport {\n pageViewConfigSchema,\n pageViewMetadataSchema,\n type PageViewConfig,\n type PageViewMetadata,\n} from './page-view';\nimport {\n phoneClickConfigSchema,\n phoneClickMetadataSchema,\n type PhoneClickConfig,\n type PhoneClickMetadata,\n} from './phone-click';\nimport {\n scrollDepthConfigSchema,\n scrollDepthMetadataSchema,\n type ScrollDepthConfig,\n type ScrollDepthMetadata,\n} from './scroll-depth';\nimport {\n specificPageVisitConfigSchema,\n specificPageVisitMetadataSchema,\n type SpecificPageVisitConfig,\n type SpecificPageVisitMetadata,\n} from './specific-page-visit';\nimport {\n timeOnSiteConfigSchema,\n timeOnSiteMetadataSchema,\n type TimeOnSiteConfig,\n type TimeOnSiteMetadata,\n} from './time-on-site';\n\n// Event kind — automatic events are fired by the SDK itself when their\n// client-side signal fires (page_view on navigation, time_on_site on timer,\n// etc). Manual events are only fireable via explicit consumer code.\nexport type EventKind = 'automatic' | 'manual';\n\n// The registry is the single client-side source of truth for \"what events\n// exist, what shape does their metadata take, and what kind are they\". The\n// backend mirror lives in apps/api/src/schemas/tracking_events.py and is\n// kept in sync via the drift test in apps/api/tests/test_event_schema_drift.py.\nexport const EVENT_REGISTRY = {\n // --- automatic triggers ---\n page_view: {\n kind: 'automatic',\n metadataSchema: pageViewMetadataSchema,\n configSchema: pageViewConfigSchema,\n },\n time_on_site: {\n kind: 'automatic',\n metadataSchema: timeOnSiteMetadataSchema,\n configSchema: timeOnSiteConfigSchema,\n },\n specific_page_visit: {\n kind: 'automatic',\n metadataSchema: specificPageVisitMetadataSchema,\n configSchema: specificPageVisitConfigSchema,\n },\n scroll_depth: {\n kind: 'automatic',\n metadataSchema: scrollDepthMetadataSchema,\n configSchema: scrollDepthConfigSchema,\n },\n multi_page_session: {\n kind: 'automatic',\n metadataSchema: multiPageSessionMetadataSchema,\n configSchema: multiPageSessionConfigSchema,\n },\n form_start: {\n kind: 'automatic',\n metadataSchema: formStartMetadataSchema,\n configSchema: formStartConfigSchema,\n },\n // --- SDK-internal automatic (not consumer-configurable) ---\n sdk_heartbeat: {\n kind: 'automatic',\n metadataSchema: sdkHeartbeatMetadataSchema,\n configSchema: sdkHeartbeatConfigSchema,\n },\n // --- manual triggers ---\n form_submit: {\n kind: 'manual',\n metadataSchema: formSubmitMetadataSchema,\n configSchema: formSubmitConfigSchema,\n },\n phone_click: {\n kind: 'manual',\n metadataSchema: phoneClickMetadataSchema,\n configSchema: phoneClickConfigSchema,\n },\n cta_click: {\n kind: 'manual',\n metadataSchema: ctaClickMetadataSchema,\n configSchema: ctaClickConfigSchema,\n },\n} as const;\n\nexport type EventName = keyof typeof EVENT_REGISTRY;\n\n// Narrow the event name union based on `kind`.\nexport type AutomaticEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'automatic' ? K : never;\n}[EventName];\n\nexport type ManualEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'manual' ? K : never;\n}[EventName];\n\n// Map an event name to its explicit metadata / config TS type. We pull the\n// inferred types directly from the per-event files rather than deriving\n// via `z.infer<typeof EVENT_REGISTRY[K]['metadataSchema']>` so that error\n// messages name the event-specific type (PageViewMetadata, not an\n// anonymous zod inference).\ntype MetadataByName = {\n page_view: PageViewMetadata;\n time_on_site: TimeOnSiteMetadata;\n specific_page_visit: SpecificPageVisitMetadata;\n scroll_depth: ScrollDepthMetadata;\n multi_page_session: MultiPageSessionMetadata;\n form_start: FormStartMetadata;\n sdk_heartbeat: SdkHeartbeatMetadata;\n form_submit: FormSubmitMetadata;\n phone_click: PhoneClickMetadata;\n cta_click: CtaClickMetadata;\n};\n\ntype ConfigByName = {\n page_view: PageViewConfig;\n time_on_site: TimeOnSiteConfig;\n specific_page_visit: SpecificPageVisitConfig;\n scroll_depth: ScrollDepthConfig;\n multi_page_session: MultiPageSessionConfig;\n form_start: FormStartConfig;\n sdk_heartbeat: SdkHeartbeatConfig;\n form_submit: FormSubmitConfig;\n phone_click: PhoneClickConfig;\n cta_click: CtaClickConfig;\n};\n\nexport type EventMetadata<K extends EventName> = MetadataByName[K];\nexport type EventConfig<K extends EventName> = ConfigByName[K];\n\n// Runtime constant arrays for iteration at consumer / factory time.\nexport const ALL_AUTOMATIC_EVENT_NAMES: readonly AutomaticEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'automatic')\n .map(([name]) => name) as AutomaticEventName[];\n\nexport const ALL_MANUAL_EVENT_NAMES: readonly ManualEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'manual')\n .map(([name]) => name) as ManualEventName[];\n\n// The shape passed to createTracking({ triggers: ... }).\n//\n// `automatic.page_view` is **required** — if you're installing tracking you\n// always want page views, and forgetting to register it is a footgun we\n// prefer to catch at compile time. Every other automatic event is optional.\n// Manual events are all optional — register only the ones you actually\n// intend to fire from consumer code.\nexport type TriggerRegistryConfig = {\n automatic: {\n page_view: EventConfig<'page_view'>;\n } & Partial<{\n time_on_site: EventConfig<'time_on_site'>;\n specific_page_visit: EventConfig<'specific_page_visit'>;\n scroll_depth: EventConfig<'scroll_depth'>;\n multi_page_session: EventConfig<'multi_page_session'>;\n form_start: EventConfig<'form_start'>;\n }>;\n manual?: Partial<{\n form_submit: EventConfig<'form_submit'>;\n phone_click: EventConfig<'phone_click'>;\n cta_click: EventConfig<'cta_click'>;\n }>;\n};\n\n// Given a registry, which manual event names did the consumer actually\n// register? These are the only ones `trackEvent()` will accept as its\n// first argument. `NonNullable` collapses the case where `manual` was\n// omitted entirely.\nexport type RegisteredManualEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof NonNullable<TRegistry['manual']>, ManualEventName>;\n\n// Given a registry, which automatic event names did the consumer register?\n// Used by the factory to decide which auto-trigger attach helpers to wire up.\nexport type RegisteredAutomaticEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof TRegistry['automatic'], AutomaticEventName>;\n\n// Internal helper used by the factory: given a registry, return the full\n// discriminated union of valid (eventName, metadata) pairs the consumer\n// can pass to trackEvent().\nexport type TrackableEvent<TRegistry extends TriggerRegistryConfig> = {\n [K in RegisteredManualEvents<TRegistry>]: {\n eventType: K;\n metadata: EventMetadata<K>;\n };\n}[RegisteredManualEvents<TRegistry>];\n\n// Runtime helper: look up a schema pair by name. Cast through `unknown`\n// because the registry is `as const` and TS loses the specific schema type\n// when indexing via a dynamic key.\nexport function getEventDefinition(name: EventName): {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n} {\n return EVENT_REGISTRY[name] as unknown as {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n };\n}\n"],"mappings":"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","names":["import_zod","import_zod","import_zod","import_zod","import_zod","import_zod","import_zod","import_zod","import_zod"]}
1
+ {"version":3,"sources":["../src/middleware.ts","../../tracking-core/src/tracking.ts","../../tracking-core/src/session.ts","../../tracking-core/src/events/page-view.ts","../../tracking-core/src/events/cta-click.ts","../../tracking-core/src/events/sdk-heartbeat.ts","../../tracking-core/src/events/form-start.ts","../../tracking-core/src/events/form-submit.ts","../../tracking-core/src/events/multi-page-session.ts","../../tracking-core/src/events/phone-click.ts","../../tracking-core/src/events/scroll-depth.ts","../../tracking-core/src/events/specific-page-visit.ts","../../tracking-core/src/events/time-on-site.ts","../../tracking-core/src/events/registry.ts"],"sourcesContent":["import 'server-only';\n\nimport { NextResponse } from 'next/server';\nimport type { NextMiddleware, NextRequest } from 'next/server';\n\nimport { getTrackingQueryValues } from '../../tracking-core/src/index';\n\nexport function createTrackingMiddleware(): NextMiddleware {\n return (request: NextRequest) => {\n const response = NextResponse.next();\n const trackingValues = getTrackingQueryValues(request.nextUrl.searchParams);\n\n Object.entries(trackingValues).forEach(([param, value]) => {\n response.cookies.set(param, value, {\n maxAge: 7776000,\n path: '/',\n httpOnly: false,\n });\n });\n\n return response;\n };\n}\n","import type { TrackingParams } from './types';\n\nexport const TRACKING_COOKIE_MAX_AGE_SECONDS = 7_776_000;\n\nexport const TRACKING_PARAM_KEYS = [\n 'gclid',\n 'fbclid',\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n] as const;\n\nexport type TrackingParamKey = (typeof TRACKING_PARAM_KEYS)[number];\n\nexport function createEmptyTrackingParams(): TrackingParams {\n return {\n gclid: null,\n fbclid: null,\n utm_source: null,\n utm_medium: null,\n utm_campaign: null,\n utm_term: null,\n utm_content: null,\n };\n}\n\nexport function normalizeTrackingCookieValue(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nexport function getTrackingParamsFromCookieReader(\n readCookie: (key: TrackingParamKey) => unknown,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((params, key) => {\n params[key] = normalizeTrackingCookieValue(readCookie(key));\n return params;\n }, createEmptyTrackingParams());\n}\n\nexport function getTrackingQueryValues(\n searchParams: Pick<URLSearchParams, 'get'>,\n): Partial<Record<TrackingParamKey, string>> {\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const value = searchParams.get(key);\n\n if (typeof value === 'string' && value.trim().length > 0) {\n params[key] = value;\n }\n\n return params;\n }, {});\n}\n\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n if (typeof document === 'undefined')\n return null;\n\n const cookies = document.cookie ? document.cookie.split('; ') : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${key}=`));\n\n if (!match)\n return null;\n\n const [, rawValue = ''] = match.split('=');\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n}\n\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n if (typeof document === 'undefined')\n return;\n\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${key}=${encodedValue}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n}\n\nexport function persistTrackingParamsFromSearchParams(\n searchParams: Pick<URLSearchParams, 'get'>,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): Partial<Record<TrackingParamKey, string>> {\n const trackingValues = getTrackingQueryValues(searchParams);\n\n Object.entries(trackingValues).forEach(([key, value]) => {\n setTrackingCookie(key as TrackingParamKey, value, maxAgeSeconds);\n });\n\n return trackingValues;\n}\n\nexport function captureTrackingParamsFromLocation(\n url = typeof window === 'undefined' ? '' : window.location.href,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): TrackingParams {\n const resolvedUrl = typeof window === 'undefined'\n ? new URL(url || 'https://example.invalid')\n : new URL(url, window.location.origin);\n persistTrackingParamsFromSearchParams(resolvedUrl.searchParams, maxAgeSeconds);\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n","// Visitor + session identity for the tracking SDK.\n//\n// Visitor: persistent localStorage UUID, never expires until the user clears\n// browser storage. Used for cross-session correlation.\n//\n// Session: rolling 30-minute idle window. Regenerated when more than\n// SESSION_IDLE_MS has passed since the last event. Matches the behavior of\n// GA4, PostHog, Mixpanel, etc., so analytics is comparable.\n\nexport const VISITOR_STORAGE_KEY = 'aranova_tracking_visitor';\nexport const SESSION_STORAGE_KEY = 'aranova_tracking_session';\nexport const SESSION_IDLE_MS = 30 * 60 * 1000;\n\nexport interface StoredSession {\n id: string;\n last_event_at: number;\n}\n\nfunction safeUuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')\n return crypto.randomUUID();\n // Fallback for ancient browsers — not cryptographically perfect but unique enough.\n return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction readLocalStorage(key: string): string | null {\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value);\n } catch {\n // Storage may be denied (private mode, blocked cookies, quota). Caller is\n // responsible for degrading gracefully.\n }\n}\n\nexport function getVisitorId(): string {\n if (typeof window === 'undefined')\n return safeUuid();\n\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0)\n return existing;\n\n const fresh = safeUuid();\n writeLocalStorage(VISITOR_STORAGE_KEY, fresh);\n return fresh;\n}\n\nexport interface SessionIdResult {\n id: string;\n isNew: boolean;\n}\n\nexport function getOrRotateSessionId(now: number = Date.now()): SessionIdResult {\n if (typeof window === 'undefined')\n return { id: safeUuid(), isNew: true };\n\n const raw = readLocalStorage(SESSION_STORAGE_KEY);\n if (raw) {\n try {\n const parsed = JSON.parse(raw) as Partial<StoredSession>;\n if (typeof parsed.id === 'string' && typeof parsed.last_event_at === 'number') {\n if (now - parsed.last_event_at <= SESSION_IDLE_MS) {\n const refreshed: StoredSession = { id: parsed.id, last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));\n return { id: parsed.id, isNew: false };\n }\n }\n } catch {\n // Fall through to a fresh session.\n }\n }\n\n const fresh: StoredSession = { id: safeUuid(), last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));\n return { id: fresh.id, isNew: true };\n}\n\n/** Test/debug helper — wipes both ids. */\nexport function resetTrackingIdentity(): void {\n if (typeof window === 'undefined')\n return;\n try {\n window.localStorage.removeItem(VISITOR_STORAGE_KEY);\n window.localStorage.removeItem(SESSION_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","import { z } from 'zod';\n\n// page_view is automatic and auto-emitted by attachAutoPageView() on initial\n// load, SPA route changes (pushState / replaceState / popstate), and bfcache\n// restore (pageshow with event.persisted === true).\n\nexport const pageViewMetadataSchema = z\n .object({\n page: z\n .object({\n title: z.string().nullable(),\n path: z.string(),\n search: z.string(),\n hash: z.string(),\n })\n .strict(),\n referrer: z.string().nullable(),\n // `.nullable().optional()` — absent (undefined) OR explicit null OR a\n // real viewport object. Mirrors Pydantic's `_Viewport | None = None`\n // on the backend side so the drift test stays clean.\n viewport: z\n .object({\n w: z.number(),\n h: z.number(),\n })\n .strict()\n .nullable()\n .optional(),\n })\n .strict();\n\nexport type PageViewMetadata = z.infer<typeof pageViewMetadataSchema>;\n\n// No configurable options today, but must be registered for the SDK to attach\n// the auto-page-view handler. Reserved for future options like `throttleMs`\n// or `includeHash`.\nexport const pageViewConfigSchema = z.object({}).strict();\nexport type PageViewConfig = z.infer<typeof pageViewConfigSchema>;\n","import { z } from 'zod';\n\n// cta_click is manual. Consumer code calls trackEvent('cta_click', ...) from\n// click handlers on CTAs like \"Get Directions\", \"Book Appointment\", etc.\n// The cta_name is a free-form string chosen by the consumer to identify the\n// action (e.g. \"get_directions\", \"book_appointment\", \"download_brochure\").\n\nexport const ctaClickMetadataSchema = z\n .object({\n cta_name: z.string(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n section: z.string().nullable().optional(),\n destination_url: z.string().nullable().optional(),\n })\n .strict();\n\nexport type CtaClickMetadata = z.infer<typeof ctaClickMetadataSchema>;\n\nexport const ctaClickConfigSchema = z.object({}).strict();\nexport type CtaClickConfig = z.infer<typeof ctaClickConfigSchema>;\n","import { z } from 'zod';\n\n// sdk_heartbeat is an SDK-internal automatic event fired exactly once at the\n// start of each new session. It carries the SDK version and the complete\n// trigger registry the consumer registered, so the backend knows which\n// events a client site is configured to capture without requiring that\n// information on every single event payload.\n\nexport const sdkHeartbeatTriggersSchema = z.object({\n automatic: z.array(z.string()),\n manual: z.array(z.string()),\n}).strict();\n\nexport const sdkHeartbeatMetadataSchema = z\n .object({\n sdk_version: z.string(),\n package_name: z.string().nullable(),\n surface: z.enum(['next', 'react', 'script']),\n triggers: sdkHeartbeatTriggersSchema,\n trigger_config: z.record(z.string(), z.record(z.string(), z.unknown())).nullable().optional(),\n })\n .strict();\n\nexport type SdkHeartbeatMetadata = z.infer<typeof sdkHeartbeatMetadataSchema>;\n\n// No consumer-facing config — heartbeat is SDK-internal.\nexport const sdkHeartbeatConfigSchema = z.object({}).strict();\nexport type SdkHeartbeatConfig = z.infer<typeof sdkHeartbeatConfigSchema>;\n","import { z } from 'zod';\n\n// form_start is automatic. The SDK uses focusin event delegation on document\n// to detect the first field interaction inside a <form>. Fires once per form\n// per page lifecycle. Resets on SPA navigation.\n\nexport const formStartMetadataSchema = z\n .object({\n form: z\n .object({\n id: z.string(),\n action: z.string().nullable(),\n })\n .strict(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type FormStartMetadata = z.infer<typeof formStartMetadataSchema>;\n\nexport const formStartConfigSchema = z\n .object({\n selector: z.string().optional(),\n })\n .strict();\n\nexport type FormStartConfig = z.infer<typeof formStartConfigSchema>;\n","import { z } from 'zod';\n\n// form_submit is manual — the SDK never auto-fires this. Consumer code calls\n// `tracking.trackEvent('form_submit', { form, page })` from their own submit\n// handler. Registering it enables the type-level permission; omitting it\n// turns manual calls into a compile error.\n//\n// The optional `fields` array captures submitted form field metadata and JSON\n// values. Consumers explicitly build the fields array themselves so they\n// control exactly what is sent.\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport const jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>\n z.union([\n z.string(),\n z.number().finite(),\n z.boolean(),\n z.null(),\n z.array(jsonValueSchema),\n z.record(jsonValueSchema),\n ]),\n);\n\nexport const formSubmitMetadataSchema = z\n .object({\n form: z\n .object({\n id: z.string(),\n action: z.string().nullable(),\n fields: z\n .array(\n z\n .object({\n name: z.string(),\n type: z.string(),\n label: z.string().nullable(),\n value: jsonValueSchema,\n })\n .strict(),\n )\n .optional(),\n })\n .strict(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type FormSubmitMetadata = z.infer<typeof formSubmitMetadataSchema>;\n\nexport const formSubmitConfigSchema = z.object({}).strict();\nexport type FormSubmitConfig = z.infer<typeof formSubmitConfigSchema>;\n","import { z } from 'zod';\n\n// multi_page_session is automatic. The SDK tracks distinct page paths visited\n// in the current session via sessionStorage. When the count reaches the\n// configured threshold, fires once. Resets on session rotation.\n\nexport const multiPageSessionMetadataSchema = z\n .object({\n page_count: z.number().int().min(2),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type MultiPageSessionMetadata = z.infer<typeof multiPageSessionMetadataSchema>;\n\nexport const multiPageSessionConfigSchema = z\n .object({\n pageThreshold: z.number().int().min(2),\n })\n .strict();\n\nexport type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;\n","import { z } from 'zod';\n\n// phone_click is manual. Consumer code calls trackEvent('phone_click', ...)\n// from a click handler on a tel: anchor or similar. The phone_number is the\n// business's number (extracted from the href), not the visitor's. The optional\n// section helps distinguish multiple phone links on the same page.\n\nexport const phoneClickMetadataSchema = z\n .object({\n phone_number: z.string(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n section: z.string().nullable().optional(),\n })\n .strict();\n\nexport type PhoneClickMetadata = z.infer<typeof phoneClickMetadataSchema>;\n\nexport const phoneClickConfigSchema = z.object({}).strict();\nexport type PhoneClickConfig = z.infer<typeof phoneClickConfigSchema>;\n","import { z } from 'zod';\n\n// scroll_depth is automatic. The SDK installs a requestAnimationFrame-throttled\n// scroll listener and fires once per configured threshold per page. Resets on\n// SPA navigation.\n\nexport const scrollDepthMetadataSchema = z\n .object({\n depth_percent: z.number().int().min(1).max(100),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type ScrollDepthMetadata = z.infer<typeof scrollDepthMetadataSchema>;\n\nexport const scrollDepthConfigSchema = z\n .object({\n thresholds: z.array(z.number().int().min(1).max(100)).min(1),\n })\n .strict();\n\nexport type ScrollDepthConfig = z.infer<typeof scrollDepthConfigSchema>;\n","import { z } from 'zod';\n\n// specific_page_visit is automatic. The SDK observes every navigation and\n// emits a specific_page_visit event when the current pathname matches one of\n// the consumer-provided patterns. Each pattern has a strongly typed name\n// (contact_page, services_page, etc.) so the event stream is self-describing\n// and downstream analytics can group by page intent without re-applying regexes.\n\nexport const SPECIFIC_PAGE_NAMES = [\n 'contact_page',\n 'about_page',\n 'services_page',\n 'booking_page',\n 'location_page',\n 'pricing_page',\n 'faq_page',\n 'testimonials_page',\n] as const;\n\nexport type SpecificPageName = (typeof SPECIFIC_PAGE_NAMES)[number];\n\nexport const specificPageNameSchema = z.enum(SPECIFIC_PAGE_NAMES);\n\nexport const specificPageVisitMetadataSchema = z\n .object({\n page_name: specificPageNameSchema,\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type SpecificPageVisitMetadata = z.infer<typeof specificPageVisitMetadataSchema>;\n\nexport const specificPageVisitConfigSchema = z\n .object({\n pages: z\n .array(\n z\n .object({\n name: specificPageNameSchema,\n pathPattern: z.custom<RegExp>((value) => value instanceof RegExp, {\n message: 'pathPattern must be a RegExp',\n }),\n })\n .strict(),\n )\n .min(1),\n })\n .strict();\n\nexport type SpecificPageVisitConfig = z.infer<typeof specificPageVisitConfigSchema>;\n","import { z } from 'zod';\n\n// time_on_site is automatic. The SDK starts a visibility-aware timer on page\n// load and fires once when the visible-time threshold is crossed. The timer\n// pauses when the tab goes hidden so a user who walks away from a tab\n// doesn't generate fake engagement.\n\nexport const timeOnSiteMetadataSchema = z\n .object({\n duration_ms: z.number().int().nonnegative(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type TimeOnSiteMetadata = z.infer<typeof timeOnSiteMetadataSchema>;\n\nexport const timeOnSiteConfigSchema = z\n .object({\n thresholdSeconds: z.number().int().positive(),\n })\n .strict();\nexport type TimeOnSiteConfig = z.infer<typeof timeOnSiteConfigSchema>;\n","import type { z } from 'zod';\n\nimport {\n ctaClickConfigSchema,\n ctaClickMetadataSchema,\n type CtaClickConfig,\n type CtaClickMetadata,\n} from './cta-click';\nimport {\n sdkHeartbeatConfigSchema,\n sdkHeartbeatMetadataSchema,\n type SdkHeartbeatConfig,\n type SdkHeartbeatMetadata,\n} from './sdk-heartbeat';\nimport {\n formStartConfigSchema,\n formStartMetadataSchema,\n type FormStartConfig,\n type FormStartMetadata,\n} from './form-start';\nimport {\n formSubmitConfigSchema,\n formSubmitMetadataSchema,\n type FormSubmitConfig,\n type FormSubmitMetadata,\n} from './form-submit';\nimport {\n multiPageSessionConfigSchema,\n multiPageSessionMetadataSchema,\n type MultiPageSessionConfig,\n type MultiPageSessionMetadata,\n} from './multi-page-session';\nimport {\n pageViewConfigSchema,\n pageViewMetadataSchema,\n type PageViewConfig,\n type PageViewMetadata,\n} from './page-view';\nimport {\n phoneClickConfigSchema,\n phoneClickMetadataSchema,\n type PhoneClickConfig,\n type PhoneClickMetadata,\n} from './phone-click';\nimport {\n scrollDepthConfigSchema,\n scrollDepthMetadataSchema,\n type ScrollDepthConfig,\n type ScrollDepthMetadata,\n} from './scroll-depth';\nimport {\n specificPageVisitConfigSchema,\n specificPageVisitMetadataSchema,\n type SpecificPageVisitConfig,\n type SpecificPageVisitMetadata,\n} from './specific-page-visit';\nimport {\n timeOnSiteConfigSchema,\n timeOnSiteMetadataSchema,\n type TimeOnSiteConfig,\n type TimeOnSiteMetadata,\n} from './time-on-site';\n\n// Event kind — automatic events are fired by the SDK itself when their\n// client-side signal fires (page_view on navigation, time_on_site on timer,\n// etc). Manual events are only fireable via explicit consumer code.\nexport type EventKind = 'automatic' | 'manual';\n\n// The registry is the single client-side source of truth for \"what events\n// exist, what shape does their metadata take, and what kind are they\". The\n// backend mirror lives in apps/api/src/schemas/tracking_events.py and is\n// kept in sync via the drift test in apps/api/tests/test_event_schema_drift.py.\nexport const EVENT_REGISTRY = {\n // --- automatic triggers ---\n page_view: {\n kind: 'automatic',\n metadataSchema: pageViewMetadataSchema,\n configSchema: pageViewConfigSchema,\n },\n time_on_site: {\n kind: 'automatic',\n metadataSchema: timeOnSiteMetadataSchema,\n configSchema: timeOnSiteConfigSchema,\n },\n specific_page_visit: {\n kind: 'automatic',\n metadataSchema: specificPageVisitMetadataSchema,\n configSchema: specificPageVisitConfigSchema,\n },\n scroll_depth: {\n kind: 'automatic',\n metadataSchema: scrollDepthMetadataSchema,\n configSchema: scrollDepthConfigSchema,\n },\n multi_page_session: {\n kind: 'automatic',\n metadataSchema: multiPageSessionMetadataSchema,\n configSchema: multiPageSessionConfigSchema,\n },\n form_start: {\n kind: 'automatic',\n metadataSchema: formStartMetadataSchema,\n configSchema: formStartConfigSchema,\n },\n // --- SDK-internal automatic (not consumer-configurable) ---\n sdk_heartbeat: {\n kind: 'automatic',\n metadataSchema: sdkHeartbeatMetadataSchema,\n configSchema: sdkHeartbeatConfigSchema,\n },\n // --- manual triggers ---\n form_submit: {\n kind: 'manual',\n metadataSchema: formSubmitMetadataSchema,\n configSchema: formSubmitConfigSchema,\n },\n phone_click: {\n kind: 'manual',\n metadataSchema: phoneClickMetadataSchema,\n configSchema: phoneClickConfigSchema,\n },\n cta_click: {\n kind: 'manual',\n metadataSchema: ctaClickMetadataSchema,\n configSchema: ctaClickConfigSchema,\n },\n} as const;\n\nexport type EventName = keyof typeof EVENT_REGISTRY;\n\n// Narrow the event name union based on `kind`.\nexport type AutomaticEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'automatic' ? K : never;\n}[EventName];\n\nexport type ManualEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'manual' ? K : never;\n}[EventName];\n\n// Map an event name to its explicit metadata / config TS type. We pull the\n// inferred types directly from the per-event files rather than deriving\n// via `z.infer<typeof EVENT_REGISTRY[K]['metadataSchema']>` so that error\n// messages name the event-specific type (PageViewMetadata, not an\n// anonymous zod inference).\ntype MetadataByName = {\n page_view: PageViewMetadata;\n time_on_site: TimeOnSiteMetadata;\n specific_page_visit: SpecificPageVisitMetadata;\n scroll_depth: ScrollDepthMetadata;\n multi_page_session: MultiPageSessionMetadata;\n form_start: FormStartMetadata;\n sdk_heartbeat: SdkHeartbeatMetadata;\n form_submit: FormSubmitMetadata;\n phone_click: PhoneClickMetadata;\n cta_click: CtaClickMetadata;\n};\n\ntype ConfigByName = {\n page_view: PageViewConfig;\n time_on_site: TimeOnSiteConfig;\n specific_page_visit: SpecificPageVisitConfig;\n scroll_depth: ScrollDepthConfig;\n multi_page_session: MultiPageSessionConfig;\n form_start: FormStartConfig;\n sdk_heartbeat: SdkHeartbeatConfig;\n form_submit: FormSubmitConfig;\n phone_click: PhoneClickConfig;\n cta_click: CtaClickConfig;\n};\n\nexport type EventMetadata<K extends EventName> = MetadataByName[K];\nexport type EventConfig<K extends EventName> = ConfigByName[K];\n\n// Runtime constant arrays for iteration at consumer / factory time.\nexport const ALL_AUTOMATIC_EVENT_NAMES: readonly AutomaticEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'automatic')\n .map(([name]) => name) as AutomaticEventName[];\n\nexport const ALL_MANUAL_EVENT_NAMES: readonly ManualEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'manual')\n .map(([name]) => name) as ManualEventName[];\n\n// The shape passed to createTracking({ triggers: ... }).\n//\n// `automatic.page_view` is **required** — if you're installing tracking you\n// always want page views, and forgetting to register it is a footgun we\n// prefer to catch at compile time. Every other automatic event is optional.\n// Manual events are all optional — register only the ones you actually\n// intend to fire from consumer code.\nexport type TriggerRegistryConfig = {\n automatic: {\n page_view: EventConfig<'page_view'>;\n } & Partial<{\n time_on_site: EventConfig<'time_on_site'>;\n specific_page_visit: EventConfig<'specific_page_visit'>;\n scroll_depth: EventConfig<'scroll_depth'>;\n multi_page_session: EventConfig<'multi_page_session'>;\n form_start: EventConfig<'form_start'>;\n }>;\n manual?: Partial<{\n form_submit: EventConfig<'form_submit'>;\n phone_click: EventConfig<'phone_click'>;\n cta_click: EventConfig<'cta_click'>;\n }>;\n};\n\n// Given a registry, which manual event names did the consumer actually\n// register? These are the only ones `trackEvent()` will accept as its\n// first argument. `NonNullable` collapses the case where `manual` was\n// omitted entirely.\nexport type RegisteredManualEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof NonNullable<TRegistry['manual']>, ManualEventName>;\n\n// Given a registry, which automatic event names did the consumer register?\n// Used by the factory to decide which auto-trigger attach helpers to wire up.\nexport type RegisteredAutomaticEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof TRegistry['automatic'], AutomaticEventName>;\n\n// Internal helper used by the factory: given a registry, return the full\n// discriminated union of valid (eventName, metadata) pairs the consumer\n// can pass to trackEvent().\nexport type TrackableEvent<TRegistry extends TriggerRegistryConfig> = {\n [K in RegisteredManualEvents<TRegistry>]: {\n eventType: K;\n metadata: EventMetadata<K>;\n };\n}[RegisteredManualEvents<TRegistry>];\n\n// Runtime helper: look up a schema pair by name. Cast through `unknown`\n// because the registry is `as const` and TS loses the specific schema type\n// when indexing via a dynamic key.\nexport function getEventDefinition(name: EventName): {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n} {\n return EVENT_REGISTRY[name] as unknown as {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAO;AAEP,oBAA6B;;;ACEtB,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA6BO,SAAS,uBACd,cAC2C;AAC3C,SAAO,oBAAoB,OAAkD,CAAC,QAAQ,QAAQ;AAC5F,UAAM,QAAQ,aAAa,IAAI,GAAG;AAElC,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,aAAO,GAAG,IAAI;AAAA,IAChB;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AACP;;;AC1CO,IAAM,kBAAkB,KAAK,KAAK;;;ACXzC,iBAAkB;AAMX,IAAM,yBAAyB,aACnC,OAAO;AAAA,EACN,MAAM,aACH,OAAO;AAAA,IACN,OAAO,aAAE,OAAO,EAAE,SAAS;AAAA,IAC3B,MAAM,aAAE,OAAO;AAAA,IACf,QAAQ,aAAE,OAAO;AAAA,IACjB,MAAM,aAAE,OAAO;AAAA,EACjB,CAAC,EACA,OAAO;AAAA,EACV,UAAU,aAAE,OAAO,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA,EAI9B,UAAU,aACP,OAAO;AAAA,IACN,GAAG,aAAE,OAAO;AAAA,IACZ,GAAG,aAAE,OAAO;AAAA,EACd,CAAC,EACA,OAAO,EACP,SAAS,EACT,SAAS;AACd,CAAC,EACA,OAAO;AAOH,IAAM,uBAAuB,aAAE,OAAO,CAAC,CAAC,EAAE,OAAO;;;ACpCxD,IAAAA,cAAkB;AAOX,IAAM,yBAAyB,cACnC,OAAO;AAAA,EACN,UAAU,cAAE,OAAO;AAAA,EACnB,MAAM,cACH,OAAO;AAAA,IACN,MAAM,cAAE,OAAO;AAAA,EACjB,CAAC,EACA,OAAO;AAAA,EACV,SAAS,cAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACxC,iBAAiB,cAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAClD,CAAC,EACA,OAAO;AAIH,IAAM,uBAAuB,cAAE,OAAO,CAAC,CAAC,EAAE,OAAO;;;ACtBxD,IAAAC,cAAkB;AAQX,IAAM,6BAA6B,cAAE,OAAO;AAAA,EACjD,WAAW,cAAE,MAAM,cAAE,OAAO,CAAC;AAAA,EAC7B,QAAQ,cAAE,MAAM,cAAE,OAAO,CAAC;AAC5B,CAAC,EAAE,OAAO;AAEH,IAAM,6BAA6B,cACvC,OAAO;AAAA,EACN,aAAa,cAAE,OAAO;AAAA,EACtB,cAAc,cAAE,OAAO,EAAE,SAAS;AAAA,EAClC,SAAS,cAAE,KAAK,CAAC,QAAQ,SAAS,QAAQ,CAAC;AAAA,EAC3C,UAAU;AAAA,EACV,gBAAgB,cAAE,OAAO,cAAE,OAAO,GAAG,cAAE,OAAO,cAAE,OAAO,GAAG,cAAE,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS;AAC9F,CAAC,EACA,OAAO;AAKH,IAAM,2BAA2B,cAAE,OAAO,CAAC,CAAC,EAAE,OAAO;;;AC1B5D,IAAAC,cAAkB;AAMX,IAAM,0BAA0B,cACpC,OAAO;AAAA,EACN,MAAM,cACH,OAAO;AAAA,IACN,IAAI,cAAE,OAAO;AAAA,IACb,QAAQ,cAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,CAAC,EACA,OAAO;AAAA,EACV,MAAM,cACH,OAAO;AAAA,IACN,MAAM,cAAE,OAAO;AAAA,EACjB,CAAC,EACA,OAAO;AACZ,CAAC,EACA,OAAO;AAIH,IAAM,wBAAwB,cAClC,OAAO;AAAA,EACN,UAAU,cAAE,OAAO,EAAE,SAAS;AAChC,CAAC,EACA,OAAO;;;AC5BV,IAAAC,cAAkB;AAmBX,IAAM,kBAAwC,cAAE;AAAA,EAAK,MAC1D,cAAE,MAAM;AAAA,IACN,cAAE,OAAO;AAAA,IACT,cAAE,OAAO,EAAE,OAAO;AAAA,IAClB,cAAE,QAAQ;AAAA,IACV,cAAE,KAAK;AAAA,IACP,cAAE,MAAM,eAAe;AAAA,IACvB,cAAE,OAAO,eAAe;AAAA,EAC1B,CAAC;AACH;AAEO,IAAM,2BAA2B,cACrC,OAAO;AAAA,EACN,MAAM,cACH,OAAO;AAAA,IACN,IAAI,cAAE,OAAO;AAAA,IACb,QAAQ,cAAE,OAAO,EAAE,SAAS;AAAA,IAC5B,QAAQ,cACL;AAAA,MACC,cACG,OAAO;AAAA,QACN,MAAM,cAAE,OAAO;AAAA,QACf,MAAM,cAAE,OAAO;AAAA,QACf,OAAO,cAAE,OAAO,EAAE,SAAS;AAAA,QAC3B,OAAO;AAAA,MACT,CAAC,EACA,OAAO;AAAA,IACZ,EACC,SAAS;AAAA,EACd,CAAC,EACA,OAAO;AAAA,EACV,MAAM,cACH,OAAO;AAAA,IACN,MAAM,cAAE,OAAO;AAAA,EACjB,CAAC,EACA,OAAO;AACZ,CAAC,EACA,OAAO;AAIH,IAAM,yBAAyB,cAAE,OAAO,CAAC,CAAC,EAAE,OAAO;;;AC5D1D,IAAAC,cAAkB;AAMX,IAAM,iCAAiC,cAC3C,OAAO;AAAA,EACN,YAAY,cAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AAAA,EAClC,MAAM,cACH,OAAO;AAAA,IACN,MAAM,cAAE,OAAO;AAAA,EACjB,CAAC,EACA,OAAO;AACZ,CAAC,EACA,OAAO;AAIH,IAAM,+BAA+B,cACzC,OAAO;AAAA,EACN,eAAe,cAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AACvC,CAAC,EACA,OAAO;;;ACvBV,IAAAC,cAAkB;AAOX,IAAM,2BAA2B,cACrC,OAAO;AAAA,EACN,cAAc,cAAE,OAAO;AAAA,EACvB,MAAM,cACH,OAAO;AAAA,IACN,MAAM,cAAE,OAAO;AAAA,EACjB,CAAC,EACA,OAAO;AAAA,EACV,SAAS,cAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC1C,CAAC,EACA,OAAO;AAIH,IAAM,yBAAyB,cAAE,OAAO,CAAC,CAAC,EAAE,OAAO;;;ACrB1D,IAAAC,cAAkB;AAMX,IAAM,4BAA4B,cACtC,OAAO;AAAA,EACN,eAAe,cAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC9C,MAAM,cACH,OAAO;AAAA,IACN,MAAM,cAAE,OAAO;AAAA,EACjB,CAAC,EACA,OAAO;AACZ,CAAC,EACA,OAAO;AAIH,IAAM,0BAA0B,cACpC,OAAO;AAAA,EACN,YAAY,cAAE,MAAM,cAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,CAAC;AAC7D,CAAC,EACA,OAAO;;;ACvBV,IAAAC,cAAkB;AAQX,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,IAAM,yBAAyB,cAAE,KAAK,mBAAmB;AAEzD,IAAM,kCAAkC,cAC5C,OAAO;AAAA,EACN,WAAW;AAAA,EACX,MAAM,cACH,OAAO;AAAA,IACN,MAAM,cAAE,OAAO;AAAA,EACjB,CAAC,EACA,OAAO;AACZ,CAAC,EACA,OAAO;AAIH,IAAM,gCAAgC,cAC1C,OAAO;AAAA,EACN,OAAO,cACJ;AAAA,IACC,cACG,OAAO;AAAA,MACN,MAAM;AAAA,MACN,aAAa,cAAE,OAAe,CAAC,UAAU,iBAAiB,QAAQ;AAAA,QAChE,SAAS;AAAA,MACX,CAAC;AAAA,IACH,CAAC,EACA,OAAO;AAAA,EACZ,EACC,IAAI,CAAC;AACV,CAAC,EACA,OAAO;;;ACnDV,IAAAC,eAAkB;AAOX,IAAM,2BAA2B,eACrC,OAAO;AAAA,EACN,aAAa,eAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EAC1C,MAAM,eACH,OAAO;AAAA,IACN,MAAM,eAAE,OAAO;AAAA,EACjB,CAAC,EACA,OAAO;AACZ,CAAC,EACA,OAAO;AAIH,IAAM,yBAAyB,eACnC,OAAO;AAAA,EACN,kBAAkB,eAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAC9C,CAAC,EACA,OAAO;;;ACgDH,IAAM,iBAAiB;AAAA;AAAA,EAE5B,WAAW;AAAA,IACT,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA,EACA,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA,EACA,qBAAqB;AAAA,IACnB,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA,EACA,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA,EACA,oBAAoB;AAAA,IAClB,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA,EACA,YAAY;AAAA,IACV,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA;AAAA,EAEA,eAAe;AAAA,IACb,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA;AAAA,EAEA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA,EACA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AACF;AAgDO,IAAM,4BACX,OAAO,QAAQ,cAAc,EAE5B,OAAO,CAAC,CAAC,EAAE,GAAG,MAAM,IAAI,SAAS,WAAW,EAC5C,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI;AAEhB,IAAM,yBACX,OAAO,QAAQ,cAAc,EAE5B,OAAO,CAAC,CAAC,EAAE,GAAG,MAAM,IAAI,SAAS,QAAQ,EACzC,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI;;;AbjLhB,SAAS,2BAA2C;AACzD,SAAO,CAAC,YAAyB;AAC/B,UAAM,WAAW,2BAAa,KAAK;AACnC,UAAM,iBAAiB,uBAAuB,QAAQ,QAAQ,YAAY;AAE1E,WAAO,QAAQ,cAAc,EAAE,QAAQ,CAAC,CAAC,OAAO,KAAK,MAAM;AACzD,eAAS,QAAQ,IAAI,OAAO,OAAO;AAAA,QACjC,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,UAAU;AAAA,MACZ,CAAC;AAAA,IACH,CAAC;AAED,WAAO;AAAA,EACT;AACF;","names":["import_zod","import_zod","import_zod","import_zod","import_zod","import_zod","import_zod","import_zod","import_zod"]}
@@ -33,7 +33,7 @@ var pageViewMetadataSchema = z.object({
33
33
  path: z.string(),
34
34
  search: z.string(),
35
35
  hash: z.string()
36
- }),
36
+ }).strict(),
37
37
  referrer: z.string().nullable(),
38
38
  // `.nullable().optional()` — absent (undefined) OR explicit null OR a
39
39
  // real viewport object. Mirrors Pydantic's `_Viewport | None = None`
@@ -41,7 +41,7 @@ var pageViewMetadataSchema = z.object({
41
41
  viewport: z.object({
42
42
  w: z.number(),
43
43
  h: z.number()
44
- }).nullable().optional()
44
+ }).strict().nullable().optional()
45
45
  }).strict();
46
46
  var pageViewConfigSchema = z.object({}).strict();
47
47
 
@@ -51,7 +51,7 @@ var ctaClickMetadataSchema = z2.object({
51
51
  cta_name: z2.string(),
52
52
  page: z2.object({
53
53
  path: z2.string()
54
- }),
54
+ }).strict(),
55
55
  section: z2.string().nullable().optional(),
56
56
  destination_url: z2.string().nullable().optional()
57
57
  }).strict();
@@ -78,10 +78,10 @@ var formStartMetadataSchema = z4.object({
78
78
  form: z4.object({
79
79
  id: z4.string(),
80
80
  action: z4.string().nullable()
81
- }),
81
+ }).strict(),
82
82
  page: z4.object({
83
83
  path: z4.string()
84
- })
84
+ }).strict()
85
85
  }).strict();
86
86
  var formStartConfigSchema = z4.object({
87
87
  selector: z4.string().optional()
@@ -89,6 +89,16 @@ var formStartConfigSchema = z4.object({
89
89
 
90
90
  // ../tracking-core/src/events/form-submit.ts
91
91
  import { z as z5 } from "zod";
92
+ var jsonValueSchema = z5.lazy(
93
+ () => z5.union([
94
+ z5.string(),
95
+ z5.number().finite(),
96
+ z5.boolean(),
97
+ z5.null(),
98
+ z5.array(jsonValueSchema),
99
+ z5.record(jsonValueSchema)
100
+ ])
101
+ );
92
102
  var formSubmitMetadataSchema = z5.object({
93
103
  form: z5.object({
94
104
  id: z5.string(),
@@ -98,13 +108,13 @@ var formSubmitMetadataSchema = z5.object({
98
108
  name: z5.string(),
99
109
  type: z5.string(),
100
110
  label: z5.string().nullable(),
101
- has_value: z5.boolean()
102
- })
111
+ value: jsonValueSchema
112
+ }).strict()
103
113
  ).optional()
104
- }),
114
+ }).strict(),
105
115
  page: z5.object({
106
116
  path: z5.string()
107
- })
117
+ }).strict()
108
118
  }).strict();
109
119
  var formSubmitConfigSchema = z5.object({}).strict();
110
120
 
@@ -114,7 +124,7 @@ var multiPageSessionMetadataSchema = z6.object({
114
124
  page_count: z6.number().int().min(2),
115
125
  page: z6.object({
116
126
  path: z6.string()
117
- })
127
+ }).strict()
118
128
  }).strict();
119
129
  var multiPageSessionConfigSchema = z6.object({
120
130
  pageThreshold: z6.number().int().min(2)
@@ -126,7 +136,7 @@ var phoneClickMetadataSchema = z7.object({
126
136
  phone_number: z7.string(),
127
137
  page: z7.object({
128
138
  path: z7.string()
129
- }),
139
+ }).strict(),
130
140
  section: z7.string().nullable().optional()
131
141
  }).strict();
132
142
  var phoneClickConfigSchema = z7.object({}).strict();
@@ -137,7 +147,7 @@ var scrollDepthMetadataSchema = z8.object({
137
147
  depth_percent: z8.number().int().min(1).max(100),
138
148
  page: z8.object({
139
149
  path: z8.string()
140
- })
150
+ }).strict()
141
151
  }).strict();
142
152
  var scrollDepthConfigSchema = z8.object({
143
153
  thresholds: z8.array(z8.number().int().min(1).max(100)).min(1)
@@ -160,7 +170,7 @@ var specificPageVisitMetadataSchema = z9.object({
160
170
  page_name: specificPageNameSchema,
161
171
  page: z9.object({
162
172
  path: z9.string()
163
- })
173
+ }).strict()
164
174
  }).strict();
165
175
  var specificPageVisitConfigSchema = z9.object({
166
176
  pages: z9.array(
@@ -169,7 +179,7 @@ var specificPageVisitConfigSchema = z9.object({
169
179
  pathPattern: z9.custom((value) => value instanceof RegExp, {
170
180
  message: "pathPattern must be a RegExp"
171
181
  })
172
- })
182
+ }).strict()
173
183
  ).min(1)
174
184
  }).strict();
175
185
 
@@ -179,7 +189,7 @@ var timeOnSiteMetadataSchema = z10.object({
179
189
  duration_ms: z10.number().int().nonnegative(),
180
190
  page: z10.object({
181
191
  path: z10.string()
182
- })
192
+ }).strict()
183
193
  }).strict();
184
194
  var timeOnSiteConfigSchema = z10.object({
185
195
  thresholdSeconds: z10.number().int().positive()
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware.ts","../../tracking-core/src/tracking.ts","../../tracking-core/src/session.ts","../../tracking-core/src/events/page-view.ts","../../tracking-core/src/events/cta-click.ts","../../tracking-core/src/events/sdk-heartbeat.ts","../../tracking-core/src/events/form-start.ts","../../tracking-core/src/events/form-submit.ts","../../tracking-core/src/events/multi-page-session.ts","../../tracking-core/src/events/phone-click.ts","../../tracking-core/src/events/scroll-depth.ts","../../tracking-core/src/events/specific-page-visit.ts","../../tracking-core/src/events/time-on-site.ts","../../tracking-core/src/events/registry.ts"],"sourcesContent":["import 'server-only';\n\nimport { NextResponse } from 'next/server';\nimport type { NextMiddleware, NextRequest } from 'next/server';\n\nimport { getTrackingQueryValues } from '../../tracking-core/src/index';\n\nexport function createTrackingMiddleware(): NextMiddleware {\n return (request: NextRequest) => {\n const response = NextResponse.next();\n const trackingValues = getTrackingQueryValues(request.nextUrl.searchParams);\n\n Object.entries(trackingValues).forEach(([param, value]) => {\n response.cookies.set(param, value, {\n maxAge: 7776000,\n path: '/',\n httpOnly: false,\n });\n });\n\n return response;\n };\n}\n","import type { TrackingParams } from './types';\n\nexport const TRACKING_COOKIE_MAX_AGE_SECONDS = 7_776_000;\n\nexport const TRACKING_PARAM_KEYS = [\n 'gclid',\n 'fbclid',\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n] as const;\n\nexport type TrackingParamKey = (typeof TRACKING_PARAM_KEYS)[number];\n\nexport function createEmptyTrackingParams(): TrackingParams {\n return {\n gclid: null,\n fbclid: null,\n utm_source: null,\n utm_medium: null,\n utm_campaign: null,\n utm_term: null,\n utm_content: null,\n };\n}\n\nexport function normalizeTrackingCookieValue(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nexport function getTrackingParamsFromCookieReader(\n readCookie: (key: TrackingParamKey) => unknown,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((params, key) => {\n params[key] = normalizeTrackingCookieValue(readCookie(key));\n return params;\n }, createEmptyTrackingParams());\n}\n\nexport function getTrackingQueryValues(\n searchParams: Pick<URLSearchParams, 'get'>,\n): Partial<Record<TrackingParamKey, string>> {\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const value = searchParams.get(key);\n\n if (typeof value === 'string' && value.trim().length > 0) {\n params[key] = value;\n }\n\n return params;\n }, {});\n}\n\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n if (typeof document === 'undefined')\n return null;\n\n const cookies = document.cookie ? document.cookie.split('; ') : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${key}=`));\n\n if (!match)\n return null;\n\n const [, rawValue = ''] = match.split('=');\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n}\n\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n if (typeof document === 'undefined')\n return;\n\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${key}=${encodedValue}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n}\n\nexport function persistTrackingParamsFromSearchParams(\n searchParams: Pick<URLSearchParams, 'get'>,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): Partial<Record<TrackingParamKey, string>> {\n const trackingValues = getTrackingQueryValues(searchParams);\n\n Object.entries(trackingValues).forEach(([key, value]) => {\n setTrackingCookie(key as TrackingParamKey, value, maxAgeSeconds);\n });\n\n return trackingValues;\n}\n\nexport function captureTrackingParamsFromLocation(\n url = typeof window === 'undefined' ? '' : window.location.href,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): TrackingParams {\n const resolvedUrl = typeof window === 'undefined'\n ? new URL(url || 'https://example.invalid')\n : new URL(url, window.location.origin);\n persistTrackingParamsFromSearchParams(resolvedUrl.searchParams, maxAgeSeconds);\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n","// Visitor + session identity for the tracking SDK.\n//\n// Visitor: persistent localStorage UUID, never expires until the user clears\n// browser storage. Used for cross-session correlation.\n//\n// Session: rolling 30-minute idle window. Regenerated when more than\n// SESSION_IDLE_MS has passed since the last event. Matches the behavior of\n// GA4, PostHog, Mixpanel, etc., so analytics is comparable.\n\nexport const VISITOR_STORAGE_KEY = 'aranova_tracking_visitor';\nexport const SESSION_STORAGE_KEY = 'aranova_tracking_session';\nexport const SESSION_IDLE_MS = 30 * 60 * 1000;\n\nexport interface StoredSession {\n id: string;\n last_event_at: number;\n}\n\nfunction safeUuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')\n return crypto.randomUUID();\n // Fallback for ancient browsers — not cryptographically perfect but unique enough.\n return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction readLocalStorage(key: string): string | null {\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value);\n } catch {\n // Storage may be denied (private mode, blocked cookies, quota). Caller is\n // responsible for degrading gracefully.\n }\n}\n\nexport function getVisitorId(): string {\n if (typeof window === 'undefined')\n return safeUuid();\n\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0)\n return existing;\n\n const fresh = safeUuid();\n writeLocalStorage(VISITOR_STORAGE_KEY, fresh);\n return fresh;\n}\n\nexport interface SessionIdResult {\n id: string;\n isNew: boolean;\n}\n\nexport function getOrRotateSessionId(now: number = Date.now()): SessionIdResult {\n if (typeof window === 'undefined')\n return { id: safeUuid(), isNew: true };\n\n const raw = readLocalStorage(SESSION_STORAGE_KEY);\n if (raw) {\n try {\n const parsed = JSON.parse(raw) as Partial<StoredSession>;\n if (typeof parsed.id === 'string' && typeof parsed.last_event_at === 'number') {\n if (now - parsed.last_event_at <= SESSION_IDLE_MS) {\n const refreshed: StoredSession = { id: parsed.id, last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));\n return { id: parsed.id, isNew: false };\n }\n }\n } catch {\n // Fall through to a fresh session.\n }\n }\n\n const fresh: StoredSession = { id: safeUuid(), last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));\n return { id: fresh.id, isNew: true };\n}\n\n/** Test/debug helper — wipes both ids. */\nexport function resetTrackingIdentity(): void {\n if (typeof window === 'undefined')\n return;\n try {\n window.localStorage.removeItem(VISITOR_STORAGE_KEY);\n window.localStorage.removeItem(SESSION_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","import { z } from 'zod';\n\n// page_view is automatic and auto-emitted by attachAutoPageView() on initial\n// load, SPA route changes (pushState / replaceState / popstate), and bfcache\n// restore (pageshow with event.persisted === true).\n\nexport const pageViewMetadataSchema = z\n .object({\n page: z.object({\n title: z.string().nullable(),\n path: z.string(),\n search: z.string(),\n hash: z.string(),\n }),\n referrer: z.string().nullable(),\n // `.nullable().optional()` — absent (undefined) OR explicit null OR a\n // real viewport object. Mirrors Pydantic's `_Viewport | None = None`\n // on the backend side so the drift test stays clean.\n viewport: z\n .object({\n w: z.number(),\n h: z.number(),\n })\n .nullable()\n .optional(),\n })\n .strict();\n\nexport type PageViewMetadata = z.infer<typeof pageViewMetadataSchema>;\n\n// No configurable options today, but must be registered for the SDK to attach\n// the auto-page-view handler. Reserved for future options like `throttleMs`\n// or `includeHash`.\nexport const pageViewConfigSchema = z.object({}).strict();\nexport type PageViewConfig = z.infer<typeof pageViewConfigSchema>;\n","import { z } from 'zod';\n\n// cta_click is manual. Consumer code calls trackEvent('cta_click', ...) from\n// click handlers on CTAs like \"Get Directions\", \"Book Appointment\", etc.\n// The cta_name is a free-form string chosen by the consumer to identify the\n// action (e.g. \"get_directions\", \"book_appointment\", \"download_brochure\").\n\nexport const ctaClickMetadataSchema = z\n .object({\n cta_name: z.string(),\n page: z.object({\n path: z.string(),\n }),\n section: z.string().nullable().optional(),\n destination_url: z.string().nullable().optional(),\n })\n .strict();\n\nexport type CtaClickMetadata = z.infer<typeof ctaClickMetadataSchema>;\n\nexport const ctaClickConfigSchema = z.object({}).strict();\nexport type CtaClickConfig = z.infer<typeof ctaClickConfigSchema>;\n","import { z } from 'zod';\n\n// sdk_heartbeat is an SDK-internal automatic event fired exactly once at the\n// start of each new session. It carries the SDK version and the complete\n// trigger registry the consumer registered, so the backend knows which\n// events a client site is configured to capture without requiring that\n// information on every single event payload.\n\nexport const sdkHeartbeatTriggersSchema = z.object({\n automatic: z.array(z.string()),\n manual: z.array(z.string()),\n}).strict();\n\nexport const sdkHeartbeatMetadataSchema = z\n .object({\n sdk_version: z.string(),\n package_name: z.string().nullable(),\n surface: z.enum(['next', 'react', 'script']),\n triggers: sdkHeartbeatTriggersSchema,\n trigger_config: z.record(z.string(), z.record(z.string(), z.unknown())).nullable().optional(),\n })\n .strict();\n\nexport type SdkHeartbeatMetadata = z.infer<typeof sdkHeartbeatMetadataSchema>;\n\n// No consumer-facing config — heartbeat is SDK-internal.\nexport const sdkHeartbeatConfigSchema = z.object({}).strict();\nexport type SdkHeartbeatConfig = z.infer<typeof sdkHeartbeatConfigSchema>;\n","import { z } from 'zod';\n\n// form_start is automatic. The SDK uses focusin event delegation on document\n// to detect the first field interaction inside a <form>. Fires once per form\n// per page lifecycle. Resets on SPA navigation.\n\nexport const formStartMetadataSchema = z\n .object({\n form: z.object({\n id: z.string(),\n action: z.string().nullable(),\n }),\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type FormStartMetadata = z.infer<typeof formStartMetadataSchema>;\n\nexport const formStartConfigSchema = z\n .object({\n selector: z.string().optional(),\n })\n .strict();\n\nexport type FormStartConfig = z.infer<typeof formStartConfigSchema>;\n","import { z } from 'zod';\n\n// form_submit is manual — the SDK never auto-fires this. Consumer code calls\n// `tracking.trackEvent('form_submit', { form, page })` from their own submit\n// handler. Registering it enables the type-level permission; omitting it\n// turns manual calls into a compile error.\n//\n// The optional `fields` array captures structural metadata about the form\n// fields (name, type, label, whether the field was filled). It NEVER captures\n// actual field values — this is a hard PII boundary. Consumers explicitly\n// build the fields array themselves so they control exactly what is sent.\n\nexport const formSubmitMetadataSchema = z\n .object({\n form: z.object({\n id: z.string(),\n action: z.string().nullable(),\n fields: z\n .array(\n z.object({\n name: z.string(),\n type: z.string(),\n label: z.string().nullable(),\n has_value: z.boolean(),\n }),\n )\n .optional(),\n }),\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type FormSubmitMetadata = z.infer<typeof formSubmitMetadataSchema>;\n\nexport const formSubmitConfigSchema = z.object({}).strict();\nexport type FormSubmitConfig = z.infer<typeof formSubmitConfigSchema>;\n","import { z } from 'zod';\n\n// multi_page_session is automatic. The SDK tracks distinct page paths visited\n// in the current session via sessionStorage. When the count reaches the\n// configured threshold, fires once. Resets on session rotation.\n\nexport const multiPageSessionMetadataSchema = z\n .object({\n page_count: z.number().int().min(2),\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type MultiPageSessionMetadata = z.infer<typeof multiPageSessionMetadataSchema>;\n\nexport const multiPageSessionConfigSchema = z\n .object({\n pageThreshold: z.number().int().min(2),\n })\n .strict();\n\nexport type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;\n","import { z } from 'zod';\n\n// phone_click is manual. Consumer code calls trackEvent('phone_click', ...)\n// from a click handler on a tel: anchor or similar. The phone_number is the\n// business's number (extracted from the href), not the visitor's. The optional\n// section helps distinguish multiple phone links on the same page.\n\nexport const phoneClickMetadataSchema = z\n .object({\n phone_number: z.string(),\n page: z.object({\n path: z.string(),\n }),\n section: z.string().nullable().optional(),\n })\n .strict();\n\nexport type PhoneClickMetadata = z.infer<typeof phoneClickMetadataSchema>;\n\nexport const phoneClickConfigSchema = z.object({}).strict();\nexport type PhoneClickConfig = z.infer<typeof phoneClickConfigSchema>;\n","import { z } from 'zod';\n\n// scroll_depth is automatic. The SDK installs a requestAnimationFrame-throttled\n// scroll listener and fires once per configured threshold per page. Resets on\n// SPA navigation.\n\nexport const scrollDepthMetadataSchema = z\n .object({\n depth_percent: z.number().int().min(1).max(100),\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type ScrollDepthMetadata = z.infer<typeof scrollDepthMetadataSchema>;\n\nexport const scrollDepthConfigSchema = z\n .object({\n thresholds: z.array(z.number().int().min(1).max(100)).min(1),\n })\n .strict();\n\nexport type ScrollDepthConfig = z.infer<typeof scrollDepthConfigSchema>;\n","import { z } from 'zod';\n\n// specific_page_visit is automatic. The SDK observes every navigation and\n// emits a specific_page_visit event when the current pathname matches one of\n// the consumer-provided patterns. Each pattern has a strongly typed name\n// (contact_page, services_page, etc.) so the event stream is self-describing\n// and downstream analytics can group by page intent without re-applying regexes.\n\nexport const SPECIFIC_PAGE_NAMES = [\n 'contact_page',\n 'about_page',\n 'services_page',\n 'booking_page',\n 'location_page',\n 'pricing_page',\n 'faq_page',\n 'testimonials_page',\n] as const;\n\nexport type SpecificPageName = (typeof SPECIFIC_PAGE_NAMES)[number];\n\nexport const specificPageNameSchema = z.enum(SPECIFIC_PAGE_NAMES);\n\nexport const specificPageVisitMetadataSchema = z\n .object({\n page_name: specificPageNameSchema,\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type SpecificPageVisitMetadata = z.infer<typeof specificPageVisitMetadataSchema>;\n\nexport const specificPageVisitConfigSchema = z\n .object({\n pages: z\n .array(\n z.object({\n name: specificPageNameSchema,\n pathPattern: z.custom<RegExp>((value) => value instanceof RegExp, {\n message: 'pathPattern must be a RegExp',\n }),\n }),\n )\n .min(1),\n })\n .strict();\n\nexport type SpecificPageVisitConfig = z.infer<typeof specificPageVisitConfigSchema>;\n","import { z } from 'zod';\n\n// time_on_site is automatic. The SDK starts a visibility-aware timer on page\n// load and fires once when the visible-time threshold is crossed. The timer\n// pauses when the tab goes hidden so a user who walks away from a tab\n// doesn't generate fake engagement.\n\nexport const timeOnSiteMetadataSchema = z\n .object({\n duration_ms: z.number().int().nonnegative(),\n page: z.object({\n path: z.string(),\n }),\n })\n .strict();\n\nexport type TimeOnSiteMetadata = z.infer<typeof timeOnSiteMetadataSchema>;\n\nexport const timeOnSiteConfigSchema = z\n .object({\n thresholdSeconds: z.number().int().positive(),\n })\n .strict();\nexport type TimeOnSiteConfig = z.infer<typeof timeOnSiteConfigSchema>;\n","import type { z } from 'zod';\n\nimport {\n ctaClickConfigSchema,\n ctaClickMetadataSchema,\n type CtaClickConfig,\n type CtaClickMetadata,\n} from './cta-click';\nimport {\n sdkHeartbeatConfigSchema,\n sdkHeartbeatMetadataSchema,\n type SdkHeartbeatConfig,\n type SdkHeartbeatMetadata,\n} from './sdk-heartbeat';\nimport {\n formStartConfigSchema,\n formStartMetadataSchema,\n type FormStartConfig,\n type FormStartMetadata,\n} from './form-start';\nimport {\n formSubmitConfigSchema,\n formSubmitMetadataSchema,\n type FormSubmitConfig,\n type FormSubmitMetadata,\n} from './form-submit';\nimport {\n multiPageSessionConfigSchema,\n multiPageSessionMetadataSchema,\n type MultiPageSessionConfig,\n type MultiPageSessionMetadata,\n} from './multi-page-session';\nimport {\n pageViewConfigSchema,\n pageViewMetadataSchema,\n type PageViewConfig,\n type PageViewMetadata,\n} from './page-view';\nimport {\n phoneClickConfigSchema,\n phoneClickMetadataSchema,\n type PhoneClickConfig,\n type PhoneClickMetadata,\n} from './phone-click';\nimport {\n scrollDepthConfigSchema,\n scrollDepthMetadataSchema,\n type ScrollDepthConfig,\n type ScrollDepthMetadata,\n} from './scroll-depth';\nimport {\n specificPageVisitConfigSchema,\n specificPageVisitMetadataSchema,\n type SpecificPageVisitConfig,\n type SpecificPageVisitMetadata,\n} from './specific-page-visit';\nimport {\n timeOnSiteConfigSchema,\n timeOnSiteMetadataSchema,\n type TimeOnSiteConfig,\n type TimeOnSiteMetadata,\n} from './time-on-site';\n\n// Event kind — automatic events are fired by the SDK itself when their\n// client-side signal fires (page_view on navigation, time_on_site on timer,\n// etc). Manual events are only fireable via explicit consumer code.\nexport type EventKind = 'automatic' | 'manual';\n\n// The registry is the single client-side source of truth for \"what events\n// exist, what shape does their metadata take, and what kind are they\". The\n// backend mirror lives in apps/api/src/schemas/tracking_events.py and is\n// kept in sync via the drift test in apps/api/tests/test_event_schema_drift.py.\nexport const EVENT_REGISTRY = {\n // --- automatic triggers ---\n page_view: {\n kind: 'automatic',\n metadataSchema: pageViewMetadataSchema,\n configSchema: pageViewConfigSchema,\n },\n time_on_site: {\n kind: 'automatic',\n metadataSchema: timeOnSiteMetadataSchema,\n configSchema: timeOnSiteConfigSchema,\n },\n specific_page_visit: {\n kind: 'automatic',\n metadataSchema: specificPageVisitMetadataSchema,\n configSchema: specificPageVisitConfigSchema,\n },\n scroll_depth: {\n kind: 'automatic',\n metadataSchema: scrollDepthMetadataSchema,\n configSchema: scrollDepthConfigSchema,\n },\n multi_page_session: {\n kind: 'automatic',\n metadataSchema: multiPageSessionMetadataSchema,\n configSchema: multiPageSessionConfigSchema,\n },\n form_start: {\n kind: 'automatic',\n metadataSchema: formStartMetadataSchema,\n configSchema: formStartConfigSchema,\n },\n // --- SDK-internal automatic (not consumer-configurable) ---\n sdk_heartbeat: {\n kind: 'automatic',\n metadataSchema: sdkHeartbeatMetadataSchema,\n configSchema: sdkHeartbeatConfigSchema,\n },\n // --- manual triggers ---\n form_submit: {\n kind: 'manual',\n metadataSchema: formSubmitMetadataSchema,\n configSchema: formSubmitConfigSchema,\n },\n phone_click: {\n kind: 'manual',\n metadataSchema: phoneClickMetadataSchema,\n configSchema: phoneClickConfigSchema,\n },\n cta_click: {\n kind: 'manual',\n metadataSchema: ctaClickMetadataSchema,\n configSchema: ctaClickConfigSchema,\n },\n} as const;\n\nexport type EventName = keyof typeof EVENT_REGISTRY;\n\n// Narrow the event name union based on `kind`.\nexport type AutomaticEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'automatic' ? K : never;\n}[EventName];\n\nexport type ManualEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'manual' ? K : never;\n}[EventName];\n\n// Map an event name to its explicit metadata / config TS type. We pull the\n// inferred types directly from the per-event files rather than deriving\n// via `z.infer<typeof EVENT_REGISTRY[K]['metadataSchema']>` so that error\n// messages name the event-specific type (PageViewMetadata, not an\n// anonymous zod inference).\ntype MetadataByName = {\n page_view: PageViewMetadata;\n time_on_site: TimeOnSiteMetadata;\n specific_page_visit: SpecificPageVisitMetadata;\n scroll_depth: ScrollDepthMetadata;\n multi_page_session: MultiPageSessionMetadata;\n form_start: FormStartMetadata;\n sdk_heartbeat: SdkHeartbeatMetadata;\n form_submit: FormSubmitMetadata;\n phone_click: PhoneClickMetadata;\n cta_click: CtaClickMetadata;\n};\n\ntype ConfigByName = {\n page_view: PageViewConfig;\n time_on_site: TimeOnSiteConfig;\n specific_page_visit: SpecificPageVisitConfig;\n scroll_depth: ScrollDepthConfig;\n multi_page_session: MultiPageSessionConfig;\n form_start: FormStartConfig;\n sdk_heartbeat: SdkHeartbeatConfig;\n form_submit: FormSubmitConfig;\n phone_click: PhoneClickConfig;\n cta_click: CtaClickConfig;\n};\n\nexport type EventMetadata<K extends EventName> = MetadataByName[K];\nexport type EventConfig<K extends EventName> = ConfigByName[K];\n\n// Runtime constant arrays for iteration at consumer / factory time.\nexport const ALL_AUTOMATIC_EVENT_NAMES: readonly AutomaticEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'automatic')\n .map(([name]) => name) as AutomaticEventName[];\n\nexport const ALL_MANUAL_EVENT_NAMES: readonly ManualEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'manual')\n .map(([name]) => name) as ManualEventName[];\n\n// The shape passed to createTracking({ triggers: ... }).\n//\n// `automatic.page_view` is **required** — if you're installing tracking you\n// always want page views, and forgetting to register it is a footgun we\n// prefer to catch at compile time. Every other automatic event is optional.\n// Manual events are all optional — register only the ones you actually\n// intend to fire from consumer code.\nexport type TriggerRegistryConfig = {\n automatic: {\n page_view: EventConfig<'page_view'>;\n } & Partial<{\n time_on_site: EventConfig<'time_on_site'>;\n specific_page_visit: EventConfig<'specific_page_visit'>;\n scroll_depth: EventConfig<'scroll_depth'>;\n multi_page_session: EventConfig<'multi_page_session'>;\n form_start: EventConfig<'form_start'>;\n }>;\n manual?: Partial<{\n form_submit: EventConfig<'form_submit'>;\n phone_click: EventConfig<'phone_click'>;\n cta_click: EventConfig<'cta_click'>;\n }>;\n};\n\n// Given a registry, which manual event names did the consumer actually\n// register? These are the only ones `trackEvent()` will accept as its\n// first argument. `NonNullable` collapses the case where `manual` was\n// omitted entirely.\nexport type RegisteredManualEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof NonNullable<TRegistry['manual']>, ManualEventName>;\n\n// Given a registry, which automatic event names did the consumer register?\n// Used by the factory to decide which auto-trigger attach helpers to wire up.\nexport type RegisteredAutomaticEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof TRegistry['automatic'], AutomaticEventName>;\n\n// Internal helper used by the factory: given a registry, return the full\n// discriminated union of valid (eventName, metadata) pairs the consumer\n// can pass to trackEvent().\nexport type TrackableEvent<TRegistry extends TriggerRegistryConfig> = {\n [K in RegisteredManualEvents<TRegistry>]: {\n eventType: K;\n metadata: EventMetadata<K>;\n };\n}[RegisteredManualEvents<TRegistry>];\n\n// Runtime helper: look up a schema pair by name. Cast through `unknown`\n// because the registry is `as const` and TS loses the specific schema type\n// when indexing via a dynamic key.\nexport function getEventDefinition(name: EventName): {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n} {\n return EVENT_REGISTRY[name] as unknown as {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n };\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z"]}
1
+ {"version":3,"sources":["../src/middleware.ts","../../tracking-core/src/tracking.ts","../../tracking-core/src/session.ts","../../tracking-core/src/events/page-view.ts","../../tracking-core/src/events/cta-click.ts","../../tracking-core/src/events/sdk-heartbeat.ts","../../tracking-core/src/events/form-start.ts","../../tracking-core/src/events/form-submit.ts","../../tracking-core/src/events/multi-page-session.ts","../../tracking-core/src/events/phone-click.ts","../../tracking-core/src/events/scroll-depth.ts","../../tracking-core/src/events/specific-page-visit.ts","../../tracking-core/src/events/time-on-site.ts","../../tracking-core/src/events/registry.ts"],"sourcesContent":["import 'server-only';\n\nimport { NextResponse } from 'next/server';\nimport type { NextMiddleware, NextRequest } from 'next/server';\n\nimport { getTrackingQueryValues } from '../../tracking-core/src/index';\n\nexport function createTrackingMiddleware(): NextMiddleware {\n return (request: NextRequest) => {\n const response = NextResponse.next();\n const trackingValues = getTrackingQueryValues(request.nextUrl.searchParams);\n\n Object.entries(trackingValues).forEach(([param, value]) => {\n response.cookies.set(param, value, {\n maxAge: 7776000,\n path: '/',\n httpOnly: false,\n });\n });\n\n return response;\n };\n}\n","import type { TrackingParams } from './types';\n\nexport const TRACKING_COOKIE_MAX_AGE_SECONDS = 7_776_000;\n\nexport const TRACKING_PARAM_KEYS = [\n 'gclid',\n 'fbclid',\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n] as const;\n\nexport type TrackingParamKey = (typeof TRACKING_PARAM_KEYS)[number];\n\nexport function createEmptyTrackingParams(): TrackingParams {\n return {\n gclid: null,\n fbclid: null,\n utm_source: null,\n utm_medium: null,\n utm_campaign: null,\n utm_term: null,\n utm_content: null,\n };\n}\n\nexport function normalizeTrackingCookieValue(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nexport function getTrackingParamsFromCookieReader(\n readCookie: (key: TrackingParamKey) => unknown,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((params, key) => {\n params[key] = normalizeTrackingCookieValue(readCookie(key));\n return params;\n }, createEmptyTrackingParams());\n}\n\nexport function getTrackingQueryValues(\n searchParams: Pick<URLSearchParams, 'get'>,\n): Partial<Record<TrackingParamKey, string>> {\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const value = searchParams.get(key);\n\n if (typeof value === 'string' && value.trim().length > 0) {\n params[key] = value;\n }\n\n return params;\n }, {});\n}\n\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n if (typeof document === 'undefined')\n return null;\n\n const cookies = document.cookie ? document.cookie.split('; ') : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${key}=`));\n\n if (!match)\n return null;\n\n const [, rawValue = ''] = match.split('=');\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n}\n\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n if (typeof document === 'undefined')\n return;\n\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${key}=${encodedValue}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n}\n\nexport function persistTrackingParamsFromSearchParams(\n searchParams: Pick<URLSearchParams, 'get'>,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): Partial<Record<TrackingParamKey, string>> {\n const trackingValues = getTrackingQueryValues(searchParams);\n\n Object.entries(trackingValues).forEach(([key, value]) => {\n setTrackingCookie(key as TrackingParamKey, value, maxAgeSeconds);\n });\n\n return trackingValues;\n}\n\nexport function captureTrackingParamsFromLocation(\n url = typeof window === 'undefined' ? '' : window.location.href,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): TrackingParams {\n const resolvedUrl = typeof window === 'undefined'\n ? new URL(url || 'https://example.invalid')\n : new URL(url, window.location.origin);\n persistTrackingParamsFromSearchParams(resolvedUrl.searchParams, maxAgeSeconds);\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n","// Visitor + session identity for the tracking SDK.\n//\n// Visitor: persistent localStorage UUID, never expires until the user clears\n// browser storage. Used for cross-session correlation.\n//\n// Session: rolling 30-minute idle window. Regenerated when more than\n// SESSION_IDLE_MS has passed since the last event. Matches the behavior of\n// GA4, PostHog, Mixpanel, etc., so analytics is comparable.\n\nexport const VISITOR_STORAGE_KEY = 'aranova_tracking_visitor';\nexport const SESSION_STORAGE_KEY = 'aranova_tracking_session';\nexport const SESSION_IDLE_MS = 30 * 60 * 1000;\n\nexport interface StoredSession {\n id: string;\n last_event_at: number;\n}\n\nfunction safeUuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')\n return crypto.randomUUID();\n // Fallback for ancient browsers — not cryptographically perfect but unique enough.\n return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction readLocalStorage(key: string): string | null {\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value);\n } catch {\n // Storage may be denied (private mode, blocked cookies, quota). Caller is\n // responsible for degrading gracefully.\n }\n}\n\nexport function getVisitorId(): string {\n if (typeof window === 'undefined')\n return safeUuid();\n\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0)\n return existing;\n\n const fresh = safeUuid();\n writeLocalStorage(VISITOR_STORAGE_KEY, fresh);\n return fresh;\n}\n\nexport interface SessionIdResult {\n id: string;\n isNew: boolean;\n}\n\nexport function getOrRotateSessionId(now: number = Date.now()): SessionIdResult {\n if (typeof window === 'undefined')\n return { id: safeUuid(), isNew: true };\n\n const raw = readLocalStorage(SESSION_STORAGE_KEY);\n if (raw) {\n try {\n const parsed = JSON.parse(raw) as Partial<StoredSession>;\n if (typeof parsed.id === 'string' && typeof parsed.last_event_at === 'number') {\n if (now - parsed.last_event_at <= SESSION_IDLE_MS) {\n const refreshed: StoredSession = { id: parsed.id, last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));\n return { id: parsed.id, isNew: false };\n }\n }\n } catch {\n // Fall through to a fresh session.\n }\n }\n\n const fresh: StoredSession = { id: safeUuid(), last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));\n return { id: fresh.id, isNew: true };\n}\n\n/** Test/debug helper — wipes both ids. */\nexport function resetTrackingIdentity(): void {\n if (typeof window === 'undefined')\n return;\n try {\n window.localStorage.removeItem(VISITOR_STORAGE_KEY);\n window.localStorage.removeItem(SESSION_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","import { z } from 'zod';\n\n// page_view is automatic and auto-emitted by attachAutoPageView() on initial\n// load, SPA route changes (pushState / replaceState / popstate), and bfcache\n// restore (pageshow with event.persisted === true).\n\nexport const pageViewMetadataSchema = z\n .object({\n page: z\n .object({\n title: z.string().nullable(),\n path: z.string(),\n search: z.string(),\n hash: z.string(),\n })\n .strict(),\n referrer: z.string().nullable(),\n // `.nullable().optional()` — absent (undefined) OR explicit null OR a\n // real viewport object. Mirrors Pydantic's `_Viewport | None = None`\n // on the backend side so the drift test stays clean.\n viewport: z\n .object({\n w: z.number(),\n h: z.number(),\n })\n .strict()\n .nullable()\n .optional(),\n })\n .strict();\n\nexport type PageViewMetadata = z.infer<typeof pageViewMetadataSchema>;\n\n// No configurable options today, but must be registered for the SDK to attach\n// the auto-page-view handler. Reserved for future options like `throttleMs`\n// or `includeHash`.\nexport const pageViewConfigSchema = z.object({}).strict();\nexport type PageViewConfig = z.infer<typeof pageViewConfigSchema>;\n","import { z } from 'zod';\n\n// cta_click is manual. Consumer code calls trackEvent('cta_click', ...) from\n// click handlers on CTAs like \"Get Directions\", \"Book Appointment\", etc.\n// The cta_name is a free-form string chosen by the consumer to identify the\n// action (e.g. \"get_directions\", \"book_appointment\", \"download_brochure\").\n\nexport const ctaClickMetadataSchema = z\n .object({\n cta_name: z.string(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n section: z.string().nullable().optional(),\n destination_url: z.string().nullable().optional(),\n })\n .strict();\n\nexport type CtaClickMetadata = z.infer<typeof ctaClickMetadataSchema>;\n\nexport const ctaClickConfigSchema = z.object({}).strict();\nexport type CtaClickConfig = z.infer<typeof ctaClickConfigSchema>;\n","import { z } from 'zod';\n\n// sdk_heartbeat is an SDK-internal automatic event fired exactly once at the\n// start of each new session. It carries the SDK version and the complete\n// trigger registry the consumer registered, so the backend knows which\n// events a client site is configured to capture without requiring that\n// information on every single event payload.\n\nexport const sdkHeartbeatTriggersSchema = z.object({\n automatic: z.array(z.string()),\n manual: z.array(z.string()),\n}).strict();\n\nexport const sdkHeartbeatMetadataSchema = z\n .object({\n sdk_version: z.string(),\n package_name: z.string().nullable(),\n surface: z.enum(['next', 'react', 'script']),\n triggers: sdkHeartbeatTriggersSchema,\n trigger_config: z.record(z.string(), z.record(z.string(), z.unknown())).nullable().optional(),\n })\n .strict();\n\nexport type SdkHeartbeatMetadata = z.infer<typeof sdkHeartbeatMetadataSchema>;\n\n// No consumer-facing config — heartbeat is SDK-internal.\nexport const sdkHeartbeatConfigSchema = z.object({}).strict();\nexport type SdkHeartbeatConfig = z.infer<typeof sdkHeartbeatConfigSchema>;\n","import { z } from 'zod';\n\n// form_start is automatic. The SDK uses focusin event delegation on document\n// to detect the first field interaction inside a <form>. Fires once per form\n// per page lifecycle. Resets on SPA navigation.\n\nexport const formStartMetadataSchema = z\n .object({\n form: z\n .object({\n id: z.string(),\n action: z.string().nullable(),\n })\n .strict(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type FormStartMetadata = z.infer<typeof formStartMetadataSchema>;\n\nexport const formStartConfigSchema = z\n .object({\n selector: z.string().optional(),\n })\n .strict();\n\nexport type FormStartConfig = z.infer<typeof formStartConfigSchema>;\n","import { z } from 'zod';\n\n// form_submit is manual — the SDK never auto-fires this. Consumer code calls\n// `tracking.trackEvent('form_submit', { form, page })` from their own submit\n// handler. Registering it enables the type-level permission; omitting it\n// turns manual calls into a compile error.\n//\n// The optional `fields` array captures submitted form field metadata and JSON\n// values. Consumers explicitly build the fields array themselves so they\n// control exactly what is sent.\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport const jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>\n z.union([\n z.string(),\n z.number().finite(),\n z.boolean(),\n z.null(),\n z.array(jsonValueSchema),\n z.record(jsonValueSchema),\n ]),\n);\n\nexport const formSubmitMetadataSchema = z\n .object({\n form: z\n .object({\n id: z.string(),\n action: z.string().nullable(),\n fields: z\n .array(\n z\n .object({\n name: z.string(),\n type: z.string(),\n label: z.string().nullable(),\n value: jsonValueSchema,\n })\n .strict(),\n )\n .optional(),\n })\n .strict(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type FormSubmitMetadata = z.infer<typeof formSubmitMetadataSchema>;\n\nexport const formSubmitConfigSchema = z.object({}).strict();\nexport type FormSubmitConfig = z.infer<typeof formSubmitConfigSchema>;\n","import { z } from 'zod';\n\n// multi_page_session is automatic. The SDK tracks distinct page paths visited\n// in the current session via sessionStorage. When the count reaches the\n// configured threshold, fires once. Resets on session rotation.\n\nexport const multiPageSessionMetadataSchema = z\n .object({\n page_count: z.number().int().min(2),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type MultiPageSessionMetadata = z.infer<typeof multiPageSessionMetadataSchema>;\n\nexport const multiPageSessionConfigSchema = z\n .object({\n pageThreshold: z.number().int().min(2),\n })\n .strict();\n\nexport type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;\n","import { z } from 'zod';\n\n// phone_click is manual. Consumer code calls trackEvent('phone_click', ...)\n// from a click handler on a tel: anchor or similar. The phone_number is the\n// business's number (extracted from the href), not the visitor's. The optional\n// section helps distinguish multiple phone links on the same page.\n\nexport const phoneClickMetadataSchema = z\n .object({\n phone_number: z.string(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n section: z.string().nullable().optional(),\n })\n .strict();\n\nexport type PhoneClickMetadata = z.infer<typeof phoneClickMetadataSchema>;\n\nexport const phoneClickConfigSchema = z.object({}).strict();\nexport type PhoneClickConfig = z.infer<typeof phoneClickConfigSchema>;\n","import { z } from 'zod';\n\n// scroll_depth is automatic. The SDK installs a requestAnimationFrame-throttled\n// scroll listener and fires once per configured threshold per page. Resets on\n// SPA navigation.\n\nexport const scrollDepthMetadataSchema = z\n .object({\n depth_percent: z.number().int().min(1).max(100),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type ScrollDepthMetadata = z.infer<typeof scrollDepthMetadataSchema>;\n\nexport const scrollDepthConfigSchema = z\n .object({\n thresholds: z.array(z.number().int().min(1).max(100)).min(1),\n })\n .strict();\n\nexport type ScrollDepthConfig = z.infer<typeof scrollDepthConfigSchema>;\n","import { z } from 'zod';\n\n// specific_page_visit is automatic. The SDK observes every navigation and\n// emits a specific_page_visit event when the current pathname matches one of\n// the consumer-provided patterns. Each pattern has a strongly typed name\n// (contact_page, services_page, etc.) so the event stream is self-describing\n// and downstream analytics can group by page intent without re-applying regexes.\n\nexport const SPECIFIC_PAGE_NAMES = [\n 'contact_page',\n 'about_page',\n 'services_page',\n 'booking_page',\n 'location_page',\n 'pricing_page',\n 'faq_page',\n 'testimonials_page',\n] as const;\n\nexport type SpecificPageName = (typeof SPECIFIC_PAGE_NAMES)[number];\n\nexport const specificPageNameSchema = z.enum(SPECIFIC_PAGE_NAMES);\n\nexport const specificPageVisitMetadataSchema = z\n .object({\n page_name: specificPageNameSchema,\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type SpecificPageVisitMetadata = z.infer<typeof specificPageVisitMetadataSchema>;\n\nexport const specificPageVisitConfigSchema = z\n .object({\n pages: z\n .array(\n z\n .object({\n name: specificPageNameSchema,\n pathPattern: z.custom<RegExp>((value) => value instanceof RegExp, {\n message: 'pathPattern must be a RegExp',\n }),\n })\n .strict(),\n )\n .min(1),\n })\n .strict();\n\nexport type SpecificPageVisitConfig = z.infer<typeof specificPageVisitConfigSchema>;\n","import { z } from 'zod';\n\n// time_on_site is automatic. The SDK starts a visibility-aware timer on page\n// load and fires once when the visible-time threshold is crossed. The timer\n// pauses when the tab goes hidden so a user who walks away from a tab\n// doesn't generate fake engagement.\n\nexport const timeOnSiteMetadataSchema = z\n .object({\n duration_ms: z.number().int().nonnegative(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type TimeOnSiteMetadata = z.infer<typeof timeOnSiteMetadataSchema>;\n\nexport const timeOnSiteConfigSchema = z\n .object({\n thresholdSeconds: z.number().int().positive(),\n })\n .strict();\nexport type TimeOnSiteConfig = z.infer<typeof timeOnSiteConfigSchema>;\n","import type { z } from 'zod';\n\nimport {\n ctaClickConfigSchema,\n ctaClickMetadataSchema,\n type CtaClickConfig,\n type CtaClickMetadata,\n} from './cta-click';\nimport {\n sdkHeartbeatConfigSchema,\n sdkHeartbeatMetadataSchema,\n type SdkHeartbeatConfig,\n type SdkHeartbeatMetadata,\n} from './sdk-heartbeat';\nimport {\n formStartConfigSchema,\n formStartMetadataSchema,\n type FormStartConfig,\n type FormStartMetadata,\n} from './form-start';\nimport {\n formSubmitConfigSchema,\n formSubmitMetadataSchema,\n type FormSubmitConfig,\n type FormSubmitMetadata,\n} from './form-submit';\nimport {\n multiPageSessionConfigSchema,\n multiPageSessionMetadataSchema,\n type MultiPageSessionConfig,\n type MultiPageSessionMetadata,\n} from './multi-page-session';\nimport {\n pageViewConfigSchema,\n pageViewMetadataSchema,\n type PageViewConfig,\n type PageViewMetadata,\n} from './page-view';\nimport {\n phoneClickConfigSchema,\n phoneClickMetadataSchema,\n type PhoneClickConfig,\n type PhoneClickMetadata,\n} from './phone-click';\nimport {\n scrollDepthConfigSchema,\n scrollDepthMetadataSchema,\n type ScrollDepthConfig,\n type ScrollDepthMetadata,\n} from './scroll-depth';\nimport {\n specificPageVisitConfigSchema,\n specificPageVisitMetadataSchema,\n type SpecificPageVisitConfig,\n type SpecificPageVisitMetadata,\n} from './specific-page-visit';\nimport {\n timeOnSiteConfigSchema,\n timeOnSiteMetadataSchema,\n type TimeOnSiteConfig,\n type TimeOnSiteMetadata,\n} from './time-on-site';\n\n// Event kind — automatic events are fired by the SDK itself when their\n// client-side signal fires (page_view on navigation, time_on_site on timer,\n// etc). Manual events are only fireable via explicit consumer code.\nexport type EventKind = 'automatic' | 'manual';\n\n// The registry is the single client-side source of truth for \"what events\n// exist, what shape does their metadata take, and what kind are they\". The\n// backend mirror lives in apps/api/src/schemas/tracking_events.py and is\n// kept in sync via the drift test in apps/api/tests/test_event_schema_drift.py.\nexport const EVENT_REGISTRY = {\n // --- automatic triggers ---\n page_view: {\n kind: 'automatic',\n metadataSchema: pageViewMetadataSchema,\n configSchema: pageViewConfigSchema,\n },\n time_on_site: {\n kind: 'automatic',\n metadataSchema: timeOnSiteMetadataSchema,\n configSchema: timeOnSiteConfigSchema,\n },\n specific_page_visit: {\n kind: 'automatic',\n metadataSchema: specificPageVisitMetadataSchema,\n configSchema: specificPageVisitConfigSchema,\n },\n scroll_depth: {\n kind: 'automatic',\n metadataSchema: scrollDepthMetadataSchema,\n configSchema: scrollDepthConfigSchema,\n },\n multi_page_session: {\n kind: 'automatic',\n metadataSchema: multiPageSessionMetadataSchema,\n configSchema: multiPageSessionConfigSchema,\n },\n form_start: {\n kind: 'automatic',\n metadataSchema: formStartMetadataSchema,\n configSchema: formStartConfigSchema,\n },\n // --- SDK-internal automatic (not consumer-configurable) ---\n sdk_heartbeat: {\n kind: 'automatic',\n metadataSchema: sdkHeartbeatMetadataSchema,\n configSchema: sdkHeartbeatConfigSchema,\n },\n // --- manual triggers ---\n form_submit: {\n kind: 'manual',\n metadataSchema: formSubmitMetadataSchema,\n configSchema: formSubmitConfigSchema,\n },\n phone_click: {\n kind: 'manual',\n metadataSchema: phoneClickMetadataSchema,\n configSchema: phoneClickConfigSchema,\n },\n cta_click: {\n kind: 'manual',\n metadataSchema: ctaClickMetadataSchema,\n configSchema: ctaClickConfigSchema,\n },\n} as const;\n\nexport type EventName = keyof typeof EVENT_REGISTRY;\n\n// Narrow the event name union based on `kind`.\nexport type AutomaticEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'automatic' ? K : never;\n}[EventName];\n\nexport type ManualEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'manual' ? K : never;\n}[EventName];\n\n// Map an event name to its explicit metadata / config TS type. We pull the\n// inferred types directly from the per-event files rather than deriving\n// via `z.infer<typeof EVENT_REGISTRY[K]['metadataSchema']>` so that error\n// messages name the event-specific type (PageViewMetadata, not an\n// anonymous zod inference).\ntype MetadataByName = {\n page_view: PageViewMetadata;\n time_on_site: TimeOnSiteMetadata;\n specific_page_visit: SpecificPageVisitMetadata;\n scroll_depth: ScrollDepthMetadata;\n multi_page_session: MultiPageSessionMetadata;\n form_start: FormStartMetadata;\n sdk_heartbeat: SdkHeartbeatMetadata;\n form_submit: FormSubmitMetadata;\n phone_click: PhoneClickMetadata;\n cta_click: CtaClickMetadata;\n};\n\ntype ConfigByName = {\n page_view: PageViewConfig;\n time_on_site: TimeOnSiteConfig;\n specific_page_visit: SpecificPageVisitConfig;\n scroll_depth: ScrollDepthConfig;\n multi_page_session: MultiPageSessionConfig;\n form_start: FormStartConfig;\n sdk_heartbeat: SdkHeartbeatConfig;\n form_submit: FormSubmitConfig;\n phone_click: PhoneClickConfig;\n cta_click: CtaClickConfig;\n};\n\nexport type EventMetadata<K extends EventName> = MetadataByName[K];\nexport type EventConfig<K extends EventName> = ConfigByName[K];\n\n// Runtime constant arrays for iteration at consumer / factory time.\nexport const ALL_AUTOMATIC_EVENT_NAMES: readonly AutomaticEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'automatic')\n .map(([name]) => name) as AutomaticEventName[];\n\nexport const ALL_MANUAL_EVENT_NAMES: readonly ManualEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'manual')\n .map(([name]) => name) as ManualEventName[];\n\n// The shape passed to createTracking({ triggers: ... }).\n//\n// `automatic.page_view` is **required** — if you're installing tracking you\n// always want page views, and forgetting to register it is a footgun we\n// prefer to catch at compile time. Every other automatic event is optional.\n// Manual events are all optional — register only the ones you actually\n// intend to fire from consumer code.\nexport type TriggerRegistryConfig = {\n automatic: {\n page_view: EventConfig<'page_view'>;\n } & Partial<{\n time_on_site: EventConfig<'time_on_site'>;\n specific_page_visit: EventConfig<'specific_page_visit'>;\n scroll_depth: EventConfig<'scroll_depth'>;\n multi_page_session: EventConfig<'multi_page_session'>;\n form_start: EventConfig<'form_start'>;\n }>;\n manual?: Partial<{\n form_submit: EventConfig<'form_submit'>;\n phone_click: EventConfig<'phone_click'>;\n cta_click: EventConfig<'cta_click'>;\n }>;\n};\n\n// Given a registry, which manual event names did the consumer actually\n// register? These are the only ones `trackEvent()` will accept as its\n// first argument. `NonNullable` collapses the case where `manual` was\n// omitted entirely.\nexport type RegisteredManualEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof NonNullable<TRegistry['manual']>, ManualEventName>;\n\n// Given a registry, which automatic event names did the consumer register?\n// Used by the factory to decide which auto-trigger attach helpers to wire up.\nexport type RegisteredAutomaticEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof TRegistry['automatic'], AutomaticEventName>;\n\n// Internal helper used by the factory: given a registry, return the full\n// discriminated union of valid (eventName, metadata) pairs the consumer\n// can pass to trackEvent().\nexport type TrackableEvent<TRegistry extends TriggerRegistryConfig> = {\n [K in RegisteredManualEvents<TRegistry>]: {\n eventType: K;\n metadata: EventMetadata<K>;\n };\n}[RegisteredManualEvents<TRegistry>];\n\n// Runtime helper: look up a schema pair by name. Cast through `unknown`\n// because the registry is `as const` and TS loses the specific schema type\n// when indexing via a dynamic key.\nexport function getEventDefinition(name: EventName): {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n} {\n return EVENT_REGISTRY[name] as unknown as {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n };\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z"]}