@datatechsolutions/ui 3.1.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,14 +4,14 @@
4
4
  var chunkRGI74SQH_js = require('../chunk-RGI74SQH.js');
5
5
  var chunkKR2X2WHJ_js = require('../chunk-KR2X2WHJ.js');
6
6
  var chunkTVMLV675_js = require('../chunk-TVMLV675.js');
7
- var chunkP4RVGMZL_js = require('../chunk-P4RVGMZL.js');
8
7
  var chunkZM5MVWIT_js = require('../chunk-ZM5MVWIT.js');
9
- var chunkSU3YPWFW_js = require('../chunk-SU3YPWFW.js');
8
+ var chunkP4RVGMZL_js = require('../chunk-P4RVGMZL.js');
10
9
  var chunkW5OEBO6E_js = require('../chunk-W5OEBO6E.js');
11
10
  var chunkRXZNACMI_js = require('../chunk-RXZNACMI.js');
12
11
  var chunkAOUUZ52N_js = require('../chunk-AOUUZ52N.js');
13
12
  var chunkJSNRCYSO_js = require('../chunk-JSNRCYSO.js');
14
13
  var chunkHDCUWUNH_js = require('../chunk-HDCUWUNH.js');
14
+ var chunkSU3YPWFW_js = require('../chunk-SU3YPWFW.js');
15
15
  var chunkY6AEE56Q_js = require('../chunk-Y6AEE56Q.js');
16
16
  require('../chunk-TIJJHW2Z.js');
17
17
  var chunk3JJWPOK6_js = require('../chunk-3JJWPOK6.js');
@@ -5329,17 +5329,13 @@ Object.defineProperty(exports, "VerifyEmail", {
5329
5329
  enumerable: true,
5330
5330
  get: function () { return chunkTVMLV675_js.VerifyEmail; }
5331
5331
  });
5332
- Object.defineProperty(exports, "UpgradePrompt", {
5333
- enumerable: true,
5334
- get: function () { return chunkP4RVGMZL_js.UpgradePrompt; }
5335
- });
5336
5332
  Object.defineProperty(exports, "ImpersonationBanner", {
5337
5333
  enumerable: true,
5338
5334
  get: function () { return chunkZM5MVWIT_js.ImpersonationBanner; }
5339
5335
  });
5340
- Object.defineProperty(exports, "WorkflowWorkspace", {
5336
+ Object.defineProperty(exports, "UpgradePrompt", {
5341
5337
  enumerable: true,
5342
- get: function () { return chunkSU3YPWFW_js.WorkflowWorkspace; }
5338
+ get: function () { return chunkP4RVGMZL_js.UpgradePrompt; }
5343
5339
  });
5344
5340
  Object.defineProperty(exports, "PlatformSettings", {
5345
5341
  enumerable: true,
@@ -5401,6 +5397,10 @@ Object.defineProperty(exports, "BillingPanel", {
5401
5397
  enumerable: true,
5402
5398
  get: function () { return chunkHDCUWUNH_js.BillingPanel; }
5403
5399
  });
5400
+ Object.defineProperty(exports, "WorkflowWorkspace", {
5401
+ enumerable: true,
5402
+ get: function () { return chunkSU3YPWFW_js.WorkflowWorkspace; }
5403
+ });
5404
5404
  Object.defineProperty(exports, "AgentsWorkspace", {
5405
5405
  enumerable: true,
5406
5406
  get: function () { return chunkY6AEE56Q_js.AgentsWorkspace; }
@@ -2,15 +2,15 @@
2
2
  export { WorkflowCanvasShell } from '../chunk-YIB2YAM5.mjs';
3
3
  export { AdminOrganizationForm, AdminOrganizationList, AdminPermissionList, AdminUserForm, AdminUserList } from '../chunk-B67DP7MI.mjs';
4
4
  export { ConsentScreen, ForgotPassword, MfaChallenge, PasswordlessSignIn, ResetPassword, SignIn, SignUp, SsoConnectorList, SsoEmailForm, UserButton, VerifyEmail } from '../chunk-MVBIAXVN.mjs';
5
- export { UpgradePrompt } from '../chunk-DJ33CSGJ.mjs';
6
5
  export { ImpersonationBanner } from '../chunk-OL73LBX5.mjs';
7
- import { UsersPageView, RolesPageView } from '../chunk-YV72JM4B.mjs';
8
- export { WorkflowWorkspace } from '../chunk-YV72JM4B.mjs';
6
+ export { UpgradePrompt } from '../chunk-DJ33CSGJ.mjs';
9
7
  export { PlatformSettings } from '../chunk-UDDZTTLO.mjs';
10
8
  export { AdminOrganizationDetail } from '../chunk-IRPS5UCS.mjs';
11
9
  export { LinkedAccountsTabContent, MfaManage, MfaSetup, ProfileTabContent, SecurityTabContent, SessionsTabContent, UserProfile } from '../chunk-LEKZUS6N.mjs';
12
10
  export { AuthLayout, BackupCodeGrid, OtpInput, PasswordStrengthMeter, SocialLoginButtons } from '../chunk-5GDKCFM5.mjs';
13
11
  export { BillingPanel } from '../chunk-R4TQWXNG.mjs';
12
+ import { UsersPageView, RolesPageView } from '../chunk-YV72JM4B.mjs';
13
+ export { WorkflowWorkspace } from '../chunk-YV72JM4B.mjs';
14
14
  export { AgentsWorkspace, applyWorkflowExecutionEventToStore, resetWorkflowRunPresentation, useRunEvents, useWorkflowExecution, useWorkflowRunPresentation } from '../chunk-H2D2CRTD.mjs';
15
15
  import '../chunk-5RM6NGZ6.mjs';
16
16
  import { usePlatformState } from '../chunk-3ZUMJTDT.mjs';
@@ -14,9 +14,18 @@ type TelemetryUser = {
14
14
  type TelemetryProviderProps = {
15
15
  /** App slug — e.g. `astrlabe`, `sextant`, `data-br`. Tags every event. */
16
16
  app: string;
17
- /** PostHog project token (write-only key, safe in browser bundles). */
17
+ /**
18
+ * PostHog project token (write-only key, safe in browser bundles).
19
+ * If unset, the provider falls back to `window.__APP_CONFIG__.posthog.key`
20
+ * (set by the CDK-managed `runtime-config.js`) and then to
21
+ * `import.meta.env.VITE_POSTHOG_KEY`. Pass explicitly only for tests
22
+ * or sub-app embeds.
23
+ */
18
24
  projectKey?: string;
19
- /** PostHog ingestion host. Defaults to US Cloud. */
25
+ /**
26
+ * PostHog ingestion host. Falls back through the same chain as
27
+ * `projectKey`; defaults to US Cloud if nothing else resolves.
28
+ */
20
29
  apiHost?: string;
21
30
  /** Whether to record session replays. Defaults to true. */
22
31
  recordSessions?: boolean;
@@ -29,7 +38,7 @@ type TelemetryProviderProps = {
29
38
  * PostHog associates events with the right tenant; pass `null` after
30
39
  * sign-out so the next session is anonymous.
31
40
  */
32
- declare function TelemetryProvider({ app, projectKey, apiHost, recordSessions, user, children, }: TelemetryProviderProps): react_jsx_runtime.JSX.Element;
41
+ declare function TelemetryProvider({ app, projectKey: propProjectKey, apiHost: propApiHost, recordSessions, user, children, }: TelemetryProviderProps): react_jsx_runtime.JSX.Element;
33
42
  /**
34
43
  * Re-export the posthog-js/react hook so consumer SPAs can capture
35
44
  * custom events without importing posthog-js directly:
@@ -14,9 +14,18 @@ type TelemetryUser = {
14
14
  type TelemetryProviderProps = {
15
15
  /** App slug — e.g. `astrlabe`, `sextant`, `data-br`. Tags every event. */
16
16
  app: string;
17
- /** PostHog project token (write-only key, safe in browser bundles). */
17
+ /**
18
+ * PostHog project token (write-only key, safe in browser bundles).
19
+ * If unset, the provider falls back to `window.__APP_CONFIG__.posthog.key`
20
+ * (set by the CDK-managed `runtime-config.js`) and then to
21
+ * `import.meta.env.VITE_POSTHOG_KEY`. Pass explicitly only for tests
22
+ * or sub-app embeds.
23
+ */
18
24
  projectKey?: string;
19
- /** PostHog ingestion host. Defaults to US Cloud. */
25
+ /**
26
+ * PostHog ingestion host. Falls back through the same chain as
27
+ * `projectKey`; defaults to US Cloud if nothing else resolves.
28
+ */
20
29
  apiHost?: string;
21
30
  /** Whether to record session replays. Defaults to true. */
22
31
  recordSessions?: boolean;
@@ -29,7 +38,7 @@ type TelemetryProviderProps = {
29
38
  * PostHog associates events with the right tenant; pass `null` after
30
39
  * sign-out so the next session is anonymous.
31
40
  */
32
- declare function TelemetryProvider({ app, projectKey, apiHost, recordSessions, user, children, }: TelemetryProviderProps): react_jsx_runtime.JSX.Element;
41
+ declare function TelemetryProvider({ app, projectKey: propProjectKey, apiHost: propApiHost, recordSessions, user, children, }: TelemetryProviderProps): react_jsx_runtime.JSX.Element;
33
42
  /**
34
43
  * Re-export the posthog-js/react hook so consumer SPAs can capture
35
44
  * custom events without importing posthog-js directly:
@@ -12,14 +12,35 @@ var posthog__default = /*#__PURE__*/_interopDefault(posthog);
12
12
 
13
13
  // src/platform/telemetry/posthog-provider.tsx
14
14
  var DEFAULT_API_HOST = "https://us.i.posthog.com";
15
+ function resolveConfig(propKey, propHost) {
16
+ let key = propKey ?? "";
17
+ let host = propHost ?? "";
18
+ if (typeof window !== "undefined") {
19
+ const runtime = window.__APP_CONFIG__?.posthog;
20
+ if (!key && runtime?.key) key = runtime.key;
21
+ if (!host && runtime?.host) host = runtime.host;
22
+ }
23
+ try {
24
+ const env = undefined ?? {};
25
+ if (!key) key = env.VITE_POSTHOG_KEY ?? "";
26
+ if (!host) host = env.VITE_POSTHOG_HOST ?? "";
27
+ } catch {
28
+ }
29
+ if (!host) host = DEFAULT_API_HOST;
30
+ return { key, host };
31
+ }
15
32
  function TelemetryProvider({
16
33
  app,
17
- projectKey,
18
- apiHost = DEFAULT_API_HOST,
34
+ projectKey: propProjectKey,
35
+ apiHost: propApiHost,
19
36
  recordSessions = true,
20
37
  user,
21
38
  children
22
39
  }) {
40
+ const { key: projectKey, host: apiHost } = react$1.useMemo(
41
+ () => resolveConfig(propProjectKey, propApiHost),
42
+ [propProjectKey, propApiHost]
43
+ );
23
44
  const enabled = react$1.useMemo(() => {
24
45
  if (!projectKey) return false;
25
46
  if (typeof window === "undefined") return false;
@@ -42,6 +63,15 @@ function TelemetryProvider({
42
63
  capture_pageview: "history_change",
43
64
  capture_pageleave: true,
44
65
  autocapture: true,
66
+ // PostHog's default UA filter silently drops events from headless
67
+ // browsers (incl. Playwright) — the SDK logs
68
+ // "[WebExperiments] Refusing to render … likely bot" and never
69
+ // POSTs to /e/. We want e2e test sessions visible in PostHog so
70
+ // session replay can reproduce SPA bugs caught by Playwright. Real
71
+ // crawler spam is already filtered at the CloudFront edge by WAF
72
+ // rules; opting out here doesn't widen the spam surface in
73
+ // practice for our deployed nonprod / prod hosts.
74
+ opt_out_useragent_filter: true,
45
75
  // Always tag every event with the SPA slug so we can filter.
46
76
  loaded: (instance) => {
47
77
  instance.register({ app });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/platform/telemetry/posthog-provider.tsx"],"names":["useMemo","useEffect","posthog","jsx","Fragment","PostHogProvider","usePostHog"],"mappings":";;;;;;;;;;;;AAkDA,IAAM,gBAAA,GAAmB,0BAAA;AAOlB,SAAS,iBAAA,CAAkB;AAAA,EAChC,GAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA,GAAU,gBAAA;AAAA,EACV,cAAA,GAAiB,IAAA;AAAA,EACjB,IAAA;AAAA,EACA;AACF,CAAA,EAA2B;AACzB,EAAA,MAAM,OAAA,GAAUA,gBAAQ,MAAM;AAC5B,IAAA,IAAI,CAAC,YAAY,OAAO,KAAA;AACxB,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,KAAA;AAC1C,IAAA,IAAI,OAAO,YAAA,EAAc,OAAA,CAAQ,iBAAiB,CAAA,KAAM,QAAQ,OAAO,KAAA;AACvE,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAAC,iBAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,UAAA,EAAY;AAE7B,IAAAC,wBAAA,CAAQ,KAAK,UAAA,EAAY;AAAA,MACvB,QAAA,EAAU,OAAA;AAAA,MACV,QAAA,EAAU,YAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKV,mBAAmB,cAAA,GACf;AAAA,QACE,aAAA,EAAe,IAAA;AAAA,QACf,wBAAA,EAA0B;AAAA,OAC5B,GACA,MAAA;AAAA,MACJ,gBAAA,EAAkB,gBAAA;AAAA,MAClB,iBAAA,EAAmB,IAAA;AAAA,MACnB,WAAA,EAAa,IAAA;AAAA;AAAA,MAEb,MAAA,EAAQ,CAAC,QAAA,KAAa;AACpB,QAAA,QAAA,CAAS,QAAA,CAAS,EAAE,GAAA,EAAK,CAAA;AAAA,MAC3B;AAAA,KACD,CAAA;AAAA,EACH,GAAG,CAAC,OAAA,EAAS,YAAY,OAAA,EAAS,cAAA,EAAgB,GAAG,CAAC,CAAA;AAEtD,EAAAD,iBAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IAAI,MAAM,EAAA,EAAI;AACZ,MAAAC,wBAAA,CAAQ,QAAA,CAAS,KAAK,EAAA,EAAI;AAAA,QACxB,OAAO,IAAA,CAAK,KAAA;AAAA,QACZ,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,iBAAiB,IAAA,CAAK;AAAA,OACvB,CAAA;AACD,MAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,QAAAA,wBAAA,CAAQ,KAAA,CAAM,cAAA,EAAgB,IAAA,CAAK,cAAc,CAAA;AAAA,MACnD;AAAA,IACF,CAAA,MAAO;AAGL,MAAAA,wBAAA,CAAQ,KAAA,EAAM;AAAA,IAChB;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,IAAA,EAAM,EAAA,EAAI,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,cAAc,CAAC,CAAA;AAEjF,EAAA,IAAI,CAAC,OAAA,EAAS,uBAAOC,cAAA,CAAAC,mBAAA,EAAA,EAAG,QAAA,EAAS,CAAA;AAEjC,EAAA,uBAAOD,cAAA,CAACE,qBAAA,EAAA,EAAgB,MAAA,EAAQH,wBAAA,EAAU,QAAA,EAAS,CAAA;AACrD;AASO,IAAM,YAAA,GAAeI","file":"index.js","sourcesContent":["/**\n * PostHog telemetry provider.\n *\n * Mounted once at the root of every Datatech SPA (astrlabe, sextant,\n * data-br, kori-erp, windsock-ui). When a `projectKey` is supplied, it\n * initializes posthog-js, enables session replay (DOM + console + network),\n * and identifies the current user from windsock JWT claims as soon as\n * they're available.\n *\n * Why a shared provider instead of per-app posthog.init():\n * - Single bundle dep (peer-installed in each SPA, but the wiring\n * code lives here so identification + opt-out logic stays consistent).\n * - Auto-tags every event with `app` (SPA name) and `organization_id`\n * (windsock JWT claim) so analytics across the platform can pivot\n * by tenant without per-team plumbing.\n *\n * Opt-out:\n * - Tracking disabled if `projectKey` is empty or undefined (lets dev\n * run without phoning home).\n * - Tracking disabled if `localStorage.posthog_opt_out === 'true'`.\n * - Replay can be disabled separately via `recordSessions={false}`.\n */\nimport posthog from 'posthog-js'\nimport { PostHogProvider, usePostHog } from 'posthog-js/react'\nimport { type ReactNode, useEffect, useMemo } from 'react'\n\nexport type TelemetryUser = {\n /** Stable user id — typically the windsock JWT `sub`. */\n id: string\n email?: string\n name?: string\n role?: string\n /** Tenant id — from `https://datatechsolutions.com.br/organization_id` claim. */\n organizationId?: string\n}\n\nexport type TelemetryProviderProps = {\n /** App slug — e.g. `astrlabe`, `sextant`, `data-br`. Tags every event. */\n app: string\n /** PostHog project token (write-only key, safe in browser bundles). */\n projectKey?: string\n /** PostHog ingestion host. Defaults to US Cloud. */\n apiHost?: string\n /** Whether to record session replays. Defaults to true. */\n recordSessions?: boolean\n /** Current authenticated user, if any. The provider re-identifies on change. */\n user?: TelemetryUser | null\n children: ReactNode\n}\n\nconst DEFAULT_API_HOST = 'https://us.i.posthog.com'\n\n/**\n * Wrap every SPA in this provider. Pass the windsock-resolved user so\n * PostHog associates events with the right tenant; pass `null` after\n * sign-out so the next session is anonymous.\n */\nexport function TelemetryProvider({\n app,\n projectKey,\n apiHost = DEFAULT_API_HOST,\n recordSessions = true,\n user,\n children,\n}: TelemetryProviderProps) {\n const enabled = useMemo(() => {\n if (!projectKey) return false\n if (typeof window === 'undefined') return false\n if (window.localStorage?.getItem('posthog_opt_out') === 'true') return false\n return true\n }, [projectKey])\n\n useEffect(() => {\n if (!enabled || !projectKey) return\n // Idempotent — posthog.init no-ops on second call with same key.\n posthog.init(projectKey, {\n api_host: apiHost,\n defaults: '2026-01-30',\n // Session replay: capture DOM + console + network (excluding\n // request/response bodies — PII risk). The masking config blanks\n // input fields by default; sensitive forms (login, MFA) should\n // mark themselves with `data-ph-no-capture` to be extra-safe.\n session_recording: recordSessions\n ? {\n maskAllInputs: true,\n recordCrossOriginIframes: false,\n }\n : undefined,\n capture_pageview: 'history_change',\n capture_pageleave: true,\n autocapture: true,\n // Always tag every event with the SPA slug so we can filter.\n loaded: (instance) => {\n instance.register({ app })\n },\n })\n }, [enabled, projectKey, apiHost, recordSessions, app])\n\n useEffect(() => {\n if (!enabled) return\n if (user?.id) {\n posthog.identify(user.id, {\n email: user.email,\n name: user.name,\n role: user.role,\n organization_id: user.organizationId,\n })\n if (user.organizationId) {\n posthog.group('organization', user.organizationId)\n }\n } else {\n // No user (sign-out, never authenticated) → reset PostHog identity\n // so the next sign-in starts a fresh session.\n posthog.reset()\n }\n }, [enabled, user?.id, user?.email, user?.name, user?.role, user?.organizationId])\n\n if (!enabled) return <>{children}</>\n\n return <PostHogProvider client={posthog}>{children}</PostHogProvider>\n}\n\n/**\n * Re-export the posthog-js/react hook so consumer SPAs can capture\n * custom events without importing posthog-js directly:\n *\n * const ph = useTelemetry()\n * ph?.capture('workflow_run_started', { workflowId })\n */\nexport const useTelemetry = usePostHog\n"]}
1
+ {"version":3,"sources":["../../../src/platform/telemetry/posthog-provider.tsx"],"names":["useMemo","useEffect","posthog","jsx","Fragment","PostHogProvider","usePostHog"],"mappings":";;;;;;;;;;;;AAiEA,IAAM,gBAAA,GAAmB,0BAAA;AAQzB,SAAS,aAAA,CAAc,SAAkB,QAAA,EAAkD;AACzF,EAAA,IAAI,MAAM,OAAA,IAAW,EAAA;AACrB,EAAA,IAAI,OAAO,QAAA,IAAY,EAAA;AAEvB,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,MAAM,OAAA,GAAW,OACd,cAAA,EAAgB,OAAA;AACnB,IAAA,IAAI,CAAC,GAAA,IAAO,OAAA,EAAS,GAAA,QAAW,OAAA,CAAQ,GAAA;AACxC,IAAA,IAAI,CAAC,IAAA,IAAQ,OAAA,EAAS,IAAA,SAAa,OAAA,CAAQ,IAAA;AAAA,EAC7C;AAIA,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAO,SAAwE,IAAO,EAAC;AAC7F,IAAA,IAAI,CAAC,GAAA,EAAK,GAAA,GAAM,GAAA,CAAI,gBAAA,IAAoB,EAAA;AACxC,IAAA,IAAI,CAAC,IAAA,EAAM,IAAA,GAAO,GAAA,CAAI,iBAAA,IAAqB,EAAA;AAAA,EAC7C,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,IAAI,CAAC,MAAM,IAAA,GAAO,gBAAA;AAClB,EAAA,OAAO,EAAE,KAAK,IAAA,EAAK;AACrB;AAOO,SAAS,iBAAA,CAAkB;AAAA,EAChC,GAAA;AAAA,EACA,UAAA,EAAY,cAAA;AAAA,EACZ,OAAA,EAAS,WAAA;AAAA,EACT,cAAA,GAAiB,IAAA;AAAA,EACjB,IAAA;AAAA,EACA;AACF,CAAA,EAA2B;AACzB,EAAA,MAAM,EAAE,GAAA,EAAK,UAAA,EAAY,IAAA,EAAM,SAAQ,GAAIA,eAAA;AAAA,IACzC,MAAM,aAAA,CAAc,cAAA,EAAgB,WAAW,CAAA;AAAA,IAC/C,CAAC,gBAAgB,WAAW;AAAA,GAC9B;AAEA,EAAA,MAAM,OAAA,GAAUA,gBAAQ,MAAM;AAC5B,IAAA,IAAI,CAAC,YAAY,OAAO,KAAA;AACxB,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,KAAA;AAC1C,IAAA,IAAI,OAAO,YAAA,EAAc,OAAA,CAAQ,iBAAiB,CAAA,KAAM,QAAQ,OAAO,KAAA;AACvE,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAAC,iBAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,UAAA,EAAY;AAE7B,IAAAC,wBAAA,CAAQ,KAAK,UAAA,EAAY;AAAA,MACvB,QAAA,EAAU,OAAA;AAAA,MACV,QAAA,EAAU,YAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKV,mBAAmB,cAAA,GACf;AAAA,QACE,aAAA,EAAe,IAAA;AAAA,QACf,wBAAA,EAA0B;AAAA,OAC5B,GACA,MAAA;AAAA,MACJ,gBAAA,EAAkB,gBAAA;AAAA,MAClB,iBAAA,EAAmB,IAAA;AAAA,MACnB,WAAA,EAAa,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASb,wBAAA,EAA0B,IAAA;AAAA;AAAA,MAE1B,MAAA,EAAQ,CAAC,QAAA,KAAa;AACpB,QAAA,QAAA,CAAS,QAAA,CAAS,EAAE,GAAA,EAAK,CAAA;AAAA,MAC3B;AAAA,KACD,CAAA;AAAA,EACH,GAAG,CAAC,OAAA,EAAS,YAAY,OAAA,EAAS,cAAA,EAAgB,GAAG,CAAC,CAAA;AAEtD,EAAAD,iBAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IAAI,MAAM,EAAA,EAAI;AACZ,MAAAC,wBAAA,CAAQ,QAAA,CAAS,KAAK,EAAA,EAAI;AAAA,QACxB,OAAO,IAAA,CAAK,KAAA;AAAA,QACZ,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,iBAAiB,IAAA,CAAK;AAAA,OACvB,CAAA;AACD,MAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,QAAAA,wBAAA,CAAQ,KAAA,CAAM,cAAA,EAAgB,IAAA,CAAK,cAAc,CAAA;AAAA,MACnD;AAAA,IACF,CAAA,MAAO;AAGL,MAAAA,wBAAA,CAAQ,KAAA,EAAM;AAAA,IAChB;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,IAAA,EAAM,EAAA,EAAI,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,cAAc,CAAC,CAAA;AAEjF,EAAA,IAAI,CAAC,OAAA,EAAS,uBAAOC,cAAA,CAAAC,mBAAA,EAAA,EAAG,QAAA,EAAS,CAAA;AAEjC,EAAA,uBAAOD,cAAA,CAACE,qBAAA,EAAA,EAAgB,MAAA,EAAQH,wBAAA,EAAU,QAAA,EAAS,CAAA;AACrD;AASO,IAAM,YAAA,GAAeI","file":"index.js","sourcesContent":["/**\n * PostHog telemetry provider.\n *\n * Mounted once at the root of every Datatech SPA (astrlabe, sextant,\n * data-br, kori-erp, windsock-ui). Reads the PostHog project key + host\n * from `window.__APP_CONFIG__.posthog` — populated by `runtime-config.js`\n * which the deployed SPA's index.html loads BEFORE main.tsx. The CDK\n * FrontendStack uploads `runtime-config.js` to the bucket from SSM\n * (`/datatech/{env}/posthog/{project-key,api-host}`).\n *\n * For local dev, `import.meta.env.VITE_POSTHOG_KEY` is the fallback —\n * empty in most local sessions, so telemetry stays off without ceremony.\n *\n * Explicit prop overrides (`projectKey` / `apiHost`) win over both —\n * useful for tests and one-off sub-app embeds.\n *\n * Why a shared provider instead of per-app posthog.init():\n * - Single bundle dep (peer-installed in each SPA, but the wiring\n * code lives here so identification + opt-out logic stays consistent).\n * - Auto-tags every event with `app` (SPA name) and `organization_id`\n * (windsock JWT claim) so analytics across the platform can pivot\n * by tenant without per-team plumbing.\n *\n * Opt-out:\n * - Tracking disabled if no project key is resolvable from any source.\n * - Tracking disabled if `localStorage.posthog_opt_out === 'true'`.\n * - Replay can be disabled separately via `recordSessions={false}`.\n */\nimport posthog from 'posthog-js'\nimport { PostHogProvider, usePostHog } from 'posthog-js/react'\nimport { type ReactNode, useEffect, useMemo } from 'react'\n\nexport type TelemetryUser = {\n /** Stable user id — typically the windsock JWT `sub`. */\n id: string\n email?: string\n name?: string\n role?: string\n /** Tenant id — from `https://datatechsolutions.com.br/organization_id` claim. */\n organizationId?: string\n}\n\nexport type TelemetryProviderProps = {\n /** App slug — e.g. `astrlabe`, `sextant`, `data-br`. Tags every event. */\n app: string\n /**\n * PostHog project token (write-only key, safe in browser bundles).\n * If unset, the provider falls back to `window.__APP_CONFIG__.posthog.key`\n * (set by the CDK-managed `runtime-config.js`) and then to\n * `import.meta.env.VITE_POSTHOG_KEY`. Pass explicitly only for tests\n * or sub-app embeds.\n */\n projectKey?: string\n /**\n * PostHog ingestion host. Falls back through the same chain as\n * `projectKey`; defaults to US Cloud if nothing else resolves.\n */\n apiHost?: string\n /** Whether to record session replays. Defaults to true. */\n recordSessions?: boolean\n /** Current authenticated user, if any. The provider re-identifies on change. */\n user?: TelemetryUser | null\n children: ReactNode\n}\n\nconst DEFAULT_API_HOST = 'https://us.i.posthog.com'\n\n/**\n * Resolve the PostHog config from the first source that has a value:\n * 1. Explicit prop (test override)\n * 2. `window.__APP_CONFIG__.posthog.{key,host}` (deployed runtime-config.js)\n * 3. `import.meta.env.VITE_POSTHOG_*` (local dev)\n */\nfunction resolveConfig(propKey?: string, propHost?: string): { key: string; host: string } {\n let key = propKey ?? ''\n let host = propHost ?? ''\n\n if (typeof window !== 'undefined') {\n const runtime = (window as unknown as { __APP_CONFIG__?: { posthog?: { key?: string; host?: string } } })\n .__APP_CONFIG__?.posthog\n if (!key && runtime?.key) key = runtime.key\n if (!host && runtime?.host) host = runtime.host\n }\n\n // Vite injects `import.meta.env.*` at build time; reading is safe in\n // both dev and prod bundles. Empty string when not defined.\n try {\n const env = (import.meta as unknown as { env?: Record<string, string | undefined> }).env ?? {}\n if (!key) key = env.VITE_POSTHOG_KEY ?? ''\n if (!host) host = env.VITE_POSTHOG_HOST ?? ''\n } catch {\n /* import.meta unavailable (CJS test env) — treat as no env */\n }\n\n if (!host) host = DEFAULT_API_HOST\n return { key, host }\n}\n\n/**\n * Wrap every SPA in this provider. Pass the windsock-resolved user so\n * PostHog associates events with the right tenant; pass `null` after\n * sign-out so the next session is anonymous.\n */\nexport function TelemetryProvider({\n app,\n projectKey: propProjectKey,\n apiHost: propApiHost,\n recordSessions = true,\n user,\n children,\n}: TelemetryProviderProps) {\n const { key: projectKey, host: apiHost } = useMemo(\n () => resolveConfig(propProjectKey, propApiHost),\n [propProjectKey, propApiHost],\n )\n\n const enabled = useMemo(() => {\n if (!projectKey) return false\n if (typeof window === 'undefined') return false\n if (window.localStorage?.getItem('posthog_opt_out') === 'true') return false\n return true\n }, [projectKey])\n\n useEffect(() => {\n if (!enabled || !projectKey) return\n // Idempotent — posthog.init no-ops on second call with same key.\n posthog.init(projectKey, {\n api_host: apiHost,\n defaults: '2026-01-30',\n // Session replay: capture DOM + console + network (excluding\n // request/response bodies — PII risk). The masking config blanks\n // input fields by default; sensitive forms (login, MFA) should\n // mark themselves with `data-ph-no-capture` to be extra-safe.\n session_recording: recordSessions\n ? {\n maskAllInputs: true,\n recordCrossOriginIframes: false,\n }\n : undefined,\n capture_pageview: 'history_change',\n capture_pageleave: true,\n autocapture: true,\n // PostHog's default UA filter silently drops events from headless\n // browsers (incl. Playwright) — the SDK logs\n // \"[WebExperiments] Refusing to render … likely bot\" and never\n // POSTs to /e/. We want e2e test sessions visible in PostHog so\n // session replay can reproduce SPA bugs caught by Playwright. Real\n // crawler spam is already filtered at the CloudFront edge by WAF\n // rules; opting out here doesn't widen the spam surface in\n // practice for our deployed nonprod / prod hosts.\n opt_out_useragent_filter: true,\n // Always tag every event with the SPA slug so we can filter.\n loaded: (instance) => {\n instance.register({ app })\n },\n })\n }, [enabled, projectKey, apiHost, recordSessions, app])\n\n useEffect(() => {\n if (!enabled) return\n if (user?.id) {\n posthog.identify(user.id, {\n email: user.email,\n name: user.name,\n role: user.role,\n organization_id: user.organizationId,\n })\n if (user.organizationId) {\n posthog.group('organization', user.organizationId)\n }\n } else {\n // No user (sign-out, never authenticated) → reset PostHog identity\n // so the next sign-in starts a fresh session.\n posthog.reset()\n }\n }, [enabled, user?.id, user?.email, user?.name, user?.role, user?.organizationId])\n\n if (!enabled) return <>{children}</>\n\n return <PostHogProvider client={posthog}>{children}</PostHogProvider>\n}\n\n/**\n * Re-export the posthog-js/react hook so consumer SPAs can capture\n * custom events without importing posthog-js directly:\n *\n * const ph = useTelemetry()\n * ph?.capture('workflow_run_started', { workflowId })\n */\nexport const useTelemetry = usePostHog\n"]}
@@ -6,14 +6,35 @@ import { jsx, Fragment } from 'react/jsx-runtime';
6
6
 
7
7
  // src/platform/telemetry/posthog-provider.tsx
8
8
  var DEFAULT_API_HOST = "https://us.i.posthog.com";
9
+ function resolveConfig(propKey, propHost) {
10
+ let key = propKey ?? "";
11
+ let host = propHost ?? "";
12
+ if (typeof window !== "undefined") {
13
+ const runtime = window.__APP_CONFIG__?.posthog;
14
+ if (!key && runtime?.key) key = runtime.key;
15
+ if (!host && runtime?.host) host = runtime.host;
16
+ }
17
+ try {
18
+ const env = import.meta.env ?? {};
19
+ if (!key) key = env.VITE_POSTHOG_KEY ?? "";
20
+ if (!host) host = env.VITE_POSTHOG_HOST ?? "";
21
+ } catch {
22
+ }
23
+ if (!host) host = DEFAULT_API_HOST;
24
+ return { key, host };
25
+ }
9
26
  function TelemetryProvider({
10
27
  app,
11
- projectKey,
12
- apiHost = DEFAULT_API_HOST,
28
+ projectKey: propProjectKey,
29
+ apiHost: propApiHost,
13
30
  recordSessions = true,
14
31
  user,
15
32
  children
16
33
  }) {
34
+ const { key: projectKey, host: apiHost } = useMemo(
35
+ () => resolveConfig(propProjectKey, propApiHost),
36
+ [propProjectKey, propApiHost]
37
+ );
17
38
  const enabled = useMemo(() => {
18
39
  if (!projectKey) return false;
19
40
  if (typeof window === "undefined") return false;
@@ -36,6 +57,15 @@ function TelemetryProvider({
36
57
  capture_pageview: "history_change",
37
58
  capture_pageleave: true,
38
59
  autocapture: true,
60
+ // PostHog's default UA filter silently drops events from headless
61
+ // browsers (incl. Playwright) — the SDK logs
62
+ // "[WebExperiments] Refusing to render … likely bot" and never
63
+ // POSTs to /e/. We want e2e test sessions visible in PostHog so
64
+ // session replay can reproduce SPA bugs caught by Playwright. Real
65
+ // crawler spam is already filtered at the CloudFront edge by WAF
66
+ // rules; opting out here doesn't widen the spam surface in
67
+ // practice for our deployed nonprod / prod hosts.
68
+ opt_out_useragent_filter: true,
39
69
  // Always tag every event with the SPA slug so we can filter.
40
70
  loaded: (instance) => {
41
71
  instance.register({ app });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/platform/telemetry/posthog-provider.tsx"],"names":[],"mappings":";;;;;;AAkDA,IAAM,gBAAA,GAAmB,0BAAA;AAOlB,SAAS,iBAAA,CAAkB;AAAA,EAChC,GAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA,GAAU,gBAAA;AAAA,EACV,cAAA,GAAiB,IAAA;AAAA,EACjB,IAAA;AAAA,EACA;AACF,CAAA,EAA2B;AACzB,EAAA,MAAM,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,IAAI,CAAC,YAAY,OAAO,KAAA;AACxB,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,KAAA;AAC1C,IAAA,IAAI,OAAO,YAAA,EAAc,OAAA,CAAQ,iBAAiB,CAAA,KAAM,QAAQ,OAAO,KAAA;AACvE,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,UAAA,EAAY;AAE7B,IAAA,OAAA,CAAQ,KAAK,UAAA,EAAY;AAAA,MACvB,QAAA,EAAU,OAAA;AAAA,MACV,QAAA,EAAU,YAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKV,mBAAmB,cAAA,GACf;AAAA,QACE,aAAA,EAAe,IAAA;AAAA,QACf,wBAAA,EAA0B;AAAA,OAC5B,GACA,MAAA;AAAA,MACJ,gBAAA,EAAkB,gBAAA;AAAA,MAClB,iBAAA,EAAmB,IAAA;AAAA,MACnB,WAAA,EAAa,IAAA;AAAA;AAAA,MAEb,MAAA,EAAQ,CAAC,QAAA,KAAa;AACpB,QAAA,QAAA,CAAS,QAAA,CAAS,EAAE,GAAA,EAAK,CAAA;AAAA,MAC3B;AAAA,KACD,CAAA;AAAA,EACH,GAAG,CAAC,OAAA,EAAS,YAAY,OAAA,EAAS,cAAA,EAAgB,GAAG,CAAC,CAAA;AAEtD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IAAI,MAAM,EAAA,EAAI;AACZ,MAAA,OAAA,CAAQ,QAAA,CAAS,KAAK,EAAA,EAAI;AAAA,QACxB,OAAO,IAAA,CAAK,KAAA;AAAA,QACZ,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,iBAAiB,IAAA,CAAK;AAAA,OACvB,CAAA;AACD,MAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,QAAA,OAAA,CAAQ,KAAA,CAAM,cAAA,EAAgB,IAAA,CAAK,cAAc,CAAA;AAAA,MACnD;AAAA,IACF,CAAA,MAAO;AAGL,MAAA,OAAA,CAAQ,KAAA,EAAM;AAAA,IAChB;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,IAAA,EAAM,EAAA,EAAI,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,cAAc,CAAC,CAAA;AAEjF,EAAA,IAAI,CAAC,OAAA,EAAS,uBAAO,GAAA,CAAA,QAAA,EAAA,EAAG,QAAA,EAAS,CAAA;AAEjC,EAAA,uBAAO,GAAA,CAAC,eAAA,EAAA,EAAgB,MAAA,EAAQ,OAAA,EAAU,QAAA,EAAS,CAAA;AACrD;AASO,IAAM,YAAA,GAAe","file":"index.mjs","sourcesContent":["/**\n * PostHog telemetry provider.\n *\n * Mounted once at the root of every Datatech SPA (astrlabe, sextant,\n * data-br, kori-erp, windsock-ui). When a `projectKey` is supplied, it\n * initializes posthog-js, enables session replay (DOM + console + network),\n * and identifies the current user from windsock JWT claims as soon as\n * they're available.\n *\n * Why a shared provider instead of per-app posthog.init():\n * - Single bundle dep (peer-installed in each SPA, but the wiring\n * code lives here so identification + opt-out logic stays consistent).\n * - Auto-tags every event with `app` (SPA name) and `organization_id`\n * (windsock JWT claim) so analytics across the platform can pivot\n * by tenant without per-team plumbing.\n *\n * Opt-out:\n * - Tracking disabled if `projectKey` is empty or undefined (lets dev\n * run without phoning home).\n * - Tracking disabled if `localStorage.posthog_opt_out === 'true'`.\n * - Replay can be disabled separately via `recordSessions={false}`.\n */\nimport posthog from 'posthog-js'\nimport { PostHogProvider, usePostHog } from 'posthog-js/react'\nimport { type ReactNode, useEffect, useMemo } from 'react'\n\nexport type TelemetryUser = {\n /** Stable user id — typically the windsock JWT `sub`. */\n id: string\n email?: string\n name?: string\n role?: string\n /** Tenant id — from `https://datatechsolutions.com.br/organization_id` claim. */\n organizationId?: string\n}\n\nexport type TelemetryProviderProps = {\n /** App slug — e.g. `astrlabe`, `sextant`, `data-br`. Tags every event. */\n app: string\n /** PostHog project token (write-only key, safe in browser bundles). */\n projectKey?: string\n /** PostHog ingestion host. Defaults to US Cloud. */\n apiHost?: string\n /** Whether to record session replays. Defaults to true. */\n recordSessions?: boolean\n /** Current authenticated user, if any. The provider re-identifies on change. */\n user?: TelemetryUser | null\n children: ReactNode\n}\n\nconst DEFAULT_API_HOST = 'https://us.i.posthog.com'\n\n/**\n * Wrap every SPA in this provider. Pass the windsock-resolved user so\n * PostHog associates events with the right tenant; pass `null` after\n * sign-out so the next session is anonymous.\n */\nexport function TelemetryProvider({\n app,\n projectKey,\n apiHost = DEFAULT_API_HOST,\n recordSessions = true,\n user,\n children,\n}: TelemetryProviderProps) {\n const enabled = useMemo(() => {\n if (!projectKey) return false\n if (typeof window === 'undefined') return false\n if (window.localStorage?.getItem('posthog_opt_out') === 'true') return false\n return true\n }, [projectKey])\n\n useEffect(() => {\n if (!enabled || !projectKey) return\n // Idempotent — posthog.init no-ops on second call with same key.\n posthog.init(projectKey, {\n api_host: apiHost,\n defaults: '2026-01-30',\n // Session replay: capture DOM + console + network (excluding\n // request/response bodies — PII risk). The masking config blanks\n // input fields by default; sensitive forms (login, MFA) should\n // mark themselves with `data-ph-no-capture` to be extra-safe.\n session_recording: recordSessions\n ? {\n maskAllInputs: true,\n recordCrossOriginIframes: false,\n }\n : undefined,\n capture_pageview: 'history_change',\n capture_pageleave: true,\n autocapture: true,\n // Always tag every event with the SPA slug so we can filter.\n loaded: (instance) => {\n instance.register({ app })\n },\n })\n }, [enabled, projectKey, apiHost, recordSessions, app])\n\n useEffect(() => {\n if (!enabled) return\n if (user?.id) {\n posthog.identify(user.id, {\n email: user.email,\n name: user.name,\n role: user.role,\n organization_id: user.organizationId,\n })\n if (user.organizationId) {\n posthog.group('organization', user.organizationId)\n }\n } else {\n // No user (sign-out, never authenticated) → reset PostHog identity\n // so the next sign-in starts a fresh session.\n posthog.reset()\n }\n }, [enabled, user?.id, user?.email, user?.name, user?.role, user?.organizationId])\n\n if (!enabled) return <>{children}</>\n\n return <PostHogProvider client={posthog}>{children}</PostHogProvider>\n}\n\n/**\n * Re-export the posthog-js/react hook so consumer SPAs can capture\n * custom events without importing posthog-js directly:\n *\n * const ph = useTelemetry()\n * ph?.capture('workflow_run_started', { workflowId })\n */\nexport const useTelemetry = usePostHog\n"]}
1
+ {"version":3,"sources":["../../../src/platform/telemetry/posthog-provider.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * PostHog telemetry provider.\n *\n * Mounted once at the root of every Datatech SPA (astrlabe, sextant,\n * data-br, kori-erp, windsock-ui). Reads the PostHog project key + host\n * from `window.__APP_CONFIG__.posthog` — populated by `runtime-config.js`\n * which the deployed SPA's index.html loads BEFORE main.tsx. The CDK\n * FrontendStack uploads `runtime-config.js` to the bucket from SSM\n * (`/datatech/{env}/posthog/{project-key,api-host}`).\n *\n * For local dev, `import.meta.env.VITE_POSTHOG_KEY` is the fallback —\n * empty in most local sessions, so telemetry stays off without ceremony.\n *\n * Explicit prop overrides (`projectKey` / `apiHost`) win over both —\n * useful for tests and one-off sub-app embeds.\n *\n * Why a shared provider instead of per-app posthog.init():\n * - Single bundle dep (peer-installed in each SPA, but the wiring\n * code lives here so identification + opt-out logic stays consistent).\n * - Auto-tags every event with `app` (SPA name) and `organization_id`\n * (windsock JWT claim) so analytics across the platform can pivot\n * by tenant without per-team plumbing.\n *\n * Opt-out:\n * - Tracking disabled if no project key is resolvable from any source.\n * - Tracking disabled if `localStorage.posthog_opt_out === 'true'`.\n * - Replay can be disabled separately via `recordSessions={false}`.\n */\nimport posthog from 'posthog-js'\nimport { PostHogProvider, usePostHog } from 'posthog-js/react'\nimport { type ReactNode, useEffect, useMemo } from 'react'\n\nexport type TelemetryUser = {\n /** Stable user id — typically the windsock JWT `sub`. */\n id: string\n email?: string\n name?: string\n role?: string\n /** Tenant id — from `https://datatechsolutions.com.br/organization_id` claim. */\n organizationId?: string\n}\n\nexport type TelemetryProviderProps = {\n /** App slug — e.g. `astrlabe`, `sextant`, `data-br`. Tags every event. */\n app: string\n /**\n * PostHog project token (write-only key, safe in browser bundles).\n * If unset, the provider falls back to `window.__APP_CONFIG__.posthog.key`\n * (set by the CDK-managed `runtime-config.js`) and then to\n * `import.meta.env.VITE_POSTHOG_KEY`. Pass explicitly only for tests\n * or sub-app embeds.\n */\n projectKey?: string\n /**\n * PostHog ingestion host. Falls back through the same chain as\n * `projectKey`; defaults to US Cloud if nothing else resolves.\n */\n apiHost?: string\n /** Whether to record session replays. Defaults to true. */\n recordSessions?: boolean\n /** Current authenticated user, if any. The provider re-identifies on change. */\n user?: TelemetryUser | null\n children: ReactNode\n}\n\nconst DEFAULT_API_HOST = 'https://us.i.posthog.com'\n\n/**\n * Resolve the PostHog config from the first source that has a value:\n * 1. Explicit prop (test override)\n * 2. `window.__APP_CONFIG__.posthog.{key,host}` (deployed runtime-config.js)\n * 3. `import.meta.env.VITE_POSTHOG_*` (local dev)\n */\nfunction resolveConfig(propKey?: string, propHost?: string): { key: string; host: string } {\n let key = propKey ?? ''\n let host = propHost ?? ''\n\n if (typeof window !== 'undefined') {\n const runtime = (window as unknown as { __APP_CONFIG__?: { posthog?: { key?: string; host?: string } } })\n .__APP_CONFIG__?.posthog\n if (!key && runtime?.key) key = runtime.key\n if (!host && runtime?.host) host = runtime.host\n }\n\n // Vite injects `import.meta.env.*` at build time; reading is safe in\n // both dev and prod bundles. Empty string when not defined.\n try {\n const env = (import.meta as unknown as { env?: Record<string, string | undefined> }).env ?? {}\n if (!key) key = env.VITE_POSTHOG_KEY ?? ''\n if (!host) host = env.VITE_POSTHOG_HOST ?? ''\n } catch {\n /* import.meta unavailable (CJS test env) — treat as no env */\n }\n\n if (!host) host = DEFAULT_API_HOST\n return { key, host }\n}\n\n/**\n * Wrap every SPA in this provider. Pass the windsock-resolved user so\n * PostHog associates events with the right tenant; pass `null` after\n * sign-out so the next session is anonymous.\n */\nexport function TelemetryProvider({\n app,\n projectKey: propProjectKey,\n apiHost: propApiHost,\n recordSessions = true,\n user,\n children,\n}: TelemetryProviderProps) {\n const { key: projectKey, host: apiHost } = useMemo(\n () => resolveConfig(propProjectKey, propApiHost),\n [propProjectKey, propApiHost],\n )\n\n const enabled = useMemo(() => {\n if (!projectKey) return false\n if (typeof window === 'undefined') return false\n if (window.localStorage?.getItem('posthog_opt_out') === 'true') return false\n return true\n }, [projectKey])\n\n useEffect(() => {\n if (!enabled || !projectKey) return\n // Idempotent — posthog.init no-ops on second call with same key.\n posthog.init(projectKey, {\n api_host: apiHost,\n defaults: '2026-01-30',\n // Session replay: capture DOM + console + network (excluding\n // request/response bodies — PII risk). The masking config blanks\n // input fields by default; sensitive forms (login, MFA) should\n // mark themselves with `data-ph-no-capture` to be extra-safe.\n session_recording: recordSessions\n ? {\n maskAllInputs: true,\n recordCrossOriginIframes: false,\n }\n : undefined,\n capture_pageview: 'history_change',\n capture_pageleave: true,\n autocapture: true,\n // PostHog's default UA filter silently drops events from headless\n // browsers (incl. Playwright) — the SDK logs\n // \"[WebExperiments] Refusing to render … likely bot\" and never\n // POSTs to /e/. We want e2e test sessions visible in PostHog so\n // session replay can reproduce SPA bugs caught by Playwright. Real\n // crawler spam is already filtered at the CloudFront edge by WAF\n // rules; opting out here doesn't widen the spam surface in\n // practice for our deployed nonprod / prod hosts.\n opt_out_useragent_filter: true,\n // Always tag every event with the SPA slug so we can filter.\n loaded: (instance) => {\n instance.register({ app })\n },\n })\n }, [enabled, projectKey, apiHost, recordSessions, app])\n\n useEffect(() => {\n if (!enabled) return\n if (user?.id) {\n posthog.identify(user.id, {\n email: user.email,\n name: user.name,\n role: user.role,\n organization_id: user.organizationId,\n })\n if (user.organizationId) {\n posthog.group('organization', user.organizationId)\n }\n } else {\n // No user (sign-out, never authenticated) → reset PostHog identity\n // so the next sign-in starts a fresh session.\n posthog.reset()\n }\n }, [enabled, user?.id, user?.email, user?.name, user?.role, user?.organizationId])\n\n if (!enabled) return <>{children}</>\n\n return <PostHogProvider client={posthog}>{children}</PostHogProvider>\n}\n\n/**\n * Re-export the posthog-js/react hook so consumer SPAs can capture\n * custom events without importing posthog-js directly:\n *\n * const ph = useTelemetry()\n * ph?.capture('workflow_run_started', { workflowId })\n */\nexport const useTelemetry = usePostHog\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datatechsolutions/ui",
3
- "version": "3.1.0",
3
+ "version": "3.2.1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",