@agent-native/core 0.133.2 → 0.134.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +62 -0
- package/corpus/core/docs/content/observability.mdx +41 -1
- package/corpus/core/docs/content/tracking.mdx +25 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/run-manager.ts +7 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +10 -0
- package/corpus/core/src/client/analytics.ts +105 -1
- package/corpus/core/src/client/use-action.ts +6 -0
- package/corpus/core/src/deploy/build.ts +38 -1
- package/corpus/core/src/observability/posthog-ai.ts +294 -0
- package/corpus/core/src/observability/routes.ts +29 -6
- package/corpus/core/src/observability/traces.ts +287 -12
- package/corpus/core/src/observability/types.ts +11 -0
- package/corpus/core/src/secrets/register-framework-secrets.ts +16 -0
- package/corpus/core/src/server/agent-run-context.ts +21 -0
- package/corpus/core/src/server/capture-error.ts +8 -0
- package/corpus/core/src/server/core-routes-plugin.ts +75 -4
- package/corpus/core/src/server/deploy-environment.ts +57 -0
- package/corpus/core/src/server/error-noise-filter.ts +268 -0
- package/corpus/core/src/server/posthog-config.ts +75 -0
- package/corpus/core/src/server/request-context.ts +6 -0
- package/corpus/core/src/server/sentry-config.ts +4 -8
- package/corpus/core/src/server/sentry-plugin.ts +13 -29
- package/corpus/core/src/server/sentry.ts +13 -156
- package/corpus/core/src/server/ssr-handler.ts +6 -1
- package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +36 -10
- package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +55 -1
- package/corpus/core/src/tracking/error-capture.ts +44 -109
- package/corpus/core/src/tracking/index.ts +11 -0
- package/corpus/core/src/tracking/posthog-exception.ts +305 -0
- package/corpus/core/src/tracking/providers.ts +91 -14
- package/corpus/core/src/tracking/redaction.ts +105 -0
- package/corpus/templates/content/actions/_batch-utils.ts +33 -0
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +173 -81
- package/corpus/templates/content/actions/_content-files.ts +31 -21
- package/corpus/templates/content/actions/_database-source-utils.ts +653 -155
- package/corpus/templates/content/actions/_database-utils.ts +535 -41
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +291 -240
- package/corpus/templates/content/actions/add-database-item.ts +15 -1
- package/corpus/templates/content/actions/attach-content-database-source.ts +156 -58
- package/corpus/templates/content/actions/bind-content-database-source-field.ts +2 -2
- package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +4 -1
- package/corpus/templates/content/actions/delete-database-items.ts +8 -2
- package/corpus/templates/content/actions/disconnect-content-database-source.ts +2 -2
- package/corpus/templates/content/actions/duplicate-database-item.ts +15 -1
- package/corpus/templates/content/actions/duplicate-database-items.ts +19 -1
- package/corpus/templates/content/actions/execute-builder-source-execution.ts +2 -1
- package/corpus/templates/content/actions/get-content-database.ts +12 -64
- package/corpus/templates/content/actions/move-database-item.ts +4 -1
- package/corpus/templates/content/actions/prepare-builder-source-execution.ts +1 -1
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +4 -8
- package/corpus/templates/content/actions/preview-content-database-source-attach.ts +92 -0
- package/corpus/templates/content/actions/process-builder-body-hydration.ts +2 -1
- package/corpus/templates/content/actions/query-content-database-items.ts +64 -0
- package/corpus/templates/content/actions/refresh-content-database-source.ts +7 -0
- package/corpus/templates/content/actions/review-content-database-source-change-set.ts +1 -1
- package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +1 -1
- package/corpus/templates/content/actions/stage-builder-revision.ts +1 -1
- package/corpus/templates/content/actions/update-content-database-personal-view.ts +29 -3
- package/corpus/templates/content/actions/update-content-database-view.ts +1 -1
- package/corpus/templates/content/actions/validate-builder-source-execution.ts +1 -1
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +3 -22
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +267 -105
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +82 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +285 -24
- package/corpus/templates/content/app/hooks/use-document-properties.ts +7 -6
- package/corpus/templates/content/app/i18n-data.ts +10 -0
- package/corpus/templates/content/changelog/2026-07-29-large-databases-keep-useful-rows-visible.md +6 -0
- package/corpus/templates/content/changelog/2026-07-30-builder-source-columns-now-appear-immediately-when-connected.md +6 -0
- package/corpus/templates/content/changelog/2026-07-30-large-builder-backed-tables-show-useful-rows-sooner-and-fini.md +6 -0
- package/corpus/templates/content/changelog/2026-08-01-large-builder-databases-now-show-rows-immediately-and-finish.md +6 -0
- package/corpus/templates/content/parity/matrix.md +2 -1
- package/corpus/templates/content/parity/matrix.ts +25 -0
- package/corpus/templates/content/shared/api.ts +54 -4
- package/corpus/templates/content/shared/database-query.ts +359 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +7 -0
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +12 -0
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/analytics.d.ts +8 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +95 -1
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +6 -0
- package/dist/client/use-action.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +38 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/posthog-ai.d.ts +126 -0
- package/dist/observability/posthog-ai.d.ts.map +1 -0
- package/dist/observability/posthog-ai.js +190 -0
- package/dist/observability/posthog-ai.js.map +1 -0
- package/dist/observability/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts.map +1 -1
- package/dist/observability/routes.js +26 -6
- package/dist/observability/routes.js.map +1 -1
- package/dist/observability/traces.d.ts +9 -0
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +219 -10
- package/dist/observability/traces.js.map +1 -1
- package/dist/observability/types.d.ts +10 -0
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/observability/types.js +1 -0
- package/dist/observability/types.js.map +1 -1
- package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
- package/dist/secrets/register-framework-secrets.js +14 -0
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/server/agent-run-context.d.ts +8 -0
- package/dist/server/agent-run-context.d.ts.map +1 -1
- package/dist/server/agent-run-context.js +18 -0
- package/dist/server/agent-run-context.js.map +1 -1
- package/dist/server/capture-error.d.ts +8 -0
- package/dist/server/capture-error.d.ts.map +1 -1
- package/dist/server/capture-error.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +0 -23
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +71 -7
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/deploy-environment.d.ts +17 -0
- package/dist/server/deploy-environment.d.ts.map +1 -0
- package/dist/server/deploy-environment.js +47 -0
- package/dist/server/deploy-environment.js.map +1 -0
- package/dist/server/error-noise-filter.d.ts +80 -0
- package/dist/server/error-noise-filter.d.ts.map +1 -0
- package/dist/server/error-noise-filter.js +173 -0
- package/dist/server/error-noise-filter.js.map +1 -0
- package/dist/server/posthog-config.d.ts +28 -0
- package/dist/server/posthog-config.d.ts.map +1 -0
- package/dist/server/posthog-config.js +53 -0
- package/dist/server/posthog-config.js.map +1 -0
- package/dist/server/request-context.d.ts +6 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/sentry-config.d.ts +1 -0
- package/dist/server/sentry-config.d.ts.map +1 -1
- package/dist/server/sentry-config.js +3 -1
- package/dist/server/sentry-config.js.map +1 -1
- package/dist/server/sentry-plugin.d.ts.map +1 -1
- package/dist/server/sentry-plugin.js +12 -27
- package/dist/server/sentry-plugin.js.map +1 -1
- package/dist/server/sentry.d.ts.map +1 -1
- package/dist/server/sentry.js +8 -124
- package/dist/server/sentry.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +6 -1
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +36 -10
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +55 -1
- package/dist/tracking/error-capture.d.ts +7 -0
- package/dist/tracking/error-capture.d.ts.map +1 -1
- package/dist/tracking/error-capture.js +10 -73
- package/dist/tracking/error-capture.js.map +1 -1
- package/dist/tracking/index.d.ts +1 -0
- package/dist/tracking/index.d.ts.map +1 -1
- package/dist/tracking/index.js +1 -0
- package/dist/tracking/index.js.map +1 -1
- package/dist/tracking/posthog-exception.d.ts +103 -0
- package/dist/tracking/posthog-exception.d.ts.map +1 -0
- package/dist/tracking/posthog-exception.js +181 -0
- package/dist/tracking/posthog-exception.js.map +1 -0
- package/dist/tracking/providers.d.ts +14 -0
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +63 -11
- package/dist/tracking/providers.js.map +1 -1
- package/dist/tracking/redaction.d.ts +25 -0
- package/dist/tracking/redaction.d.ts.map +1 -0
- package/dist/tracking/redaction.js +85 -0
- package/dist/tracking/redaction.js.map +1 -0
- package/docs/content/observability.mdx +41 -1
- package/docs/content/tracking.mdx +25 -0
- package/package.json +1 -1
- package/src/agent/run-manager.ts +7 -0
- package/src/client/agent-chat-adapter.ts +10 -0
- package/src/client/analytics.ts +105 -1
- package/src/client/use-action.ts +6 -0
- package/src/deploy/build.ts +38 -1
- package/src/observability/posthog-ai.ts +294 -0
- package/src/observability/routes.ts +29 -6
- package/src/observability/traces.ts +287 -12
- package/src/observability/types.ts +11 -0
- package/src/secrets/register-framework-secrets.ts +16 -0
- package/src/server/agent-run-context.ts +21 -0
- package/src/server/capture-error.ts +8 -0
- package/src/server/core-routes-plugin.ts +75 -4
- package/src/server/deploy-environment.ts +57 -0
- package/src/server/error-noise-filter.ts +268 -0
- package/src/server/posthog-config.ts +75 -0
- package/src/server/request-context.ts +6 -0
- package/src/server/sentry-config.ts +4 -8
- package/src/server/sentry-plugin.ts +13 -29
- package/src/server/sentry.ts +13 -156
- package/src/server/ssr-handler.ts +6 -1
- package/src/shared/mcp-embed-headers.ts +1 -1
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +36 -10
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +55 -1
- package/src/tracking/error-capture.ts +44 -109
- package/src/tracking/index.ts +11 -0
- package/src/tracking/posthog-exception.ts +305 -0
- package/src/tracking/providers.ts +91 -14
- package/src/tracking/redaction.ts +105 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build PostHog error-tracking payloads from a raw JS error or from the
|
|
3
|
+
* camelCase exception properties the framework's own `captureException()`
|
|
4
|
+
* emits.
|
|
5
|
+
*
|
|
6
|
+
* PostHog's error tracking only groups and renders an issue when the event
|
|
7
|
+
* carries `$exception_list` with per-frame stack data. An event named
|
|
8
|
+
* `$exception` without it is ingested and displayed as an empty, ungroupable
|
|
9
|
+
* issue — which is worse than no event, because the count looks like coverage.
|
|
10
|
+
*
|
|
11
|
+
* Frame parsing follows posthog-js (itself derived from Sentry's TraceKit
|
|
12
|
+
* fork), because PostHog's ingestion is written against that shape:
|
|
13
|
+
* - `Error: …` header lines are skipped, not parsed as frames
|
|
14
|
+
* - lines over 1 KB are skipped (the regexes backtrack)
|
|
15
|
+
* - frames are capped at 50 and reversed to oldest-call-first
|
|
16
|
+
* - an unresolvable function name is `?`, never empty
|
|
17
|
+
*
|
|
18
|
+
* Runs unchanged in Node and the browser: no `process`, no Node built-ins.
|
|
19
|
+
*
|
|
20
|
+
* @see https://posthog.com/docs/error-tracking/installation/manual
|
|
21
|
+
*/
|
|
22
|
+
export type PostHogExceptionLevel = "fatal" | "error" | "warning" | "info" | "debug";
|
|
23
|
+
export interface PostHogStackFrame {
|
|
24
|
+
/**
|
|
25
|
+
* Always `"custom"`. PostHog reserves the language-specific platforms for
|
|
26
|
+
* frames it will try to symbolicate against uploaded source maps; we upload
|
|
27
|
+
* none, so claiming one would render every minified frame as a failed
|
|
28
|
+
* resolution rather than as the raw frame it is.
|
|
29
|
+
*/
|
|
30
|
+
platform: "custom";
|
|
31
|
+
lang: string;
|
|
32
|
+
function: string;
|
|
33
|
+
filename?: string;
|
|
34
|
+
lineno?: number;
|
|
35
|
+
colno?: number;
|
|
36
|
+
in_app: boolean;
|
|
37
|
+
resolved: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface PostHogExceptionEntry {
|
|
40
|
+
type: string;
|
|
41
|
+
value: string;
|
|
42
|
+
mechanism: {
|
|
43
|
+
handled: boolean;
|
|
44
|
+
synthetic: boolean;
|
|
45
|
+
type?: string;
|
|
46
|
+
};
|
|
47
|
+
stacktrace?: {
|
|
48
|
+
type: "raw";
|
|
49
|
+
frames: PostHogStackFrame[];
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export interface PostHogExceptionProperties {
|
|
53
|
+
$exception_list: PostHogExceptionEntry[];
|
|
54
|
+
$exception_level: PostHogExceptionLevel;
|
|
55
|
+
$exception_fingerprint?: string;
|
|
56
|
+
[key: string]: unknown;
|
|
57
|
+
}
|
|
58
|
+
export interface PostHogExceptionInput {
|
|
59
|
+
/** Error class name, e.g. `TypeError`. */
|
|
60
|
+
type: string;
|
|
61
|
+
/** Error message. */
|
|
62
|
+
value: string;
|
|
63
|
+
/** Raw `error.stack` string, when available. */
|
|
64
|
+
stack?: string;
|
|
65
|
+
/** `false` for errors that crashed the request/page rather than being caught. */
|
|
66
|
+
handled?: boolean;
|
|
67
|
+
/** `true` when the framework synthesized the error from a non-Error throw. */
|
|
68
|
+
synthetic?: boolean;
|
|
69
|
+
/** Mechanism label, e.g. `onunhandledrejection`, `nitro.error`. */
|
|
70
|
+
mechanismType?: string;
|
|
71
|
+
level?: PostHogExceptionLevel;
|
|
72
|
+
/** Overrides PostHog's default grouping. */
|
|
73
|
+
fingerprint?: string;
|
|
74
|
+
/** Frame language tag. Defaults to `javascript`. */
|
|
75
|
+
lang?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Parse a `error.stack` string into PostHog stack frames, oldest call first.
|
|
79
|
+
*
|
|
80
|
+
* Returns an empty array when nothing parsed. Callers must treat that as
|
|
81
|
+
* "no frames" and omit `stacktrace` entirely rather than sending an empty
|
|
82
|
+
* frame list — PostHog renders the latter as a stack that exists and is empty.
|
|
83
|
+
*/
|
|
84
|
+
export declare function parseStackFrames(stack: string | undefined, lang?: string): PostHogStackFrame[];
|
|
85
|
+
/**
|
|
86
|
+
* Build the `$exception_*` properties for a PostHog error-tracking event.
|
|
87
|
+
*
|
|
88
|
+
* The caller merges these into the event properties alongside `distinct_id`
|
|
89
|
+
* and any app dimensions.
|
|
90
|
+
*/
|
|
91
|
+
export declare function toPostHogExceptionProperties(input: PostHogExceptionInput): PostHogExceptionProperties;
|
|
92
|
+
/** Build `$exception_*` properties directly from a thrown value. */
|
|
93
|
+
export declare function errorToPostHogExceptionProperties(error: unknown, options?: Omit<PostHogExceptionInput, "type" | "value" | "stack">): PostHogExceptionProperties;
|
|
94
|
+
/**
|
|
95
|
+
* Reshape the camelCase properties emitted by `tracking/error-capture.ts` into
|
|
96
|
+
* PostHog's `$exception_list` form.
|
|
97
|
+
*
|
|
98
|
+
* Returns `undefined` when the event carries no recognizable exception fields,
|
|
99
|
+
* so the caller can pass it through untouched instead of inventing an empty
|
|
100
|
+
* issue out of an unrelated event that happens to be named `$exception`.
|
|
101
|
+
*/
|
|
102
|
+
export declare function reshapeTrackedExceptionProperties(properties: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
|
|
103
|
+
//# sourceMappingURL=posthog-exception.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"posthog-exception.d.ts","sourceRoot":"","sources":["../../src/tracking/posthog-exception.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAuBH,MAAM,MAAM,qBAAqB,GAC7B,OAAO,GACP,OAAO,GACP,SAAS,GACT,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,iBAAiB,EAAE,CAAA;KAAE,CAAC;CAC3D;AAED,MAAM,WAAW,0BAA0B;IACzC,eAAe,EAAE,qBAAqB,EAAE,CAAC;IACzC,gBAAgB,EAAE,qBAAqB,CAAC;IACxC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,qBAAqB,CAAC;IAC9B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAmED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,IAAI,SAAe,GAClB,iBAAiB,EAAE,CAkBrB;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,qBAAqB,GAC3B,0BAA0B,CA0B5B;AAED,oEAAoE;AACpE,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAM,GACpE,0BAA0B,CAS5B;AAED;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC9C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAqCrC"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build PostHog error-tracking payloads from a raw JS error or from the
|
|
3
|
+
* camelCase exception properties the framework's own `captureException()`
|
|
4
|
+
* emits.
|
|
5
|
+
*
|
|
6
|
+
* PostHog's error tracking only groups and renders an issue when the event
|
|
7
|
+
* carries `$exception_list` with per-frame stack data. An event named
|
|
8
|
+
* `$exception` without it is ingested and displayed as an empty, ungroupable
|
|
9
|
+
* issue — which is worse than no event, because the count looks like coverage.
|
|
10
|
+
*
|
|
11
|
+
* Frame parsing follows posthog-js (itself derived from Sentry's TraceKit
|
|
12
|
+
* fork), because PostHog's ingestion is written against that shape:
|
|
13
|
+
* - `Error: …` header lines are skipped, not parsed as frames
|
|
14
|
+
* - lines over 1 KB are skipped (the regexes backtrack)
|
|
15
|
+
* - frames are capped at 50 and reversed to oldest-call-first
|
|
16
|
+
* - an unresolvable function name is `?`, never empty
|
|
17
|
+
*
|
|
18
|
+
* Runs unchanged in Node and the browser: no `process`, no Node built-ins.
|
|
19
|
+
*
|
|
20
|
+
* @see https://posthog.com/docs/error-tracking/installation/manual
|
|
21
|
+
*/
|
|
22
|
+
import { MAX_MESSAGE_LENGTH, MAX_STACK_LENGTH, boundedText, exceptionParts, } from "./redaction.js";
|
|
23
|
+
/** Matches posthog-js's `UNKNOWN_FUNCTION`. */
|
|
24
|
+
const UNKNOWN_FUNCTION = "?";
|
|
25
|
+
const STACKTRACE_FRAME_LIMIT = 50;
|
|
26
|
+
const MAX_STACK_LINE_LENGTH = 1024;
|
|
27
|
+
const ERROR_HEADER_RE = /\S*Error: /;
|
|
28
|
+
const WEBPACK_ERROR_RE = /\(error: (.*)\)/;
|
|
29
|
+
// " at fn (file:1:2)" / " at async Foo.bar (/app/x.js:2:3)" / " at /app/x.js:1:2"
|
|
30
|
+
const V8_FRAME_RE = /^\s*at (?:async )?(?:(.+?)\s+\()?(?:(.+?):(\d+):(\d+)|([^)]+))\)?\s*$/;
|
|
31
|
+
// "fn@https://example.com/s.js:1:2" / "@https://example.com/s.js:1:2"
|
|
32
|
+
const GECKO_FRAME_RE = /^\s*(.*?)@(.+?)(?::(\d+))?(?::(\d+))?\s*$/;
|
|
33
|
+
function isInAppFrame(filename) {
|
|
34
|
+
if (!filename)
|
|
35
|
+
return true;
|
|
36
|
+
return (!filename.startsWith("node:") &&
|
|
37
|
+
!filename.includes("node_modules") &&
|
|
38
|
+
!filename.includes("/internal/") &&
|
|
39
|
+
filename !== "native");
|
|
40
|
+
}
|
|
41
|
+
function makeFrame(lang, fn, filename, lineno, colno) {
|
|
42
|
+
const name = !fn || fn === "<anonymous>" ? UNKNOWN_FUNCTION : fn;
|
|
43
|
+
return {
|
|
44
|
+
platform: "custom",
|
|
45
|
+
lang,
|
|
46
|
+
function: name,
|
|
47
|
+
...(filename ? { filename } : {}),
|
|
48
|
+
...(lineno !== undefined && Number.isFinite(lineno) ? { lineno } : {}),
|
|
49
|
+
...(colno !== undefined && Number.isFinite(colno) ? { colno } : {}),
|
|
50
|
+
in_app: isInAppFrame(filename),
|
|
51
|
+
// Never `true`: we ship no source maps to PostHog, so a minified browser
|
|
52
|
+
// frame is exactly as informative as it looks. Claiming it is resolved
|
|
53
|
+
// would present a mangled name as the real one.
|
|
54
|
+
resolved: false,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function parseFrameLine(line, lang) {
|
|
58
|
+
const v8 = V8_FRAME_RE.exec(line);
|
|
59
|
+
if (v8) {
|
|
60
|
+
const [, fn, file, lineNo, colNo, bare] = v8;
|
|
61
|
+
const filename = (file ?? bare)?.replace(/^file:\/\//, "");
|
|
62
|
+
return makeFrame(lang, fn ?? UNKNOWN_FUNCTION, filename, lineNo ? Number(lineNo) : undefined, colNo ? Number(colNo) : undefined);
|
|
63
|
+
}
|
|
64
|
+
const gecko = GECKO_FRAME_RE.exec(line);
|
|
65
|
+
if (gecko) {
|
|
66
|
+
const [, fn, file, lineNo, colNo] = gecko;
|
|
67
|
+
return makeFrame(lang, fn || UNKNOWN_FUNCTION, file, lineNo ? Number(lineNo) : undefined, colNo ? Number(colNo) : undefined);
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Parse a `error.stack` string into PostHog stack frames, oldest call first.
|
|
73
|
+
*
|
|
74
|
+
* Returns an empty array when nothing parsed. Callers must treat that as
|
|
75
|
+
* "no frames" and omit `stacktrace` entirely rather than sending an empty
|
|
76
|
+
* frame list — PostHog renders the latter as a stack that exists and is empty.
|
|
77
|
+
*/
|
|
78
|
+
export function parseStackFrames(stack, lang = "javascript") {
|
|
79
|
+
if (!stack)
|
|
80
|
+
return [];
|
|
81
|
+
const frames = [];
|
|
82
|
+
for (const rawLine of stack.split("\n")) {
|
|
83
|
+
if (rawLine.length > MAX_STACK_LINE_LENGTH)
|
|
84
|
+
continue;
|
|
85
|
+
const line = WEBPACK_ERROR_RE.test(rawLine)
|
|
86
|
+
? rawLine.replace(WEBPACK_ERROR_RE, "$1")
|
|
87
|
+
: rawLine;
|
|
88
|
+
if (ERROR_HEADER_RE.test(line))
|
|
89
|
+
continue;
|
|
90
|
+
const frame = parseFrameLine(line, lang);
|
|
91
|
+
if (frame)
|
|
92
|
+
frames.push(frame);
|
|
93
|
+
if (frames.length >= STACKTRACE_FRAME_LIMIT)
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
frames.reverse();
|
|
97
|
+
return frames;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build the `$exception_*` properties for a PostHog error-tracking event.
|
|
101
|
+
*
|
|
102
|
+
* The caller merges these into the event properties alongside `distinct_id`
|
|
103
|
+
* and any app dimensions.
|
|
104
|
+
*/
|
|
105
|
+
export function toPostHogExceptionProperties(input) {
|
|
106
|
+
const lang = input.lang ?? "javascript";
|
|
107
|
+
const frames = parseStackFrames(input.stack, lang);
|
|
108
|
+
const entry = {
|
|
109
|
+
type: boundedText(input.type || "Error", 200),
|
|
110
|
+
value: boundedText(input.value || input.type || "Error", MAX_MESSAGE_LENGTH),
|
|
111
|
+
mechanism: {
|
|
112
|
+
handled: input.handled ?? true,
|
|
113
|
+
synthetic: input.synthetic ?? false,
|
|
114
|
+
...(input.mechanismType
|
|
115
|
+
? { type: boundedText(input.mechanismType, 100) }
|
|
116
|
+
: {}),
|
|
117
|
+
},
|
|
118
|
+
...(frames.length ? { stacktrace: { type: "raw", frames } } : {}),
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
$exception_list: [entry],
|
|
122
|
+
$exception_level: input.level ?? "error",
|
|
123
|
+
...(input.fingerprint
|
|
124
|
+
? { $exception_fingerprint: boundedText(input.fingerprint, 200) }
|
|
125
|
+
: {}),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** Build `$exception_*` properties directly from a thrown value. */
|
|
129
|
+
export function errorToPostHogExceptionProperties(error, options = {}) {
|
|
130
|
+
const parts = exceptionParts(error);
|
|
131
|
+
return toPostHogExceptionProperties({
|
|
132
|
+
...options,
|
|
133
|
+
type: parts.type,
|
|
134
|
+
value: parts.message,
|
|
135
|
+
stack: parts.stack,
|
|
136
|
+
synthetic: options.synthetic ?? !(error instanceof Error),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Reshape the camelCase properties emitted by `tracking/error-capture.ts` into
|
|
141
|
+
* PostHog's `$exception_list` form.
|
|
142
|
+
*
|
|
143
|
+
* Returns `undefined` when the event carries no recognizable exception fields,
|
|
144
|
+
* so the caller can pass it through untouched instead of inventing an empty
|
|
145
|
+
* issue out of an unrelated event that happens to be named `$exception`.
|
|
146
|
+
*/
|
|
147
|
+
export function reshapeTrackedExceptionProperties(properties) {
|
|
148
|
+
if (!properties)
|
|
149
|
+
return undefined;
|
|
150
|
+
// Already in PostHog form (e.g. relayed from the browser) — leave it alone.
|
|
151
|
+
if (Array.isArray(properties.$exception_list))
|
|
152
|
+
return properties;
|
|
153
|
+
const type = properties.exceptionType;
|
|
154
|
+
const message = properties.exceptionMessage;
|
|
155
|
+
if (typeof type !== "string" && typeof message !== "string") {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
const stack = typeof properties.exceptionStack === "string"
|
|
159
|
+
? properties.exceptionStack.slice(0, MAX_STACK_LENGTH)
|
|
160
|
+
: undefined;
|
|
161
|
+
const level = properties.level;
|
|
162
|
+
const { exceptionType: _type, exceptionMessage: _message, exceptionStack: _stack, handled: _handled, level: _level, ...rest } = properties;
|
|
163
|
+
return {
|
|
164
|
+
...rest,
|
|
165
|
+
...toPostHogExceptionProperties({
|
|
166
|
+
type: typeof type === "string" ? type : "Error",
|
|
167
|
+
value: typeof message === "string" ? message : "Error",
|
|
168
|
+
stack,
|
|
169
|
+
handled: typeof properties.handled === "boolean" ? properties.handled : true,
|
|
170
|
+
level: isExceptionLevel(level) ? level : "error",
|
|
171
|
+
}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function isExceptionLevel(value) {
|
|
175
|
+
return (value === "fatal" ||
|
|
176
|
+
value === "error" ||
|
|
177
|
+
value === "warning" ||
|
|
178
|
+
value === "info" ||
|
|
179
|
+
value === "debug");
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=posthog-exception.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"posthog-exception.js","sourceRoot":"","sources":["../../src/tracking/posthog-exception.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Build PostHog error-tracking payloads from a raw JS error or from the\n * camelCase exception properties the framework's own `captureException()`\n * emits.\n *\n * PostHog's error tracking only groups and renders an issue when the event\n * carries `$exception_list` with per-frame stack data. An event named\n * `$exception` without it is ingested and displayed as an empty, ungroupable\n * issue — which is worse than no event, because the count looks like coverage.\n *\n * Frame parsing follows posthog-js (itself derived from Sentry's TraceKit\n * fork), because PostHog's ingestion is written against that shape:\n * - `Error: …` header lines are skipped, not parsed as frames\n * - lines over 1 KB are skipped (the regexes backtrack)\n * - frames are capped at 50 and reversed to oldest-call-first\n * - an unresolvable function name is `?`, never empty\n *\n * Runs unchanged in Node and the browser: no `process`, no Node built-ins.\n *\n * @see https://posthog.com/docs/error-tracking/installation/manual\n */\n\nimport {\n MAX_MESSAGE_LENGTH,\n MAX_STACK_LENGTH,\n boundedText,\n exceptionParts,\n} from \"./redaction.js\";\n\n/** Matches posthog-js's `UNKNOWN_FUNCTION`. */\nconst UNKNOWN_FUNCTION = \"?\";\nconst STACKTRACE_FRAME_LIMIT = 50;\nconst MAX_STACK_LINE_LENGTH = 1024;\n\nconst ERROR_HEADER_RE = /\\S*Error: /;\nconst WEBPACK_ERROR_RE = /\\(error: (.*)\\)/;\n\n// \" at fn (file:1:2)\" / \" at async Foo.bar (/app/x.js:2:3)\" / \" at /app/x.js:1:2\"\nconst V8_FRAME_RE =\n /^\\s*at (?:async )?(?:(.+?)\\s+\\()?(?:(.+?):(\\d+):(\\d+)|([^)]+))\\)?\\s*$/;\n// \"fn@https://example.com/s.js:1:2\" / \"@https://example.com/s.js:1:2\"\nconst GECKO_FRAME_RE = /^\\s*(.*?)@(.+?)(?::(\\d+))?(?::(\\d+))?\\s*$/;\n\nexport type PostHogExceptionLevel =\n | \"fatal\"\n | \"error\"\n | \"warning\"\n | \"info\"\n | \"debug\";\n\nexport interface PostHogStackFrame {\n /**\n * Always `\"custom\"`. PostHog reserves the language-specific platforms for\n * frames it will try to symbolicate against uploaded source maps; we upload\n * none, so claiming one would render every minified frame as a failed\n * resolution rather than as the raw frame it is.\n */\n platform: \"custom\";\n lang: string;\n function: string;\n filename?: string;\n lineno?: number;\n colno?: number;\n in_app: boolean;\n resolved: boolean;\n}\n\nexport interface PostHogExceptionEntry {\n type: string;\n value: string;\n mechanism: { handled: boolean; synthetic: boolean; type?: string };\n stacktrace?: { type: \"raw\"; frames: PostHogStackFrame[] };\n}\n\nexport interface PostHogExceptionProperties {\n $exception_list: PostHogExceptionEntry[];\n $exception_level: PostHogExceptionLevel;\n $exception_fingerprint?: string;\n [key: string]: unknown;\n}\n\nexport interface PostHogExceptionInput {\n /** Error class name, e.g. `TypeError`. */\n type: string;\n /** Error message. */\n value: string;\n /** Raw `error.stack` string, when available. */\n stack?: string;\n /** `false` for errors that crashed the request/page rather than being caught. */\n handled?: boolean;\n /** `true` when the framework synthesized the error from a non-Error throw. */\n synthetic?: boolean;\n /** Mechanism label, e.g. `onunhandledrejection`, `nitro.error`. */\n mechanismType?: string;\n level?: PostHogExceptionLevel;\n /** Overrides PostHog's default grouping. */\n fingerprint?: string;\n /** Frame language tag. Defaults to `javascript`. */\n lang?: string;\n}\n\nfunction isInAppFrame(filename: string | undefined): boolean {\n if (!filename) return true;\n return (\n !filename.startsWith(\"node:\") &&\n !filename.includes(\"node_modules\") &&\n !filename.includes(\"/internal/\") &&\n filename !== \"native\"\n );\n}\n\nfunction makeFrame(\n lang: string,\n fn: string,\n filename: string | undefined,\n lineno: number | undefined,\n colno: number | undefined,\n): PostHogStackFrame {\n const name = !fn || fn === \"<anonymous>\" ? UNKNOWN_FUNCTION : fn;\n return {\n platform: \"custom\",\n lang,\n function: name,\n ...(filename ? { filename } : {}),\n ...(lineno !== undefined && Number.isFinite(lineno) ? { lineno } : {}),\n ...(colno !== undefined && Number.isFinite(colno) ? { colno } : {}),\n in_app: isInAppFrame(filename),\n // Never `true`: we ship no source maps to PostHog, so a minified browser\n // frame is exactly as informative as it looks. Claiming it is resolved\n // would present a mangled name as the real one.\n resolved: false,\n };\n}\n\nfunction parseFrameLine(\n line: string,\n lang: string,\n): PostHogStackFrame | undefined {\n const v8 = V8_FRAME_RE.exec(line);\n if (v8) {\n const [, fn, file, lineNo, colNo, bare] = v8;\n const filename = (file ?? bare)?.replace(/^file:\\/\\//, \"\");\n return makeFrame(\n lang,\n fn ?? UNKNOWN_FUNCTION,\n filename,\n lineNo ? Number(lineNo) : undefined,\n colNo ? Number(colNo) : undefined,\n );\n }\n\n const gecko = GECKO_FRAME_RE.exec(line);\n if (gecko) {\n const [, fn, file, lineNo, colNo] = gecko;\n return makeFrame(\n lang,\n fn || UNKNOWN_FUNCTION,\n file,\n lineNo ? Number(lineNo) : undefined,\n colNo ? Number(colNo) : undefined,\n );\n }\n\n return undefined;\n}\n\n/**\n * Parse a `error.stack` string into PostHog stack frames, oldest call first.\n *\n * Returns an empty array when nothing parsed. Callers must treat that as\n * \"no frames\" and omit `stacktrace` entirely rather than sending an empty\n * frame list — PostHog renders the latter as a stack that exists and is empty.\n */\nexport function parseStackFrames(\n stack: string | undefined,\n lang = \"javascript\",\n): PostHogStackFrame[] {\n if (!stack) return [];\n const frames: PostHogStackFrame[] = [];\n\n for (const rawLine of stack.split(\"\\n\")) {\n if (rawLine.length > MAX_STACK_LINE_LENGTH) continue;\n const line = WEBPACK_ERROR_RE.test(rawLine)\n ? rawLine.replace(WEBPACK_ERROR_RE, \"$1\")\n : rawLine;\n if (ERROR_HEADER_RE.test(line)) continue;\n\n const frame = parseFrameLine(line, lang);\n if (frame) frames.push(frame);\n if (frames.length >= STACKTRACE_FRAME_LIMIT) break;\n }\n\n frames.reverse();\n return frames;\n}\n\n/**\n * Build the `$exception_*` properties for a PostHog error-tracking event.\n *\n * The caller merges these into the event properties alongside `distinct_id`\n * and any app dimensions.\n */\nexport function toPostHogExceptionProperties(\n input: PostHogExceptionInput,\n): PostHogExceptionProperties {\n const lang = input.lang ?? \"javascript\";\n const frames = parseStackFrames(input.stack, lang);\n const entry: PostHogExceptionEntry = {\n type: boundedText(input.type || \"Error\", 200),\n value: boundedText(\n input.value || input.type || \"Error\",\n MAX_MESSAGE_LENGTH,\n ),\n mechanism: {\n handled: input.handled ?? true,\n synthetic: input.synthetic ?? false,\n ...(input.mechanismType\n ? { type: boundedText(input.mechanismType, 100) }\n : {}),\n },\n ...(frames.length ? { stacktrace: { type: \"raw\" as const, frames } } : {}),\n };\n\n return {\n $exception_list: [entry],\n $exception_level: input.level ?? \"error\",\n ...(input.fingerprint\n ? { $exception_fingerprint: boundedText(input.fingerprint, 200) }\n : {}),\n };\n}\n\n/** Build `$exception_*` properties directly from a thrown value. */\nexport function errorToPostHogExceptionProperties(\n error: unknown,\n options: Omit<PostHogExceptionInput, \"type\" | \"value\" | \"stack\"> = {},\n): PostHogExceptionProperties {\n const parts = exceptionParts(error);\n return toPostHogExceptionProperties({\n ...options,\n type: parts.type,\n value: parts.message,\n stack: parts.stack,\n synthetic: options.synthetic ?? !(error instanceof Error),\n });\n}\n\n/**\n * Reshape the camelCase properties emitted by `tracking/error-capture.ts` into\n * PostHog's `$exception_list` form.\n *\n * Returns `undefined` when the event carries no recognizable exception fields,\n * so the caller can pass it through untouched instead of inventing an empty\n * issue out of an unrelated event that happens to be named `$exception`.\n */\nexport function reshapeTrackedExceptionProperties(\n properties: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!properties) return undefined;\n // Already in PostHog form (e.g. relayed from the browser) — leave it alone.\n if (Array.isArray(properties.$exception_list)) return properties;\n\n const type = properties.exceptionType;\n const message = properties.exceptionMessage;\n if (typeof type !== \"string\" && typeof message !== \"string\") {\n return undefined;\n }\n\n const stack =\n typeof properties.exceptionStack === \"string\"\n ? properties.exceptionStack.slice(0, MAX_STACK_LENGTH)\n : undefined;\n const level = properties.level;\n\n const {\n exceptionType: _type,\n exceptionMessage: _message,\n exceptionStack: _stack,\n handled: _handled,\n level: _level,\n ...rest\n } = properties;\n\n return {\n ...rest,\n ...toPostHogExceptionProperties({\n type: typeof type === \"string\" ? type : \"Error\",\n value: typeof message === \"string\" ? message : \"Error\",\n stack,\n handled:\n typeof properties.handled === \"boolean\" ? properties.handled : true,\n level: isExceptionLevel(level) ? level : \"error\",\n }),\n };\n}\n\nfunction isExceptionLevel(value: unknown): value is PostHogExceptionLevel {\n return (\n value === \"fatal\" ||\n value === \"error\" ||\n value === \"warning\" ||\n value === \"info\" ||\n value === \"debug\"\n );\n}\n"]}
|
|
@@ -12,5 +12,19 @@
|
|
|
12
12
|
* Call `registerBuiltinProviders()` at server startup (done
|
|
13
13
|
* automatically by the core-routes plugin).
|
|
14
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* Send one event to PostHog only, bypassing the provider fan-out.
|
|
17
|
+
*
|
|
18
|
+
* For payloads whose *shape* is PostHog-specific and whose *content* other
|
|
19
|
+
* backends must not receive. `track()` broadcasts to every configured provider,
|
|
20
|
+
* so a PostHog-only integration (e.g. enabling a survey id) would otherwise
|
|
21
|
+
* start exporting that integration's content — including user-authored text —
|
|
22
|
+
* to Mixpanel, Amplitude, webhooks, and Agent Native Analytics as a side
|
|
23
|
+
* effect nobody opted into.
|
|
24
|
+
*
|
|
25
|
+
* Returns `false` when PostHog is not configured, so callers can tell "not
|
|
26
|
+
* sent" from "sent".
|
|
27
|
+
*/
|
|
28
|
+
export declare function sendPostHogEvent(name: string, properties: Record<string, unknown>, distinctId: string): boolean;
|
|
15
29
|
export declare function registerBuiltinProviders(): void;
|
|
16
30
|
//# sourceMappingURL=providers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/tracking/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/tracking/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA6OH;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,UAAU,EAAE,MAAM,GACjB,OAAO,CAiBT;AAkKD,wBAAgB,wBAAwB,IAAI,IAAI,CAqD/C"}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* Call `registerBuiltinProviders()` at server startup (done
|
|
13
13
|
* automatically by the core-routes plugin).
|
|
14
14
|
*/
|
|
15
|
+
import { reshapeTrackedExceptionProperties } from "./posthog-exception.js";
|
|
15
16
|
import { registerTrackingProvider } from "./registry.js";
|
|
16
17
|
const POSTHOG_DEFAULT_HOST = "https://us.i.posthog.com";
|
|
17
18
|
const AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT = "https://analytics.agent-native.com/track";
|
|
@@ -119,23 +120,48 @@ function agentNativeAnalyticsFlushesImmediately() {
|
|
|
119
120
|
function isPostHogAiObservabilityEvent(eventName) {
|
|
120
121
|
return eventName.startsWith("$ai_");
|
|
121
122
|
}
|
|
122
|
-
|
|
123
|
+
/**
|
|
124
|
+
* `$ai_*` and `$exception` are ingested through PostHog's dedicated endpoint
|
|
125
|
+
* rather than `/capture/`, and `$exception` additionally has to carry
|
|
126
|
+
* `$exception_list` — the framework's own `captureException()` emits camelCase
|
|
127
|
+
* fields that PostHog would otherwise render as an empty, ungroupable issue.
|
|
128
|
+
*/
|
|
129
|
+
function createPostHogProvider(apiKey, host, errorTracking) {
|
|
130
|
+
const sendToEventsEndpoint = (event, properties, distinctId) => {
|
|
131
|
+
enqueue(`${host}/i/v0/e/`, JSON.stringify({
|
|
132
|
+
api_key: apiKey,
|
|
133
|
+
event: event.name,
|
|
134
|
+
properties: {
|
|
135
|
+
distinct_id: distinctId,
|
|
136
|
+
...properties,
|
|
137
|
+
timestamp: event.timestamp,
|
|
138
|
+
},
|
|
139
|
+
}));
|
|
140
|
+
};
|
|
123
141
|
return {
|
|
124
142
|
name: "posthog",
|
|
125
143
|
track(event) {
|
|
126
144
|
const distinctId = event.userId || "anonymous";
|
|
127
145
|
if (isPostHogAiObservabilityEvent(event.name)) {
|
|
128
|
-
|
|
129
|
-
api_key: apiKey,
|
|
130
|
-
event: event.name,
|
|
131
|
-
properties: {
|
|
132
|
-
distinct_id: distinctId,
|
|
133
|
-
...event.properties,
|
|
134
|
-
timestamp: event.timestamp,
|
|
135
|
-
},
|
|
136
|
-
}));
|
|
146
|
+
sendToEventsEndpoint(event, event.properties, distinctId);
|
|
137
147
|
return;
|
|
138
148
|
}
|
|
149
|
+
if (event.name === "$exception") {
|
|
150
|
+
// `POSTHOG_ERROR_TRACKING=false` keeps product analytics flowing while
|
|
151
|
+
// another backend owns crashes. Drop rather than downgrade to
|
|
152
|
+
// `/capture/`: a malformed exception is what this branch exists to
|
|
153
|
+
// prevent.
|
|
154
|
+
if (!errorTracking)
|
|
155
|
+
return;
|
|
156
|
+
const reshaped = reshapeTrackedExceptionProperties(event.properties);
|
|
157
|
+
if (reshaped) {
|
|
158
|
+
sendToEventsEndpoint(event, reshaped, distinctId);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
// No recognizable exception fields. Fall through to `/capture/` so the
|
|
162
|
+
// event is still recorded as-is rather than becoming an issue with
|
|
163
|
+
// nothing in it.
|
|
164
|
+
}
|
|
139
165
|
enqueue(`${host}/capture/`, JSON.stringify({
|
|
140
166
|
api_key: apiKey,
|
|
141
167
|
event: event.name,
|
|
@@ -159,6 +185,32 @@ function createPostHogProvider(apiKey, host) {
|
|
|
159
185
|
},
|
|
160
186
|
};
|
|
161
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Send one event to PostHog only, bypassing the provider fan-out.
|
|
190
|
+
*
|
|
191
|
+
* For payloads whose *shape* is PostHog-specific and whose *content* other
|
|
192
|
+
* backends must not receive. `track()` broadcasts to every configured provider,
|
|
193
|
+
* so a PostHog-only integration (e.g. enabling a survey id) would otherwise
|
|
194
|
+
* start exporting that integration's content — including user-authored text —
|
|
195
|
+
* to Mixpanel, Amplitude, webhooks, and Agent Native Analytics as a side
|
|
196
|
+
* effect nobody opted into.
|
|
197
|
+
*
|
|
198
|
+
* Returns `false` when PostHog is not configured, so callers can tell "not
|
|
199
|
+
* sent" from "sent".
|
|
200
|
+
*/
|
|
201
|
+
export function sendPostHogEvent(name, properties, distinctId) {
|
|
202
|
+
const apiKey = process.env.POSTHOG_API_KEY;
|
|
203
|
+
if (!apiKey)
|
|
204
|
+
return false;
|
|
205
|
+
const host = (process.env.POSTHOG_HOST || POSTHOG_DEFAULT_HOST).replace(/\/+$/, "");
|
|
206
|
+
enqueue(`${host}/capture/`, JSON.stringify({
|
|
207
|
+
api_key: apiKey,
|
|
208
|
+
event: name,
|
|
209
|
+
distinct_id: distinctId,
|
|
210
|
+
properties: { ...properties, timestamp: new Date().toISOString() },
|
|
211
|
+
}));
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
162
214
|
// ─── Mixpanel ──────────────────────────────────────────────────────────────
|
|
163
215
|
function createMixpanelProvider(token) {
|
|
164
216
|
return {
|
|
@@ -292,7 +344,7 @@ export function registerBuiltinProviders() {
|
|
|
292
344
|
const posthogKey = process.env.POSTHOG_API_KEY;
|
|
293
345
|
if (posthogKey) {
|
|
294
346
|
const host = (process.env.POSTHOG_HOST || POSTHOG_DEFAULT_HOST).replace(/\/+$/, "");
|
|
295
|
-
registerTrackingProvider(createPostHogProvider(posthogKey, host));
|
|
347
|
+
registerTrackingProvider(createPostHogProvider(posthogKey, host, process.env.POSTHOG_ERROR_TRACKING?.trim().toLowerCase() !== "false"));
|
|
296
348
|
}
|
|
297
349
|
const mixpanelToken = process.env.MIXPANEL_TOKEN;
|
|
298
350
|
if (mixpanelToken) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/tracking/providers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in tracking providers that auto-register from env vars.\n *\n * No SDK dependencies — uses raw HTTP to keep core lightweight.\n * Set the env var and tracking starts automatically.\n *\n * POSTHOG_API_KEY + POSTHOG_HOST → PostHog\n * MIXPANEL_TOKEN → Mixpanel\n * AMPLITUDE_API_KEY → Amplitude\n * AGENT_NATIVE_ANALYTICS_PUBLIC_KEY → Agent Native Analytics\n *\n * Call `registerBuiltinProviders()` at server startup (done\n * automatically by the core-routes plugin).\n */\n\nimport { registerTrackingProvider } from \"./registry.js\";\nimport type { TrackingProvider, TrackingEvent } from \"./types.js\";\n\nconst POSTHOG_DEFAULT_HOST = \"https://us.i.posthog.com\";\nconst AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT =\n \"https://analytics.agent-native.com/track\";\nconst BATCH_INTERVAL_MS = 10_000;\nconst MAX_BATCH_SIZE = 50;\n\n// ─── Batched sender ────────────────────────────────────────────────────────\n\ninterface QueuedEvent {\n url: string;\n body: string;\n headers?: Record<string, string>;\n}\n\ninterface EnqueueOptions {\n flushImmediately?: boolean;\n}\n\n// Use globalThis so multiple ESM graph instances (Vite dev + Nitro symlinks)\n// share one queue, matching the same pattern as the tracking registry.\nconst QUEUE_KEY = Symbol.for(\"@agent-native/core/tracking.queue\");\nconst TIMER_KEY = Symbol.for(\"@agent-native/core/tracking.timer\");\n\ninterface GlobalWithQueue {\n [QUEUE_KEY]?: QueuedEvent[];\n [TIMER_KEY]?: ReturnType<typeof setTimeout> | null;\n}\n\nfunction getQueue(): QueuedEvent[] {\n const g = globalThis as unknown as GlobalWithQueue;\n if (!g[QUEUE_KEY]) g[QUEUE_KEY] = [];\n return g[QUEUE_KEY]!;\n}\n\nfunction getTimer(): ReturnType<typeof setTimeout> | null {\n const g = globalThis as unknown as GlobalWithQueue;\n return g[TIMER_KEY] ?? null;\n}\n\nfunction setTimer(t: ReturnType<typeof setTimeout> | null): void {\n (globalThis as unknown as GlobalWithQueue)[TIMER_KEY] = t;\n}\n\nfunction enqueue(\n url: string,\n body: string,\n headers?: Record<string, string>,\n options?: EnqueueOptions,\n): void {\n const queue = getQueue();\n queue.push({ url, body, headers });\n if (options?.flushImmediately || queue.length >= MAX_BATCH_SIZE) {\n void drainQueue();\n } else if (!getTimer()) {\n const timer = setTimeout(() => {\n void drainQueue();\n }, BATCH_INTERVAL_MS);\n if (timer.unref) timer.unref();\n setTimer(timer);\n }\n}\n\nfunction drainQueue(): Promise<void[]> {\n const t = getTimer();\n if (t) {\n clearTimeout(t);\n setTimer(null);\n }\n const queue = getQueue();\n const batch = queue.splice(0, queue.length);\n return Promise.all(\n batch.map((item) =>\n fetch(item.url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", ...item.headers },\n body: item.body,\n }).then(\n () => undefined,\n () => undefined,\n ),\n ),\n );\n}\n\nfunction isLocalhostUrl(value: string | undefined): boolean {\n if (!value || !value.trim()) return false;\n const raw = value.trim();\n const withProtocol = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw)\n ? raw\n : `https://${raw}`;\n try {\n const { hostname } = new URL(withProtocol);\n const h = hostname.toLowerCase();\n return (\n h === \"localhost\" ||\n h === \"127.0.0.1\" ||\n h === \"::1\" ||\n h === \"[::1]\" ||\n h.endsWith(\".localhost\") ||\n h.endsWith(\".local\")\n );\n } catch {\n return false;\n }\n}\n\nfunction shouldSkipAgentNativeAnalyticsForLocalhost(): boolean {\n if (process.env.AGENT_NATIVE_ANALYTICS_ALLOW_LOCALHOST === \"true\") {\n return false;\n }\n if (process.env.NODE_ENV === \"development\") return true;\n return [\n process.env.APP_URL,\n process.env.BETTER_AUTH_URL,\n process.env.URL,\n process.env.DEPLOY_URL,\n process.env.VERCEL_PROJECT_PRODUCTION_URL,\n process.env.VERCEL_URL,\n ].some(isLocalhostUrl);\n}\n\nfunction isServerlessRuntime(): boolean {\n return Boolean(\n process.env.NETLIFY ||\n process.env.VERCEL ||\n process.env.AWS_LAMBDA_FUNCTION_NAME ||\n process.env.AWS_EXECUTION_ENV ||\n process.env.LAMBDA_TASK_ROOT ||\n process.env.FUNCTION_NAME,\n );\n}\n\nfunction agentNativeAnalyticsFlushesImmediately(): boolean {\n const mode =\n process.env.AGENT_NATIVE_ANALYTICS_FLUSH_MODE?.trim().toLowerCase();\n if (mode === \"batch\") return false;\n if (mode === \"immediate\") return true;\n return isServerlessRuntime();\n}\n\n// ─── PostHog ───────────────────────────────────────────────────────────────\n\nfunction isPostHogAiObservabilityEvent(eventName: string): boolean {\n return eventName.startsWith(\"$ai_\");\n}\n\nfunction createPostHogProvider(apiKey: string, host: string): TrackingProvider {\n return {\n name: \"posthog\",\n track(event: TrackingEvent) {\n const distinctId = event.userId || \"anonymous\";\n if (isPostHogAiObservabilityEvent(event.name)) {\n enqueue(\n `${host}/i/v0/e/`,\n JSON.stringify({\n api_key: apiKey,\n event: event.name,\n properties: {\n distinct_id: distinctId,\n ...event.properties,\n timestamp: event.timestamp,\n },\n }),\n );\n return;\n }\n\n enqueue(\n `${host}/capture/`,\n JSON.stringify({\n api_key: apiKey,\n event: event.name,\n distinct_id: distinctId,\n properties: {\n ...event.properties,\n timestamp: event.timestamp,\n },\n }),\n );\n },\n identify(userId, traits) {\n enqueue(\n `${host}/capture/`,\n JSON.stringify({\n api_key: apiKey,\n event: \"$identify\",\n distinct_id: userId,\n properties: { $set: traits },\n }),\n );\n },\n flush: () => {\n return drainQueue().then(() => undefined);\n },\n };\n}\n\n// ─── Mixpanel ──────────────────────────────────────────────────────────────\n\nfunction createMixpanelProvider(token: string): TrackingProvider {\n return {\n name: \"mixpanel\",\n track(event: TrackingEvent) {\n const data = {\n event: event.name,\n properties: {\n token,\n distinct_id: event.userId || \"anonymous\",\n time: event.timestamp\n ? new Date(event.timestamp).getTime() / 1000\n : undefined,\n ...event.properties,\n },\n };\n enqueue(\"https://api.mixpanel.com/track\", JSON.stringify([data]));\n },\n identify(userId, traits) {\n const data = {\n $token: token,\n $distinct_id: userId,\n $set: traits,\n };\n enqueue(\"https://api.mixpanel.com/engage\", JSON.stringify([data]));\n },\n flush: () => {\n return drainQueue().then(() => undefined);\n },\n };\n}\n\n// ─── Amplitude ─────────────────────────────────────────────────────────────\n\nfunction createAmplitudeProvider(apiKey: string): TrackingProvider {\n return {\n name: \"amplitude\",\n track(event: TrackingEvent) {\n const data = {\n api_key: apiKey,\n events: [\n {\n event_type: event.name,\n user_id: event.userId || \"anonymous\",\n event_properties: event.properties,\n time: event.timestamp\n ? new Date(event.timestamp).getTime()\n : undefined,\n },\n ],\n };\n enqueue(\"https://api2.amplitude.com/2/httpapi\", JSON.stringify(data));\n },\n identify(userId, traits) {\n const data = {\n api_key: apiKey,\n events: [\n {\n event_type: \"$identify\",\n user_id: userId,\n user_properties: { $set: traits },\n },\n ],\n };\n enqueue(\"https://api2.amplitude.com/2/httpapi\", JSON.stringify(data));\n },\n flush: () => {\n return drainQueue().then(() => undefined);\n },\n };\n}\n\n// ─── Webhook (custom HTTP endpoint) ───────────────────────────────────────\n\nfunction createWebhookProvider(\n url: string,\n authHeader?: string,\n): TrackingProvider {\n const extra = authHeader ? { Authorization: authHeader } : undefined;\n return {\n name: \"webhook\",\n track(event: TrackingEvent) {\n enqueue(\n url,\n JSON.stringify({\n event: event.name,\n properties: event.properties,\n userId: event.userId,\n timestamp: event.timestamp,\n }),\n extra,\n );\n },\n identify(userId, traits) {\n enqueue(\n url,\n JSON.stringify({\n event: \"$identify\",\n userId,\n traits,\n timestamp: new Date().toISOString(),\n }),\n extra,\n );\n },\n flush: () => {\n return drainQueue().then(() => undefined);\n },\n };\n}\n\n// ─── Agent Native Analytics ───────────────────────────────────────────────\n\nfunction createAgentNativeAnalyticsProvider(\n publicKey: string,\n endpoint: string,\n): TrackingProvider {\n const flushImmediately = agentNativeAnalyticsFlushesImmediately();\n return {\n name: \"agent-native-analytics\",\n track(event: TrackingEvent) {\n enqueue(\n endpoint,\n JSON.stringify({\n publicKey,\n event: event.name,\n properties: event.properties ?? {},\n userId: event.userId,\n anonymousId: event.anonymousId,\n timestamp: event.timestamp,\n }),\n undefined,\n { flushImmediately },\n );\n },\n identify(userId, traits) {\n enqueue(\n endpoint,\n JSON.stringify({\n publicKey,\n event: \"$identify\",\n userId,\n properties: traits ?? {},\n timestamp: new Date().toISOString(),\n }),\n undefined,\n { flushImmediately },\n );\n },\n flush: () => {\n return drainQueue().then(() => undefined);\n },\n };\n}\n\n// ─── Auto-registration ────────────────────────────────────────────────────\n\nlet _registered = false;\n\nexport function registerBuiltinProviders(): void {\n if (_registered) return;\n _registered = true;\n\n const posthogKey = process.env.POSTHOG_API_KEY;\n if (posthogKey) {\n const host = (process.env.POSTHOG_HOST || POSTHOG_DEFAULT_HOST).replace(\n /\\/+$/,\n \"\",\n );\n registerTrackingProvider(createPostHogProvider(posthogKey, host));\n }\n\n const mixpanelToken = process.env.MIXPANEL_TOKEN;\n if (mixpanelToken) {\n registerTrackingProvider(createMixpanelProvider(mixpanelToken));\n }\n\n const amplitudeKey = process.env.AMPLITUDE_API_KEY;\n if (amplitudeKey) {\n registerTrackingProvider(createAmplitudeProvider(amplitudeKey));\n }\n\n const agentNativeAnalyticsKey =\n process.env.AGENT_NATIVE_ANALYTICS_PUBLIC_KEY ||\n process.env.VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY;\n if (\n agentNativeAnalyticsKey &&\n !shouldSkipAgentNativeAnalyticsForLocalhost()\n ) {\n registerTrackingProvider(\n createAgentNativeAnalyticsProvider(\n agentNativeAnalyticsKey,\n (\n process.env.AGENT_NATIVE_ANALYTICS_ENDPOINT ||\n AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT\n ).replace(/\\/+$/, \"\"),\n ),\n );\n }\n\n const webhookUrl = process.env.TRACKING_WEBHOOK_URL;\n if (webhookUrl) {\n registerTrackingProvider(\n createWebhookProvider(webhookUrl, process.env.TRACKING_WEBHOOK_AUTH),\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/tracking/providers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in tracking providers that auto-register from env vars.\n *\n * No SDK dependencies — uses raw HTTP to keep core lightweight.\n * Set the env var and tracking starts automatically.\n *\n * POSTHOG_API_KEY + POSTHOG_HOST → PostHog\n * MIXPANEL_TOKEN → Mixpanel\n * AMPLITUDE_API_KEY → Amplitude\n * AGENT_NATIVE_ANALYTICS_PUBLIC_KEY → Agent Native Analytics\n *\n * Call `registerBuiltinProviders()` at server startup (done\n * automatically by the core-routes plugin).\n */\n\nimport { reshapeTrackedExceptionProperties } from \"./posthog-exception.js\";\nimport { registerTrackingProvider } from \"./registry.js\";\nimport type { TrackingProvider, TrackingEvent } from \"./types.js\";\n\nconst POSTHOG_DEFAULT_HOST = \"https://us.i.posthog.com\";\nconst AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT =\n \"https://analytics.agent-native.com/track\";\nconst BATCH_INTERVAL_MS = 10_000;\nconst MAX_BATCH_SIZE = 50;\n\n// ─── Batched sender ────────────────────────────────────────────────────────\n\ninterface QueuedEvent {\n url: string;\n body: string;\n headers?: Record<string, string>;\n}\n\ninterface EnqueueOptions {\n flushImmediately?: boolean;\n}\n\n// Use globalThis so multiple ESM graph instances (Vite dev + Nitro symlinks)\n// share one queue, matching the same pattern as the tracking registry.\nconst QUEUE_KEY = Symbol.for(\"@agent-native/core/tracking.queue\");\nconst TIMER_KEY = Symbol.for(\"@agent-native/core/tracking.timer\");\n\ninterface GlobalWithQueue {\n [QUEUE_KEY]?: QueuedEvent[];\n [TIMER_KEY]?: ReturnType<typeof setTimeout> | null;\n}\n\nfunction getQueue(): QueuedEvent[] {\n const g = globalThis as unknown as GlobalWithQueue;\n if (!g[QUEUE_KEY]) g[QUEUE_KEY] = [];\n return g[QUEUE_KEY]!;\n}\n\nfunction getTimer(): ReturnType<typeof setTimeout> | null {\n const g = globalThis as unknown as GlobalWithQueue;\n return g[TIMER_KEY] ?? null;\n}\n\nfunction setTimer(t: ReturnType<typeof setTimeout> | null): void {\n (globalThis as unknown as GlobalWithQueue)[TIMER_KEY] = t;\n}\n\nfunction enqueue(\n url: string,\n body: string,\n headers?: Record<string, string>,\n options?: EnqueueOptions,\n): void {\n const queue = getQueue();\n queue.push({ url, body, headers });\n if (options?.flushImmediately || queue.length >= MAX_BATCH_SIZE) {\n void drainQueue();\n } else if (!getTimer()) {\n const timer = setTimeout(() => {\n void drainQueue();\n }, BATCH_INTERVAL_MS);\n if (timer.unref) timer.unref();\n setTimer(timer);\n }\n}\n\nfunction drainQueue(): Promise<void[]> {\n const t = getTimer();\n if (t) {\n clearTimeout(t);\n setTimer(null);\n }\n const queue = getQueue();\n const batch = queue.splice(0, queue.length);\n return Promise.all(\n batch.map((item) =>\n fetch(item.url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", ...item.headers },\n body: item.body,\n }).then(\n () => undefined,\n () => undefined,\n ),\n ),\n );\n}\n\nfunction isLocalhostUrl(value: string | undefined): boolean {\n if (!value || !value.trim()) return false;\n const raw = value.trim();\n const withProtocol = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw)\n ? raw\n : `https://${raw}`;\n try {\n const { hostname } = new URL(withProtocol);\n const h = hostname.toLowerCase();\n return (\n h === \"localhost\" ||\n h === \"127.0.0.1\" ||\n h === \"::1\" ||\n h === \"[::1]\" ||\n h.endsWith(\".localhost\") ||\n h.endsWith(\".local\")\n );\n } catch {\n return false;\n }\n}\n\nfunction shouldSkipAgentNativeAnalyticsForLocalhost(): boolean {\n if (process.env.AGENT_NATIVE_ANALYTICS_ALLOW_LOCALHOST === \"true\") {\n return false;\n }\n if (process.env.NODE_ENV === \"development\") return true;\n return [\n process.env.APP_URL,\n process.env.BETTER_AUTH_URL,\n process.env.URL,\n process.env.DEPLOY_URL,\n process.env.VERCEL_PROJECT_PRODUCTION_URL,\n process.env.VERCEL_URL,\n ].some(isLocalhostUrl);\n}\n\nfunction isServerlessRuntime(): boolean {\n return Boolean(\n process.env.NETLIFY ||\n process.env.VERCEL ||\n process.env.AWS_LAMBDA_FUNCTION_NAME ||\n process.env.AWS_EXECUTION_ENV ||\n process.env.LAMBDA_TASK_ROOT ||\n process.env.FUNCTION_NAME,\n );\n}\n\nfunction agentNativeAnalyticsFlushesImmediately(): boolean {\n const mode =\n process.env.AGENT_NATIVE_ANALYTICS_FLUSH_MODE?.trim().toLowerCase();\n if (mode === \"batch\") return false;\n if (mode === \"immediate\") return true;\n return isServerlessRuntime();\n}\n\n// ─── PostHog ───────────────────────────────────────────────────────────────\n\nfunction isPostHogAiObservabilityEvent(eventName: string): boolean {\n return eventName.startsWith(\"$ai_\");\n}\n\n/**\n * `$ai_*` and `$exception` are ingested through PostHog's dedicated endpoint\n * rather than `/capture/`, and `$exception` additionally has to carry\n * `$exception_list` — the framework's own `captureException()` emits camelCase\n * fields that PostHog would otherwise render as an empty, ungroupable issue.\n */\nfunction createPostHogProvider(\n apiKey: string,\n host: string,\n errorTracking: boolean,\n): TrackingProvider {\n const sendToEventsEndpoint = (\n event: TrackingEvent,\n properties: Record<string, unknown> | undefined,\n distinctId: string,\n ): void => {\n enqueue(\n `${host}/i/v0/e/`,\n JSON.stringify({\n api_key: apiKey,\n event: event.name,\n properties: {\n distinct_id: distinctId,\n ...properties,\n timestamp: event.timestamp,\n },\n }),\n );\n };\n\n return {\n name: \"posthog\",\n track(event: TrackingEvent) {\n const distinctId = event.userId || \"anonymous\";\n if (isPostHogAiObservabilityEvent(event.name)) {\n sendToEventsEndpoint(event, event.properties, distinctId);\n return;\n }\n\n if (event.name === \"$exception\") {\n // `POSTHOG_ERROR_TRACKING=false` keeps product analytics flowing while\n // another backend owns crashes. Drop rather than downgrade to\n // `/capture/`: a malformed exception is what this branch exists to\n // prevent.\n if (!errorTracking) return;\n const reshaped = reshapeTrackedExceptionProperties(event.properties);\n if (reshaped) {\n sendToEventsEndpoint(event, reshaped, distinctId);\n return;\n }\n // No recognizable exception fields. Fall through to `/capture/` so the\n // event is still recorded as-is rather than becoming an issue with\n // nothing in it.\n }\n\n enqueue(\n `${host}/capture/`,\n JSON.stringify({\n api_key: apiKey,\n event: event.name,\n distinct_id: distinctId,\n properties: {\n ...event.properties,\n timestamp: event.timestamp,\n },\n }),\n );\n },\n identify(userId, traits) {\n enqueue(\n `${host}/capture/`,\n JSON.stringify({\n api_key: apiKey,\n event: \"$identify\",\n distinct_id: userId,\n properties: { $set: traits },\n }),\n );\n },\n flush: () => {\n return drainQueue().then(() => undefined);\n },\n };\n}\n\n/**\n * Send one event to PostHog only, bypassing the provider fan-out.\n *\n * For payloads whose *shape* is PostHog-specific and whose *content* other\n * backends must not receive. `track()` broadcasts to every configured provider,\n * so a PostHog-only integration (e.g. enabling a survey id) would otherwise\n * start exporting that integration's content — including user-authored text —\n * to Mixpanel, Amplitude, webhooks, and Agent Native Analytics as a side\n * effect nobody opted into.\n *\n * Returns `false` when PostHog is not configured, so callers can tell \"not\n * sent\" from \"sent\".\n */\nexport function sendPostHogEvent(\n name: string,\n properties: Record<string, unknown>,\n distinctId: string,\n): boolean {\n const apiKey = process.env.POSTHOG_API_KEY;\n if (!apiKey) return false;\n const host = (process.env.POSTHOG_HOST || POSTHOG_DEFAULT_HOST).replace(\n /\\/+$/,\n \"\",\n );\n enqueue(\n `${host}/capture/`,\n JSON.stringify({\n api_key: apiKey,\n event: name,\n distinct_id: distinctId,\n properties: { ...properties, timestamp: new Date().toISOString() },\n }),\n );\n return true;\n}\n\n// ─── Mixpanel ──────────────────────────────────────────────────────────────\n\nfunction createMixpanelProvider(token: string): TrackingProvider {\n return {\n name: \"mixpanel\",\n track(event: TrackingEvent) {\n const data = {\n event: event.name,\n properties: {\n token,\n distinct_id: event.userId || \"anonymous\",\n time: event.timestamp\n ? new Date(event.timestamp).getTime() / 1000\n : undefined,\n ...event.properties,\n },\n };\n enqueue(\"https://api.mixpanel.com/track\", JSON.stringify([data]));\n },\n identify(userId, traits) {\n const data = {\n $token: token,\n $distinct_id: userId,\n $set: traits,\n };\n enqueue(\"https://api.mixpanel.com/engage\", JSON.stringify([data]));\n },\n flush: () => {\n return drainQueue().then(() => undefined);\n },\n };\n}\n\n// ─── Amplitude ─────────────────────────────────────────────────────────────\n\nfunction createAmplitudeProvider(apiKey: string): TrackingProvider {\n return {\n name: \"amplitude\",\n track(event: TrackingEvent) {\n const data = {\n api_key: apiKey,\n events: [\n {\n event_type: event.name,\n user_id: event.userId || \"anonymous\",\n event_properties: event.properties,\n time: event.timestamp\n ? new Date(event.timestamp).getTime()\n : undefined,\n },\n ],\n };\n enqueue(\"https://api2.amplitude.com/2/httpapi\", JSON.stringify(data));\n },\n identify(userId, traits) {\n const data = {\n api_key: apiKey,\n events: [\n {\n event_type: \"$identify\",\n user_id: userId,\n user_properties: { $set: traits },\n },\n ],\n };\n enqueue(\"https://api2.amplitude.com/2/httpapi\", JSON.stringify(data));\n },\n flush: () => {\n return drainQueue().then(() => undefined);\n },\n };\n}\n\n// ─── Webhook (custom HTTP endpoint) ───────────────────────────────────────\n\nfunction createWebhookProvider(\n url: string,\n authHeader?: string,\n): TrackingProvider {\n const extra = authHeader ? { Authorization: authHeader } : undefined;\n return {\n name: \"webhook\",\n track(event: TrackingEvent) {\n enqueue(\n url,\n JSON.stringify({\n event: event.name,\n properties: event.properties,\n userId: event.userId,\n timestamp: event.timestamp,\n }),\n extra,\n );\n },\n identify(userId, traits) {\n enqueue(\n url,\n JSON.stringify({\n event: \"$identify\",\n userId,\n traits,\n timestamp: new Date().toISOString(),\n }),\n extra,\n );\n },\n flush: () => {\n return drainQueue().then(() => undefined);\n },\n };\n}\n\n// ─── Agent Native Analytics ───────────────────────────────────────────────\n\nfunction createAgentNativeAnalyticsProvider(\n publicKey: string,\n endpoint: string,\n): TrackingProvider {\n const flushImmediately = agentNativeAnalyticsFlushesImmediately();\n return {\n name: \"agent-native-analytics\",\n track(event: TrackingEvent) {\n enqueue(\n endpoint,\n JSON.stringify({\n publicKey,\n event: event.name,\n properties: event.properties ?? {},\n userId: event.userId,\n anonymousId: event.anonymousId,\n timestamp: event.timestamp,\n }),\n undefined,\n { flushImmediately },\n );\n },\n identify(userId, traits) {\n enqueue(\n endpoint,\n JSON.stringify({\n publicKey,\n event: \"$identify\",\n userId,\n properties: traits ?? {},\n timestamp: new Date().toISOString(),\n }),\n undefined,\n { flushImmediately },\n );\n },\n flush: () => {\n return drainQueue().then(() => undefined);\n },\n };\n}\n\n// ─── Auto-registration ────────────────────────────────────────────────────\n\nlet _registered = false;\n\nexport function registerBuiltinProviders(): void {\n if (_registered) return;\n _registered = true;\n\n const posthogKey = process.env.POSTHOG_API_KEY;\n if (posthogKey) {\n const host = (process.env.POSTHOG_HOST || POSTHOG_DEFAULT_HOST).replace(\n /\\/+$/,\n \"\",\n );\n registerTrackingProvider(\n createPostHogProvider(\n posthogKey,\n host,\n process.env.POSTHOG_ERROR_TRACKING?.trim().toLowerCase() !== \"false\",\n ),\n );\n }\n\n const mixpanelToken = process.env.MIXPANEL_TOKEN;\n if (mixpanelToken) {\n registerTrackingProvider(createMixpanelProvider(mixpanelToken));\n }\n\n const amplitudeKey = process.env.AMPLITUDE_API_KEY;\n if (amplitudeKey) {\n registerTrackingProvider(createAmplitudeProvider(amplitudeKey));\n }\n\n const agentNativeAnalyticsKey =\n process.env.AGENT_NATIVE_ANALYTICS_PUBLIC_KEY ||\n process.env.VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY;\n if (\n agentNativeAnalyticsKey &&\n !shouldSkipAgentNativeAnalyticsForLocalhost()\n ) {\n registerTrackingProvider(\n createAgentNativeAnalyticsProvider(\n agentNativeAnalyticsKey,\n (\n process.env.AGENT_NATIVE_ANALYTICS_ENDPOINT ||\n AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT\n ).replace(/\\/+$/, \"\"),\n ),\n );\n }\n\n const webhookUrl = process.env.TRACKING_WEBHOOK_URL;\n if (webhookUrl) {\n registerTrackingProvider(\n createWebhookProvider(webhookUrl, process.env.TRACKING_WEBHOOK_AUTH),\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounding and redaction helpers shared by every exception emitter.
|
|
3
|
+
*
|
|
4
|
+
* Kept free of `process.env` and Node built-ins so the browser bundle can use
|
|
5
|
+
* the same rules — an exception shaped one way on the server and another way
|
|
6
|
+
* in the client is how a leak ships in only one of them.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_MESSAGE_LENGTH = 1000;
|
|
9
|
+
export declare const MAX_STACK_LENGTH = 8000;
|
|
10
|
+
export declare const MAX_TAGS = 30;
|
|
11
|
+
export declare const MAX_EXTRA_KEYS = 30;
|
|
12
|
+
export declare const MAX_EXTRA_VALUE_LENGTH = 1000;
|
|
13
|
+
export declare const SECRET_KEY_RE: RegExp;
|
|
14
|
+
export declare function redact(value: string): string;
|
|
15
|
+
export declare function boundedText(value: unknown, max: number): string;
|
|
16
|
+
export declare function safeValue(value: unknown, depth?: number): unknown;
|
|
17
|
+
export declare function safeTags(tags: Record<string, string | undefined> | undefined): Record<string, string>;
|
|
18
|
+
export interface ExceptionParts {
|
|
19
|
+
type: string;
|
|
20
|
+
message: string;
|
|
21
|
+
stack?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Split an unknown thrown value into bounded, redacted type/message/stack. */
|
|
24
|
+
export declare function exceptionParts(error: unknown): ExceptionParts;
|
|
25
|
+
//# sourceMappingURL=redaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../src/tracking/redaction.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AACvC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,QAAQ,KAAK,CAAC;AAC3B,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAI3C,eAAO,MAAM,aAAa,QACiF,CAAC;AAE5G,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO5C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO,CA0B5D;AAED,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,GACnD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAiB7D"}
|