@se-studio/core-ui 1.33.3 → 1.35.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.
Files changed (59) hide show
  1. package/ANALYTICS.md +33 -5
  2. package/CHANGELOG.md +18 -0
  3. package/CONSENT.md +52 -0
  4. package/dist/analytics/adapters/CompositeAnalyticsAdapter.d.ts +2 -1
  5. package/dist/analytics/adapters/CompositeAnalyticsAdapter.d.ts.map +1 -1
  6. package/dist/analytics/adapters/CompositeAnalyticsAdapter.js +6 -5
  7. package/dist/analytics/adapters/CompositeAnalyticsAdapter.js.map +1 -1
  8. package/dist/analytics/adapters/ConsentAwareAdapter.d.ts +29 -2
  9. package/dist/analytics/adapters/ConsentAwareAdapter.d.ts.map +1 -1
  10. package/dist/analytics/adapters/ConsentAwareAdapter.js +49 -5
  11. package/dist/analytics/adapters/ConsentAwareAdapter.js.map +1 -1
  12. package/dist/analytics/adapters/GoogleTagManagerAdapter.d.ts +18 -17
  13. package/dist/analytics/adapters/GoogleTagManagerAdapter.d.ts.map +1 -1
  14. package/dist/analytics/adapters/GoogleTagManagerAdapter.js +64 -35
  15. package/dist/analytics/adapters/GoogleTagManagerAdapter.js.map +1 -1
  16. package/dist/analytics/adapters/adapterMethods.d.ts +4 -0
  17. package/dist/analytics/adapters/adapterMethods.d.ts.map +1 -0
  18. package/dist/analytics/adapters/adapterMethods.js +11 -0
  19. package/dist/analytics/adapters/adapterMethods.js.map +1 -0
  20. package/dist/analytics/adapters/index.d.ts +1 -0
  21. package/dist/analytics/adapters/index.d.ts.map +1 -1
  22. package/dist/analytics/adapters/index.js.map +1 -1
  23. package/dist/analytics/consent/resolveAnalyticsConsent.d.ts +39 -0
  24. package/dist/analytics/consent/resolveAnalyticsConsent.d.ts.map +1 -0
  25. package/dist/analytics/consent/resolveAnalyticsConsent.js +53 -0
  26. package/dist/analytics/consent/resolveAnalyticsConsent.js.map +1 -0
  27. package/dist/analytics/form/createFormAnalyticsSession.d.ts +36 -0
  28. package/dist/analytics/form/createFormAnalyticsSession.d.ts.map +1 -0
  29. package/dist/analytics/form/createFormAnalyticsSession.js +62 -0
  30. package/dist/analytics/form/createFormAnalyticsSession.js.map +1 -0
  31. package/dist/analytics/form/formAnalyticsTypes.d.ts +43 -0
  32. package/dist/analytics/form/formAnalyticsTypes.d.ts.map +1 -0
  33. package/dist/analytics/form/formAnalyticsTypes.js +40 -0
  34. package/dist/analytics/form/formAnalyticsTypes.js.map +1 -0
  35. package/dist/analytics/form/index.d.ts +5 -0
  36. package/dist/analytics/form/index.d.ts.map +1 -0
  37. package/dist/analytics/form/index.js +5 -0
  38. package/dist/analytics/form/index.js.map +1 -0
  39. package/dist/analytics/form/sanitizeFormAnalyticsProperties.d.ts +9 -0
  40. package/dist/analytics/form/sanitizeFormAnalyticsProperties.d.ts.map +1 -0
  41. package/dist/analytics/form/sanitizeFormAnalyticsProperties.js +62 -0
  42. package/dist/analytics/form/sanitizeFormAnalyticsProperties.js.map +1 -0
  43. package/dist/analytics/form/useFormAnalytics.d.ts +13 -0
  44. package/dist/analytics/form/useFormAnalytics.d.ts.map +1 -0
  45. package/dist/analytics/form/useFormAnalytics.js +67 -0
  46. package/dist/analytics/form/useFormAnalytics.js.map +1 -0
  47. package/dist/analytics/formAnalyticsEntry.d.ts +10 -0
  48. package/dist/analytics/formAnalyticsEntry.d.ts.map +1 -0
  49. package/dist/analytics/formAnalyticsEntry.js +9 -0
  50. package/dist/analytics/formAnalyticsEntry.js.map +1 -0
  51. package/dist/analytics/types.d.ts +20 -0
  52. package/dist/analytics/types.d.ts.map +1 -1
  53. package/dist/index.d.ts +4 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +1 -0
  56. package/dist/index.js.map +1 -1
  57. package/docs/PACKAGE_EXPORT_BOUNDARIES.md +1 -0
  58. package/docs/llms.md +34 -8
  59. package/package.json +5 -1
@@ -1,4 +1,5 @@
1
1
  export { CompositeAnalyticsAdapter } from './CompositeAnalyticsAdapter';
2
+ export type { ConsentAwareAdapterOptions } from './ConsentAwareAdapter';
2
3
  export { ConsentAwareAdapter } from './ConsentAwareAdapter';
3
4
  export { ConsoleAnalyticsAdapter } from './ConsoleAnalyticsAdapter';
4
5
  export { GoogleTagManagerAdapter } from './GoogleTagManagerAdapter';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analytics/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analytics/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analytics/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analytics/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAExE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,39 @@
1
+ export type ConsentStatus = 'granted' | 'denied';
2
+ export type ConsentModel = 'opt-in' | 'opt-out';
3
+ /**
4
+ * Browser privacy-signal surface. `Navigator` plus GPC (`globalPrivacyControl`).
5
+ */
6
+ export interface PrivacySignalNavigator {
7
+ doNotTrack?: string | null;
8
+ msDoNotTrack?: string | null;
9
+ globalPrivacyControl?: boolean;
10
+ }
11
+ export interface ResolveAnalyticsConsentOptions {
12
+ /**
13
+ * Site-configured consent source (cookie, CMP, orejime, etc.).
14
+ * `true` / `'granted'` allow tracking; `false` / `'denied'` block it.
15
+ * `null` / `undefined` means unset — the `model` default applies.
16
+ */
17
+ source?: () => boolean | ConsentStatus | null | undefined;
18
+ /**
19
+ * When `source` is unset: `opt-out` grants tracking, `opt-in` denies it.
20
+ * Defaults to `opt-out` so sites that never configure consent keep tracking.
21
+ */
22
+ model?: ConsentModel;
23
+ /** Treat Global Privacy Control as an opt-out. Default `false`. */
24
+ honorGlobalPrivacyControl?: boolean;
25
+ /** Treat Do Not Track as an opt-out. Default `false`. */
26
+ honorDoNotTrack?: boolean;
27
+ /** Override `navigator` (tests). */
28
+ navigator?: PrivacySignalNavigator;
29
+ }
30
+ /**
31
+ * Resolve whether analytics tracking is allowed.
32
+ *
33
+ * Unconfigured sites (`{}` or omitted options) return `true` (opt-out / track).
34
+ * Honored GPC and DNT always deny, even if the site source would grant.
35
+ */
36
+ export declare function resolveAnalyticsConsent(options?: ResolveAnalyticsConsentOptions): boolean;
37
+ /** Stable getter for `ConsentAwareAdapter` / HubSpot `getConsent`. */
38
+ export declare function createConsentGetter(options?: ResolveAnalyticsConsentOptions): () => boolean;
39
+ //# sourceMappingURL=resolveAnalyticsConsent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveAnalyticsConsent.d.ts","sourceRoot":"","sources":["../../../src/analytics/consent/resolveAnalyticsConsent.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,QAAQ,CAAC;AACjD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,OAAO,GAAG,aAAa,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1D;;;OAGG;IACH,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,mEAAmE;IACnE,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,yDAAyD;IACzD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oCAAoC;IACpC,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC;AA+BD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,8BAAmC,GAAG,OAAO,CAmB7F;AAED,sEAAsE;AACtE,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,8BAAmC,GAAG,MAAM,OAAO,CAE/F"}
@@ -0,0 +1,53 @@
1
+ function readSource(source) {
2
+ if (!source) {
3
+ return undefined;
4
+ }
5
+ const value = source();
6
+ if (value === null || value === undefined) {
7
+ return undefined;
8
+ }
9
+ if (value === 'granted' || value === true) {
10
+ return true;
11
+ }
12
+ if (value === 'denied' || value === false) {
13
+ return false;
14
+ }
15
+ return undefined;
16
+ }
17
+ function isDoNotTrackEnabled(nav) {
18
+ if (!nav) {
19
+ return false;
20
+ }
21
+ const signal = nav.doNotTrack ?? nav.msDoNotTrack;
22
+ return signal === '1' || signal === 'yes';
23
+ }
24
+ function isGlobalPrivacyControlEnabled(nav) {
25
+ return nav?.globalPrivacyControl === true;
26
+ }
27
+ /**
28
+ * Resolve whether analytics tracking is allowed.
29
+ *
30
+ * Unconfigured sites (`{}` or omitted options) return `true` (opt-out / track).
31
+ * Honored GPC and DNT always deny, even if the site source would grant.
32
+ */
33
+ export function resolveAnalyticsConsent(options = {}) {
34
+ const nav = options.navigator ??
35
+ (typeof navigator !== 'undefined' ? navigator : undefined);
36
+ if (options.honorGlobalPrivacyControl && isGlobalPrivacyControlEnabled(nav)) {
37
+ return false;
38
+ }
39
+ if (options.honorDoNotTrack && isDoNotTrackEnabled(nav)) {
40
+ return false;
41
+ }
42
+ const fromSource = readSource(options.source);
43
+ if (fromSource !== undefined) {
44
+ return fromSource;
45
+ }
46
+ const model = options.model ?? 'opt-out';
47
+ return model === 'opt-out';
48
+ }
49
+ /** Stable getter for `ConsentAwareAdapter` / HubSpot `getConsent`. */
50
+ export function createConsentGetter(options = {}) {
51
+ return () => resolveAnalyticsConsent(options);
52
+ }
53
+ //# sourceMappingURL=resolveAnalyticsConsent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveAnalyticsConsent.js","sourceRoot":"","sources":["../../../src/analytics/consent/resolveAnalyticsConsent.ts"],"names":[],"mappings":"AAgCA,SAAS,UAAU,CAAC,MAAgD;IAClE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC;IACvB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAuC;IAClE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,YAAY,CAAC;IAClD,OAAO,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,KAAK,CAAC;AAC5C,CAAC;AAED,SAAS,6BAA6B,CAAC,GAAuC;IAC5E,OAAO,GAAG,EAAE,oBAAoB,KAAK,IAAI,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAO,GAAmC,EAAE;IAClF,MAAM,GAAG,GACP,OAAO,CAAC,SAAS;QACjB,CAAC,OAAO,SAAS,KAAK,WAAW,CAAC,CAAC,CAAE,SAAoC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEzF,IAAI,OAAO,CAAC,yBAAyB,IAAI,6BAA6B,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,eAAe,IAAI,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,SAAS,CAAC;IACzC,OAAO,KAAK,KAAK,SAAS,CAAC;AAC7B,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,mBAAmB,CAAC,OAAO,GAAmC,EAAE;IAC9E,OAAO,GAAG,EAAE,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { FormAnalyticsErrorInfo, FormAnalyticsTrackFn } from './formAnalyticsTypes';
2
+ import { type FormAnalyticsIdentity } from './formAnalyticsTypes';
3
+ export interface CreateFormAnalyticsSessionOptions {
4
+ identity: FormAnalyticsIdentity;
5
+ track: FormAnalyticsTrackFn;
6
+ }
7
+ export interface FormAnalyticsSession {
8
+ /** Fire `form_view` once for this form instance. */
9
+ markView: () => void;
10
+ /** Fire `form_start` once on first interaction. */
11
+ markStart: () => void;
12
+ /** Fire `form_submit` on successful accept (each success). */
13
+ markSubmit: () => void;
14
+ /**
15
+ * Fire `form_error`. Validation errors are deduped per attempt (multiple
16
+ * invalid fields on one submit → one event). API/network errors fire each time.
17
+ */
18
+ markError: (info?: FormAnalyticsErrorInfo) => void;
19
+ /**
20
+ * Snapshot for unit tests / debugging only.
21
+ */
22
+ getState: () => Readonly<FormAnalyticsSessionState>;
23
+ }
24
+ export interface FormAnalyticsSessionState {
25
+ viewed: boolean;
26
+ started: boolean;
27
+ submitCount: number;
28
+ errorCount: number;
29
+ validationErrorThisAttempt: boolean;
30
+ }
31
+ /**
32
+ * Pure form analytics session. Callers (hooks / HubSpot submit) translate DOM
33
+ * events into these methods. Never pass field values into `track`.
34
+ */
35
+ export declare function createFormAnalyticsSession(options: CreateFormAnalyticsSessionOptions): FormAnalyticsSession;
36
+ //# sourceMappingURL=createFormAnalyticsSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createFormAnalyticsSession.d.ts","sourceRoot":"","sources":["../../../src/analytics/form/createFormAnalyticsSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACzF,OAAO,EAAyB,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGzF,MAAM,WAAW,iCAAiC;IAChD,QAAQ,EAAE,qBAAqB,CAAC;IAChC,KAAK,EAAE,oBAAoB,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,mDAAmD;IACnD,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB;;;OAGG;IACH,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACnD;;OAEG;IACH,QAAQ,EAAE,MAAM,QAAQ,CAAC,yBAAyB,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B,EAAE,OAAO,CAAC;CACrC;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iCAAiC,GACzC,oBAAoB,CAyDtB"}
@@ -0,0 +1,62 @@
1
+ import { FORM_ANALYTICS_EVENTS } from './formAnalyticsTypes';
2
+ import { sanitizeFormAnalyticsProperties } from './sanitizeFormAnalyticsProperties';
3
+ /**
4
+ * Pure form analytics session. Callers (hooks / HubSpot submit) translate DOM
5
+ * events into these methods. Never pass field values into `track`.
6
+ */
7
+ export function createFormAnalyticsSession(options) {
8
+ const { identity, track } = options;
9
+ const state = {
10
+ viewed: false,
11
+ started: false,
12
+ submitCount: 0,
13
+ errorCount: 0,
14
+ validationErrorThisAttempt: false,
15
+ };
16
+ const emit = (event, extra) => {
17
+ const properties = sanitizeFormAnalyticsProperties({
18
+ ...identity,
19
+ ...extra,
20
+ });
21
+ track(event, properties);
22
+ };
23
+ return {
24
+ markView: () => {
25
+ if (state.viewed) {
26
+ return;
27
+ }
28
+ state.viewed = true;
29
+ emit(FORM_ANALYTICS_EVENTS.view);
30
+ },
31
+ markStart: () => {
32
+ if (state.started) {
33
+ state.validationErrorThisAttempt = false;
34
+ return;
35
+ }
36
+ state.started = true;
37
+ emit(FORM_ANALYTICS_EVENTS.start);
38
+ },
39
+ markSubmit: () => {
40
+ state.validationErrorThisAttempt = false;
41
+ state.submitCount += 1;
42
+ emit(FORM_ANALYTICS_EVENTS.submit);
43
+ },
44
+ markError: (info) => {
45
+ const category = info?.error_category;
46
+ if (category === 'validation') {
47
+ if (!state.started) {
48
+ state.started = true;
49
+ emit(FORM_ANALYTICS_EVENTS.start);
50
+ }
51
+ if (state.validationErrorThisAttempt) {
52
+ return;
53
+ }
54
+ state.validationErrorThisAttempt = true;
55
+ }
56
+ state.errorCount += 1;
57
+ emit(FORM_ANALYTICS_EVENTS.error, info);
58
+ },
59
+ getState: () => state,
60
+ };
61
+ }
62
+ //# sourceMappingURL=createFormAnalyticsSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createFormAnalyticsSession.js","sourceRoot":"","sources":["../../../src/analytics/form/createFormAnalyticsSession.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAA8B,MAAM,sBAAsB,CAAC;AACzF,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AAiCpF;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAA0C;IAE1C,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAEpC,MAAM,KAAK,GAA8B;QACvC,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,CAAC;QACd,UAAU,EAAE,CAAC;QACb,0BAA0B,EAAE,KAAK;KAClC,CAAC;IAEF,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,KAA8B,EAAE,EAAE;QAC7D,MAAM,UAAU,GAAG,+BAA+B,CAAC;YACjD,GAAG,QAAQ;YACX,GAAG,KAAK;SACT,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC3B,CAAC,CAAC;IAEF,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE;YACb,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjB,OAAO;YACT,CAAC;YACD,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,SAAS,EAAE,GAAG,EAAE;YACd,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBAClB,KAAK,CAAC,0BAA0B,GAAG,KAAK,CAAC;gBACzC,OAAO;YACT,CAAC;YACD,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;YACrB,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QACD,UAAU,EAAE,GAAG,EAAE;YACf,KAAK,CAAC,0BAA0B,GAAG,KAAK,CAAC;YACzC,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QACD,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;YAClB,MAAM,QAAQ,GAAG,IAAI,EAAE,cAAc,CAAC;YACtC,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;gBAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;oBACnB,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;oBACrB,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;gBACpC,CAAC;gBACD,IAAI,KAAK,CAAC,0BAA0B,EAAE,CAAC;oBACrC,OAAO;gBACT,CAAC;gBACD,KAAK,CAAC,0BAA0B,GAAG,IAAI,CAAC;YAC1C,CAAC;YACD,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC1C,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK;KACtB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * First-party form lifecycle events fired via `trackEvent`.
3
+ * Sites map these names to GTM tags or HubSpot behavioural events.
4
+ *
5
+ * Payloads are non-PII: never field values, emails, names, phones, or free text.
6
+ */
7
+ export declare const FORM_ANALYTICS_EVENTS: {
8
+ readonly view: 'form_view';
9
+ readonly start: 'form_start';
10
+ readonly submit: 'form_submit';
11
+ readonly error: 'form_error';
12
+ };
13
+ export type FormAnalyticsEventName = (typeof FORM_ANALYTICS_EVENTS)[keyof typeof FORM_ANALYTICS_EVENTS];
14
+ export declare const FORM_ANALYTICS_EVENT_NAMES: readonly FormAnalyticsEventName[];
15
+ /** Allowed payload keys — anything else is dropped before `trackEvent`. */
16
+ export declare const FORM_ANALYTICS_ALLOWED_KEYS: readonly ['form_id', 'form_name', 'form_type', 'cms_entry_id', 'step', 'error_code', 'error_category'];
17
+ export type FormAnalyticsAllowedKey = (typeof FORM_ANALYTICS_ALLOWED_KEYS)[number];
18
+ /**
19
+ * Closed set of error categories. Do not pass visitor-facing or API error messages —
20
+ * those can echo field values.
21
+ */
22
+ export declare const FORM_ERROR_CATEGORIES: {
23
+ readonly validation: 'validation';
24
+ readonly api: 'api';
25
+ readonly too_fast: 'too_fast';
26
+ readonly network: 'network';
27
+ readonly redirect: 'redirect';
28
+ };
29
+ export type FormErrorCategory = (typeof FORM_ERROR_CATEGORIES)[keyof typeof FORM_ERROR_CATEGORIES];
30
+ export interface FormAnalyticsIdentity {
31
+ form_id?: string;
32
+ form_name?: string;
33
+ form_type?: string;
34
+ cms_entry_id?: string;
35
+ step?: string | number;
36
+ }
37
+ export interface FormAnalyticsErrorInfo {
38
+ error_code?: string;
39
+ error_category?: FormErrorCategory | string;
40
+ }
41
+ export type FormAnalyticsProperties = FormAnalyticsIdentity & FormAnalyticsErrorInfo;
42
+ export type FormAnalyticsTrackFn = (event: string, properties: Record<string, unknown>) => void;
43
+ //# sourceMappingURL=formAnalyticsTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formAnalyticsTypes.d.ts","sourceRoot":"","sources":["../../../src/analytics/form/formAnalyticsTypes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;aAChC,IAAI,EAAE,WAAW;aACjB,KAAK,EAAE,YAAY;aACnB,MAAM,EAAE,aAAa;aACrB,KAAK,EAAE,YAAY;CACX,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAChC,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC;AAErE,eAAO,MAAM,0BAA0B,EAAE,SAAS,sBAAsB,EAKvE,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,2BAA2B,YACtC,SAAS,EACT,WAAW,EACX,WAAW,EACX,cAAc,EACd,MAAM,EACN,YAAY,EACZ,gBAAgB,CACR,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnF;;;GAGG;AACH,eAAO,MAAM,qBAAqB;aAChC,UAAU,EAAE,YAAY;aACxB,GAAG,EAAE,KAAK;aACV,QAAQ,EAAE,UAAU;aACpB,OAAO,EAAE,SAAS;aAClB,QAAQ,EAAE,UAAU;CACZ,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC;AAEnG,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;CAC7C;AAED,MAAM,MAAM,uBAAuB,GAAG,qBAAqB,GAAG,sBAAsB,CAAC;AAErF,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * First-party form lifecycle events fired via `trackEvent`.
3
+ * Sites map these names to GTM tags or HubSpot behavioural events.
4
+ *
5
+ * Payloads are non-PII: never field values, emails, names, phones, or free text.
6
+ */
7
+ export const FORM_ANALYTICS_EVENTS = {
8
+ view: 'form_view',
9
+ start: 'form_start',
10
+ submit: 'form_submit',
11
+ error: 'form_error',
12
+ };
13
+ export const FORM_ANALYTICS_EVENT_NAMES = [
14
+ FORM_ANALYTICS_EVENTS.view,
15
+ FORM_ANALYTICS_EVENTS.start,
16
+ FORM_ANALYTICS_EVENTS.submit,
17
+ FORM_ANALYTICS_EVENTS.error,
18
+ ];
19
+ /** Allowed payload keys — anything else is dropped before `trackEvent`. */
20
+ export const FORM_ANALYTICS_ALLOWED_KEYS = [
21
+ 'form_id',
22
+ 'form_name',
23
+ 'form_type',
24
+ 'cms_entry_id',
25
+ 'step',
26
+ 'error_code',
27
+ 'error_category',
28
+ ];
29
+ /**
30
+ * Closed set of error categories. Do not pass visitor-facing or API error messages —
31
+ * those can echo field values.
32
+ */
33
+ export const FORM_ERROR_CATEGORIES = {
34
+ validation: 'validation',
35
+ api: 'api',
36
+ too_fast: 'too_fast',
37
+ network: 'network',
38
+ redirect: 'redirect',
39
+ };
40
+ //# sourceMappingURL=formAnalyticsTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formAnalyticsTypes.js","sourceRoot":"","sources":["../../../src/analytics/form/formAnalyticsTypes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,YAAY;IACnB,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE,YAAY;CACX,CAAC;AAKX,MAAM,CAAC,MAAM,0BAA0B,GAAsC;IAC3E,qBAAqB,CAAC,IAAI;IAC1B,qBAAqB,CAAC,KAAK;IAC3B,qBAAqB,CAAC,MAAM;IAC5B,qBAAqB,CAAC,KAAK;CAC5B,CAAC;AAEF,2EAA2E;AAC3E,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,SAAS;IACT,WAAW;IACX,WAAW;IACX,cAAc;IACd,MAAM;IACN,YAAY;IACZ,gBAAgB;CACR,CAAC;AAIX;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,UAAU,EAAE,YAAY;IACxB,GAAG,EAAE,KAAK;IACV,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;CACZ,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { type CreateFormAnalyticsSessionOptions, createFormAnalyticsSession, type FormAnalyticsSession, type FormAnalyticsSessionState, } from './createFormAnalyticsSession';
2
+ export { FORM_ANALYTICS_ALLOWED_KEYS, FORM_ANALYTICS_EVENT_NAMES, FORM_ANALYTICS_EVENTS, FORM_ERROR_CATEGORIES, type FormAnalyticsAllowedKey, type FormAnalyticsErrorInfo, type FormAnalyticsEventName, type FormAnalyticsIdentity, type FormAnalyticsProperties, type FormAnalyticsTrackFn, type FormErrorCategory, } from './formAnalyticsTypes';
3
+ export { sanitizeFormAnalyticsProperties } from './sanitizeFormAnalyticsProperties';
4
+ export { type UseFormAnalyticsOptions, useFormAnalytics } from './useFormAnalytics';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analytics/form/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iCAAiC,EACtC,0BAA0B,EAC1B,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,GAC/B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,EAAE,KAAK,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { createFormAnalyticsSession, } from './createFormAnalyticsSession';
2
+ export { FORM_ANALYTICS_ALLOWED_KEYS, FORM_ANALYTICS_EVENT_NAMES, FORM_ANALYTICS_EVENTS, FORM_ERROR_CATEGORIES, } from './formAnalyticsTypes';
3
+ export { sanitizeFormAnalyticsProperties } from './sanitizeFormAnalyticsProperties';
4
+ export { useFormAnalytics } from './useFormAnalytics';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analytics/form/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,0BAA0B,GAG3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,qBAAqB,EACrB,qBAAqB,GAQtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,EAAgC,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type FormAnalyticsProperties } from './formAnalyticsTypes';
2
+ /**
3
+ * Whitelist form analytics payloads. Drops unknown keys, nested objects, arrays,
4
+ * and unconstrained free text. Email/phone heuristics apply to `form_name` only;
5
+ * `form_id`, `cms_entry_id`, and `error_code` use the identifier token pattern
6
+ * so HubSpot UUIDs and digits-only ids are not dropped.
7
+ */
8
+ export declare function sanitizeFormAnalyticsProperties(properties: FormAnalyticsProperties | Record<string, unknown>): Record<string, string | number>;
9
+ //# sourceMappingURL=sanitizeFormAnalyticsProperties.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizeFormAnalyticsProperties.d.ts","sourceRoot":"","sources":["../../../src/analytics/form/sanitizeFormAnalyticsProperties.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AA4C9B;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5D,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAqBjC"}
@@ -0,0 +1,62 @@
1
+ import { FORM_ANALYTICS_ALLOWED_KEYS, FORM_ERROR_CATEGORIES, } from './formAnalyticsTypes';
2
+ const ERROR_CATEGORY_SET = new Set(Object.values(FORM_ERROR_CATEGORIES));
3
+ /** Identifier-like tokens: HubSpot GUIDs, Contentful ids, snake_case codes. */
4
+ const SAFE_TOKEN = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,99}$/;
5
+ const MAX_STRING_LENGTH = 100;
6
+ function looksLikeEmail(value) {
7
+ return value.includes('@');
8
+ }
9
+ function looksLikePhone(value) {
10
+ const digits = value.replace(/\D/g, '');
11
+ return digits.length >= 7 && digits.length / value.replace(/\s/g, '').length >= 0.6;
12
+ }
13
+ function sanitizeString(key, raw) {
14
+ const trimmed = raw.trim().slice(0, MAX_STRING_LENGTH);
15
+ if (!trimmed) {
16
+ return undefined;
17
+ }
18
+ if (key === 'form_name') {
19
+ if (looksLikeEmail(trimmed) || looksLikePhone(trimmed)) {
20
+ return undefined;
21
+ }
22
+ return trimmed;
23
+ }
24
+ if (key === 'error_category') {
25
+ return ERROR_CATEGORY_SET.has(trimmed) ? trimmed : undefined;
26
+ }
27
+ if (key === 'error_code' ||
28
+ key === 'form_id' ||
29
+ key === 'form_type' ||
30
+ key === 'cms_entry_id' ||
31
+ key === 'step') {
32
+ return SAFE_TOKEN.test(trimmed) ? trimmed : undefined;
33
+ }
34
+ return undefined;
35
+ }
36
+ /**
37
+ * Whitelist form analytics payloads. Drops unknown keys, nested objects, arrays,
38
+ * and unconstrained free text. Email/phone heuristics apply to `form_name` only;
39
+ * `form_id`, `cms_entry_id`, and `error_code` use the identifier token pattern
40
+ * so HubSpot UUIDs and digits-only ids are not dropped.
41
+ */
42
+ export function sanitizeFormAnalyticsProperties(properties) {
43
+ const out = {};
44
+ for (const key of FORM_ANALYTICS_ALLOWED_KEYS) {
45
+ const value = properties[key];
46
+ if (value === undefined || value === null) {
47
+ continue;
48
+ }
49
+ if (typeof value === 'number' && Number.isFinite(value) && key === 'step') {
50
+ out[key] = value;
51
+ continue;
52
+ }
53
+ if (typeof value === 'string') {
54
+ const sanitized = sanitizeString(key, value);
55
+ if (sanitized !== undefined) {
56
+ out[key] = sanitized;
57
+ }
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+ //# sourceMappingURL=sanitizeFormAnalyticsProperties.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizeFormAnalyticsProperties.js","sourceRoot":"","sources":["../../../src/analytics/form/sanitizeFormAnalyticsProperties.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,GAGtB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC;AAEjF,+EAA+E;AAC/E,MAAM,UAAU,GAAG,oCAAoC,CAAC;AAExD,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACxC,OAAO,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC;AACtF,CAAC;AAED,SAAS,cAAc,CAAC,GAA4B,EAAE,GAAW;IAC/D,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;IACvD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;QACxB,IAAI,cAAc,CAAC,OAAO,CAAC,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YACvD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,GAAG,KAAK,gBAAgB,EAAE,CAAC;QAC7B,OAAO,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,CAAC;IACD,IACE,GAAG,KAAK,YAAY;QACpB,GAAG,KAAK,SAAS;QACjB,GAAG,KAAK,WAAW;QACnB,GAAG,KAAK,cAAc;QACtB,GAAG,KAAK,MAAM,EACd,CAAC;QACD,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACxD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,+BAA+B,CAC7C,UAA6D;IAE7D,MAAM,GAAG,GAAoC,EAAE,CAAC;IAEhD,KAAK,MAAM,GAAG,IAAI,2BAA2B,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAC1C,SAAS;QACX,CAAC;QACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YAC1E,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACjB,SAAS;QACX,CAAC;QACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC7C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { type FormAnalyticsSession } from './createFormAnalyticsSession';
2
+ import type { FormAnalyticsIdentity } from './formAnalyticsTypes';
3
+ export interface UseFormAnalyticsOptions {
4
+ identity: FormAnalyticsIdentity;
5
+ /** When false, no events are emitted. Default true. */
6
+ enabled?: boolean;
7
+ }
8
+ /**
9
+ * Binds a form analytics session to `useAnalytics().trackEvent`.
10
+ * Fires `form_view` once on mount when enabled (not on viewport impression).
11
+ */
12
+ export declare function useFormAnalytics({ identity, enabled, }: UseFormAnalyticsOptions): FormAnalyticsSession;
13
+ //# sourceMappingURL=useFormAnalytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFormAnalytics.d.ts","sourceRoot":"","sources":["../../../src/analytics/form/useFormAnalytics.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAA0B,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE1F,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,qBAAqB,CAAC;IAChC,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,OAAc,GACf,EAAE,uBAAuB,GAAG,oBAAoB,CAsEhD"}
@@ -0,0 +1,67 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useRef } from 'react';
3
+ import { useAnalytics } from '../useAnalytics';
4
+ import { createFormAnalyticsSession, } from './createFormAnalyticsSession';
5
+ /**
6
+ * Binds a form analytics session to `useAnalytics().trackEvent`.
7
+ * Fires `form_view` once on mount when enabled (not on viewport impression).
8
+ */
9
+ export function useFormAnalytics({ identity, enabled = true, }) {
10
+ const { trackEvent } = useAnalytics();
11
+ const trackEventRef = useRef(trackEvent);
12
+ trackEventRef.current = trackEvent;
13
+ const sessionRef = useRef(null);
14
+ const formId = identity.form_id ?? '';
15
+ const formName = identity.form_name ?? '';
16
+ const formType = identity.form_type ?? '';
17
+ const cmsEntryId = identity.cms_entry_id ?? '';
18
+ const step = identity.step ?? '';
19
+ useEffect(() => {
20
+ if (!enabled) {
21
+ sessionRef.current = null;
22
+ return;
23
+ }
24
+ const session = createFormAnalyticsSession({
25
+ identity: {
26
+ form_id: formId || undefined,
27
+ form_name: formName || undefined,
28
+ form_type: formType || undefined,
29
+ cms_entry_id: cmsEntryId || undefined,
30
+ step: step === '' ? undefined : step,
31
+ },
32
+ track: (event, properties) => {
33
+ trackEventRef.current(event, properties);
34
+ },
35
+ });
36
+ sessionRef.current = session;
37
+ session.markView();
38
+ return () => {
39
+ if (sessionRef.current === session) {
40
+ sessionRef.current = null;
41
+ }
42
+ };
43
+ }, [enabled, formId, formName, formType, cmsEntryId, step]);
44
+ const markView = useCallback(() => {
45
+ sessionRef.current?.markView();
46
+ }, []);
47
+ const markStart = useCallback(() => {
48
+ sessionRef.current?.markStart();
49
+ }, []);
50
+ const markSubmit = useCallback(() => {
51
+ sessionRef.current?.markSubmit();
52
+ }, []);
53
+ const markError = useCallback((info) => {
54
+ sessionRef.current?.markError(info);
55
+ }, []);
56
+ const getState = useCallback(() => {
57
+ return (sessionRef.current?.getState() ?? {
58
+ viewed: false,
59
+ started: false,
60
+ submitCount: 0,
61
+ errorCount: 0,
62
+ validationErrorThisAttempt: false,
63
+ });
64
+ }, []);
65
+ return { markView, markStart, markSubmit, markError, getState };
66
+ }
67
+ //# sourceMappingURL=useFormAnalytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFormAnalytics.js","sourceRoot":"","sources":["../../../src/analytics/form/useFormAnalytics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Slim client-safe entry for form lifecycle analytics.
3
+ * Import from `@se-studio/core-ui/form-analytics` in HubSpot/forms so the
4
+ * main `core-ui` barrel (Visual / Lottie) is not pulled into the form graph.
5
+ */
6
+ export { AnalyticsProvider, useAnalyticsContext } from './AnalyticsProvider';
7
+ export { type CreateFormAnalyticsSessionOptions, createFormAnalyticsSession, FORM_ANALYTICS_ALLOWED_KEYS, FORM_ANALYTICS_EVENT_NAMES, FORM_ANALYTICS_EVENTS, FORM_ERROR_CATEGORIES, type FormAnalyticsAllowedKey, type FormAnalyticsErrorInfo, type FormAnalyticsEventName, type FormAnalyticsIdentity, type FormAnalyticsProperties, type FormAnalyticsSession, type FormAnalyticsSessionState, type FormAnalyticsTrackFn, type FormErrorCategory, sanitizeFormAnalyticsProperties, type UseFormAnalyticsOptions, useFormAnalytics, } from './form';
8
+ export type { AnalyticsAdapter, ClickTrackingProperties, PageTrackingProperties } from './types';
9
+ export { useAnalytics } from './useAnalytics';
10
+ //# sourceMappingURL=formAnalyticsEntry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formAnalyticsEntry.d.ts","sourceRoot":"","sources":["../../src/analytics/formAnalyticsEntry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EACL,KAAK,iCAAiC,EACtC,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,EAC1B,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,+BAA+B,EAC/B,KAAK,uBAAuB,EAC5B,gBAAgB,GACjB,MAAM,QAAQ,CAAC;AAChB,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Slim client-safe entry for form lifecycle analytics.
3
+ * Import from `@se-studio/core-ui/form-analytics` in HubSpot/forms so the
4
+ * main `core-ui` barrel (Visual / Lottie) is not pulled into the form graph.
5
+ */
6
+ export { AnalyticsProvider, useAnalyticsContext } from './AnalyticsProvider';
7
+ export { createFormAnalyticsSession, FORM_ANALYTICS_ALLOWED_KEYS, FORM_ANALYTICS_EVENT_NAMES, FORM_ANALYTICS_EVENTS, FORM_ERROR_CATEGORIES, sanitizeFormAnalyticsProperties, useFormAnalytics, } from './form';
8
+ export { useAnalytics } from './useAnalytics';
9
+ //# sourceMappingURL=formAnalyticsEntry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formAnalyticsEntry.js","sourceRoot":"","sources":["../../src/analytics/formAnalyticsEntry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAEL,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,EAC1B,qBAAqB,EACrB,qBAAqB,EAUrB,+BAA+B,EAE/B,gBAAgB,GACjB,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
@@ -17,6 +17,15 @@ export interface PageTrackingProperties extends IAnalyticsContext {
17
17
  url: string;
18
18
  pathname: string;
19
19
  }
20
+ /**
21
+ * Consent Mode / adapter consent payload.
22
+ * `analytics` maps to analytics_storage (GTM) and HubSpot tracking.
23
+ * `advertising` maps to ad_storage / ad_user_data / ad_personalization (GTM).
24
+ */
25
+ export interface AnalyticsConsentState {
26
+ analytics?: boolean;
27
+ advertising?: boolean;
28
+ }
20
29
  /**
21
30
  * Interface that analytics adapters must implement
22
31
  */
@@ -38,5 +47,16 @@ export interface AnalyticsAdapter {
38
47
  * @param properties - Click tracking properties
39
48
  */
40
49
  trackClick(properties: ClickTrackingProperties): void;
50
+ /**
51
+ * Load vendor scripts. Optional — not all adapters need a script.
52
+ * `ConsentAwareAdapter` always forwards this; vendors that must not load
53
+ * while consent is denied (HubSpot) self-gate inside `initialize`.
54
+ */
55
+ initialize?(): void;
56
+ /**
57
+ * Apply a consent change (GTM Consent Mode update, HubSpot `doNotTrack`).
58
+ * Called by `ConsentAwareAdapter` on initialize and when consent flips.
59
+ */
60
+ updateConsent?(consent: AnalyticsConsentState): void;
41
61
  }
42
62
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/analytics/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEvF;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAErE;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAEjE;;;OAGG;IACH,UAAU,CAAC,UAAU,EAAE,uBAAuB,GAAG,IAAI,CAAC;CACvD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/analytics/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEvF;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAErE;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAEjE;;;OAGG;IACH,UAAU,CAAC,UAAU,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAEtD;;;;OAIG;IACH,UAAU,CAAC,IAAI,IAAI,CAAC;IAEpB;;;OAGG;IACH,aAAa,CAAC,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACtD"}
package/dist/index.d.ts CHANGED
@@ -37,8 +37,11 @@ export type { RenderMark, RenderNode, RenderText } from '@contentful/rich-text-r
37
37
  export type { AnalyticsPageTrackerProps } from './analytics/AnalyticsPageTracker';
38
38
  export { AnalyticsPageTracker } from './analytics/AnalyticsPageTracker';
39
39
  export { AnalyticsProvider, useAnalyticsContext } from './analytics/AnalyticsProvider';
40
+ export type { ConsentAwareAdapterOptions } from './analytics/adapters';
40
41
  export { CompositeAnalyticsAdapter, ConsentAwareAdapter, ConsoleAnalyticsAdapter, GoogleTagManagerAdapter, } from './analytics/adapters';
41
- export type { AnalyticsAdapter, ClickTrackingProperties, PageTrackingProperties, } from './analytics/types';
42
+ export type { ConsentModel, ConsentStatus, PrivacySignalNavigator, ResolveAnalyticsConsentOptions, } from './analytics/consent/resolveAnalyticsConsent';
43
+ export { createConsentGetter, resolveAnalyticsConsent, } from './analytics/consent/resolveAnalyticsConsent';
44
+ export type { AnalyticsAdapter, AnalyticsConsentState, ClickTrackingProperties, PageTrackingProperties, } from './analytics/types';
42
45
  export { useAnalytics } from './analytics/useAnalytics';
43
46
  export type { CreateVideoEngagementSessionOptions, UseVideoEngagementOptions, VideoEngagementEventName, VideoEngagementIdentity, VideoEngagementSession, VideoEngagementTrackFn, VideoProvider, } from './analytics/video';
44
47
  export { createVideoEngagementSession, useHtml5VideoEngagement, useVideoEngagement, VIDEO_ENGAGEMENT_EVENT_NAMES, VIDEO_ENGAGEMENT_EVENTS, VIDEO_ENGAGEMENT_THRESHOLDS, } from './analytics/video';