@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.
- package/dist/platform/index.js +8 -8
- package/dist/platform/index.mjs +3 -3
- package/dist/platform/telemetry/index.d.mts +12 -3
- package/dist/platform/telemetry/index.d.ts +12 -3
- package/dist/platform/telemetry/index.js +32 -2
- package/dist/platform/telemetry/index.js.map +1 -1
- package/dist/platform/telemetry/index.mjs +32 -2
- package/dist/platform/telemetry/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/platform/index.js
CHANGED
|
@@ -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
|
|
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, "
|
|
5336
|
+
Object.defineProperty(exports, "UpgradePrompt", {
|
|
5341
5337
|
enumerable: true,
|
|
5342
|
-
get: function () { return
|
|
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; }
|
package/dist/platform/index.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
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":"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","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
|
|
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"]}
|