@workwell-global/toolkit 1.1.49 → 1.1.50

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 (33) hide show
  1. package/dist/analytics/consent.d.ts +33 -0
  2. package/dist/analytics/consent.d.ts.map +1 -0
  3. package/dist/analytics/consent.js +53 -0
  4. package/dist/analytics/consent.js.map +1 -0
  5. package/dist/analytics/consent.test.d.ts +2 -0
  6. package/dist/analytics/consent.test.d.ts.map +1 -0
  7. package/dist/analytics/consent.test.js +51 -0
  8. package/dist/analytics/consent.test.js.map +1 -0
  9. package/dist/analytics/events.d.ts +82 -0
  10. package/dist/analytics/events.d.ts.map +1 -0
  11. package/dist/analytics/events.js +73 -0
  12. package/dist/analytics/events.js.map +1 -0
  13. package/dist/analytics/index.d.ts +4 -0
  14. package/dist/analytics/index.d.ts.map +1 -0
  15. package/dist/analytics/index.js +4 -0
  16. package/dist/analytics/index.js.map +1 -0
  17. package/dist/analytics/serverTracker.d.ts +36 -0
  18. package/dist/analytics/serverTracker.d.ts.map +1 -0
  19. package/dist/analytics/serverTracker.js +139 -0
  20. package/dist/analytics/serverTracker.js.map +1 -0
  21. package/dist/analytics/serverTracker.test.d.ts +2 -0
  22. package/dist/analytics/serverTracker.test.d.ts.map +1 -0
  23. package/dist/analytics/serverTracker.test.js +139 -0
  24. package/dist/analytics/serverTracker.test.js.map +1 -0
  25. package/dist/models/schemas/api/assignment/TAssignmentAPI.d.ts +1 -1
  26. package/dist/models/schemas/api/batch/SBatchAPI.d.ts +6 -6
  27. package/dist/models/schemas/api/changeRequest/TChangeRequestAPI.d.ts +4 -4
  28. package/dist/models/schemas/api/changeRequest/index.d.ts +4 -4
  29. package/dist/models/schemas/api/referral/TReferralAPI.d.ts +1 -1
  30. package/dist/models/schemas/api/systemUser/TSystemUserAPI.d.ts +4 -4
  31. package/dist/models/schemas/api/systemUser/index.d.ts +4 -4
  32. package/dist/models/schemas/bff/changeRequest/SGetChangeRequestBFF.d.ts +5 -5
  33. package/package.json +10 -1
@@ -0,0 +1,33 @@
1
+ /** Cookie set on the parent domain so one accept covers all subdomains. */
2
+ export declare const CONSENT_COOKIE_NAME = "wwl_consent";
3
+ /** Request header web apps forward to the API so server-side events respect consent. */
4
+ export declare const CONSENT_HEADER_NAME = "x-wwl-consent";
5
+ /** Bump when the consent policy text/categories change; a lower stored version re-prompts the banner. */
6
+ export declare const CONSENT_VERSION = 1;
7
+ export interface IConsentCategories {
8
+ /** Always true: auth/session + the consent cookie itself. */
9
+ functional: true;
10
+ /** Product analytics (Mixpanel). */
11
+ statistics: boolean;
12
+ }
13
+ export interface IConsentState {
14
+ /** Consent policy version the user responded to. */
15
+ v: number;
16
+ /** Epoch ms of the user's choice. */
17
+ ts: number;
18
+ cat: IConsentCategories;
19
+ }
20
+ export declare const buildConsentState: (statistics: boolean, nowMs: number) => IConsentState;
21
+ /** Serialize for the cookie value / forwarded header. URI-encoded JSON. */
22
+ export declare const serializeConsent: (state: IConsentState) => string;
23
+ /**
24
+ * Parse a cookie/header value. Returns null for anything malformed or absent:
25
+ * callers must treat null as "no consent given" (and the banner re-prompts).
26
+ */
27
+ export declare const parseConsent: (raw: string | null | undefined) => IConsentState | null;
28
+ /**
29
+ * True only for a well-formed, current-version consent with statistics on.
30
+ * An outdated version means the user has not consented to the CURRENT policy.
31
+ */
32
+ export declare const hasStatisticsConsent: (raw: string | null | undefined) => boolean;
33
+ //# sourceMappingURL=consent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/analytics/consent.ts"],"names":[],"mappings":"AAKA,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,gBAAgB,CAAA;AAEhD,wFAAwF;AACxF,eAAO,MAAM,mBAAmB,kBAAkB,CAAA;AAElD,yGAAyG;AACzG,eAAO,MAAM,eAAe,IAAI,CAAA;AAEhC,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,UAAU,EAAE,IAAI,CAAA;IAChB,oCAAoC;IACpC,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,CAAC,EAAE,MAAM,CAAA;IACT,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,kBAAkB,CAAA;CACxB;AAED,eAAO,MAAM,iBAAiB,GAAI,YAAY,OAAO,EAAE,OAAO,MAAM,KAAG,aAIrE,CAAA;AAEF,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,GAAI,OAAO,aAAa,KAAG,MACb,CAAA;AAE3C;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,aAAa,GAAG,IAyB7E,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAC/B,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,KAC7B,OAGF,CAAA"}
@@ -0,0 +1,53 @@
1
+ // Consent contract shared by the web apps (banner writes / reads the cookie)
2
+ // and the API (parses the forwarded header). Blanket v1: 'statistics' is the
3
+ // only toggleable category; 'functional' is always active. Stored per-category
4
+ // from day one so a tiered model later needs no format change.
5
+ /** Cookie set on the parent domain so one accept covers all subdomains. */
6
+ export const CONSENT_COOKIE_NAME = 'wwl_consent';
7
+ /** Request header web apps forward to the API so server-side events respect consent. */
8
+ export const CONSENT_HEADER_NAME = 'x-wwl-consent';
9
+ /** Bump when the consent policy text/categories change; a lower stored version re-prompts the banner. */
10
+ export const CONSENT_VERSION = 1;
11
+ export const buildConsentState = (statistics, nowMs) => ({
12
+ v: CONSENT_VERSION,
13
+ ts: nowMs,
14
+ cat: { functional: true, statistics },
15
+ });
16
+ /** Serialize for the cookie value / forwarded header. URI-encoded JSON. */
17
+ export const serializeConsent = (state) => encodeURIComponent(JSON.stringify(state));
18
+ /**
19
+ * Parse a cookie/header value. Returns null for anything malformed or absent:
20
+ * callers must treat null as "no consent given" (and the banner re-prompts).
21
+ */
22
+ export const parseConsent = (raw) => {
23
+ if (!raw)
24
+ return null;
25
+ try {
26
+ const parsed = JSON.parse(decodeURIComponent(raw));
27
+ if (typeof parsed !== 'object' || parsed === null)
28
+ return null;
29
+ const candidate = parsed;
30
+ if (typeof candidate.v !== 'number' || typeof candidate.ts !== 'number')
31
+ return null;
32
+ if (candidate.cat?.functional !== true ||
33
+ typeof candidate.cat.statistics !== 'boolean')
34
+ return null;
35
+ return {
36
+ v: candidate.v,
37
+ ts: candidate.ts,
38
+ cat: { functional: true, statistics: candidate.cat.statistics },
39
+ };
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ };
45
+ /**
46
+ * True only for a well-formed, current-version consent with statistics on.
47
+ * An outdated version means the user has not consented to the CURRENT policy.
48
+ */
49
+ export const hasStatisticsConsent = (raw) => {
50
+ const state = parseConsent(raw);
51
+ return state !== null && state.v >= CONSENT_VERSION && state.cat.statistics;
52
+ };
53
+ //# sourceMappingURL=consent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/analytics/consent.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,+DAA+D;AAE/D,2EAA2E;AAC3E,MAAM,CAAC,MAAM,mBAAmB,GAAG,aAAa,CAAA;AAEhD,wFAAwF;AACxF,MAAM,CAAC,MAAM,mBAAmB,GAAG,eAAe,CAAA;AAElD,yGAAyG;AACzG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAA;AAiBhC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,UAAmB,EAAE,KAAa,EAAiB,EAAE,CAAC,CAAC;IACvF,CAAC,EAAE,eAAe;IAClB,EAAE,EAAE,KAAK;IACT,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE;CACtC,CAAC,CAAA;AAEF,2EAA2E;AAC3E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAoB,EAAU,EAAE,CAC/D,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAA;AAE3C;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAA8B,EAAwB,EAAE;IACnF,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAA;QAC3D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,CAAA;QAC9D,MAAM,SAAS,GAAG,MAIjB,CAAA;QACD,IAAI,OAAO,SAAS,CAAC,CAAC,KAAK,QAAQ,IAAI,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ;YACrE,OAAO,IAAI,CAAA;QACb,IACE,SAAS,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI;YAClC,OAAO,SAAS,CAAC,GAAG,CAAC,UAAU,KAAK,SAAS;YAE7C,OAAO,IAAI,CAAA;QACb,OAAO;YACL,CAAC,EAAE,SAAS,CAAC,CAAC;YACd,EAAE,EAAE,SAAS,CAAC,EAAE;YAChB,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE;SAChE,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,GAA8B,EACrB,EAAE;IACX,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;IAC/B,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,IAAI,eAAe,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAA;AAC7E,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=consent.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.test.d.ts","sourceRoot":"","sources":["../../src/analytics/consent.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { buildConsentState, CONSENT_VERSION, hasStatisticsConsent, parseConsent, serializeConsent, } from './consent.js';
3
+ describe('consent serialization', () => {
4
+ it('round-trips an accept state', () => {
5
+ const state = buildConsentState(true, 1_756_000_000_000);
6
+ const parsed = parseConsent(serializeConsent(state));
7
+ expect(parsed).toEqual({
8
+ v: CONSENT_VERSION,
9
+ ts: 1_756_000_000_000,
10
+ cat: { functional: true, statistics: true },
11
+ });
12
+ });
13
+ it('round-trips a deny state', () => {
14
+ const state = buildConsentState(false, 1_756_000_000_000);
15
+ const parsed = parseConsent(serializeConsent(state));
16
+ expect(parsed?.cat.statistics).toBe(false);
17
+ expect(parsed?.cat.functional).toBe(true);
18
+ });
19
+ it.each([
20
+ ['undefined', undefined],
21
+ ['null', null],
22
+ ['empty string', ''],
23
+ ['not json', 'garbage'],
24
+ ['json but wrong shape', encodeURIComponent('{"foo":1}')],
25
+ ['missing categories', encodeURIComponent('{"v":1,"ts":2}')],
26
+ [
27
+ 'functional not always-active',
28
+ encodeURIComponent('{"v":1,"ts":2,"cat":{"functional":false,"statistics":true}}'),
29
+ ],
30
+ ])('parseConsent returns null for %s', (_label, raw) => {
31
+ expect(parseConsent(raw)).toBeNull();
32
+ });
33
+ });
34
+ describe('hasStatisticsConsent', () => {
35
+ it('is true only for current-version consent with statistics on', () => {
36
+ const accepted = serializeConsent(buildConsentState(true, 1));
37
+ const denied = serializeConsent(buildConsentState(false, 1));
38
+ expect(hasStatisticsConsent(accepted)).toBe(true);
39
+ expect(hasStatisticsConsent(denied)).toBe(false);
40
+ expect(hasStatisticsConsent(undefined)).toBe(false);
41
+ });
42
+ it('treats consent given under an older policy version as no consent', () => {
43
+ const outdated = serializeConsent({
44
+ v: CONSENT_VERSION - 1,
45
+ ts: 1,
46
+ cat: { functional: true, statistics: true },
47
+ });
48
+ expect(hasStatisticsConsent(outdated)).toBe(false);
49
+ });
50
+ });
51
+ //# sourceMappingURL=consent.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.test.js","sourceRoot":"","sources":["../../src/analytics/consent.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /** Which deployable emitted the event. Registered as a super property client-side, stamped per event server-side. */
2
+ export type TAnalyticsApp = 'auth' | 'portal' | 'services' | 'api';
3
+ /**
4
+ * Coarse population the signed-in user belongs to, for segmentation. Shared
5
+ * vocabulary so the apps cannot diverge: each derives it from whatever signal it
6
+ * already trusts (an RBAC permission in Services, the CRM customer type in the
7
+ * auth hub) but they must agree on the values.
8
+ *
9
+ * Not set by the Portal, where every user is agency-side and `app: 'portal'`
10
+ * already says so. A role filter therefore excludes Portal events by design:
11
+ * see the tracking plan, section 10.3.
12
+ */
13
+ export type TAnalyticsRole = 'worker' | 'agency';
14
+ export declare const AnalyticsEvents: {
15
+ readonly pageViewed: "page_viewed";
16
+ readonly panelViewed: "panel_viewed";
17
+ /**
18
+ * A deliberately named UI click, for journey steps that do not warrant their
19
+ * own semantic event. Middle tier: prefer a semantic KPI event where one
20
+ * exists (assignment_accepted, not element_clicked), and leave everything
21
+ * else to autocapture. Carries a stable snake_case `element_name`, never a
22
+ * translated label.
23
+ */
24
+ readonly elementClicked: "element_clicked";
25
+ readonly formStarted: "form_started";
26
+ readonly formSubmitted: "form_submitted";
27
+ readonly formFailed: "form_failed";
28
+ readonly loginStarted: "login_started";
29
+ readonly loginCompleted: "login_completed";
30
+ readonly loginFailed: "login_failed";
31
+ readonly logoutCompleted: "logout_completed";
32
+ readonly inviteOpened: "invite_opened";
33
+ readonly inviteExpiredViewed: "invite_expired_viewed";
34
+ readonly signupCompleted: "signup_completed";
35
+ readonly emailVerificationRequested: "email_verification_requested";
36
+ readonly emailVerified: "email_verified";
37
+ readonly passwordResetRequested: "password_reset_requested";
38
+ readonly passwordResetCompleted: "password_reset_completed";
39
+ readonly appTileClicked: "app_tile_clicked";
40
+ readonly workerOnboardingStarted: "worker_onboarding_started";
41
+ readonly documentUploaded: "document_uploaded";
42
+ readonly documentViewed: "document_viewed";
43
+ readonly workerViewed: "worker_viewed";
44
+ readonly assignmentViewed: "assignment_viewed";
45
+ readonly onboardingStepViewed: "onboarding_step_viewed";
46
+ readonly onboardingStepCompleted: "onboarding_step_completed";
47
+ readonly kycStarted: "kyc_started";
48
+ readonly insuranceValidationStarted: "insurance_validation_started";
49
+ readonly assignmentAccepted: "assignment_accepted";
50
+ readonly assignmentDeclined: "assignment_declined";
51
+ readonly caseFormSubmitted: "case_form_submitted";
52
+ readonly caseThreadOpened: "case_thread_opened";
53
+ /**
54
+ * The thread's "Did this answer your question?" prompt. `answer` is `yes`
55
+ * (the portal then closes the case, confirmed by the API's `case_closed`) or
56
+ * `no` (prompt dismissed; never reaches the API, so only visible here).
57
+ */
58
+ readonly caseClosePromptAnswered: "case_close_prompt_answered";
59
+ /** Funnel step only: the authoritative record is the API's `worker_referred`. */
60
+ readonly workerReferralSubmitted: "worker_referral_submitted";
61
+ readonly simulationViewed: "simulation_viewed";
62
+ readonly bankDetailsSubmitted: "bank_details_submitted";
63
+ readonly userInvited: "user_invited";
64
+ readonly userRegistered: "user_registered";
65
+ readonly caseCreated: "case_created";
66
+ readonly caseReplyCreated: "case_reply_created";
67
+ readonly caseClosed: "case_closed";
68
+ readonly assignmentStatusChanged: "assignment_status_changed";
69
+ readonly changeRequestCreated: "change_request_created";
70
+ /** An agency referred a worker (CRM referral created at stage `referred`). */
71
+ readonly workerReferred: "worker_referred";
72
+ readonly changeRequestResponded: "change_request_responded";
73
+ readonly kycVerificationCompleted: "kyc_verification_completed";
74
+ readonly documentValidationCompleted: "document_validation_completed";
75
+ readonly contractorDocumentUploaded: "contractor_document_uploaded";
76
+ };
77
+ export type TAnalyticsEventName = (typeof AnalyticsEvents)[keyof typeof AnalyticsEvents];
78
+ /** Allowed property value types. No objects/arrays: keeps properties queryable and PII-review simple. */
79
+ export type TAnalyticsPropValue = string | number | boolean | null | undefined;
80
+ /** Event properties. Property names must be snake_case; no PII (names, emails, free text). */
81
+ export type TAnalyticsProps = Record<string, TAnalyticsPropValue>;
82
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/analytics/events.ts"],"names":[],"mappings":"AAKA,qHAAqH;AACrH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAA;AAElE;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAEhD,eAAO,MAAM,eAAe;;;IAI1B;;;;;;OAMG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAoCH;;;;OAIG;;IAEH,iFAAiF;;;;;;;;;;;IAajF,8EAA8E;;;;;;CAMtE,CAAA;AAEV,MAAM,MAAM,mBAAmB,GAC7B,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,OAAO,eAAe,CAAC,CAAA;AAExD,yGAAyG;AACzG,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAA;AAE9E,8FAA8F;AAC9F,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA"}
@@ -0,0 +1,73 @@
1
+ // Mixpanel event dictionary - single source of truth for event names across
2
+ // all apps and the API. Naming convention: snake_case object_verb (past tense).
3
+ // Never encode dynamic data (ids, dates, page names) in an event NAME; use a
4
+ // property. Tracking plan: /context/docs/mixpanel-tracking-plan.md (workspace).
5
+ export const AnalyticsEvents = {
6
+ // Cross-app (identical shape in all web apps)
7
+ pageViewed: 'page_viewed',
8
+ panelViewed: 'panel_viewed',
9
+ /**
10
+ * A deliberately named UI click, for journey steps that do not warrant their
11
+ * own semantic event. Middle tier: prefer a semantic KPI event where one
12
+ * exists (assignment_accepted, not element_clicked), and leave everything
13
+ * else to autocapture. Carries a stable snake_case `element_name`, never a
14
+ * translated label.
15
+ */
16
+ elementClicked: 'element_clicked',
17
+ formStarted: 'form_started',
18
+ formSubmitted: 'form_submitted',
19
+ formFailed: 'form_failed',
20
+ // wwl-auth (identity moments)
21
+ loginStarted: 'login_started',
22
+ loginCompleted: 'login_completed',
23
+ loginFailed: 'login_failed',
24
+ logoutCompleted: 'logout_completed',
25
+ inviteOpened: 'invite_opened',
26
+ inviteExpiredViewed: 'invite_expired_viewed',
27
+ signupCompleted: 'signup_completed',
28
+ emailVerificationRequested: 'email_verification_requested',
29
+ emailVerified: 'email_verified',
30
+ passwordResetRequested: 'password_reset_requested',
31
+ passwordResetCompleted: 'password_reset_completed',
32
+ appTileClicked: 'app_tile_clicked',
33
+ // Portal (admin/managers)
34
+ workerOnboardingStarted: 'worker_onboarding_started',
35
+ documentUploaded: 'document_uploaded',
36
+ documentViewed: 'document_viewed',
37
+ workerViewed: 'worker_viewed',
38
+ assignmentViewed: 'assignment_viewed',
39
+ // Services (workers/agencies)
40
+ onboardingStepViewed: 'onboarding_step_viewed',
41
+ onboardingStepCompleted: 'onboarding_step_completed',
42
+ kycStarted: 'kyc_started',
43
+ insuranceValidationStarted: 'insurance_validation_started',
44
+ assignmentAccepted: 'assignment_accepted',
45
+ assignmentDeclined: 'assignment_declined',
46
+ caseFormSubmitted: 'case_form_submitted',
47
+ caseThreadOpened: 'case_thread_opened',
48
+ /**
49
+ * The thread's "Did this answer your question?" prompt. `answer` is `yes`
50
+ * (the portal then closes the case, confirmed by the API's `case_closed`) or
51
+ * `no` (prompt dismissed; never reaches the API, so only visible here).
52
+ */
53
+ caseClosePromptAnswered: 'case_close_prompt_answered',
54
+ /** Funnel step only: the authoritative record is the API's `worker_referred`. */
55
+ workerReferralSubmitted: 'worker_referral_submitted',
56
+ simulationViewed: 'simulation_viewed',
57
+ bankDetailsSubmitted: 'bank_details_submitted',
58
+ // API (server-truthy business outcomes - the authoritative record)
59
+ userInvited: 'user_invited',
60
+ userRegistered: 'user_registered',
61
+ caseCreated: 'case_created',
62
+ caseReplyCreated: 'case_reply_created',
63
+ caseClosed: 'case_closed',
64
+ assignmentStatusChanged: 'assignment_status_changed',
65
+ changeRequestCreated: 'change_request_created',
66
+ /** An agency referred a worker (CRM referral created at stage `referred`). */
67
+ workerReferred: 'worker_referred',
68
+ changeRequestResponded: 'change_request_responded',
69
+ kycVerificationCompleted: 'kyc_verification_completed',
70
+ documentValidationCompleted: 'document_validation_completed',
71
+ contractorDocumentUploaded: 'contractor_document_uploaded',
72
+ };
73
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/analytics/events.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,gFAAgF;AAChF,6EAA6E;AAC7E,gFAAgF;AAiBhF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,8CAA8C;IAC9C,UAAU,EAAE,aAAa;IACzB,WAAW,EAAE,cAAc;IAC3B;;;;;;OAMG;IACH,cAAc,EAAE,iBAAiB;IACjC,WAAW,EAAE,cAAc;IAC3B,aAAa,EAAE,gBAAgB;IAC/B,UAAU,EAAE,aAAa;IAEzB,8BAA8B;IAC9B,YAAY,EAAE,eAAe;IAC7B,cAAc,EAAE,iBAAiB;IACjC,WAAW,EAAE,cAAc;IAC3B,eAAe,EAAE,kBAAkB;IACnC,YAAY,EAAE,eAAe;IAC7B,mBAAmB,EAAE,uBAAuB;IAC5C,eAAe,EAAE,kBAAkB;IACnC,0BAA0B,EAAE,8BAA8B;IAC1D,aAAa,EAAE,gBAAgB;IAC/B,sBAAsB,EAAE,0BAA0B;IAClD,sBAAsB,EAAE,0BAA0B;IAClD,cAAc,EAAE,kBAAkB;IAElC,0BAA0B;IAC1B,uBAAuB,EAAE,2BAA2B;IACpD,gBAAgB,EAAE,mBAAmB;IACrC,cAAc,EAAE,iBAAiB;IACjC,YAAY,EAAE,eAAe;IAC7B,gBAAgB,EAAE,mBAAmB;IAErC,8BAA8B;IAC9B,oBAAoB,EAAE,wBAAwB;IAC9C,uBAAuB,EAAE,2BAA2B;IACpD,UAAU,EAAE,aAAa;IACzB,0BAA0B,EAAE,8BAA8B;IAC1D,kBAAkB,EAAE,qBAAqB;IACzC,kBAAkB,EAAE,qBAAqB;IACzC,iBAAiB,EAAE,qBAAqB;IACxC,gBAAgB,EAAE,oBAAoB;IACtC;;;;OAIG;IACH,uBAAuB,EAAE,4BAA4B;IACrD,iFAAiF;IACjF,uBAAuB,EAAE,2BAA2B;IACpD,gBAAgB,EAAE,mBAAmB;IACrC,oBAAoB,EAAE,wBAAwB;IAE9C,mEAAmE;IACnE,WAAW,EAAE,cAAc;IAC3B,cAAc,EAAE,iBAAiB;IACjC,WAAW,EAAE,cAAc;IAC3B,gBAAgB,EAAE,oBAAoB;IACtC,UAAU,EAAE,aAAa;IACzB,uBAAuB,EAAE,2BAA2B;IACpD,oBAAoB,EAAE,wBAAwB;IAC9C,8EAA8E;IAC9E,cAAc,EAAE,iBAAiB;IACjC,sBAAsB,EAAE,0BAA0B;IAClD,wBAAwB,EAAE,4BAA4B;IACtD,2BAA2B,EAAE,+BAA+B;IAC5D,0BAA0B,EAAE,8BAA8B;CAClD,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './events.js';
2
+ export * from './consent.js';
3
+ export * from './serverTracker.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './events.js';
2
+ export * from './consent.js';
3
+ export * from './serverTracker.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA"}
@@ -0,0 +1,36 @@
1
+ import type { TAnalyticsApp, TAnalyticsEventName, TAnalyticsProps } from './events.js';
2
+ export declare const MIXPANEL_EU_HOST = "https://api-eu.mixpanel.com";
3
+ export interface IServerTrackerOptions {
4
+ /** Mixpanel project token. Missing or an unresolved "$(...)" pipeline macro disables the tracker (no-op, one warning). */
5
+ token: string | undefined;
6
+ /** Stamped on every event. Defaults to 'api'. */
7
+ app?: TAnalyticsApp;
8
+ host?: string;
9
+ /** Events per /import request. Mixpanel caps at 2000; keep batches small. */
10
+ maxBatchSize?: number;
11
+ flushIntervalMs?: number;
12
+ /** Send attempts per batch on 429/5xx/network failure before dropping. */
13
+ maxRetries?: number;
14
+ /** Injectable for tests and future runtimes. Defaults to globalThis.fetch. */
15
+ fetchImpl?: typeof fetch;
16
+ /** Failure reporter. Defaults to console.warn. Must not throw. */
17
+ onError?: (message: string, detail?: unknown) => void;
18
+ }
19
+ export interface ITrackOptions {
20
+ /** The acting user's identity ($user_id, e.g. the Auth0 sub). Required and non-empty. */
21
+ distinctId: string;
22
+ props?: TAnalyticsProps;
23
+ /** Event time, epoch ms. Defaults to now. */
24
+ timeMs?: number;
25
+ }
26
+ export interface IServerTracker {
27
+ /** False when the token was missing/invalid at creation: all calls no-op. */
28
+ readonly enabled: boolean;
29
+ track: (event: TAnalyticsEventName, options: ITrackOptions) => void;
30
+ /** Send everything queued now. Resolves when the queue has drained (or dropped). */
31
+ flush: () => Promise<void>;
32
+ /** Stop the interval timer and flush. Call on process shutdown. */
33
+ shutdown: () => Promise<void>;
34
+ }
35
+ export declare const createServerTracker: (options: IServerTrackerOptions) => IServerTracker;
36
+ //# sourceMappingURL=serverTracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverTracker.d.ts","sourceRoot":"","sources":["../../src/analytics/serverTracker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EACnB,eAAe,EAChB,MAAM,aAAa,CAAA;AAEpB,eAAO,MAAM,gBAAgB,gCAAgC,CAAA;AAE7D,MAAM,WAAW,qBAAqB;IACpC,0HAA0H;IAC1H,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,iDAAiD;IACjD,GAAG,CAAC,EAAE,aAAa,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,kEAAkE;IAClE,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CACtD;AAED,MAAM,WAAW,aAAa;IAC5B,yFAAyF;IACzF,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,KAAK,EAAE,CAAC,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IACnE,oFAAoF;IACpF,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,mEAAmE;IACnE,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9B;AAqBD,eAAO,MAAM,mBAAmB,GAC9B,SAAS,qBAAqB,KAC7B,cAmJF,CAAA"}
@@ -0,0 +1,139 @@
1
+ // Server-side Mixpanel tracker over the /import HTTP API. Framework-agnostic
2
+ // by design (no Next.js or node: imports) so the API's planned migration off
3
+ // Next.js changes nothing here. Fire-and-forget: an analytics failure must
4
+ // never fail a business request, so nothing in here throws to the caller.
5
+ export const MIXPANEL_EU_HOST = 'https://api-eu.mixpanel.com';
6
+ const isUsableToken = (token) => typeof token === 'string' && token.length > 0 && !/^\$\(.+\)$/.test(token);
7
+ export const createServerTracker = (options) => {
8
+ const { token, app = 'api', host = MIXPANEL_EU_HOST, maxBatchSize = 200, flushIntervalMs = 10_000, maxRetries = 3, fetchImpl = globalThis.fetch, onError = (message, detail) =>
9
+ // eslint-disable-next-line no-console
10
+ console.warn(`[analytics] ${message}`, detail ?? ''), } = options;
11
+ const enabled = isUsableToken(token);
12
+ if (!enabled) {
13
+ onError('Mixpanel token missing or unresolved macro - tracking disabled');
14
+ return {
15
+ enabled: false,
16
+ track: () => undefined,
17
+ flush: () => Promise.resolve(),
18
+ shutdown: () => Promise.resolve(),
19
+ };
20
+ }
21
+ const importUrl = `${host}/import?strict=1`;
22
+ const authHeader = `Basic ${btoa(`${token}:`)}`;
23
+ let queue = [];
24
+ let timer;
25
+ let inFlight;
26
+ const scheduleFlush = () => {
27
+ if (timer !== undefined)
28
+ return;
29
+ timer = setTimeout(() => {
30
+ timer = undefined;
31
+ void flush();
32
+ }, flushIntervalMs);
33
+ // Do not keep the process alive for analytics (Node-only; absent in browsers).
34
+ const unref = timer.unref;
35
+ if (typeof unref === 'function')
36
+ unref.call(timer);
37
+ };
38
+ const sendBatch = async (batch) => {
39
+ try {
40
+ const response = await fetchImpl(importUrl, {
41
+ method: 'POST',
42
+ headers: {
43
+ authorization: authHeader,
44
+ 'content-type': 'application/json',
45
+ accept: 'application/json',
46
+ },
47
+ body: JSON.stringify(batch.map(pending => pending.body)),
48
+ });
49
+ if (response.ok)
50
+ return;
51
+ if (response.status === 429 || response.status >= 500) {
52
+ requeue(batch, `Mixpanel /import responded ${response.status}`);
53
+ return;
54
+ }
55
+ // 4xx other than 429: strict validation rejected the payload; retrying cannot help.
56
+ onError(`Mixpanel /import rejected batch (${response.status}) - ${batch.length} event(s) dropped`, await response.text().catch(() => undefined));
57
+ }
58
+ catch (error) {
59
+ requeue(batch, 'Mixpanel /import request failed');
60
+ void error;
61
+ }
62
+ };
63
+ const requeue = (batch, reason) => {
64
+ const retryable = batch
65
+ .map(pending => ({ ...pending, attempts: pending.attempts + 1 }))
66
+ .filter(pending => pending.attempts < maxRetries);
67
+ const dropped = batch.length - retryable.length;
68
+ if (dropped > 0)
69
+ onError(`${reason} - ${dropped} event(s) dropped after ${maxRetries} attempts`);
70
+ if (retryable.length > 0) {
71
+ queue.push(...retryable);
72
+ scheduleFlush();
73
+ }
74
+ };
75
+ // One drain pass sends only what was queued when it started; failed events
76
+ // are requeued at the back with attempts+1 for the next pass, so a single
77
+ // pass never resends the same event. Retry volume is bounded by maxRetries.
78
+ // $insert_id makes any duplicate delivery a safe no-op on Mixpanel's side.
79
+ const drain = async () => {
80
+ let remaining = queue.length;
81
+ while (remaining > 0) {
82
+ const batch = queue.slice(0, Math.min(maxBatchSize, remaining));
83
+ queue = queue.slice(batch.length);
84
+ remaining -= batch.length;
85
+ await sendBatch(batch);
86
+ }
87
+ };
88
+ const flush = async () => {
89
+ // Loop until the queue is fully drained: picks up events queued while a
90
+ // drain was in flight, and retries requeued failures immediately. This
91
+ // terminates because failures increment attempts and drop at maxRetries.
92
+ while (inFlight !== undefined || queue.length > 0) {
93
+ if (inFlight === undefined)
94
+ inFlight = drain().finally(() => {
95
+ inFlight = undefined;
96
+ });
97
+ await inFlight;
98
+ }
99
+ };
100
+ const track = (event, trackOptions) => {
101
+ try {
102
+ const { distinctId, props, timeMs } = trackOptions;
103
+ if (!distinctId) {
104
+ onError(`track('${event}') called without a distinctId - event dropped`);
105
+ return;
106
+ }
107
+ queue.push({
108
+ attempts: 0,
109
+ body: {
110
+ event,
111
+ properties: {
112
+ ...props,
113
+ time: timeMs ?? Date.now(),
114
+ distinct_id: distinctId,
115
+ $insert_id: globalThis.crypto.randomUUID(),
116
+ ip: '0',
117
+ app,
118
+ },
119
+ },
120
+ });
121
+ if (queue.length >= maxBatchSize)
122
+ void flush();
123
+ else
124
+ scheduleFlush();
125
+ }
126
+ catch (error) {
127
+ onError('track() failed', error);
128
+ }
129
+ };
130
+ const shutdown = async () => {
131
+ if (timer !== undefined) {
132
+ clearTimeout(timer);
133
+ timer = undefined;
134
+ }
135
+ await flush();
136
+ };
137
+ return { enabled: true, track, flush, shutdown };
138
+ };
139
+ //# sourceMappingURL=serverTracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverTracker.js","sourceRoot":"","sources":["../../src/analytics/serverTracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=serverTracker.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverTracker.test.d.ts","sourceRoot":"","sources":["../../src/analytics/serverTracker.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,139 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { AnalyticsEvents } from './events.js';
3
+ import { createServerTracker, MIXPANEL_EU_HOST } from './serverTracker.js';
4
+ const okResponse = () => new Response('{"status":"OK"}', { status: 200 });
5
+ const errorResponse = (status) => new Response('{"error":"boom"}', { status });
6
+ describe('event dictionary hygiene', () => {
7
+ it('all event names are unique snake_case', () => {
8
+ const names = Object.values(AnalyticsEvents);
9
+ expect(new Set(names).size).toBe(names.length);
10
+ for (const name of names) {
11
+ expect(name).toMatch(/^[a-z][a-z0-9]*(_[a-z0-9]+)*$/);
12
+ }
13
+ });
14
+ });
15
+ describe('createServerTracker', () => {
16
+ beforeEach(() => {
17
+ vi.useFakeTimers();
18
+ });
19
+ afterEach(() => {
20
+ vi.useRealTimers();
21
+ });
22
+ it('no-ops with a warning when the token is missing or an unresolved macro', () => {
23
+ const onError = vi.fn();
24
+ const fetchImpl = vi.fn();
25
+ for (const token of [undefined, '', '$(MIXPANEL_TOKEN)']) {
26
+ const tracker = createServerTracker({ token, onError, fetchImpl });
27
+ tracker.track(AnalyticsEvents.caseCreated, { distinctId: 'auth0|1' });
28
+ expect(tracker.enabled).toBe(false);
29
+ }
30
+ expect(onError).toHaveBeenCalledTimes(3);
31
+ expect(fetchImpl).not.toHaveBeenCalled();
32
+ });
33
+ it('sends events to EU /import with auth, $insert_id, ip 0 and app stamp', async () => {
34
+ const fetchImpl = vi.fn().mockResolvedValue(okResponse());
35
+ const tracker = createServerTracker({ token: 'tok-123', fetchImpl });
36
+ tracker.track(AnalyticsEvents.caseCreated, {
37
+ distinctId: 'auth0|abc',
38
+ props: { case_type: 'question' },
39
+ timeMs: 1_756_000_000_000,
40
+ });
41
+ await tracker.flush();
42
+ expect(fetchImpl).toHaveBeenCalledTimes(1);
43
+ const [url, init] = fetchImpl.mock.calls[0];
44
+ expect(url).toBe(`${MIXPANEL_EU_HOST}/import?strict=1`);
45
+ expect(init.headers.authorization).toBe(`Basic ${btoa('tok-123:')}`);
46
+ const body = JSON.parse(init.body);
47
+ expect(body).toHaveLength(1);
48
+ expect(body[0]?.event).toBe('case_created');
49
+ expect(body[0]?.properties).toMatchObject({
50
+ time: 1_756_000_000_000,
51
+ distinct_id: 'auth0|abc',
52
+ ip: '0',
53
+ app: 'api',
54
+ case_type: 'question',
55
+ });
56
+ expect(body[0]?.properties.$insert_id).toMatch(/^[0-9a-f-]{36}$/);
57
+ });
58
+ it('flushes on the interval without an explicit flush call', async () => {
59
+ const fetchImpl = vi.fn().mockResolvedValue(okResponse());
60
+ const tracker = createServerTracker({
61
+ token: 'tok',
62
+ fetchImpl,
63
+ flushIntervalMs: 5_000,
64
+ });
65
+ tracker.track(AnalyticsEvents.userInvited, { distinctId: 'auth0|1' });
66
+ expect(fetchImpl).not.toHaveBeenCalled();
67
+ await vi.advanceTimersByTimeAsync(5_000);
68
+ expect(fetchImpl).toHaveBeenCalledTimes(1);
69
+ });
70
+ it('flushes immediately when the batch size is reached, splitting batches', async () => {
71
+ const fetchImpl = vi.fn().mockResolvedValue(okResponse());
72
+ const tracker = createServerTracker({
73
+ token: 'tok',
74
+ fetchImpl,
75
+ maxBatchSize: 2,
76
+ });
77
+ tracker.track(AnalyticsEvents.userInvited, { distinctId: 'a' });
78
+ tracker.track(AnalyticsEvents.userInvited, { distinctId: 'b' });
79
+ tracker.track(AnalyticsEvents.userInvited, { distinctId: 'c' });
80
+ await tracker.flush();
81
+ const sentCounts = fetchImpl.mock.calls.map(call => JSON.parse(call[1].body).length);
82
+ expect(sentCounts.reduce((sum, count) => sum + count, 0)).toBe(3);
83
+ for (const count of sentCounts)
84
+ expect(count).toBeLessThanOrEqual(2);
85
+ });
86
+ it('retries retryable failures up to maxRetries within a flush, then drops', async () => {
87
+ const onError = vi.fn();
88
+ const fetchImpl = vi.fn().mockResolvedValue(errorResponse(503));
89
+ const tracker = createServerTracker({
90
+ token: 'tok',
91
+ fetchImpl,
92
+ maxRetries: 2,
93
+ onError,
94
+ });
95
+ tracker.track(AnalyticsEvents.caseCreated, { distinctId: 'a' });
96
+ await tracker.flush(); // attempt 1 fails + retried once, then dropped (maxRetries 2)
97
+ await tracker.flush(); // nothing left
98
+ expect(fetchImpl).toHaveBeenCalledTimes(2);
99
+ expect(onError).toHaveBeenCalledWith(expect.stringContaining('dropped after 2 attempts'));
100
+ });
101
+ it('recovers events on a retry after a transient failure', async () => {
102
+ const fetchImpl = vi
103
+ .fn()
104
+ .mockRejectedValueOnce(new Error('network down'))
105
+ .mockResolvedValue(okResponse());
106
+ const tracker = createServerTracker({ token: 'tok', fetchImpl });
107
+ tracker.track(AnalyticsEvents.caseCreated, { distinctId: 'a' });
108
+ await tracker.flush();
109
+ await tracker.flush();
110
+ expect(fetchImpl).toHaveBeenCalledTimes(2);
111
+ });
112
+ it('drops the batch without retry on a non-retryable 4xx', async () => {
113
+ const onError = vi.fn();
114
+ const fetchImpl = vi.fn().mockResolvedValue(errorResponse(400));
115
+ const tracker = createServerTracker({ token: 'tok', fetchImpl, onError });
116
+ tracker.track(AnalyticsEvents.caseCreated, { distinctId: 'a' });
117
+ await tracker.flush();
118
+ await tracker.flush();
119
+ expect(fetchImpl).toHaveBeenCalledTimes(1);
120
+ expect(onError).toHaveBeenCalledWith(expect.stringContaining('rejected batch (400)'), expect.anything());
121
+ });
122
+ it('drops events without a distinctId and never throws', () => {
123
+ const onError = vi.fn();
124
+ const fetchImpl = vi.fn();
125
+ const tracker = createServerTracker({ token: 'tok', fetchImpl, onError });
126
+ expect(() => tracker.track(AnalyticsEvents.caseCreated, { distinctId: '' })).not.toThrow();
127
+ expect(onError).toHaveBeenCalledWith(expect.stringContaining('without a distinctId'));
128
+ });
129
+ it('shutdown flushes the queue and stops the timer', async () => {
130
+ const fetchImpl = vi.fn().mockResolvedValue(okResponse());
131
+ const tracker = createServerTracker({ token: 'tok', fetchImpl });
132
+ tracker.track(AnalyticsEvents.caseCreated, { distinctId: 'a' });
133
+ await tracker.shutdown();
134
+ expect(fetchImpl).toHaveBeenCalledTimes(1);
135
+ await vi.advanceTimersByTimeAsync(60_000);
136
+ expect(fetchImpl).toHaveBeenCalledTimes(1);
137
+ });
138
+ });
139
+ //# sourceMappingURL=serverTracker.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverTracker.test.js","sourceRoot":"","sources":["../../src/analytics/serverTracker.test.ts"],"names":[],"mappings":"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"}
@@ -4107,7 +4107,7 @@ export type TAssignmentAPI = Omit<z.infer<typeof AssignmentEntity.SResponseAPI>,
4107
4107
  };
4108
4108
  export type TAssignmentPayloadAPI = z.infer<typeof AssignmentEntity.SPayloadAPI>;
4109
4109
  export declare const SAssignmentsQueryParams: z.ZodEffects<z.ZodType<URLSearchParams, z.ZodTypeDef, URLSearchParams>, {
4110
- filter: "agent" | "agency" | "contractor";
4110
+ filter: "agency" | "agent" | "contractor";
4111
4111
  id: string;
4112
4112
  includeChildren?: boolean | undefined;
4113
4113
  includeDocuments?: boolean | undefined;
@@ -6,15 +6,15 @@ export declare const SBatchAPI: z.ZodUnion<[z.ZodObject<{
6
6
  id: z.ZodOptional<z.ZodString>;
7
7
  refId: z.ZodOptional<z.ZodNumber>;
8
8
  }, "strip", z.ZodTypeAny, {
9
+ method: "POST" | "PATCH" | "DELETE";
9
10
  type: string;
10
11
  data: Record<string, unknown>;
11
- method: "POST" | "PATCH" | "DELETE";
12
12
  id?: string | undefined;
13
13
  refId?: number | undefined;
14
14
  }, {
15
+ method: "POST" | "PATCH" | "DELETE";
15
16
  type: string;
16
17
  data: Record<string, unknown>;
17
- method: "POST" | "PATCH" | "DELETE";
18
18
  id?: string | undefined;
19
19
  refId?: number | undefined;
20
20
  }>, z.ZodObject<{
@@ -25,18 +25,18 @@ export declare const SBatchAPI: z.ZodUnion<[z.ZodObject<{
25
25
  query: z.ZodOptional<z.ZodString>;
26
26
  key: z.ZodOptional<z.ZodString>;
27
27
  }, "strip", z.ZodTypeAny, {
28
- type: string;
29
28
  method: "GET";
29
+ type: string;
30
+ key?: string | undefined;
30
31
  id?: string | undefined;
31
32
  data?: Record<string, unknown> | undefined;
32
- key?: string | undefined;
33
33
  query?: string | undefined;
34
34
  }, {
35
- type: string;
36
35
  method: "GET";
36
+ type: string;
37
+ key?: string | undefined;
37
38
  id?: string | undefined;
38
39
  data?: Record<string, unknown> | undefined;
39
- key?: string | undefined;
40
40
  query?: string | undefined;
41
41
  }>]>;
42
42
  //# sourceMappingURL=SBatchAPI.d.ts.map
@@ -677,8 +677,8 @@ export declare const ChangeRequestEntity: {
677
677
  };
678
678
  }, false>, z.UnknownKeysParam, z.ZodTypeAny, {
679
679
  changerequestId: string;
680
- source?: number | null | undefined;
681
680
  status?: number | null | undefined;
681
+ source?: number | null | undefined;
682
682
  void?: number | null | undefined;
683
683
  workCountryValue?: string | null | undefined;
684
684
  etag?: string | null | undefined;
@@ -743,8 +743,8 @@ export declare const ChangeRequestEntity: {
743
743
  documents?: any[] | null | undefined;
744
744
  }, {
745
745
  changerequestId: string;
746
- source?: number | null | undefined;
747
746
  status?: number | null | undefined;
747
+ source?: number | null | undefined;
748
748
  void?: number | null | undefined;
749
749
  workCountryValue?: string | null | undefined;
750
750
  etag?: string | null | undefined;
@@ -855,8 +855,8 @@ export declare const ChangeRequestEntity: {
855
855
  readonly reasonForChange: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
856
856
  readonly documents: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
857
857
  }, z.UnknownKeysParam, z.ZodTypeAny, {
858
- source?: number | null | undefined;
859
858
  status?: number | null | undefined;
859
+ source?: number | null | undefined;
860
860
  void?: number | null | undefined;
861
861
  workCountryValue?: string | null | undefined;
862
862
  name?: string | null | undefined;
@@ -901,8 +901,8 @@ export declare const ChangeRequestEntity: {
901
901
  reasonForChange?: string | null | undefined;
902
902
  documents?: string | null | undefined;
903
903
  }, {
904
- source?: number | null | undefined;
905
904
  status?: number | null | undefined;
905
+ source?: number | null | undefined;
906
906
  void?: number | null | undefined;
907
907
  workCountryValue?: string | null | undefined;
908
908
  name?: string | null | undefined;
@@ -680,8 +680,8 @@ export declare const ChangeRequest: {
680
680
  };
681
681
  }, false>, import("zod").UnknownKeysParam, import("zod").ZodTypeAny, {
682
682
  changerequestId: string;
683
- source?: number | null | undefined;
684
683
  status?: number | null | undefined;
684
+ source?: number | null | undefined;
685
685
  void?: number | null | undefined;
686
686
  workCountryValue?: string | null | undefined;
687
687
  etag?: string | null | undefined;
@@ -746,8 +746,8 @@ export declare const ChangeRequest: {
746
746
  documents?: any[] | null | undefined;
747
747
  }, {
748
748
  changerequestId: string;
749
- source?: number | null | undefined;
750
749
  status?: number | null | undefined;
750
+ source?: number | null | undefined;
751
751
  void?: number | null | undefined;
752
752
  workCountryValue?: string | null | undefined;
753
753
  etag?: string | null | undefined;
@@ -858,8 +858,8 @@ export declare const ChangeRequest: {
858
858
  readonly reasonForChange: import("zod").ZodOptional<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>>;
859
859
  readonly documents: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodOptional<import("zod").ZodString>>>;
860
860
  }, import("zod").UnknownKeysParam, import("zod").ZodTypeAny, {
861
- source?: number | null | undefined;
862
861
  status?: number | null | undefined;
862
+ source?: number | null | undefined;
863
863
  void?: number | null | undefined;
864
864
  workCountryValue?: string | null | undefined;
865
865
  name?: string | null | undefined;
@@ -904,8 +904,8 @@ export declare const ChangeRequest: {
904
904
  reasonForChange?: string | null | undefined;
905
905
  documents?: string | null | undefined;
906
906
  }, {
907
- source?: number | null | undefined;
908
907
  status?: number | null | undefined;
908
+ source?: number | null | undefined;
909
909
  void?: number | null | undefined;
910
910
  workCountryValue?: string | null | undefined;
911
911
  name?: string | null | undefined;
@@ -3262,7 +3262,7 @@ export type TReferralD365 = z.infer<typeof ReferralEntity.SResponseD365>;
3262
3262
  export type TReferralAPI = z.infer<typeof ReferralEntity.SResponseAPI>;
3263
3263
  export type TReferralPayloadAPI = z.infer<typeof ReferralEntity.SPayloadAPI>;
3264
3264
  export declare const SReferralsQueryParams: z.ZodEffects<z.ZodType<URLSearchParams, z.ZodTypeDef, URLSearchParams>, {
3265
- filter: "agent" | "agency" | "contractor";
3265
+ filter: "agency" | "agent" | "contractor";
3266
3266
  id: string;
3267
3267
  includeChildren: boolean;
3268
3268
  includeDocuments: boolean;
@@ -735,11 +735,11 @@ export declare const SystemUserEntity: {
735
735
  };
736
736
  }, false>, z.UnknownKeysParam, z.ZodTypeAny, {
737
737
  systemUserId: string;
738
+ status?: boolean | null | undefined;
738
739
  description?: string | null | undefined;
739
740
  fax?: string | null | undefined;
740
741
  nickname?: string | null | undefined;
741
742
  pager?: string | null | undefined;
742
- status?: boolean | null | undefined;
743
743
  type?: number | null | undefined;
744
744
  firstName?: string | null | undefined;
745
745
  middleName?: string | null | undefined;
@@ -813,11 +813,11 @@ export declare const SystemUserEntity: {
813
813
  recipientEmails?: string | null | undefined;
814
814
  }, {
815
815
  systemUserId: string;
816
+ status?: boolean | null | undefined;
816
817
  description?: string | null | undefined;
817
818
  fax?: string | null | undefined;
818
819
  nickname?: string | null | undefined;
819
820
  pager?: string | null | undefined;
820
- status?: boolean | null | undefined;
821
821
  type?: number | null | undefined;
822
822
  firstName?: string | null | undefined;
823
823
  middleName?: string | null | undefined;
@@ -953,11 +953,11 @@ export declare const SystemUserEntity: {
953
953
  readonly recipientEmails: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
954
954
  readonly status: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodBoolean>>>;
955
955
  }, z.UnknownKeysParam, z.ZodTypeAny, {
956
+ status?: boolean | null | undefined;
956
957
  description?: string | null | undefined;
957
958
  fax?: string | null | undefined;
958
959
  nickname?: string | null | undefined;
959
960
  pager?: string | null | undefined;
960
- status?: boolean | null | undefined;
961
961
  type?: number | null | undefined;
962
962
  firstName?: string | null | undefined;
963
963
  middleName?: string | null | undefined;
@@ -1015,11 +1015,11 @@ export declare const SystemUserEntity: {
1015
1015
  omnichannelPhoneValue?: string | null | undefined;
1016
1016
  recipientEmails?: string | null | undefined;
1017
1017
  }, {
1018
+ status?: boolean | null | undefined;
1018
1019
  description?: string | null | undefined;
1019
1020
  fax?: string | null | undefined;
1020
1021
  nickname?: string | null | undefined;
1021
1022
  pager?: string | null | undefined;
1022
- status?: boolean | null | undefined;
1023
1023
  type?: number | null | undefined;
1024
1024
  firstName?: string | null | undefined;
1025
1025
  middleName?: string | null | undefined;
@@ -739,11 +739,11 @@ export declare const SystemUser: {
739
739
  };
740
740
  }, false>, import("zod").UnknownKeysParam, import("zod").ZodTypeAny, {
741
741
  systemUserId: string;
742
+ status?: boolean | null | undefined;
742
743
  description?: string | null | undefined;
743
744
  fax?: string | null | undefined;
744
745
  nickname?: string | null | undefined;
745
746
  pager?: string | null | undefined;
746
- status?: boolean | null | undefined;
747
747
  type?: number | null | undefined;
748
748
  firstName?: string | null | undefined;
749
749
  middleName?: string | null | undefined;
@@ -817,11 +817,11 @@ export declare const SystemUser: {
817
817
  recipientEmails?: string | null | undefined;
818
818
  }, {
819
819
  systemUserId: string;
820
+ status?: boolean | null | undefined;
820
821
  description?: string | null | undefined;
821
822
  fax?: string | null | undefined;
822
823
  nickname?: string | null | undefined;
823
824
  pager?: string | null | undefined;
824
- status?: boolean | null | undefined;
825
825
  type?: number | null | undefined;
826
826
  firstName?: string | null | undefined;
827
827
  middleName?: string | null | undefined;
@@ -957,11 +957,11 @@ export declare const SystemUser: {
957
957
  readonly recipientEmails: import("zod").ZodOptional<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>>;
958
958
  readonly status: import("zod").ZodOptional<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodBoolean>>>;
959
959
  }, import("zod").UnknownKeysParam, import("zod").ZodTypeAny, {
960
+ status?: boolean | null | undefined;
960
961
  description?: string | null | undefined;
961
962
  fax?: string | null | undefined;
962
963
  nickname?: string | null | undefined;
963
964
  pager?: string | null | undefined;
964
- status?: boolean | null | undefined;
965
965
  type?: number | null | undefined;
966
966
  firstName?: string | null | undefined;
967
967
  middleName?: string | null | undefined;
@@ -1019,11 +1019,11 @@ export declare const SystemUser: {
1019
1019
  omnichannelPhoneValue?: string | null | undefined;
1020
1020
  recipientEmails?: string | null | undefined;
1021
1021
  }, {
1022
+ status?: boolean | null | undefined;
1022
1023
  description?: string | null | undefined;
1023
1024
  fax?: string | null | undefined;
1024
1025
  nickname?: string | null | undefined;
1025
1026
  pager?: string | null | undefined;
1026
- status?: boolean | null | undefined;
1027
1027
  type?: number | null | undefined;
1028
1028
  firstName?: string | null | undefined;
1029
1029
  middleName?: string | null | undefined;
@@ -280,7 +280,7 @@ export declare const SGetChangeRequestBFF: z.ZodEffects<z.ZodObject<Pick<import(
280
280
  readonly entityKey: "contractorDocument";
281
281
  readonly isArray: true;
282
282
  };
283
- }, false>, "source" | "status" | "endDate" | "changeApplies" | "changeType" | "legalStatus" | "relatedAssignmentValue" | "requestedDate" | "requestedByValue" | "assignmentStageAtSubmission" | "proposedEndDate"> & {
283
+ }, false>, "status" | "source" | "endDate" | "changeApplies" | "changeType" | "legalStatus" | "relatedAssignmentValue" | "requestedDate" | "requestedByValue" | "assignmentStageAtSubmission" | "proposedEndDate"> & {
284
284
  documents: z.ZodOptional<z.ZodArray<z.ZodOptional<z.ZodNullable<z.ZodObject<Pick<import("../../api/_entityTools/SchemasEntityTools.js").TPrettyShape<{
285
285
  readonly _ownerid_value: {
286
286
  readonly pretty: "ownerIdValue";
@@ -593,8 +593,8 @@ export declare const SGetChangeRequestBFF: z.ZodEffects<z.ZodObject<Pick<import(
593
593
  fileName?: string | null | undefined;
594
594
  }>>>, "many">>;
595
595
  }, z.UnknownKeysParam, z.ZodTypeAny, {
596
- source?: number | null | undefined;
597
596
  status?: number | null | undefined;
597
+ source?: number | null | undefined;
598
598
  endDate?: string | null | undefined;
599
599
  changeApplies?: number | null | undefined;
600
600
  changeType?: number | null | undefined;
@@ -610,8 +610,8 @@ export declare const SGetChangeRequestBFF: z.ZodEffects<z.ZodObject<Pick<import(
610
610
  fileName?: string | null | undefined;
611
611
  } | null | undefined)[] | undefined;
612
612
  }, {
613
- source?: number | null | undefined;
614
613
  status?: number | null | undefined;
614
+ source?: number | null | undefined;
615
615
  endDate?: string | null | undefined;
616
616
  changeApplies?: number | null | undefined;
617
617
  changeType?: number | null | undefined;
@@ -627,8 +627,8 @@ export declare const SGetChangeRequestBFF: z.ZodEffects<z.ZodObject<Pick<import(
627
627
  fileName?: string | null | undefined;
628
628
  } | null | undefined)[] | undefined;
629
629
  }>, {
630
- source?: number | null | undefined;
631
630
  status?: number | null | undefined;
631
+ source?: number | null | undefined;
632
632
  endDate?: string | null | undefined;
633
633
  changeApplies?: number | null | undefined;
634
634
  changeType?: number | null | undefined;
@@ -644,8 +644,8 @@ export declare const SGetChangeRequestBFF: z.ZodEffects<z.ZodObject<Pick<import(
644
644
  assignmentId: string | null | undefined;
645
645
  requestedById: string | null | undefined;
646
646
  }, {
647
- source?: number | null | undefined;
648
647
  status?: number | null | undefined;
648
+ source?: number | null | undefined;
649
649
  endDate?: string | null | undefined;
650
650
  changeApplies?: number | null | undefined;
651
651
  changeType?: number | null | undefined;
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "name": "@workwell-global/toolkit",
8
8
  "type": "module",
9
9
  "types": "dist/index.d.ts",
10
- "version": "1.1.49",
10
+ "version": "1.1.50",
11
11
  "files": [
12
12
  "dist"
13
13
  ],
@@ -20,6 +20,9 @@
20
20
  "*": [
21
21
  "./dist/index.d.ts"
22
22
  ],
23
+ "analytics": [
24
+ "./dist/analytics/index.d.ts"
25
+ ],
23
26
  "enums": [
24
27
  "./dist/enums/index.d.ts"
25
28
  ],
@@ -87,6 +90,12 @@
87
90
  "default": "./dist/index.js"
88
91
  },
89
92
  "./package.json": "./package.json",
93
+ "./analytics": {
94
+ "types": "./dist/analytics/index.d.ts",
95
+ "import": "./dist/analytics/index.js",
96
+ "require": "./dist/analytics/index.js",
97
+ "default": "./dist/analytics/index.js"
98
+ },
90
99
  "./enums": {
91
100
  "types": "./dist/enums/index.d.ts",
92
101
  "import": "./dist/enums/index.js",