@tetrascience-npm/tetrascience-react-ui 1.0.0-beta.135.1 → 1.0.0-beta.136.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.
Files changed (42) hide show
  1. package/dist/telemetry/context.cjs +2 -0
  2. package/dist/telemetry/context.cjs.map +1 -0
  3. package/dist/telemetry/context.d.ts +6 -0
  4. package/dist/telemetry/context.js +6 -0
  5. package/dist/telemetry/context.js.map +1 -0
  6. package/dist/telemetry/error-boundary.cjs +2 -0
  7. package/dist/telemetry/error-boundary.cjs.map +1 -0
  8. package/dist/telemetry/error-boundary.d.ts +39 -0
  9. package/dist/telemetry/error-boundary.js +33 -0
  10. package/dist/telemetry/error-boundary.js.map +1 -0
  11. package/dist/telemetry/events.cjs +2 -0
  12. package/dist/telemetry/events.cjs.map +1 -0
  13. package/dist/telemetry/events.d.ts +15 -0
  14. package/dist/telemetry/events.js +13 -0
  15. package/dist/telemetry/events.js.map +1 -0
  16. package/dist/telemetry/facade.cjs +2 -0
  17. package/dist/telemetry/facade.cjs.map +1 -0
  18. package/dist/telemetry/facade.d.ts +15 -0
  19. package/dist/telemetry/facade.js +86 -0
  20. package/dist/telemetry/facade.js.map +1 -0
  21. package/dist/telemetry/global-handlers.cjs +2 -0
  22. package/dist/telemetry/global-handlers.cjs.map +1 -0
  23. package/dist/telemetry/global-handlers.d.ts +25 -0
  24. package/dist/telemetry/global-handlers.js +31 -0
  25. package/dist/telemetry/global-handlers.js.map +1 -0
  26. package/dist/telemetry/index.d.ts +27 -0
  27. package/dist/telemetry/telemetry-provider.cjs +2 -0
  28. package/dist/telemetry/telemetry-provider.cjs.map +1 -0
  29. package/dist/telemetry/telemetry-provider.d.ts +36 -0
  30. package/dist/telemetry/telemetry-provider.js +65 -0
  31. package/dist/telemetry/telemetry-provider.js.map +1 -0
  32. package/dist/telemetry/types.d.ts +140 -0
  33. package/dist/telemetry/use-tetra-events.cjs +2 -0
  34. package/dist/telemetry/use-tetra-events.cjs.map +1 -0
  35. package/dist/telemetry/use-tetra-events.d.ts +19 -0
  36. package/dist/telemetry/use-tetra-events.js +55 -0
  37. package/dist/telemetry/use-tetra-events.js.map +1 -0
  38. package/dist/telemetry.cjs +2 -0
  39. package/dist/telemetry.cjs.map +1 -0
  40. package/dist/telemetry.js +24 -0
  41. package/dist/telemetry.js.map +1 -0
  42. package/package.json +19 -2
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react"),t=e.createContext(null);exports.TelemetryContext=t;
2
+ //# sourceMappingURL=context.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.cjs","sources":["../../src/telemetry/context.ts"],"sourcesContent":["import {createContext} from \"react\";\n\nimport type {Telemetry} from \"@tetrascience-npm/request/telemetry\";\n\n/**\n * `null` outside a {@link TelemetryProvider} — consumers no-op rather than\n * throw (telemetry never breaks the app; see `use-tetra-events.ts`).\n */\nexport const TelemetryContext = createContext<Telemetry | null>(null);\n"],"names":["TelemetryContext","createContext"],"mappings":"yGAQaA,EAAmBC,EAAAA,cAAgC,IAAI"}
@@ -0,0 +1,6 @@
1
+ import { Telemetry } from '@tetrascience-npm/request/telemetry';
2
+ /**
3
+ * `null` outside a {@link TelemetryProvider} — consumers no-op rather than
4
+ * throw (telemetry never breaks the app; see `use-tetra-events.ts`).
5
+ */
6
+ export declare const TelemetryContext: import('react').Context<Telemetry | null>;
@@ -0,0 +1,6 @@
1
+ import { createContext as t } from "react";
2
+ const o = t(null);
3
+ export {
4
+ o as TelemetryContext
5
+ };
6
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sources":["../../src/telemetry/context.ts"],"sourcesContent":["import {createContext} from \"react\";\n\nimport type {Telemetry} from \"@tetrascience-npm/request/telemetry\";\n\n/**\n * `null` outside a {@link TelemetryProvider} — consumers no-op rather than\n * throw (telemetry never breaks the app; see `use-tetra-events.ts`).\n */\nexport const TelemetryContext = createContext<Telemetry | null>(null);\n"],"names":["TelemetryContext","createContext"],"mappings":";AAQO,MAAMA,IAAmBC,EAAgC,IAAI;"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react"),s=require("./context.cjs"),r=require("./events.cjs");class n extends o.Component{static contextType=s.TelemetryContext;state={error:null};static getDerivedStateFromError(t){return{error:t}}componentDidCatch(t,e){this.context?.trackError(t,{name:this.props.eventName??r.TelemetryEvent.ReactError,attributes:{...this.props.attributes,"error.source":"react","react.component_stack":(e.componentStack??"").slice(0,r.MAX_COMPONENT_STACK_LENGTH)}}),this.props.onError?.(t,e)}reset=()=>{this.setState({error:null})};render(){const{error:t}=this.state;if(!t)return this.props.children;const{fallback:e}=this.props;return typeof e=="function"?e(t,this.reset):e??null}}exports.TSErrorBoundary=n;
2
+ //# sourceMappingURL=error-boundary.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-boundary.cjs","sources":["../../src/telemetry/error-boundary.tsx"],"sourcesContent":["import {Component, type ContextType, type ErrorInfo, type ReactNode} from \"react\";\n\nimport {TelemetryContext} from \"./context\";\nimport {MAX_COMPONENT_STACK_LENGTH, TelemetryEvent} from \"./events\";\n\n/** Props for {@link TSErrorBoundary}. */\nexport interface TSErrorBoundaryProps {\n\tchildren?: ReactNode;\n\t/**\n\t * Rendered instead of the children once a render error is caught. A\n\t * function receives the error and a `reset` callback for retry UI.\n\t */\n\tfallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);\n\t/** Event name override. Default: `App:Error:React`. */\n\teventName?: string;\n\t/** Extra attributes merged into the emitted event. */\n\tattributes?: Record<string, unknown>;\n\t/** Escape hatch for app-side handling (logging, resetting state). */\n\tonError?: (error: Error, info: ErrorInfo) => void;\n}\n\ninterface State {\n\terror: Error | null;\n}\n\n/**\n * React error boundary that records render errors as a stable-named product\n * event with the component stack attached, then renders `fallback`.\n *\n * Boundaries only catch errors thrown during render/lifecycle. Async\n * callbacks and promise rejections need {@link installGlobalErrorHandlers}.\n * The exception message is truncated by the core emitter; the component stack\n * is truncated here.\n */\nexport class TSErrorBoundary extends Component<TSErrorBoundaryProps, State> {\n\tstatic contextType = TelemetryContext;\n\tdeclare context: ContextType<typeof TelemetryContext>;\n\n\tstate: State = {error: null};\n\n\tstatic getDerivedStateFromError(error: Error): State {\n\t\treturn {error};\n\t}\n\n\tcomponentDidCatch(error: Error, info: ErrorInfo): void {\n\t\tthis.context?.trackError(error, {\n\t\t\tname: this.props.eventName ?? TelemetryEvent.ReactError,\n\t\t\tattributes: {\n\t\t\t\t...this.props.attributes,\n\t\t\t\t\"error.source\": \"react\",\n\t\t\t\t\"react.component_stack\": (info.componentStack ?? \"\").slice(0, MAX_COMPONENT_STACK_LENGTH),\n\t\t\t},\n\t\t});\n\t\tthis.props.onError?.(error, info);\n\t}\n\n\tprivate readonly reset = (): void => {\n\t\tthis.setState({error: null});\n\t};\n\n\trender(): ReactNode {\n\t\tconst {error} = this.state;\n\t\tif (!error) return this.props.children;\n\t\tconst {fallback} = this.props;\n\t\treturn typeof fallback === \"function\" ? fallback(error, this.reset) : (fallback ?? null);\n\t}\n}\n"],"names":["TSErrorBoundary","Component","TelemetryContext","error","info","TelemetryEvent","MAX_COMPONENT_STACK_LENGTH","fallback"],"mappings":"8JAkCO,MAAMA,UAAwBC,EAAAA,SAAuC,CAC3E,OAAO,YAAcC,EAAAA,iBAGrB,MAAe,CAAC,MAAO,IAAA,EAEvB,OAAO,yBAAyBC,EAAqB,CACpD,MAAO,CAAC,MAAAA,CAAA,CACT,CAEA,kBAAkBA,EAAcC,EAAuB,CACtD,KAAK,SAAS,WAAWD,EAAO,CAC/B,KAAM,KAAK,MAAM,WAAaE,EAAAA,eAAe,WAC7C,WAAY,CACX,GAAG,KAAK,MAAM,WACd,eAAgB,QAChB,yBAA0BD,EAAK,gBAAkB,IAAI,MAAM,EAAGE,EAAAA,0BAA0B,CAAA,CACzF,CACA,EACD,KAAK,MAAM,UAAUH,EAAOC,CAAI,CACjC,CAEiB,MAAQ,IAAY,CACpC,KAAK,SAAS,CAAC,MAAO,IAAA,CAAK,CAC5B,EAEA,QAAoB,CACnB,KAAM,CAAC,MAAAD,GAAS,KAAK,MACrB,GAAI,CAACA,EAAO,OAAO,KAAK,MAAM,SAC9B,KAAM,CAAC,SAAAI,GAAY,KAAK,MACxB,OAAO,OAAOA,GAAa,WAAaA,EAASJ,EAAO,KAAK,KAAK,EAAKI,GAAY,IACpF,CACD"}
@@ -0,0 +1,39 @@
1
+ import { Component, ContextType, ErrorInfo, ReactNode } from 'react';
2
+ import { TelemetryContext } from './context';
3
+ /** Props for {@link TSErrorBoundary}. */
4
+ export interface TSErrorBoundaryProps {
5
+ children?: ReactNode;
6
+ /**
7
+ * Rendered instead of the children once a render error is caught. A
8
+ * function receives the error and a `reset` callback for retry UI.
9
+ */
10
+ fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
11
+ /** Event name override. Default: `App:Error:React`. */
12
+ eventName?: string;
13
+ /** Extra attributes merged into the emitted event. */
14
+ attributes?: Record<string, unknown>;
15
+ /** Escape hatch for app-side handling (logging, resetting state). */
16
+ onError?: (error: Error, info: ErrorInfo) => void;
17
+ }
18
+ interface State {
19
+ error: Error | null;
20
+ }
21
+ /**
22
+ * React error boundary that records render errors as a stable-named product
23
+ * event with the component stack attached, then renders `fallback`.
24
+ *
25
+ * Boundaries only catch errors thrown during render/lifecycle. Async
26
+ * callbacks and promise rejections need {@link installGlobalErrorHandlers}.
27
+ * The exception message is truncated by the core emitter; the component stack
28
+ * is truncated here.
29
+ */
30
+ export declare class TSErrorBoundary extends Component<TSErrorBoundaryProps, State> {
31
+ static contextType: import('react').Context<import('@tetrascience-npm/request/telemetry').Telemetry | null>;
32
+ context: ContextType<typeof TelemetryContext>;
33
+ state: State;
34
+ static getDerivedStateFromError(error: Error): State;
35
+ componentDidCatch(error: Error, info: ErrorInfo): void;
36
+ private readonly reset;
37
+ render(): ReactNode;
38
+ }
39
+ export {};
@@ -0,0 +1,33 @@
1
+ import { Component as e } from "react";
2
+ import { TelemetryContext as o } from "./context.js";
3
+ import { TelemetryEvent as s, MAX_COMPONENT_STACK_LENGTH as n } from "./events.js";
4
+ class m extends e {
5
+ static contextType = o;
6
+ state = { error: null };
7
+ static getDerivedStateFromError(t) {
8
+ return { error: t };
9
+ }
10
+ componentDidCatch(t, r) {
11
+ this.context?.trackError(t, {
12
+ name: this.props.eventName ?? s.ReactError,
13
+ attributes: {
14
+ ...this.props.attributes,
15
+ "error.source": "react",
16
+ "react.component_stack": (r.componentStack ?? "").slice(0, n)
17
+ }
18
+ }), this.props.onError?.(t, r);
19
+ }
20
+ reset = () => {
21
+ this.setState({ error: null });
22
+ };
23
+ render() {
24
+ const { error: t } = this.state;
25
+ if (!t) return this.props.children;
26
+ const { fallback: r } = this.props;
27
+ return typeof r == "function" ? r(t, this.reset) : r ?? null;
28
+ }
29
+ }
30
+ export {
31
+ m as TSErrorBoundary
32
+ };
33
+ //# sourceMappingURL=error-boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-boundary.js","sources":["../../src/telemetry/error-boundary.tsx"],"sourcesContent":["import {Component, type ContextType, type ErrorInfo, type ReactNode} from \"react\";\n\nimport {TelemetryContext} from \"./context\";\nimport {MAX_COMPONENT_STACK_LENGTH, TelemetryEvent} from \"./events\";\n\n/** Props for {@link TSErrorBoundary}. */\nexport interface TSErrorBoundaryProps {\n\tchildren?: ReactNode;\n\t/**\n\t * Rendered instead of the children once a render error is caught. A\n\t * function receives the error and a `reset` callback for retry UI.\n\t */\n\tfallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);\n\t/** Event name override. Default: `App:Error:React`. */\n\teventName?: string;\n\t/** Extra attributes merged into the emitted event. */\n\tattributes?: Record<string, unknown>;\n\t/** Escape hatch for app-side handling (logging, resetting state). */\n\tonError?: (error: Error, info: ErrorInfo) => void;\n}\n\ninterface State {\n\terror: Error | null;\n}\n\n/**\n * React error boundary that records render errors as a stable-named product\n * event with the component stack attached, then renders `fallback`.\n *\n * Boundaries only catch errors thrown during render/lifecycle. Async\n * callbacks and promise rejections need {@link installGlobalErrorHandlers}.\n * The exception message is truncated by the core emitter; the component stack\n * is truncated here.\n */\nexport class TSErrorBoundary extends Component<TSErrorBoundaryProps, State> {\n\tstatic contextType = TelemetryContext;\n\tdeclare context: ContextType<typeof TelemetryContext>;\n\n\tstate: State = {error: null};\n\n\tstatic getDerivedStateFromError(error: Error): State {\n\t\treturn {error};\n\t}\n\n\tcomponentDidCatch(error: Error, info: ErrorInfo): void {\n\t\tthis.context?.trackError(error, {\n\t\t\tname: this.props.eventName ?? TelemetryEvent.ReactError,\n\t\t\tattributes: {\n\t\t\t\t...this.props.attributes,\n\t\t\t\t\"error.source\": \"react\",\n\t\t\t\t\"react.component_stack\": (info.componentStack ?? \"\").slice(0, MAX_COMPONENT_STACK_LENGTH),\n\t\t\t},\n\t\t});\n\t\tthis.props.onError?.(error, info);\n\t}\n\n\tprivate readonly reset = (): void => {\n\t\tthis.setState({error: null});\n\t};\n\n\trender(): ReactNode {\n\t\tconst {error} = this.state;\n\t\tif (!error) return this.props.children;\n\t\tconst {fallback} = this.props;\n\t\treturn typeof fallback === \"function\" ? fallback(error, this.reset) : (fallback ?? null);\n\t}\n}\n"],"names":["TSErrorBoundary","Component","TelemetryContext","error","info","TelemetryEvent","MAX_COMPONENT_STACK_LENGTH","fallback"],"mappings":";;;AAkCO,MAAMA,UAAwBC,EAAuC;AAAA,EAC3E,OAAO,cAAcC;AAAA,EAGrB,QAAe,EAAC,OAAO,KAAA;AAAA,EAEvB,OAAO,yBAAyBC,GAAqB;AACpD,WAAO,EAAC,OAAAA,EAAA;AAAA,EACT;AAAA,EAEA,kBAAkBA,GAAcC,GAAuB;AACtD,SAAK,SAAS,WAAWD,GAAO;AAAA,MAC/B,MAAM,KAAK,MAAM,aAAaE,EAAe;AAAA,MAC7C,YAAY;AAAA,QACX,GAAG,KAAK,MAAM;AAAA,QACd,gBAAgB;AAAA,QAChB,0BAA0BD,EAAK,kBAAkB,IAAI,MAAM,GAAGE,CAA0B;AAAA,MAAA;AAAA,IACzF,CACA,GACD,KAAK,MAAM,UAAUH,GAAOC,CAAI;AAAA,EACjC;AAAA,EAEiB,QAAQ,MAAY;AACpC,SAAK,SAAS,EAAC,OAAO,KAAA,CAAK;AAAA,EAC5B;AAAA,EAEA,SAAoB;AACnB,UAAM,EAAC,OAAAD,MAAS,KAAK;AACrB,QAAI,CAACA,EAAO,QAAO,KAAK,MAAM;AAC9B,UAAM,EAAC,UAAAI,MAAY,KAAK;AACxB,WAAO,OAAOA,KAAa,aAAaA,EAASJ,GAAO,KAAK,KAAK,IAAKI,KAAY;AAAA,EACpF;AACD;"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e={ReactError:"App:Error:React",WindowError:"App:Error:Window",UnhandledRejection:"App:Error:UnhandledRejection"},r=4e3;exports.MAX_COMPONENT_STACK_LENGTH=r;exports.TelemetryEvent=e;
2
+ //# sourceMappingURL=events.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.cjs","sources":["../../src/telemetry/events.ts"],"sourcesContent":["/**\n * Stable event names emitted by the bindings themselves. Apps keep their own\n * `App[:Page][:Object]:Action` names for product events; these three exist so\n * error volume is queryable across every app without per-app naming drift.\n */\nexport const TelemetryEvent = {\n\t/** React render error caught by `TSErrorBoundary`. */\n\tReactError: \"App:Error:React\",\n\t/** Uncaught exception seen by `window.onerror`. */\n\tWindowError: \"App:Error:Window\",\n\t/** Promise rejection with no handler. */\n\tUnhandledRejection: \"App:Error:UnhandledRejection\",\n} as const;\n\n/** Max length of the React component stack attribute. */\nexport const MAX_COMPONENT_STACK_LENGTH = 4000;\n"],"names":["TelemetryEvent","MAX_COMPONENT_STACK_LENGTH"],"mappings":"gFAKO,MAAMA,EAAiB,CAE7B,WAAY,kBAEZ,YAAa,mBAEb,mBAAoB,8BACrB,EAGaC,EAA6B"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Stable event names emitted by the bindings themselves. Apps keep their own
3
+ * `App[:Page][:Object]:Action` names for product events; these three exist so
4
+ * error volume is queryable across every app without per-app naming drift.
5
+ */
6
+ export declare const TelemetryEvent: {
7
+ /** React render error caught by `TSErrorBoundary`. */
8
+ readonly ReactError: "App:Error:React";
9
+ /** Uncaught exception seen by `window.onerror`. */
10
+ readonly WindowError: "App:Error:Window";
11
+ /** Promise rejection with no handler. */
12
+ readonly UnhandledRejection: "App:Error:UnhandledRejection";
13
+ };
14
+ /** Max length of the React component stack attribute. */
15
+ export declare const MAX_COMPONENT_STACK_LENGTH = 4000;
@@ -0,0 +1,13 @@
1
+ const r = {
2
+ /** React render error caught by `TSErrorBoundary`. */
3
+ ReactError: "App:Error:React",
4
+ /** Uncaught exception seen by `window.onerror`. */
5
+ WindowError: "App:Error:Window",
6
+ /** Promise rejection with no handler. */
7
+ UnhandledRejection: "App:Error:UnhandledRejection"
8
+ }, e = 4e3;
9
+ export {
10
+ e as MAX_COMPONENT_STACK_LENGTH,
11
+ r as TelemetryEvent
12
+ };
13
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sources":["../../src/telemetry/events.ts"],"sourcesContent":["/**\n * Stable event names emitted by the bindings themselves. Apps keep their own\n * `App[:Page][:Object]:Action` names for product events; these three exist so\n * error volume is queryable across every app without per-app naming drift.\n */\nexport const TelemetryEvent = {\n\t/** React render error caught by `TSErrorBoundary`. */\n\tReactError: \"App:Error:React\",\n\t/** Uncaught exception seen by `window.onerror`. */\n\tWindowError: \"App:Error:Window\",\n\t/** Promise rejection with no handler. */\n\tUnhandledRejection: \"App:Error:UnhandledRejection\",\n} as const;\n\n/** Max length of the React component stack attribute. */\nexport const MAX_COMPONENT_STACK_LENGTH = 4000;\n"],"names":["TelemetryEvent","MAX_COMPONENT_STACK_LENGTH"],"mappings":"AAKO,MAAMA,IAAiB;AAAA;AAAA,EAE7B,YAAY;AAAA;AAAA,EAEZ,aAAa;AAAA;AAAA,EAEb,oBAAoB;AACrB,GAGaC,IAA6B;"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("@tetrascience-npm/request/telemetry"),s={trackEvent(){},trackError(){},startSpan:()=>o.NOOP_SPAN,withSpan:(a,n)=>n(o.NOOP_SPAN),counter(){},gauge(){},histogram(){},upDownCounter(){},async flush(){},async shutdown(){}},u=50;function i(){let a,n=[];const c=t=>{n.length<u&&n.push(t)};return{trackEvent(t,e,r){a?a.trackEvent(t,e,r):c({kind:"event",name:t,attributes:e,body:r})},trackError(t,e){a?a.trackError(t,e):c({kind:"error",error:t,context:e})},startSpan(t,e){return a?a.startSpan(t,e):o.NOOP_SPAN},counter(t,e,r){a?.counter(t,e,r)},gauge(t,e,r){a?.gauge(t,e,r)},histogram(t,e,r){a?.histogram(t,e,r)},upDownCounter(t,e,r){a?.upDownCounter(t,e,r)},withSpan(t,e,r){return a?a.withSpan(t,e,r):e(o.NOOP_SPAN)},async flush(){await a?.flush()},async shutdown(){await a?.shutdown()},attach(t){a=t;const e=n;n=[];for(const r of e)r.kind==="event"?t.trackEvent(r.name,r.attributes,r.body):t.trackError(r.error,r.context)},detach(t){a===t&&(a=void 0)}}}exports.NOOP_TELEMETRY=s;exports.createTelemetryFacade=i;
2
+ //# sourceMappingURL=facade.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facade.cjs","sources":["../../src/telemetry/facade.ts"],"sourcesContent":["import {NOOP_SPAN} from \"@tetrascience-npm/request/telemetry\";\n\nimport type {TelemetryFacade} from \"./types\";\nimport type {Telemetry, TrackErrorContext} from \"@tetrascience-npm/request/telemetry\";\n\n/** A client that accepts and drops everything (`enabled={false}`, no provider). */\nexport const NOOP_TELEMETRY: Telemetry = {\n\ttrackEvent() {\n\t\t// disabled: nothing is recorded, nothing is buffered\n\t},\n\ttrackError() {\n\t\t// disabled: nothing is recorded, nothing is buffered\n\t},\n\tstartSpan: () => NOOP_SPAN,\n\t// Still RUNS the callback: withSpan wraps application work, so skipping it\n\t// when telemetry is off would make disabling telemetry change what the app\n\t// does. Only the measurement is dropped.\n\twithSpan: (_name, fn) => fn(NOOP_SPAN),\n\tcounter() {\n\t\t// disabled: nothing is recorded\n\t},\n\tgauge() {\n\t\t// disabled: nothing is recorded\n\t},\n\thistogram() {\n\t\t// disabled: nothing is recorded\n\t},\n\tupDownCounter() {\n\t\t// disabled: nothing is recorded\n\t},\n\tasync flush() {\n\t\t// nothing to flush\n\t},\n\tasync shutdown() {\n\t\t// nothing to tear down\n\t},\n};\n\n/** Records emitted before the provider's effect attaches a client. */\nconst MAX_PENDING = 50;\n\ntype Pending =\n\t| {kind: \"event\"; name: string; attributes?: Record<string, unknown>; body?: unknown}\n\t| {kind: \"error\"; error: unknown; context?: TrackErrorContext};\n\n/**\n * Stable context value that forwards to the currently attached client.\n *\n * The real client is built in an effect (so React StrictMode's mount →\n * unmount → mount cycle disposes it cleanly instead of leaving a shut-down\n * provider behind), but child effects run *before* the provider's effect —\n * so records emitted in that window are held here (bounded) and replayed on\n * attach. The buffer is drained exactly once, so a StrictMode re-attach\n * cannot re-emit it.\n */\nexport function createTelemetryFacade(): TelemetryFacade {\n\tlet target: Telemetry | undefined;\n\tlet pending: Pending[] = [];\n\n\tconst buffer = (record: Pending): void => {\n\t\tif (pending.length < MAX_PENDING) pending.push(record);\n\t};\n\n\treturn {\n\t\ttrackEvent(name, attributes, body) {\n\t\t\tif (target) target.trackEvent(name, attributes, body);\n\t\t\telse buffer({kind: \"event\", name, attributes, body});\n\t\t},\n\t\ttrackError(error, context) {\n\t\t\tif (target) target.trackError(error, context);\n\t\t\telse buffer({kind: \"error\", error, context});\n\t\t},\n\t\t// Spans are NOT buffered, unlike the two above. A span carries a live\n\t\t// start and end, so replaying one after attach would invent a duration\n\t\t// that never happened — worse than not recording it. Work that runs\n\t\t// before the provider's effect attaches a client is simply untraced.\n\t\tstartSpan(name, options) {\n\t\t\treturn target ? target.startSpan(name, options) : NOOP_SPAN;\n\t\t},\n\t\t// Metric recordings before attach are DROPPED, not buffered. A gauge is a\n\t\t// point-in-time reading and a histogram observation is timestamped by the\n\t\t// SDK on record, so replaying either after attach would date it to the\n\t\t// wrong moment. Events buffer because a product event is a fact that\n\t\t// happened, not a sample of a moving value.\n\t\tcounter(name, delta, attributes) {\n\t\t\ttarget?.counter(name, delta, attributes);\n\t\t},\n\t\tgauge(name, value, attributes) {\n\t\t\ttarget?.gauge(name, value, attributes);\n\t\t},\n\t\thistogram(name, value, attributes) {\n\t\t\ttarget?.histogram(name, value, attributes);\n\t\t},\n\t\tupDownCounter(name, delta, attributes) {\n\t\t\ttarget?.upDownCounter(name, delta, attributes);\n\t\t},\n\t\twithSpan(name, fn, options) {\n\t\t\treturn target ? target.withSpan(name, fn, options) : fn(NOOP_SPAN);\n\t\t},\n\t\tasync flush() {\n\t\t\tawait target?.flush();\n\t\t},\n\t\tasync shutdown() {\n\t\t\tawait target?.shutdown();\n\t\t},\n\t\tattach(client) {\n\t\t\ttarget = client;\n\t\t\tconst replay = pending;\n\t\t\tpending = [];\n\t\t\tfor (const record of replay) {\n\t\t\t\tif (record.kind === \"event\") client.trackEvent(record.name, record.attributes, record.body);\n\t\t\t\telse client.trackError(record.error, record.context);\n\t\t\t}\n\t\t},\n\t\tdetach(client) {\n\t\t\t// Guard against an out-of-order cleanup detaching a newer client.\n\t\t\tif (target === client) target = undefined;\n\t\t},\n\t};\n}\n"],"names":["NOOP_TELEMETRY","NOOP_SPAN","_name","fn","MAX_PENDING","createTelemetryFacade","target","pending","buffer","record","name","attributes","body","error","context","options","delta","value","client","replay"],"mappings":"uIAMaA,EAA4B,CACxC,YAAa,CAEb,EACA,YAAa,CAEb,EACA,UAAW,IAAMC,EAAAA,UAIjB,SAAU,CAACC,EAAOC,IAAOA,EAAGF,EAAAA,SAAS,EACrC,SAAU,CAEV,EACA,OAAQ,CAER,EACA,WAAY,CAEZ,EACA,eAAgB,CAEhB,EACA,MAAM,OAAQ,CAEd,EACA,MAAM,UAAW,CAEjB,CACD,EAGMG,EAAc,GAgBb,SAASC,GAAyC,CACxD,IAAIC,EACAC,EAAqB,CAAA,EAEzB,MAAMC,EAAUC,GAA0B,CACrCF,EAAQ,OAASH,GAAaG,EAAQ,KAAKE,CAAM,CACtD,EAEA,MAAO,CACN,WAAWC,EAAMC,EAAYC,EAAM,CAC9BN,EAAQA,EAAO,WAAWI,EAAMC,EAAYC,CAAI,IACxC,CAAC,KAAM,QAAS,KAAAF,EAAM,WAAAC,EAAY,KAAAC,EAAK,CACpD,EACA,WAAWC,EAAOC,EAAS,CACtBR,EAAQA,EAAO,WAAWO,EAAOC,CAAO,IAChC,CAAC,KAAM,QAAS,MAAAD,EAAO,QAAAC,EAAQ,CAC5C,EAKA,UAAUJ,EAAMK,EAAS,CACxB,OAAOT,EAASA,EAAO,UAAUI,EAAMK,CAAO,EAAId,EAAAA,SACnD,EAMA,QAAQS,EAAMM,EAAOL,EAAY,CAChCL,GAAQ,QAAQI,EAAMM,EAAOL,CAAU,CACxC,EACA,MAAMD,EAAMO,EAAON,EAAY,CAC9BL,GAAQ,MAAMI,EAAMO,EAAON,CAAU,CACtC,EACA,UAAUD,EAAMO,EAAON,EAAY,CAClCL,GAAQ,UAAUI,EAAMO,EAAON,CAAU,CAC1C,EACA,cAAcD,EAAMM,EAAOL,EAAY,CACtCL,GAAQ,cAAcI,EAAMM,EAAOL,CAAU,CAC9C,EACA,SAASD,EAAMP,EAAIY,EAAS,CAC3B,OAAOT,EAASA,EAAO,SAASI,EAAMP,EAAIY,CAAO,EAAIZ,EAAGF,WAAS,CAClE,EACA,MAAM,OAAQ,CACb,MAAMK,GAAQ,MAAA,CACf,EACA,MAAM,UAAW,CAChB,MAAMA,GAAQ,SAAA,CACf,EACA,OAAOY,EAAQ,CACdZ,EAASY,EACT,MAAMC,EAASZ,EACfA,EAAU,CAAA,EACV,UAAWE,KAAUU,EAChBV,EAAO,OAAS,QAASS,EAAO,WAAWT,EAAO,KAAMA,EAAO,WAAYA,EAAO,IAAI,EACrFS,EAAO,WAAWT,EAAO,MAAOA,EAAO,OAAO,CAErD,EACA,OAAOS,EAAQ,CAEVZ,IAAWY,IAAQZ,EAAS,OACjC,CAAA,CAEF"}
@@ -0,0 +1,15 @@
1
+ import { TelemetryFacade } from './types';
2
+ import { Telemetry } from '@tetrascience-npm/request/telemetry';
3
+ /** A client that accepts and drops everything (`enabled={false}`, no provider). */
4
+ export declare const NOOP_TELEMETRY: Telemetry;
5
+ /**
6
+ * Stable context value that forwards to the currently attached client.
7
+ *
8
+ * The real client is built in an effect (so React StrictMode's mount →
9
+ * unmount → mount cycle disposes it cleanly instead of leaving a shut-down
10
+ * provider behind), but child effects run *before* the provider's effect —
11
+ * so records emitted in that window are held here (bounded) and replayed on
12
+ * attach. The buffer is drained exactly once, so a StrictMode re-attach
13
+ * cannot re-emit it.
14
+ */
15
+ export declare function createTelemetryFacade(): TelemetryFacade;
@@ -0,0 +1,86 @@
1
+ import { NOOP_SPAN as o } from "@tetrascience-npm/request/telemetry";
2
+ const i = {
3
+ trackEvent() {
4
+ },
5
+ trackError() {
6
+ },
7
+ startSpan: () => o,
8
+ // Still RUNS the callback: withSpan wraps application work, so skipping it
9
+ // when telemetry is off would make disabling telemetry change what the app
10
+ // does. Only the measurement is dropped.
11
+ withSpan: (n, a) => a(o),
12
+ counter() {
13
+ },
14
+ gauge() {
15
+ },
16
+ histogram() {
17
+ },
18
+ upDownCounter() {
19
+ },
20
+ async flush() {
21
+ },
22
+ async shutdown() {
23
+ }
24
+ }, c = 50;
25
+ function h() {
26
+ let n, a = [];
27
+ const s = (t) => {
28
+ a.length < c && a.push(t);
29
+ };
30
+ return {
31
+ trackEvent(t, r, e) {
32
+ n ? n.trackEvent(t, r, e) : s({ kind: "event", name: t, attributes: r, body: e });
33
+ },
34
+ trackError(t, r) {
35
+ n ? n.trackError(t, r) : s({ kind: "error", error: t, context: r });
36
+ },
37
+ // Spans are NOT buffered, unlike the two above. A span carries a live
38
+ // start and end, so replaying one after attach would invent a duration
39
+ // that never happened — worse than not recording it. Work that runs
40
+ // before the provider's effect attaches a client is simply untraced.
41
+ startSpan(t, r) {
42
+ return n ? n.startSpan(t, r) : o;
43
+ },
44
+ // Metric recordings before attach are DROPPED, not buffered. A gauge is a
45
+ // point-in-time reading and a histogram observation is timestamped by the
46
+ // SDK on record, so replaying either after attach would date it to the
47
+ // wrong moment. Events buffer because a product event is a fact that
48
+ // happened, not a sample of a moving value.
49
+ counter(t, r, e) {
50
+ n?.counter(t, r, e);
51
+ },
52
+ gauge(t, r, e) {
53
+ n?.gauge(t, r, e);
54
+ },
55
+ histogram(t, r, e) {
56
+ n?.histogram(t, r, e);
57
+ },
58
+ upDownCounter(t, r, e) {
59
+ n?.upDownCounter(t, r, e);
60
+ },
61
+ withSpan(t, r, e) {
62
+ return n ? n.withSpan(t, r, e) : r(o);
63
+ },
64
+ async flush() {
65
+ await n?.flush();
66
+ },
67
+ async shutdown() {
68
+ await n?.shutdown();
69
+ },
70
+ attach(t) {
71
+ n = t;
72
+ const r = a;
73
+ a = [];
74
+ for (const e of r)
75
+ e.kind === "event" ? t.trackEvent(e.name, e.attributes, e.body) : t.trackError(e.error, e.context);
76
+ },
77
+ detach(t) {
78
+ n === t && (n = void 0);
79
+ }
80
+ };
81
+ }
82
+ export {
83
+ i as NOOP_TELEMETRY,
84
+ h as createTelemetryFacade
85
+ };
86
+ //# sourceMappingURL=facade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facade.js","sources":["../../src/telemetry/facade.ts"],"sourcesContent":["import {NOOP_SPAN} from \"@tetrascience-npm/request/telemetry\";\n\nimport type {TelemetryFacade} from \"./types\";\nimport type {Telemetry, TrackErrorContext} from \"@tetrascience-npm/request/telemetry\";\n\n/** A client that accepts and drops everything (`enabled={false}`, no provider). */\nexport const NOOP_TELEMETRY: Telemetry = {\n\ttrackEvent() {\n\t\t// disabled: nothing is recorded, nothing is buffered\n\t},\n\ttrackError() {\n\t\t// disabled: nothing is recorded, nothing is buffered\n\t},\n\tstartSpan: () => NOOP_SPAN,\n\t// Still RUNS the callback: withSpan wraps application work, so skipping it\n\t// when telemetry is off would make disabling telemetry change what the app\n\t// does. Only the measurement is dropped.\n\twithSpan: (_name, fn) => fn(NOOP_SPAN),\n\tcounter() {\n\t\t// disabled: nothing is recorded\n\t},\n\tgauge() {\n\t\t// disabled: nothing is recorded\n\t},\n\thistogram() {\n\t\t// disabled: nothing is recorded\n\t},\n\tupDownCounter() {\n\t\t// disabled: nothing is recorded\n\t},\n\tasync flush() {\n\t\t// nothing to flush\n\t},\n\tasync shutdown() {\n\t\t// nothing to tear down\n\t},\n};\n\n/** Records emitted before the provider's effect attaches a client. */\nconst MAX_PENDING = 50;\n\ntype Pending =\n\t| {kind: \"event\"; name: string; attributes?: Record<string, unknown>; body?: unknown}\n\t| {kind: \"error\"; error: unknown; context?: TrackErrorContext};\n\n/**\n * Stable context value that forwards to the currently attached client.\n *\n * The real client is built in an effect (so React StrictMode's mount →\n * unmount → mount cycle disposes it cleanly instead of leaving a shut-down\n * provider behind), but child effects run *before* the provider's effect —\n * so records emitted in that window are held here (bounded) and replayed on\n * attach. The buffer is drained exactly once, so a StrictMode re-attach\n * cannot re-emit it.\n */\nexport function createTelemetryFacade(): TelemetryFacade {\n\tlet target: Telemetry | undefined;\n\tlet pending: Pending[] = [];\n\n\tconst buffer = (record: Pending): void => {\n\t\tif (pending.length < MAX_PENDING) pending.push(record);\n\t};\n\n\treturn {\n\t\ttrackEvent(name, attributes, body) {\n\t\t\tif (target) target.trackEvent(name, attributes, body);\n\t\t\telse buffer({kind: \"event\", name, attributes, body});\n\t\t},\n\t\ttrackError(error, context) {\n\t\t\tif (target) target.trackError(error, context);\n\t\t\telse buffer({kind: \"error\", error, context});\n\t\t},\n\t\t// Spans are NOT buffered, unlike the two above. A span carries a live\n\t\t// start and end, so replaying one after attach would invent a duration\n\t\t// that never happened — worse than not recording it. Work that runs\n\t\t// before the provider's effect attaches a client is simply untraced.\n\t\tstartSpan(name, options) {\n\t\t\treturn target ? target.startSpan(name, options) : NOOP_SPAN;\n\t\t},\n\t\t// Metric recordings before attach are DROPPED, not buffered. A gauge is a\n\t\t// point-in-time reading and a histogram observation is timestamped by the\n\t\t// SDK on record, so replaying either after attach would date it to the\n\t\t// wrong moment. Events buffer because a product event is a fact that\n\t\t// happened, not a sample of a moving value.\n\t\tcounter(name, delta, attributes) {\n\t\t\ttarget?.counter(name, delta, attributes);\n\t\t},\n\t\tgauge(name, value, attributes) {\n\t\t\ttarget?.gauge(name, value, attributes);\n\t\t},\n\t\thistogram(name, value, attributes) {\n\t\t\ttarget?.histogram(name, value, attributes);\n\t\t},\n\t\tupDownCounter(name, delta, attributes) {\n\t\t\ttarget?.upDownCounter(name, delta, attributes);\n\t\t},\n\t\twithSpan(name, fn, options) {\n\t\t\treturn target ? target.withSpan(name, fn, options) : fn(NOOP_SPAN);\n\t\t},\n\t\tasync flush() {\n\t\t\tawait target?.flush();\n\t\t},\n\t\tasync shutdown() {\n\t\t\tawait target?.shutdown();\n\t\t},\n\t\tattach(client) {\n\t\t\ttarget = client;\n\t\t\tconst replay = pending;\n\t\t\tpending = [];\n\t\t\tfor (const record of replay) {\n\t\t\t\tif (record.kind === \"event\") client.trackEvent(record.name, record.attributes, record.body);\n\t\t\t\telse client.trackError(record.error, record.context);\n\t\t\t}\n\t\t},\n\t\tdetach(client) {\n\t\t\t// Guard against an out-of-order cleanup detaching a newer client.\n\t\t\tif (target === client) target = undefined;\n\t\t},\n\t};\n}\n"],"names":["NOOP_TELEMETRY","NOOP_SPAN","_name","fn","MAX_PENDING","createTelemetryFacade","target","pending","buffer","record","name","attributes","body","error","context","options","delta","value","client","replay"],"mappings":";AAMO,MAAMA,IAA4B;AAAA,EACxC,aAAa;AAAA,EAEb;AAAA,EACA,aAAa;AAAA,EAEb;AAAA,EACA,WAAW,MAAMC;AAAA;AAAA;AAAA;AAAA,EAIjB,UAAU,CAACC,GAAOC,MAAOA,EAAGF,CAAS;AAAA,EACrC,UAAU;AAAA,EAEV;AAAA,EACA,QAAQ;AAAA,EAER;AAAA,EACA,YAAY;AAAA,EAEZ;AAAA,EACA,gBAAgB;AAAA,EAEhB;AAAA,EACA,MAAM,QAAQ;AAAA,EAEd;AAAA,EACA,MAAM,WAAW;AAAA,EAEjB;AACD,GAGMG,IAAc;AAgBb,SAASC,IAAyC;AACxD,MAAIC,GACAC,IAAqB,CAAA;AAEzB,QAAMC,IAAS,CAACC,MAA0B;AACzC,IAAIF,EAAQ,SAASH,KAAaG,EAAQ,KAAKE,CAAM;AAAA,EACtD;AAEA,SAAO;AAAA,IACN,WAAWC,GAAMC,GAAYC,GAAM;AAClC,MAAIN,IAAQA,EAAO,WAAWI,GAAMC,GAAYC,CAAI,MACxC,EAAC,MAAM,SAAS,MAAAF,GAAM,YAAAC,GAAY,MAAAC,GAAK;AAAA,IACpD;AAAA,IACA,WAAWC,GAAOC,GAAS;AAC1B,MAAIR,IAAQA,EAAO,WAAWO,GAAOC,CAAO,MAChC,EAAC,MAAM,SAAS,OAAAD,GAAO,SAAAC,GAAQ;AAAA,IAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,UAAUJ,GAAMK,GAAS;AACxB,aAAOT,IAASA,EAAO,UAAUI,GAAMK,CAAO,IAAId;AAAA,IACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,QAAQS,GAAMM,GAAOL,GAAY;AAChC,MAAAL,GAAQ,QAAQI,GAAMM,GAAOL,CAAU;AAAA,IACxC;AAAA,IACA,MAAMD,GAAMO,GAAON,GAAY;AAC9B,MAAAL,GAAQ,MAAMI,GAAMO,GAAON,CAAU;AAAA,IACtC;AAAA,IACA,UAAUD,GAAMO,GAAON,GAAY;AAClC,MAAAL,GAAQ,UAAUI,GAAMO,GAAON,CAAU;AAAA,IAC1C;AAAA,IACA,cAAcD,GAAMM,GAAOL,GAAY;AACtC,MAAAL,GAAQ,cAAcI,GAAMM,GAAOL,CAAU;AAAA,IAC9C;AAAA,IACA,SAASD,GAAMP,GAAIY,GAAS;AAC3B,aAAOT,IAASA,EAAO,SAASI,GAAMP,GAAIY,CAAO,IAAIZ,EAAGF,CAAS;AAAA,IAClE;AAAA,IACA,MAAM,QAAQ;AACb,YAAMK,GAAQ,MAAA;AAAA,IACf;AAAA,IACA,MAAM,WAAW;AAChB,YAAMA,GAAQ,SAAA;AAAA,IACf;AAAA,IACA,OAAOY,GAAQ;AACd,MAAAZ,IAASY;AACT,YAAMC,IAASZ;AACf,MAAAA,IAAU,CAAA;AACV,iBAAWE,KAAUU;AACpB,QAAIV,EAAO,SAAS,UAASS,EAAO,WAAWT,EAAO,MAAMA,EAAO,YAAYA,EAAO,IAAI,IACrFS,EAAO,WAAWT,EAAO,OAAOA,EAAO,OAAO;AAAA,IAErD;AAAA,IACA,OAAOS,GAAQ;AAEd,MAAIZ,MAAWY,MAAQZ,IAAS;AAAA,IACjC;AAAA,EAAA;AAEF;"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./events.cjs"),n=new WeakMap;function u(e,t={}){const a=n.get(e);if(a)return a;if(typeof window>"u"){const r=()=>{};return n.set(e,r),r}const d=r=>{e.trackError(r.error??r.message,{name:t.windowErrorEventName??i.TelemetryEvent.WindowError,attributes:{...t.attributes,"error.source":"window"}})},s=r=>{e.trackError(r.reason,{name:t.unhandledRejectionEventName??i.TelemetryEvent.UnhandledRejection,attributes:{...t.attributes,"error.source":"unhandledrejection"}})};window.addEventListener("error",d),window.addEventListener("unhandledrejection",s);const o=()=>{n.get(e)===o&&(n.delete(e),window.removeEventListener("error",d),window.removeEventListener("unhandledrejection",s))};return n.set(e,o),o}exports.installGlobalErrorHandlers=u;
2
+ //# sourceMappingURL=global-handlers.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-handlers.cjs","sources":["../../src/telemetry/global-handlers.ts"],"sourcesContent":["import {TelemetryEvent} from \"./events\";\n\nimport type {Telemetry} from \"@tetrascience-npm/request/telemetry\";\n\n/** Options for {@link installGlobalErrorHandlers}. */\nexport interface GlobalErrorHandlerOptions {\n\t/** Event name for `window.onerror`. Default: `App:Error:Window`. */\n\twindowErrorEventName?: string;\n\t/** Event name for unhandled promise rejections. Default: `App:Error:UnhandledRejection`. */\n\tunhandledRejectionEventName?: string;\n\t/** Extra attributes merged into both events. */\n\tattributes?: Record<string, unknown>;\n}\n\n// Keyed by client so two federated apps can each install their own handlers,\n// while a repeated install for the same client (StrictMode, hot reload) is a\n// no-op that returns the original uninstall.\nconst installed = new WeakMap<Telemetry, () => void>();\n\n/**\n * Explicit opt-in: capture uncaught errors and unhandled promise rejections\n * for `client`. Never installed automatically by the provider — global\n * listeners are a host-level decision, and under Module Federation several\n * apps share `window`.\n *\n * Idempotent per client; returns an uninstall function (also idempotent).\n *\n * @example\n * ```tsx\n * const client = useTelemetryClient();\n * useEffect(() => installGlobalErrorHandlers(client), [client]);\n * ```\n */\nexport function installGlobalErrorHandlers(client: Telemetry, options: GlobalErrorHandlerOptions = {}): () => void {\n\tconst existing = installed.get(client);\n\tif (existing) return existing;\n\tif (typeof window === \"undefined\") {\n\t\t// Recorded in the map like any other install, so the documented\n\t\t// once-per-client contract holds here too: without this, every call in an\n\t\t// SSR/non-browser runtime handed back a different uninstall function.\n\t\tconst noop = (): void => {\n\t\t\t// nothing was installed (SSR / non-browser runtime)\n\t\t};\n\t\tinstalled.set(client, noop);\n\t\treturn noop;\n\t}\n\n\tconst onError = (event: ErrorEvent): void => {\n\t\tclient.trackError(event.error ?? event.message, {\n\t\t\tname: options.windowErrorEventName ?? TelemetryEvent.WindowError,\n\t\t\tattributes: {...options.attributes, \"error.source\": \"window\"},\n\t\t});\n\t};\n\tconst onRejection = (event: PromiseRejectionEvent): void => {\n\t\tclient.trackError(event.reason, {\n\t\t\tname: options.unhandledRejectionEventName ?? TelemetryEvent.UnhandledRejection,\n\t\t\tattributes: {...options.attributes, \"error.source\": \"unhandledrejection\"},\n\t\t});\n\t};\n\n\twindow.addEventListener(\"error\", onError);\n\twindow.addEventListener(\"unhandledrejection\", onRejection);\n\n\tconst uninstall = (): void => {\n\t\tif (installed.get(client) !== uninstall) return;\n\t\tinstalled.delete(client);\n\t\twindow.removeEventListener(\"error\", onError);\n\t\twindow.removeEventListener(\"unhandledrejection\", onRejection);\n\t};\n\tinstalled.set(client, uninstall);\n\treturn uninstall;\n}\n"],"names":["installed","installGlobalErrorHandlers","client","options","existing","noop","onError","event","TelemetryEvent","onRejection","uninstall"],"mappings":"gHAiBMA,MAAgB,QAgBf,SAASC,EAA2BC,EAAmBC,EAAqC,GAAgB,CAClH,MAAMC,EAAWJ,EAAU,IAAIE,CAAM,EACrC,GAAIE,EAAU,OAAOA,EACrB,GAAI,OAAO,OAAW,IAAa,CAIlC,MAAMC,EAAO,IAAY,CAEzB,EACA,OAAAL,EAAU,IAAIE,EAAQG,CAAI,EACnBA,CACR,CAEA,MAAMC,EAAWC,GAA4B,CAC5CL,EAAO,WAAWK,EAAM,OAASA,EAAM,QAAS,CAC/C,KAAMJ,EAAQ,sBAAwBK,EAAAA,eAAe,YACrD,WAAY,CAAC,GAAGL,EAAQ,WAAY,eAAgB,QAAA,CAAQ,CAC5D,CACF,EACMM,EAAeF,GAAuC,CAC3DL,EAAO,WAAWK,EAAM,OAAQ,CAC/B,KAAMJ,EAAQ,6BAA+BK,EAAAA,eAAe,mBAC5D,WAAY,CAAC,GAAGL,EAAQ,WAAY,eAAgB,oBAAA,CAAoB,CACxE,CACF,EAEA,OAAO,iBAAiB,QAASG,CAAO,EACxC,OAAO,iBAAiB,qBAAsBG,CAAW,EAEzD,MAAMC,EAAY,IAAY,CACzBV,EAAU,IAAIE,CAAM,IAAMQ,IAC9BV,EAAU,OAAOE,CAAM,EACvB,OAAO,oBAAoB,QAASI,CAAO,EAC3C,OAAO,oBAAoB,qBAAsBG,CAAW,EAC7D,EACA,OAAAT,EAAU,IAAIE,EAAQQ,CAAS,EACxBA,CACR"}
@@ -0,0 +1,25 @@
1
+ import { Telemetry } from '@tetrascience-npm/request/telemetry';
2
+ /** Options for {@link installGlobalErrorHandlers}. */
3
+ export interface GlobalErrorHandlerOptions {
4
+ /** Event name for `window.onerror`. Default: `App:Error:Window`. */
5
+ windowErrorEventName?: string;
6
+ /** Event name for unhandled promise rejections. Default: `App:Error:UnhandledRejection`. */
7
+ unhandledRejectionEventName?: string;
8
+ /** Extra attributes merged into both events. */
9
+ attributes?: Record<string, unknown>;
10
+ }
11
+ /**
12
+ * Explicit opt-in: capture uncaught errors and unhandled promise rejections
13
+ * for `client`. Never installed automatically by the provider — global
14
+ * listeners are a host-level decision, and under Module Federation several
15
+ * apps share `window`.
16
+ *
17
+ * Idempotent per client; returns an uninstall function (also idempotent).
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * const client = useTelemetryClient();
22
+ * useEffect(() => installGlobalErrorHandlers(client), [client]);
23
+ * ```
24
+ */
25
+ export declare function installGlobalErrorHandlers(client: Telemetry, options?: GlobalErrorHandlerOptions): () => void;
@@ -0,0 +1,31 @@
1
+ import { TelemetryEvent as s } from "./events.js";
2
+ const n = /* @__PURE__ */ new WeakMap();
3
+ function w(e, t = {}) {
4
+ const d = n.get(e);
5
+ if (d) return d;
6
+ if (typeof window > "u") {
7
+ const r = () => {
8
+ };
9
+ return n.set(e, r), r;
10
+ }
11
+ const a = (r) => {
12
+ e.trackError(r.error ?? r.message, {
13
+ name: t.windowErrorEventName ?? s.WindowError,
14
+ attributes: { ...t.attributes, "error.source": "window" }
15
+ });
16
+ }, i = (r) => {
17
+ e.trackError(r.reason, {
18
+ name: t.unhandledRejectionEventName ?? s.UnhandledRejection,
19
+ attributes: { ...t.attributes, "error.source": "unhandledrejection" }
20
+ });
21
+ };
22
+ window.addEventListener("error", a), window.addEventListener("unhandledrejection", i);
23
+ const o = () => {
24
+ n.get(e) === o && (n.delete(e), window.removeEventListener("error", a), window.removeEventListener("unhandledrejection", i));
25
+ };
26
+ return n.set(e, o), o;
27
+ }
28
+ export {
29
+ w as installGlobalErrorHandlers
30
+ };
31
+ //# sourceMappingURL=global-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-handlers.js","sources":["../../src/telemetry/global-handlers.ts"],"sourcesContent":["import {TelemetryEvent} from \"./events\";\n\nimport type {Telemetry} from \"@tetrascience-npm/request/telemetry\";\n\n/** Options for {@link installGlobalErrorHandlers}. */\nexport interface GlobalErrorHandlerOptions {\n\t/** Event name for `window.onerror`. Default: `App:Error:Window`. */\n\twindowErrorEventName?: string;\n\t/** Event name for unhandled promise rejections. Default: `App:Error:UnhandledRejection`. */\n\tunhandledRejectionEventName?: string;\n\t/** Extra attributes merged into both events. */\n\tattributes?: Record<string, unknown>;\n}\n\n// Keyed by client so two federated apps can each install their own handlers,\n// while a repeated install for the same client (StrictMode, hot reload) is a\n// no-op that returns the original uninstall.\nconst installed = new WeakMap<Telemetry, () => void>();\n\n/**\n * Explicit opt-in: capture uncaught errors and unhandled promise rejections\n * for `client`. Never installed automatically by the provider — global\n * listeners are a host-level decision, and under Module Federation several\n * apps share `window`.\n *\n * Idempotent per client; returns an uninstall function (also idempotent).\n *\n * @example\n * ```tsx\n * const client = useTelemetryClient();\n * useEffect(() => installGlobalErrorHandlers(client), [client]);\n * ```\n */\nexport function installGlobalErrorHandlers(client: Telemetry, options: GlobalErrorHandlerOptions = {}): () => void {\n\tconst existing = installed.get(client);\n\tif (existing) return existing;\n\tif (typeof window === \"undefined\") {\n\t\t// Recorded in the map like any other install, so the documented\n\t\t// once-per-client contract holds here too: without this, every call in an\n\t\t// SSR/non-browser runtime handed back a different uninstall function.\n\t\tconst noop = (): void => {\n\t\t\t// nothing was installed (SSR / non-browser runtime)\n\t\t};\n\t\tinstalled.set(client, noop);\n\t\treturn noop;\n\t}\n\n\tconst onError = (event: ErrorEvent): void => {\n\t\tclient.trackError(event.error ?? event.message, {\n\t\t\tname: options.windowErrorEventName ?? TelemetryEvent.WindowError,\n\t\t\tattributes: {...options.attributes, \"error.source\": \"window\"},\n\t\t});\n\t};\n\tconst onRejection = (event: PromiseRejectionEvent): void => {\n\t\tclient.trackError(event.reason, {\n\t\t\tname: options.unhandledRejectionEventName ?? TelemetryEvent.UnhandledRejection,\n\t\t\tattributes: {...options.attributes, \"error.source\": \"unhandledrejection\"},\n\t\t});\n\t};\n\n\twindow.addEventListener(\"error\", onError);\n\twindow.addEventListener(\"unhandledrejection\", onRejection);\n\n\tconst uninstall = (): void => {\n\t\tif (installed.get(client) !== uninstall) return;\n\t\tinstalled.delete(client);\n\t\twindow.removeEventListener(\"error\", onError);\n\t\twindow.removeEventListener(\"unhandledrejection\", onRejection);\n\t};\n\tinstalled.set(client, uninstall);\n\treturn uninstall;\n}\n"],"names":["installed","installGlobalErrorHandlers","client","options","existing","noop","onError","event","TelemetryEvent","onRejection","uninstall"],"mappings":";AAiBA,MAAMA,wBAAgB,QAAA;AAgBf,SAASC,EAA2BC,GAAmBC,IAAqC,IAAgB;AAClH,QAAMC,IAAWJ,EAAU,IAAIE,CAAM;AACrC,MAAIE,EAAU,QAAOA;AACrB,MAAI,OAAO,SAAW,KAAa;AAIlC,UAAMC,IAAO,MAAY;AAAA,IAEzB;AACA,WAAAL,EAAU,IAAIE,GAAQG,CAAI,GACnBA;AAAA,EACR;AAEA,QAAMC,IAAU,CAACC,MAA4B;AAC5C,IAAAL,EAAO,WAAWK,EAAM,SAASA,EAAM,SAAS;AAAA,MAC/C,MAAMJ,EAAQ,wBAAwBK,EAAe;AAAA,MACrD,YAAY,EAAC,GAAGL,EAAQ,YAAY,gBAAgB,SAAA;AAAA,IAAQ,CAC5D;AAAA,EACF,GACMM,IAAc,CAACF,MAAuC;AAC3D,IAAAL,EAAO,WAAWK,EAAM,QAAQ;AAAA,MAC/B,MAAMJ,EAAQ,+BAA+BK,EAAe;AAAA,MAC5D,YAAY,EAAC,GAAGL,EAAQ,YAAY,gBAAgB,qBAAA;AAAA,IAAoB,CACxE;AAAA,EACF;AAEA,SAAO,iBAAiB,SAASG,CAAO,GACxC,OAAO,iBAAiB,sBAAsBG,CAAW;AAEzD,QAAMC,IAAY,MAAY;AAC7B,IAAIV,EAAU,IAAIE,CAAM,MAAMQ,MAC9BV,EAAU,OAAOE,CAAM,GACvB,OAAO,oBAAoB,SAASI,CAAO,GAC3C,OAAO,oBAAoB,sBAAsBG,CAAW;AAAA,EAC7D;AACA,SAAAT,EAAU,IAAIE,GAAQQ,CAAS,GACxBA;AACR;"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * React bindings for the product-event telemetry distro (SW-2322).
3
+ *
4
+ * One provider mount and one import is the whole integration:
5
+ *
6
+ * ```tsx
7
+ * import {TelemetryProvider, TSErrorBoundary, useTetraEvents} from '@tetrascience-npm/tetrascience-react-ui/telemetry';
8
+ * ```
9
+ *
10
+ * This entry IS the React layer — it exports a provider, a hook and an error
11
+ * boundary — so importing it pulls React in, and `react` is a required peer of
12
+ * this package (it carries no `peerDependenciesMeta.optional`).
13
+ *
14
+ * If you want telemetry without React, import the framework-agnostic core
15
+ * directly: `@tetrascience-npm/request/telemetry`. That is what these bindings
16
+ * wrap, and it is re-exported below so a consumer never needs both imports.
17
+ */
18
+ export * from './types';
19
+ export * from './events';
20
+ export * from './context';
21
+ export * from './facade';
22
+ export * from './telemetry-provider';
23
+ export * from './use-tetra-events';
24
+ export * from './error-boundary';
25
+ export * from './global-handlers';
26
+ export * from '@tetrascience-npm/request/telemetry';
27
+ export type { ArtifactIdentity } from '@tetrascience-npm/request/telemetry';
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("react/jsx-runtime"),T=require("@tetrascience-npm/request/telemetry"),u=require("react"),v=require("./context.cjs"),d=require("./facade.cjs");function s(t){if(typeof window>"u")return;const e=window.env?.[t];if(typeof e=="boolean")return e;if(typeof e=="string"){if(/^(true|1|yes)$/i.test(e))return!0;if(/^(false|0|no)$/i.test(e))return!1}}function f(t,e,r="data-app"){const l=t?.namespace??e?.namespace,i=t?.slug??e?.slug,n=t?.version??e?.version,c=t?.type??e?.type??r;return l&&i&&n?{namespace:l,slug:i,version:n,type:c}:void 0}function E({children:t,...e}){const r=e.enabled??s("TELEMETRY_ENABLED")??!0,l=e.debug??s("TELEMETRY_DEBUG")??!1,i=f(e.artifact,e.manifest,e.artifactType),[n]=u.useState(d.createTelemetryFacade),c=u.useRef(e.getAuthToken);c.current=e.getAuthToken;const a={artifact:i,orgSlug:e.orgSlug,logsUrl:e.logsUrl,debug:l,flushTimeoutMillis:e.flushTimeoutMillis,processors:e.processors,logger:e.logger,tracing:e.tracing,metrics:e.metrics,getAuthToken:()=>c.current?.()},g=u.useRef(a);g.current=a;const m=JSON.stringify([r,l,e.logsUrl??null,e.orgSlug??null,i??null,e.tracing?.enabled??null,e.tracing?.tracesUrl??null,e.tracing?.sampleRatio??null,(e.tracing?.spanProcessors?.length??0)>0,e.metrics?.enabled??null,e.metrics?.metricsUrl??null,e.metrics?.exportIntervalMillis??null,(e.metrics?.readers?.length??0)>0]);return u.useEffect(()=>{const o=r?T.createTelemetry(g.current):d.NOOP_TELEMETRY;return n.attach(o),()=>{n.detach(o),o.shutdown()}},[n,m,r]),y.jsx(v.TelemetryContext.Provider,{value:n,children:t})}exports.TelemetryProvider=E;exports.readHostFlag=s;exports.resolveArtifact=f;
2
+ //# sourceMappingURL=telemetry-provider.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry-provider.cjs","sources":["../../src/telemetry/telemetry-provider.tsx"],"sourcesContent":["import {\n\tcreateTelemetry,\n\ttype ArtifactType,\n\ttype TelemetryArtifact,\n\ttype TelemetryOptions,\n} from \"@tetrascience-npm/request/telemetry\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { TelemetryContext } from \"./context\";\nimport { NOOP_TELEMETRY, createTelemetryFacade } from \"./facade\";\n\nimport type { TelemetryProviderProps } from \"./types\";\n\n/**\n * Read a boolean flag from the host-provided browser config (`window.env`) —\n * the same seam the core uses for `API_URI`. Accepts real booleans and the\n * string forms hosts inject through templated config.\n */\nexport function readHostFlag(key: string): boolean | undefined {\n if (typeof window === \"undefined\") return undefined;\n const value = (window as { env?: Record<string, unknown> }).env?.[key];\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n if (/^(true|1|yes)$/i.test(value)) return true;\n if (/^(false|0|no)$/i.test(value)) return false;\n }\n return undefined;\n}\n\n/**\n * Merge host mount props with the manifest fallback, field by field. Returns\n * undefined when identity is still incomplete — the core then emits without\n * artifact identity rather than with placeholder values.\n */\nexport function resolveArtifact(\n artifact: Partial<TelemetryArtifact> | undefined,\n manifest: Partial<TelemetryArtifact> | undefined,\n artifactType: ArtifactType = \"data-app\",\n): TelemetryArtifact | undefined {\n const namespace = artifact?.namespace ?? manifest?.namespace;\n const slug = artifact?.slug ?? manifest?.slug;\n const version = artifact?.version ?? manifest?.version;\n const type = artifact?.type ?? manifest?.type ?? artifactType;\n return namespace && slug && version ? { namespace, slug, version, type } : undefined;\n}\n\n/**\n * Mount once, near the app root: builds a PER-INSTANCE telemetry client from\n * the app's identity and exposes it to `useTetraEvents` / `TSErrorBoundary`.\n *\n * The client is never module-global — federated apps share `globalThis` with\n * the host and siblings, so a global client would cross-attribute events.\n * It is built inside an effect and shut down (which flushes) on unmount, so\n * StrictMode's mount → unmount → mount cycle disposes the first client\n * cleanly instead of leaving a dead provider attached. The context value is a\n * stable façade, so hook callback identities never change.\n *\n * No user identity is ever attached client-side: `user.id` is stamped\n * server-side at the gateway from the authenticated principal.\n *\n * @example\n * ```tsx\n * <TelemetryProvider artifact={hostProps.artifact} manifest={manifest} orgSlug={org?.slug}>\n * <App />\n * </TelemetryProvider>\n * ```\n */\nexport function TelemetryProvider({ children, ...props }: TelemetryProviderProps) {\n const enabled = props.enabled ?? readHostFlag(\"TELEMETRY_ENABLED\") ?? true;\n const debug = props.debug ?? readHostFlag(\"TELEMETRY_DEBUG\") ?? false;\n const artifact = resolveArtifact(props.artifact, props.manifest, props.artifactType);\n\n const [facade] = useState(createTelemetryFacade);\n\n // Options are read at construction time; only identity-shaped changes\n // rebuild the client (a new `processors`/`logger` object per render must not).\n // The client is built ONCE per identity change, but the token resolver has to\n // track re-renders: an app writing `getAuthToken={() => token}` produces a new\n // closure each render, and the client would otherwise keep calling the first\n // one — capturing whatever token existed at mount. That fails silently and\n // late, as 401s appearing only after the first token rotation. Holding the\n // prop in a ref and calling through it means the client always invokes the\n // CURRENT closure without being rebuilt.\n const getAuthTokenRef = useRef(props.getAuthToken);\n getAuthTokenRef.current = props.getAuthToken;\n\n const options: TelemetryOptions = {\n artifact,\n orgSlug: props.orgSlug,\n logsUrl: props.logsUrl,\n debug,\n flushTimeoutMillis: props.flushTimeoutMillis,\n processors: props.processors,\n logger: props.logger,\n tracing: props.tracing,\n metrics: props.metrics,\n getAuthToken: () => getAuthTokenRef.current?.(),\n };\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // `tracing` is part of the identity: without it, flipping tracing on (or\n // changing the sample ratio) would not rebuild the client, so the change\n // would silently take effect only when some UNRELATED identity field\n // happened to change. Read field-by-field rather than by reference so an\n // inline `tracing={{enabled: true}}` object literal does not rebuild every\n // render.\n // Only SCALARS go in the key. `tracing.spanProcessors` and `metrics.readers`\n // hold live SDK instances — serialising those would make the key depend on\n // an exporter's internal state (spurious rebuilds), and a processor with a\n // circular reference would throw here and take the whole provider down on\n // render. Their PRESENCE is part of the identity; their contents are not.\n const identityKey = JSON.stringify([\n enabled,\n debug,\n props.logsUrl ?? null,\n props.orgSlug ?? null,\n artifact ?? null,\n props.tracing?.enabled ?? null,\n props.tracing?.tracesUrl ?? null,\n props.tracing?.sampleRatio ?? null,\n (props.tracing?.spanProcessors?.length ?? 0) > 0,\n // Same argument as `tracing`: without metrics in the identity, flipping\n // it on would not rebuild the client, so the change would take effect\n // only when some UNRELATED identity field happened to change.\n props.metrics?.enabled ?? null,\n props.metrics?.metricsUrl ?? null,\n props.metrics?.exportIntervalMillis ?? null,\n (props.metrics?.readers?.length ?? 0) > 0,\n ]);\n\n useEffect(() => {\n const client = enabled ? createTelemetry(optionsRef.current) : NOOP_TELEMETRY;\n facade.attach(client);\n return () => {\n facade.detach(client);\n // shutdown() flushes what is buffered, then tears the pipeline down.\n void client.shutdown();\n };\n // `enabled` is encoded in identityKey; optionsRef is intentionally not a dep.\n }, [facade, identityKey, enabled]);\n\n return <TelemetryContext.Provider value={facade}>{children}</TelemetryContext.Provider>;\n}\n"],"names":["readHostFlag","key","value","resolveArtifact","artifact","manifest","artifactType","namespace","slug","version","type","TelemetryProvider","children","props","enabled","debug","facade","useState","createTelemetryFacade","getAuthTokenRef","useRef","options","optionsRef","identityKey","useEffect","client","createTelemetry","NOOP_TELEMETRY","TelemetryContext"],"mappings":"8OAkBO,SAASA,EAAaC,EAAkC,CAC7D,GAAI,OAAO,OAAW,IAAa,OACnC,MAAMC,EAAS,OAA6C,MAAMD,CAAG,EACrE,GAAI,OAAOC,GAAU,UAAW,OAAOA,EACvC,GAAI,OAAOA,GAAU,SAAU,CAC7B,GAAI,kBAAkB,KAAKA,CAAK,EAAG,MAAO,GAC1C,GAAI,kBAAkB,KAAKA,CAAK,EAAG,MAAO,EAC5C,CAEF,CAOO,SAASC,EACdC,EACAC,EACAC,EAA6B,WACE,CAC/B,MAAMC,EAAYH,GAAU,WAAaC,GAAU,UAC7CG,EAAOJ,GAAU,MAAQC,GAAU,KACnCI,EAAUL,GAAU,SAAWC,GAAU,QACzCK,EAAON,GAAU,MAAQC,GAAU,MAAQC,EACjD,OAAOC,GAAaC,GAAQC,EAAU,CAAE,UAAAF,EAAW,KAAAC,EAAM,QAAAC,EAAS,KAAAC,GAAS,MAC7E,CAuBO,SAASC,EAAkB,CAAE,SAAAC,EAAU,GAAGC,GAAiC,CAChF,MAAMC,EAAUD,EAAM,SAAWb,EAAa,mBAAmB,GAAK,GAChEe,EAAQF,EAAM,OAASb,EAAa,iBAAiB,GAAK,GAC1DI,EAAWD,EAAgBU,EAAM,SAAUA,EAAM,SAAUA,EAAM,YAAY,EAE7E,CAACG,CAAM,EAAIC,EAAAA,SAASC,uBAAqB,EAWzCC,EAAkBC,EAAAA,OAAOP,EAAM,YAAY,EACjDM,EAAgB,QAAUN,EAAM,aAEhC,MAAMQ,EAA4B,CAChC,SAAAjB,EACA,QAASS,EAAM,QACf,QAASA,EAAM,QACf,MAAAE,EACA,mBAAoBF,EAAM,mBAC1B,WAAYA,EAAM,WAClB,OAAQA,EAAM,OACd,QAASA,EAAM,QACf,QAASA,EAAM,QACf,aAAc,IAAMM,EAAgB,UAAA,CAAU,EAE1CG,EAAaF,EAAAA,OAAOC,CAAO,EACjCC,EAAW,QAAUD,EAarB,MAAME,EAAc,KAAK,UAAU,CACjCT,EACAC,EACAF,EAAM,SAAW,KACjBA,EAAM,SAAW,KACjBT,GAAY,KACZS,EAAM,SAAS,SAAW,KAC1BA,EAAM,SAAS,WAAa,KAC5BA,EAAM,SAAS,aAAe,MAC7BA,EAAM,SAAS,gBAAgB,QAAU,GAAK,EAI/CA,EAAM,SAAS,SAAW,KAC1BA,EAAM,SAAS,YAAc,KAC7BA,EAAM,SAAS,sBAAwB,MACtCA,EAAM,SAAS,SAAS,QAAU,GAAK,CAAA,CACzC,EAEDW,OAAAA,EAAAA,UAAU,IAAM,CACd,MAAMC,EAASX,EAAUY,EAAAA,gBAAgBJ,EAAW,OAAO,EAAIK,EAAAA,eAC/DX,OAAAA,EAAO,OAAOS,CAAM,EACb,IAAM,CACXT,EAAO,OAAOS,CAAM,EAEfA,EAAO,SAAA,CACd,CAEF,EAAG,CAACT,EAAQO,EAAaT,CAAO,CAAC,QAEzBc,EAAAA,iBAAiB,SAAjB,CAA0B,MAAOZ,EAAS,SAAAJ,EAAS,CAC7D"}
@@ -0,0 +1,36 @@
1
+ import { ArtifactType, TelemetryArtifact } from '@tetrascience-npm/request/telemetry';
2
+ import { TelemetryProviderProps } from './types';
3
+ /**
4
+ * Read a boolean flag from the host-provided browser config (`window.env`) —
5
+ * the same seam the core uses for `API_URI`. Accepts real booleans and the
6
+ * string forms hosts inject through templated config.
7
+ */
8
+ export declare function readHostFlag(key: string): boolean | undefined;
9
+ /**
10
+ * Merge host mount props with the manifest fallback, field by field. Returns
11
+ * undefined when identity is still incomplete — the core then emits without
12
+ * artifact identity rather than with placeholder values.
13
+ */
14
+ export declare function resolveArtifact(artifact: Partial<TelemetryArtifact> | undefined, manifest: Partial<TelemetryArtifact> | undefined, artifactType?: ArtifactType): TelemetryArtifact | undefined;
15
+ /**
16
+ * Mount once, near the app root: builds a PER-INSTANCE telemetry client from
17
+ * the app's identity and exposes it to `useTetraEvents` / `TSErrorBoundary`.
18
+ *
19
+ * The client is never module-global — federated apps share `globalThis` with
20
+ * the host and siblings, so a global client would cross-attribute events.
21
+ * It is built inside an effect and shut down (which flushes) on unmount, so
22
+ * StrictMode's mount → unmount → mount cycle disposes the first client
23
+ * cleanly instead of leaving a dead provider attached. The context value is a
24
+ * stable façade, so hook callback identities never change.
25
+ *
26
+ * No user identity is ever attached client-side: `user.id` is stamped
27
+ * server-side at the gateway from the authenticated principal.
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * <TelemetryProvider artifact={hostProps.artifact} manifest={manifest} orgSlug={org?.slug}>
32
+ * <App />
33
+ * </TelemetryProvider>
34
+ * ```
35
+ */
36
+ export declare function TelemetryProvider({ children, ...props }: TelemetryProviderProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,65 @@
1
+ import { jsx as m } from "react/jsx-runtime";
2
+ import { createTelemetry as d } from "@tetrascience-npm/request/telemetry";
3
+ import { useState as T, useRef as a, useEffect as y } from "react";
4
+ import { TelemetryContext as v } from "./context.js";
5
+ import { createTelemetryFacade as E, NOOP_TELEMETRY as h } from "./facade.js";
6
+ function g(t) {
7
+ if (typeof window > "u") return;
8
+ const e = window.env?.[t];
9
+ if (typeof e == "boolean") return e;
10
+ if (typeof e == "string") {
11
+ if (/^(true|1|yes)$/i.test(e)) return !0;
12
+ if (/^(false|0|no)$/i.test(e)) return !1;
13
+ }
14
+ }
15
+ function b(t, e, r = "data-app") {
16
+ const l = t?.namespace ?? e?.namespace, i = t?.slug ?? e?.slug, n = t?.version ?? e?.version, o = t?.type ?? e?.type ?? r;
17
+ return l && i && n ? { namespace: l, slug: i, version: n, type: o } : void 0;
18
+ }
19
+ function S({ children: t, ...e }) {
20
+ const r = e.enabled ?? g("TELEMETRY_ENABLED") ?? !0, l = e.debug ?? g("TELEMETRY_DEBUG") ?? !1, i = b(e.artifact, e.manifest, e.artifactType), [n] = T(E), o = a(e.getAuthToken);
21
+ o.current = e.getAuthToken;
22
+ const u = {
23
+ artifact: i,
24
+ orgSlug: e.orgSlug,
25
+ logsUrl: e.logsUrl,
26
+ debug: l,
27
+ flushTimeoutMillis: e.flushTimeoutMillis,
28
+ processors: e.processors,
29
+ logger: e.logger,
30
+ tracing: e.tracing,
31
+ metrics: e.metrics,
32
+ getAuthToken: () => o.current?.()
33
+ }, s = a(u);
34
+ s.current = u;
35
+ const f = JSON.stringify([
36
+ r,
37
+ l,
38
+ e.logsUrl ?? null,
39
+ e.orgSlug ?? null,
40
+ i ?? null,
41
+ e.tracing?.enabled ?? null,
42
+ e.tracing?.tracesUrl ?? null,
43
+ e.tracing?.sampleRatio ?? null,
44
+ (e.tracing?.spanProcessors?.length ?? 0) > 0,
45
+ // Same argument as `tracing`: without metrics in the identity, flipping
46
+ // it on would not rebuild the client, so the change would take effect
47
+ // only when some UNRELATED identity field happened to change.
48
+ e.metrics?.enabled ?? null,
49
+ e.metrics?.metricsUrl ?? null,
50
+ e.metrics?.exportIntervalMillis ?? null,
51
+ (e.metrics?.readers?.length ?? 0) > 0
52
+ ]);
53
+ return y(() => {
54
+ const c = r ? d(s.current) : h;
55
+ return n.attach(c), () => {
56
+ n.detach(c), c.shutdown();
57
+ };
58
+ }, [n, f, r]), /* @__PURE__ */ m(v.Provider, { value: n, children: t });
59
+ }
60
+ export {
61
+ S as TelemetryProvider,
62
+ g as readHostFlag,
63
+ b as resolveArtifact
64
+ };
65
+ //# sourceMappingURL=telemetry-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry-provider.js","sources":["../../src/telemetry/telemetry-provider.tsx"],"sourcesContent":["import {\n\tcreateTelemetry,\n\ttype ArtifactType,\n\ttype TelemetryArtifact,\n\ttype TelemetryOptions,\n} from \"@tetrascience-npm/request/telemetry\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { TelemetryContext } from \"./context\";\nimport { NOOP_TELEMETRY, createTelemetryFacade } from \"./facade\";\n\nimport type { TelemetryProviderProps } from \"./types\";\n\n/**\n * Read a boolean flag from the host-provided browser config (`window.env`) —\n * the same seam the core uses for `API_URI`. Accepts real booleans and the\n * string forms hosts inject through templated config.\n */\nexport function readHostFlag(key: string): boolean | undefined {\n if (typeof window === \"undefined\") return undefined;\n const value = (window as { env?: Record<string, unknown> }).env?.[key];\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n if (/^(true|1|yes)$/i.test(value)) return true;\n if (/^(false|0|no)$/i.test(value)) return false;\n }\n return undefined;\n}\n\n/**\n * Merge host mount props with the manifest fallback, field by field. Returns\n * undefined when identity is still incomplete — the core then emits without\n * artifact identity rather than with placeholder values.\n */\nexport function resolveArtifact(\n artifact: Partial<TelemetryArtifact> | undefined,\n manifest: Partial<TelemetryArtifact> | undefined,\n artifactType: ArtifactType = \"data-app\",\n): TelemetryArtifact | undefined {\n const namespace = artifact?.namespace ?? manifest?.namespace;\n const slug = artifact?.slug ?? manifest?.slug;\n const version = artifact?.version ?? manifest?.version;\n const type = artifact?.type ?? manifest?.type ?? artifactType;\n return namespace && slug && version ? { namespace, slug, version, type } : undefined;\n}\n\n/**\n * Mount once, near the app root: builds a PER-INSTANCE telemetry client from\n * the app's identity and exposes it to `useTetraEvents` / `TSErrorBoundary`.\n *\n * The client is never module-global — federated apps share `globalThis` with\n * the host and siblings, so a global client would cross-attribute events.\n * It is built inside an effect and shut down (which flushes) on unmount, so\n * StrictMode's mount → unmount → mount cycle disposes the first client\n * cleanly instead of leaving a dead provider attached. The context value is a\n * stable façade, so hook callback identities never change.\n *\n * No user identity is ever attached client-side: `user.id` is stamped\n * server-side at the gateway from the authenticated principal.\n *\n * @example\n * ```tsx\n * <TelemetryProvider artifact={hostProps.artifact} manifest={manifest} orgSlug={org?.slug}>\n * <App />\n * </TelemetryProvider>\n * ```\n */\nexport function TelemetryProvider({ children, ...props }: TelemetryProviderProps) {\n const enabled = props.enabled ?? readHostFlag(\"TELEMETRY_ENABLED\") ?? true;\n const debug = props.debug ?? readHostFlag(\"TELEMETRY_DEBUG\") ?? false;\n const artifact = resolveArtifact(props.artifact, props.manifest, props.artifactType);\n\n const [facade] = useState(createTelemetryFacade);\n\n // Options are read at construction time; only identity-shaped changes\n // rebuild the client (a new `processors`/`logger` object per render must not).\n // The client is built ONCE per identity change, but the token resolver has to\n // track re-renders: an app writing `getAuthToken={() => token}` produces a new\n // closure each render, and the client would otherwise keep calling the first\n // one — capturing whatever token existed at mount. That fails silently and\n // late, as 401s appearing only after the first token rotation. Holding the\n // prop in a ref and calling through it means the client always invokes the\n // CURRENT closure without being rebuilt.\n const getAuthTokenRef = useRef(props.getAuthToken);\n getAuthTokenRef.current = props.getAuthToken;\n\n const options: TelemetryOptions = {\n artifact,\n orgSlug: props.orgSlug,\n logsUrl: props.logsUrl,\n debug,\n flushTimeoutMillis: props.flushTimeoutMillis,\n processors: props.processors,\n logger: props.logger,\n tracing: props.tracing,\n metrics: props.metrics,\n getAuthToken: () => getAuthTokenRef.current?.(),\n };\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // `tracing` is part of the identity: without it, flipping tracing on (or\n // changing the sample ratio) would not rebuild the client, so the change\n // would silently take effect only when some UNRELATED identity field\n // happened to change. Read field-by-field rather than by reference so an\n // inline `tracing={{enabled: true}}` object literal does not rebuild every\n // render.\n // Only SCALARS go in the key. `tracing.spanProcessors` and `metrics.readers`\n // hold live SDK instances — serialising those would make the key depend on\n // an exporter's internal state (spurious rebuilds), and a processor with a\n // circular reference would throw here and take the whole provider down on\n // render. Their PRESENCE is part of the identity; their contents are not.\n const identityKey = JSON.stringify([\n enabled,\n debug,\n props.logsUrl ?? null,\n props.orgSlug ?? null,\n artifact ?? null,\n props.tracing?.enabled ?? null,\n props.tracing?.tracesUrl ?? null,\n props.tracing?.sampleRatio ?? null,\n (props.tracing?.spanProcessors?.length ?? 0) > 0,\n // Same argument as `tracing`: without metrics in the identity, flipping\n // it on would not rebuild the client, so the change would take effect\n // only when some UNRELATED identity field happened to change.\n props.metrics?.enabled ?? null,\n props.metrics?.metricsUrl ?? null,\n props.metrics?.exportIntervalMillis ?? null,\n (props.metrics?.readers?.length ?? 0) > 0,\n ]);\n\n useEffect(() => {\n const client = enabled ? createTelemetry(optionsRef.current) : NOOP_TELEMETRY;\n facade.attach(client);\n return () => {\n facade.detach(client);\n // shutdown() flushes what is buffered, then tears the pipeline down.\n void client.shutdown();\n };\n // `enabled` is encoded in identityKey; optionsRef is intentionally not a dep.\n }, [facade, identityKey, enabled]);\n\n return <TelemetryContext.Provider value={facade}>{children}</TelemetryContext.Provider>;\n}\n"],"names":["readHostFlag","key","value","resolveArtifact","artifact","manifest","artifactType","namespace","slug","version","type","TelemetryProvider","children","props","enabled","debug","facade","useState","createTelemetryFacade","getAuthTokenRef","useRef","options","optionsRef","identityKey","useEffect","client","createTelemetry","NOOP_TELEMETRY","TelemetryContext"],"mappings":";;;;;AAkBO,SAASA,EAAaC,GAAkC;AAC7D,MAAI,OAAO,SAAW,IAAa;AACnC,QAAMC,IAAS,OAA6C,MAAMD,CAAG;AACrE,MAAI,OAAOC,KAAU,UAAW,QAAOA;AACvC,MAAI,OAAOA,KAAU,UAAU;AAC7B,QAAI,kBAAkB,KAAKA,CAAK,EAAG,QAAO;AAC1C,QAAI,kBAAkB,KAAKA,CAAK,EAAG,QAAO;AAAA,EAC5C;AAEF;AAOO,SAASC,EACdC,GACAC,GACAC,IAA6B,YACE;AAC/B,QAAMC,IAAYH,GAAU,aAAaC,GAAU,WAC7CG,IAAOJ,GAAU,QAAQC,GAAU,MACnCI,IAAUL,GAAU,WAAWC,GAAU,SACzCK,IAAON,GAAU,QAAQC,GAAU,QAAQC;AACjD,SAAOC,KAAaC,KAAQC,IAAU,EAAE,WAAAF,GAAW,MAAAC,GAAM,SAAAC,GAAS,MAAAC,MAAS;AAC7E;AAuBO,SAASC,EAAkB,EAAE,UAAAC,GAAU,GAAGC,KAAiC;AAChF,QAAMC,IAAUD,EAAM,WAAWb,EAAa,mBAAmB,KAAK,IAChEe,IAAQF,EAAM,SAASb,EAAa,iBAAiB,KAAK,IAC1DI,IAAWD,EAAgBU,EAAM,UAAUA,EAAM,UAAUA,EAAM,YAAY,GAE7E,CAACG,CAAM,IAAIC,EAASC,CAAqB,GAWzCC,IAAkBC,EAAOP,EAAM,YAAY;AACjD,EAAAM,EAAgB,UAAUN,EAAM;AAEhC,QAAMQ,IAA4B;AAAA,IAChC,UAAAjB;AAAA,IACA,SAASS,EAAM;AAAA,IACf,SAASA,EAAM;AAAA,IACf,OAAAE;AAAA,IACA,oBAAoBF,EAAM;AAAA,IAC1B,YAAYA,EAAM;AAAA,IAClB,QAAQA,EAAM;AAAA,IACd,SAASA,EAAM;AAAA,IACf,SAASA,EAAM;AAAA,IACf,cAAc,MAAMM,EAAgB,UAAA;AAAA,EAAU,GAE1CG,IAAaF,EAAOC,CAAO;AACjC,EAAAC,EAAW,UAAUD;AAarB,QAAME,IAAc,KAAK,UAAU;AAAA,IACjCT;AAAA,IACAC;AAAA,IACAF,EAAM,WAAW;AAAA,IACjBA,EAAM,WAAW;AAAA,IACjBT,KAAY;AAAA,IACZS,EAAM,SAAS,WAAW;AAAA,IAC1BA,EAAM,SAAS,aAAa;AAAA,IAC5BA,EAAM,SAAS,eAAe;AAAA,KAC7BA,EAAM,SAAS,gBAAgB,UAAU,KAAK;AAAA;AAAA;AAAA;AAAA,IAI/CA,EAAM,SAAS,WAAW;AAAA,IAC1BA,EAAM,SAAS,cAAc;AAAA,IAC7BA,EAAM,SAAS,wBAAwB;AAAA,KACtCA,EAAM,SAAS,SAAS,UAAU,KAAK;AAAA,EAAA,CACzC;AAED,SAAAW,EAAU,MAAM;AACd,UAAMC,IAASX,IAAUY,EAAgBJ,EAAW,OAAO,IAAIK;AAC/D,WAAAX,EAAO,OAAOS,CAAM,GACb,MAAM;AACX,MAAAT,EAAO,OAAOS,CAAM,GAEfA,EAAO,SAAA;AAAA,IACd;AAAA,EAEF,GAAG,CAACT,GAAQO,GAAaT,CAAO,CAAC,qBAEzBc,EAAiB,UAAjB,EAA0B,OAAOZ,GAAS,UAAAJ,GAAS;AAC7D;"}
@@ -0,0 +1,140 @@
1
+ import { LogRecordProcessor } from '@opentelemetry/sdk-logs';
2
+ import { RequestTrackingLogger } from '@tetrascience-npm/request';
3
+ import { ArtifactType, MetricsOptions, StartSpanOptions, Telemetry, TelemetryArtifact, TelemetryTracingOptions, TetraSpan, TrackErrorContext } from '@tetrascience-npm/request/telemetry';
4
+ import { ReactNode } from 'react';
5
+ /** Props for {@link TelemetryProvider}. */
6
+ export interface TelemetryProviderProps {
7
+ /**
8
+ * Artifact identity from the host mount props (`hostProps.artifact`).
9
+ * Fields missing here fall back to {@link TelemetryProviderProps.manifest}.
10
+ */
11
+ artifact?: Partial<TelemetryArtifact>;
12
+ /**
13
+ * Manifest fallback for standalone dev, e.g. `import manifest from './manifest.json'`.
14
+ * Used field-by-field wherever `artifact` is incomplete.
15
+ */
16
+ manifest?: Partial<TelemetryArtifact>;
17
+ /**
18
+ * What KIND of artifact is mounting this — `"data-app"` unless stated.
19
+ *
20
+ * The core distro refuses to guess this (it serves data apps and connectors
21
+ * alike, and mislabelling one as the other is worse than emitting no kind at
22
+ * all). This kit can: it is React bindings for a browser mount, and on this
23
+ * platform that is a data app. The default is a DECLARATION by a component
24
+ * only data apps mount, not a fallback for producers that do not know what
25
+ * they are — and anything else mounting it says so here.
26
+ *
27
+ * `artifact.type` / `manifest.type` win over this when present.
28
+ */
29
+ artifactType?: ArtifactType;
30
+ /**
31
+ * Org slug hint. Producer-asserted only — the gateway stamps the
32
+ * authenticated org server-side and that value wins (SW-2319).
33
+ */
34
+ orgSlug?: string;
35
+ /**
36
+ * Resolve the current user's auth token for the `ts-auth-token` header.
37
+ *
38
+ * REQUIRED for a browser data app to deliver anything. The gateway reads
39
+ * credentials from headers only, and its CORS config (`origin: ['*']`, no
40
+ * `credentials`) means a browser cannot send a session cookie — so without
41
+ * this every batch 401s and the app emits into the void.
42
+ *
43
+ * Called on every export, so pass a function that reads the host's CURRENT
44
+ * token rather than a captured string: a data app outlives any single
45
+ * short-lived token.
46
+ */
47
+ getAuthToken?: () => string | undefined | Promise<string | undefined>;
48
+ /** OTLP/HTTP logs URL override. Default: the core's endpoint resolution. */
49
+ logsUrl?: string;
50
+ /**
51
+ * Master switch. Default: `window.env.TELEMETRY_ENABLED` when the host
52
+ * provides it, otherwise `true`. When disabled the context client is a
53
+ * no-op — no OTel provider, no exporter, no network, no buffers.
54
+ */
55
+ enabled?: boolean;
56
+ /**
57
+ * Route records to the stock console exporter in addition to OTLP, so
58
+ * authors see the exact pre-gateway payload. Default:
59
+ * `window.env.TELEMETRY_DEBUG` when the host provides it, otherwise `false`.
60
+ */
61
+ debug?: boolean;
62
+ /** Log record processors override (tests, custom pipelines). */
63
+ processors?: LogRecordProcessor[];
64
+ /**
65
+ * Distributed tracing (SW-2478). Off unless `tracing.enabled` is true —
66
+ * tracing adds a second exporter and a per-span network cost, so an app
67
+ * opts in rather than acquiring it by upgrading the library.
68
+ */
69
+ tracing?: TelemetryTracingOptions;
70
+ /**
71
+ * OTel metrics (SW-2478). Off unless `metrics.enabled` is true, for the same
72
+ * reason tracing is: a metrics reader is a recurring timer and a recurring
73
+ * export.
74
+ *
75
+ * Forwarded here rather than left to the core's defaults because apps build
76
+ * their client through THIS provider — an option the provider does not pass
77
+ * through is one no React data app can ever set, which is how
78
+ * `gauge()`/`histogram()` came to be callable but permanently inert.
79
+ */
80
+ metrics?: MetricsOptions;
81
+ /**
82
+ * Cap on how long `flush()` and `shutdown()` wait for the exporter, in ms.
83
+ * Forwarded to the core, which defaults to 5000. Worth setting when the
84
+ * host unmounts on a route change and cannot afford a 5s teardown.
85
+ */
86
+ flushTimeoutMillis?: number;
87
+ /** Logger for setup failures and stripped attributes. */
88
+ logger?: RequestTrackingLogger;
89
+ children?: ReactNode;
90
+ }
91
+ /** Bound emitters returned by {@link useTetraEvents}. */
92
+ export interface TetraEvents {
93
+ /** Record a product event (`App[:Page][:Object]:Action`). */
94
+ trackEvent(name: string, attributes?: Record<string, unknown>, body?: unknown): void;
95
+ /** Record an error event with `exception.*` attributes. */
96
+ trackError(error: unknown, context?: TrackErrorContext): void;
97
+ /**
98
+ * Start a span (SW-2478). No-op unless the provider was given
99
+ * `tracing.enabled`. The caller must `end()` it — prefer `withSpan`.
100
+ */
101
+ startSpan(name: string, options?: StartSpanOptions): TetraSpan;
102
+ /**
103
+ * Run `fn` inside a span, ending it when the work settles (promises
104
+ * included) and on the error path. Errors are recorded and re-thrown.
105
+ */
106
+ withSpan<T>(name: string, fn: (span: TetraSpan) => T, options?: StartSpanOptions): T;
107
+ /**
108
+ * Add to a running total that only ever goes up (SW-2478). No-op unless the
109
+ * provider was given `metrics.enabled`.
110
+ *
111
+ * The core client and the Python distro have both exposed this from the
112
+ * start; the React bindings did not, so a data app could reach `gauge`,
113
+ * `histogram` and `upDownCounter` but not the most basic instrument of the
114
+ * four. Found by the bindings' own e2e — `events.counter is not a function`.
115
+ */
116
+ counter(name: string, delta?: number, attributes?: Record<string, unknown>): void;
117
+ /**
118
+ * Record the current value of something (SW-2478). No-op unless the
119
+ * provider was given `metrics.enabled`.
120
+ *
121
+ * Unlike {@link TetraEvents.trackEvent}, this carries a VALUE — counting is
122
+ * one-record-one-increment and cannot express "the reading right now".
123
+ */
124
+ gauge(name: string, value: number, attributes?: Record<string, unknown>): void;
125
+ /** Record a distribution sample — a duration, a size. Same opt-in as `gauge`. */
126
+ histogram(name: string, value: number, attributes?: Record<string, unknown>): void;
127
+ /** Add to a running total that can go both ways. Same opt-in as `gauge`. */
128
+ upDownCounter(name: string, delta: number, attributes?: Record<string, unknown>): void;
129
+ }
130
+ /**
131
+ * The context client: a stable façade whose underlying per-instance client is
132
+ * attached by {@link TelemetryProvider} in an effect. Identity is stable for
133
+ * the provider's lifetime, so hook callbacks never change identity.
134
+ */
135
+ export type TelemetryFacade = Telemetry & {
136
+ /** @internal */
137
+ attach(client: Telemetry): void;
138
+ /** @internal */
139
+ detach(client: Telemetry): void;
140
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react"),d=require("./context.cjs"),k=require("./facade.cjs");let a=!1;function E(){a||(a=!0,!(typeof process<"u"&&process.env?.NODE_ENV==="production")&&console.warn("[telemetry] useTetraEvents() called outside <TelemetryProvider> — events are dropped."))}function m(){a=!1}function p(){const e=s.useContext(d.TelemetryContext);return e||E(),e??k.NOOP_TELEMETRY}function b(){const e=p(),o=s.useCallback((t,n,r)=>{e.trackEvent(t,n,r)},[e]),c=s.useCallback((t,n)=>{e.trackError(t,n)},[e]),u=s.useCallback((t,n)=>e.startSpan(t,n),[e]),l=s.useCallback((t,n,r)=>e.counter(t,n,r),[e]),i=s.useCallback((t,n,r)=>e.gauge(t,n,r),[e]),C=s.useCallback((t,n,r)=>e.histogram(t,n,r),[e]),T=s.useCallback((t,n,r)=>e.upDownCounter(t,n,r),[e]),f=s.useCallback((t,n,r)=>e.withSpan(t,n,r),[e]);return s.useMemo(()=>({trackEvent:o,trackError:c,startSpan:u,withSpan:f,counter:l,gauge:i,histogram:C,upDownCounter:T}),[o,c,u,f,l,i,C,T])}exports.__resetTelemetryWarningForTests=m;exports.useTelemetryClient=p;exports.useTetraEvents=b;
2
+ //# sourceMappingURL=use-tetra-events.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tetra-events.cjs","sources":["../../src/telemetry/use-tetra-events.ts"],"sourcesContent":["import { useCallback, useContext, useMemo } from \"react\";\n\nimport { TelemetryContext } from \"./context\";\nimport { NOOP_TELEMETRY } from \"./facade\";\n\nimport type { TetraEvents } from \"./types\";\nimport type { StartSpanOptions, Telemetry, TetraSpan, TrackErrorContext } from \"@tetrascience-npm/request/telemetry\";\n\nlet warned = false;\n\n/**\n * Outside a provider the hook no-ops with one development warning rather than\n * throwing. Rationale: the core's contract is that telemetry never throws into\n * application code — a missing provider is an instrumentation defect, and\n * crashing a customer's app over it inverts the risk. The warning (dev only,\n * once per module instance) keeps the mistake visible during development and\n * is asserted in tests.\n */\nfunction warnOnce(): void {\n if (warned) return;\n warned = true;\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\") return;\n\n console.warn(\"[telemetry] useTetraEvents() called outside <TelemetryProvider> — events are dropped.\");\n}\n\n/** @internal test seam: reset the once-per-module warning latch. */\nexport function __resetTelemetryWarningForTests(): void {\n warned = false;\n}\n\n/** Access the raw context client — e.g. to wire {@link installGlobalErrorHandlers}. */\nexport function useTelemetryClient(): Telemetry {\n const client = useContext(TelemetryContext);\n if (!client) warnOnce();\n return client ?? NOOP_TELEMETRY;\n}\n\n/**\n * The app-facing emitters, bound to the provider's client.\n *\n * Identities are stable for the provider's lifetime (the context value is a\n * stable façade), so these are safe as effect dependencies.\n *\n * @example\n * ```tsx\n * const {trackEvent} = useTetraEvents();\n * trackEvent('Sandbox:Chat:Submit', {model});\n * ```\n */\nexport function useTetraEvents(): TetraEvents {\n const client = useTelemetryClient();\n\n const trackEvent = useCallback(\n (name: string, attributes?: Record<string, unknown>, body?: unknown) => {\n client.trackEvent(name, attributes, body);\n },\n [client],\n );\n\n const trackError = useCallback(\n (error: unknown, context?: TrackErrorContext) => {\n client.trackError(error, context);\n },\n [client],\n );\n\n // SW-2478. Every method here delegates to the core rather than\n // re-implementing anything, so React and non-React consumers cannot drift.\n const startSpan = useCallback(\n (name: string, options?: StartSpanOptions) => client.startSpan(name, options),\n [client],\n );\n\n // SW-2478 metrics. Same delegation rule as startSpan: the\n // instrument caching, the value guards and the publishable-name warning all\n // live in the core client, so re-implementing any of it here would give\n // React apps different behaviour from Node ones for the same call.\n const counter = useCallback(\n (name: string, delta?: number, attributes?: Record<string, unknown>) => client.counter(name, delta, attributes),\n [client],\n );\n\n const gauge = useCallback(\n (name: string, value: number, attributes?: Record<string, unknown>) => client.gauge(name, value, attributes),\n [client],\n );\n\n const histogram = useCallback(\n (name: string, value: number, attributes?: Record<string, unknown>) => client.histogram(name, value, attributes),\n [client],\n );\n\n const upDownCounter = useCallback(\n (name: string, delta: number, attributes?: Record<string, unknown>) =>\n client.upDownCounter(name, delta, attributes),\n [client],\n );\n\n const withSpan = useCallback(\n <T>(name: string, fn: (span: TetraSpan) => T, options?: StartSpanOptions) => client.withSpan(name, fn, options),\n [client],\n );\n\n return useMemo(\n () => ({ trackEvent, trackError, startSpan, withSpan, counter, gauge, histogram, upDownCounter }),\n [trackEvent, trackError, startSpan, withSpan, counter, gauge, histogram, upDownCounter],\n );\n}\n"],"names":["warned","warnOnce","__resetTelemetryWarningForTests","useTelemetryClient","client","useContext","TelemetryContext","NOOP_TELEMETRY","useTetraEvents","trackEvent","useCallback","name","attributes","body","trackError","error","context","startSpan","options","counter","delta","gauge","value","histogram","upDownCounter","withSpan","fn","useMemo"],"mappings":"8JAQA,IAAIA,EAAS,GAUb,SAASC,GAAiB,CACpBD,IACJA,EAAS,GACL,SAAO,QAAY,KAAe,QAAQ,KAAK,WAAa,eAEhE,QAAQ,KAAK,uFAAuF,EACtG,CAGO,SAASE,GAAwC,CACtDF,EAAS,EACX,CAGO,SAASG,GAAgC,CAC9C,MAAMC,EAASC,EAAAA,WAAWC,kBAAgB,EAC1C,OAAKF,GAAQH,EAAA,EACNG,GAAUG,EAAAA,cACnB,CAcO,SAASC,GAA8B,CAC5C,MAAMJ,EAASD,EAAA,EAETM,EAAaC,EAAAA,YACjB,CAACC,EAAcC,EAAsCC,IAAmB,CACtET,EAAO,WAAWO,EAAMC,EAAYC,CAAI,CAC1C,EACA,CAACT,CAAM,CAAA,EAGHU,EAAaJ,EAAAA,YACjB,CAACK,EAAgBC,IAAgC,CAC/CZ,EAAO,WAAWW,EAAOC,CAAO,CAClC,EACA,CAACZ,CAAM,CAAA,EAKHa,EAAYP,EAAAA,YAChB,CAACC,EAAcO,IAA+Bd,EAAO,UAAUO,EAAMO,CAAO,EAC5E,CAACd,CAAM,CAAA,EAOHe,EAAUT,EAAAA,YACd,CAACC,EAAcS,EAAgBR,IAAyCR,EAAO,QAAQO,EAAMS,EAAOR,CAAU,EAC9G,CAACR,CAAM,CAAA,EAGHiB,EAAQX,EAAAA,YACZ,CAACC,EAAcW,EAAeV,IAAyCR,EAAO,MAAMO,EAAMW,EAAOV,CAAU,EAC3G,CAACR,CAAM,CAAA,EAGHmB,EAAYb,EAAAA,YAChB,CAACC,EAAcW,EAAeV,IAAyCR,EAAO,UAAUO,EAAMW,EAAOV,CAAU,EAC/G,CAACR,CAAM,CAAA,EAGHoB,EAAgBd,EAAAA,YACpB,CAACC,EAAcS,EAAeR,IAC5BR,EAAO,cAAcO,EAAMS,EAAOR,CAAU,EAC9C,CAACR,CAAM,CAAA,EAGHqB,EAAWf,EAAAA,YACf,CAAIC,EAAce,EAA4BR,IAA+Bd,EAAO,SAASO,EAAMe,EAAIR,CAAO,EAC9G,CAACd,CAAM,CAAA,EAGT,OAAOuB,EAAAA,QACL,KAAO,CAAE,WAAAlB,EAAY,WAAAK,EAAY,UAAAG,EAAW,SAAAQ,EAAU,QAAAN,EAAS,MAAAE,EAAO,UAAAE,EAAW,cAAAC,IACjF,CAACf,EAAYK,EAAYG,EAAWQ,EAAUN,EAASE,EAAOE,EAAWC,CAAa,CAAA,CAE1F"}
@@ -0,0 +1,19 @@
1
+ import { TetraEvents } from './types';
2
+ import { Telemetry } from '@tetrascience-npm/request/telemetry';
3
+ /** @internal test seam: reset the once-per-module warning latch. */
4
+ export declare function __resetTelemetryWarningForTests(): void;
5
+ /** Access the raw context client — e.g. to wire {@link installGlobalErrorHandlers}. */
6
+ export declare function useTelemetryClient(): Telemetry;
7
+ /**
8
+ * The app-facing emitters, bound to the provider's client.
9
+ *
10
+ * Identities are stable for the provider's lifetime (the context value is a
11
+ * stable façade), so these are safe as effect dependencies.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const {trackEvent} = useTetraEvents();
16
+ * trackEvent('Sandbox:Chat:Submit', {model});
17
+ * ```
18
+ */
19
+ export declare function useTetraEvents(): TetraEvents;
@@ -0,0 +1,55 @@
1
+ import { useContext as E, useCallback as o, useMemo as d } from "react";
2
+ import { TelemetryContext as T } from "./context.js";
3
+ import { NOOP_TELEMETRY as g } from "./facade.js";
4
+ let s = !1;
5
+ function v() {
6
+ s || (s = !0, !(typeof process < "u" && process.env?.NODE_ENV === "production") && console.warn("[telemetry] useTetraEvents() called outside <TelemetryProvider> — events are dropped."));
7
+ }
8
+ function h() {
9
+ s = !1;
10
+ }
11
+ function w() {
12
+ const e = E(T);
13
+ return e || v(), e ?? g;
14
+ }
15
+ function O() {
16
+ const e = w(), c = o(
17
+ (t, r, n) => {
18
+ e.trackEvent(t, r, n);
19
+ },
20
+ [e]
21
+ ), a = o(
22
+ (t, r) => {
23
+ e.trackError(t, r);
24
+ },
25
+ [e]
26
+ ), u = o(
27
+ (t, r) => e.startSpan(t, r),
28
+ [e]
29
+ ), i = o(
30
+ (t, r, n) => e.counter(t, r, n),
31
+ [e]
32
+ ), l = o(
33
+ (t, r, n) => e.gauge(t, r, n),
34
+ [e]
35
+ ), p = o(
36
+ (t, r, n) => e.histogram(t, r, n),
37
+ [e]
38
+ ), f = o(
39
+ (t, r, n) => e.upDownCounter(t, r, n),
40
+ [e]
41
+ ), m = o(
42
+ (t, r, n) => e.withSpan(t, r, n),
43
+ [e]
44
+ );
45
+ return d(
46
+ () => ({ trackEvent: c, trackError: a, startSpan: u, withSpan: m, counter: i, gauge: l, histogram: p, upDownCounter: f }),
47
+ [c, a, u, m, i, l, p, f]
48
+ );
49
+ }
50
+ export {
51
+ h as __resetTelemetryWarningForTests,
52
+ w as useTelemetryClient,
53
+ O as useTetraEvents
54
+ };
55
+ //# sourceMappingURL=use-tetra-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tetra-events.js","sources":["../../src/telemetry/use-tetra-events.ts"],"sourcesContent":["import { useCallback, useContext, useMemo } from \"react\";\n\nimport { TelemetryContext } from \"./context\";\nimport { NOOP_TELEMETRY } from \"./facade\";\n\nimport type { TetraEvents } from \"./types\";\nimport type { StartSpanOptions, Telemetry, TetraSpan, TrackErrorContext } from \"@tetrascience-npm/request/telemetry\";\n\nlet warned = false;\n\n/**\n * Outside a provider the hook no-ops with one development warning rather than\n * throwing. Rationale: the core's contract is that telemetry never throws into\n * application code — a missing provider is an instrumentation defect, and\n * crashing a customer's app over it inverts the risk. The warning (dev only,\n * once per module instance) keeps the mistake visible during development and\n * is asserted in tests.\n */\nfunction warnOnce(): void {\n if (warned) return;\n warned = true;\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\") return;\n\n console.warn(\"[telemetry] useTetraEvents() called outside <TelemetryProvider> — events are dropped.\");\n}\n\n/** @internal test seam: reset the once-per-module warning latch. */\nexport function __resetTelemetryWarningForTests(): void {\n warned = false;\n}\n\n/** Access the raw context client — e.g. to wire {@link installGlobalErrorHandlers}. */\nexport function useTelemetryClient(): Telemetry {\n const client = useContext(TelemetryContext);\n if (!client) warnOnce();\n return client ?? NOOP_TELEMETRY;\n}\n\n/**\n * The app-facing emitters, bound to the provider's client.\n *\n * Identities are stable for the provider's lifetime (the context value is a\n * stable façade), so these are safe as effect dependencies.\n *\n * @example\n * ```tsx\n * const {trackEvent} = useTetraEvents();\n * trackEvent('Sandbox:Chat:Submit', {model});\n * ```\n */\nexport function useTetraEvents(): TetraEvents {\n const client = useTelemetryClient();\n\n const trackEvent = useCallback(\n (name: string, attributes?: Record<string, unknown>, body?: unknown) => {\n client.trackEvent(name, attributes, body);\n },\n [client],\n );\n\n const trackError = useCallback(\n (error: unknown, context?: TrackErrorContext) => {\n client.trackError(error, context);\n },\n [client],\n );\n\n // SW-2478. Every method here delegates to the core rather than\n // re-implementing anything, so React and non-React consumers cannot drift.\n const startSpan = useCallback(\n (name: string, options?: StartSpanOptions) => client.startSpan(name, options),\n [client],\n );\n\n // SW-2478 metrics. Same delegation rule as startSpan: the\n // instrument caching, the value guards and the publishable-name warning all\n // live in the core client, so re-implementing any of it here would give\n // React apps different behaviour from Node ones for the same call.\n const counter = useCallback(\n (name: string, delta?: number, attributes?: Record<string, unknown>) => client.counter(name, delta, attributes),\n [client],\n );\n\n const gauge = useCallback(\n (name: string, value: number, attributes?: Record<string, unknown>) => client.gauge(name, value, attributes),\n [client],\n );\n\n const histogram = useCallback(\n (name: string, value: number, attributes?: Record<string, unknown>) => client.histogram(name, value, attributes),\n [client],\n );\n\n const upDownCounter = useCallback(\n (name: string, delta: number, attributes?: Record<string, unknown>) =>\n client.upDownCounter(name, delta, attributes),\n [client],\n );\n\n const withSpan = useCallback(\n <T>(name: string, fn: (span: TetraSpan) => T, options?: StartSpanOptions) => client.withSpan(name, fn, options),\n [client],\n );\n\n return useMemo(\n () => ({ trackEvent, trackError, startSpan, withSpan, counter, gauge, histogram, upDownCounter }),\n [trackEvent, trackError, startSpan, withSpan, counter, gauge, histogram, upDownCounter],\n );\n}\n"],"names":["warned","warnOnce","__resetTelemetryWarningForTests","useTelemetryClient","client","useContext","TelemetryContext","NOOP_TELEMETRY","useTetraEvents","trackEvent","useCallback","name","attributes","body","trackError","error","context","startSpan","options","counter","delta","gauge","value","histogram","upDownCounter","withSpan","fn","useMemo"],"mappings":";;;AAQA,IAAIA,IAAS;AAUb,SAASC,IAAiB;AACxB,EAAID,MACJA,IAAS,IACL,SAAO,UAAY,OAAe,QAAQ,KAAK,aAAa,iBAEhE,QAAQ,KAAK,uFAAuF;AACtG;AAGO,SAASE,IAAwC;AACtD,EAAAF,IAAS;AACX;AAGO,SAASG,IAAgC;AAC9C,QAAMC,IAASC,EAAWC,CAAgB;AAC1C,SAAKF,KAAQH,EAAA,GACNG,KAAUG;AACnB;AAcO,SAASC,IAA8B;AAC5C,QAAMJ,IAASD,EAAA,GAETM,IAAaC;AAAA,IACjB,CAACC,GAAcC,GAAsCC,MAAmB;AACtE,MAAAT,EAAO,WAAWO,GAAMC,GAAYC,CAAI;AAAA,IAC1C;AAAA,IACA,CAACT,CAAM;AAAA,EAAA,GAGHU,IAAaJ;AAAA,IACjB,CAACK,GAAgBC,MAAgC;AAC/C,MAAAZ,EAAO,WAAWW,GAAOC,CAAO;AAAA,IAClC;AAAA,IACA,CAACZ,CAAM;AAAA,EAAA,GAKHa,IAAYP;AAAA,IAChB,CAACC,GAAcO,MAA+Bd,EAAO,UAAUO,GAAMO,CAAO;AAAA,IAC5E,CAACd,CAAM;AAAA,EAAA,GAOHe,IAAUT;AAAA,IACd,CAACC,GAAcS,GAAgBR,MAAyCR,EAAO,QAAQO,GAAMS,GAAOR,CAAU;AAAA,IAC9G,CAACR,CAAM;AAAA,EAAA,GAGHiB,IAAQX;AAAA,IACZ,CAACC,GAAcW,GAAeV,MAAyCR,EAAO,MAAMO,GAAMW,GAAOV,CAAU;AAAA,IAC3G,CAACR,CAAM;AAAA,EAAA,GAGHmB,IAAYb;AAAA,IAChB,CAACC,GAAcW,GAAeV,MAAyCR,EAAO,UAAUO,GAAMW,GAAOV,CAAU;AAAA,IAC/G,CAACR,CAAM;AAAA,EAAA,GAGHoB,IAAgBd;AAAA,IACpB,CAACC,GAAcS,GAAeR,MAC5BR,EAAO,cAAcO,GAAMS,GAAOR,CAAU;AAAA,IAC9C,CAACR,CAAM;AAAA,EAAA,GAGHqB,IAAWf;AAAA,IACf,CAAIC,GAAce,GAA4BR,MAA+Bd,EAAO,SAASO,GAAMe,GAAIR,CAAO;AAAA,IAC9G,CAACd,CAAM;AAAA,EAAA;AAGT,SAAOuB;AAAA,IACL,OAAO,EAAE,YAAAlB,GAAY,YAAAK,GAAY,WAAAG,GAAW,UAAAQ,GAAU,SAAAN,GAAS,OAAAE,GAAO,WAAAE,GAAW,eAAAC;IACjF,CAACf,GAAYK,GAAYG,GAAWQ,GAAUN,GAASE,GAAOE,GAAWC,CAAa;AAAA,EAAA;AAE1F;"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./telemetry/events.cjs"),n=require("./telemetry/context.cjs"),a=require("./telemetry/facade.cjs"),r=require("./telemetry/telemetry-provider.cjs"),t=require("./telemetry/use-tetra-events.cjs"),s=require("./telemetry/error-boundary.cjs"),T=require("./telemetry/global-handlers.cjs"),l=require("@tetrascience-npm/request/telemetry");exports.MAX_COMPONENT_STACK_LENGTH=o.MAX_COMPONENT_STACK_LENGTH;exports.TelemetryEvent=o.TelemetryEvent;exports.TelemetryContext=n.TelemetryContext;exports.NOOP_TELEMETRY=a.NOOP_TELEMETRY;exports.createTelemetryFacade=a.createTelemetryFacade;exports.TelemetryProvider=r.TelemetryProvider;exports.readHostFlag=r.readHostFlag;exports.resolveArtifact=r.resolveArtifact;exports.__resetTelemetryWarningForTests=t.__resetTelemetryWarningForTests;exports.useTelemetryClient=t.useTelemetryClient;exports.useTetraEvents=t.useTetraEvents;exports.TSErrorBoundary=s.TSErrorBoundary;exports.installGlobalErrorHandlers=T.installGlobalErrorHandlers;Object.keys(l).forEach(e=>{e!=="default"&&!Object.prototype.hasOwnProperty.call(exports,e)&&Object.defineProperty(exports,e,{enumerable:!0,get:()=>l[e]})});
2
+ //# sourceMappingURL=telemetry.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ import { MAX_COMPONENT_STACK_LENGTH as t, TelemetryEvent as o } from "./telemetry/events.js";
2
+ import { TelemetryContext as m } from "./telemetry/context.js";
3
+ import { NOOP_TELEMETRY as a, createTelemetryFacade as s } from "./telemetry/facade.js";
4
+ import { TelemetryProvider as n, readHostFlag as x, resolveArtifact as E } from "./telemetry/telemetry-provider.js";
5
+ import { __resetTelemetryWarningForTests as y, useTelemetryClient as _, useTetraEvents as d } from "./telemetry/use-tetra-events.js";
6
+ import { TSErrorBoundary as v } from "./telemetry/error-boundary.js";
7
+ import { installGlobalErrorHandlers as N } from "./telemetry/global-handlers.js";
8
+ export * from "@tetrascience-npm/request/telemetry";
9
+ export {
10
+ t as MAX_COMPONENT_STACK_LENGTH,
11
+ a as NOOP_TELEMETRY,
12
+ v as TSErrorBoundary,
13
+ m as TelemetryContext,
14
+ o as TelemetryEvent,
15
+ n as TelemetryProvider,
16
+ y as __resetTelemetryWarningForTests,
17
+ s as createTelemetryFacade,
18
+ N as installGlobalErrorHandlers,
19
+ x as readHostFlag,
20
+ E as resolveArtifact,
21
+ _ as useTelemetryClient,
22
+ d as useTetraEvents
23
+ };
24
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tetrascience-npm/tetrascience-react-ui",
3
- "version": "1.0.0-beta.135.1",
3
+ "version": "1.0.0-beta.136.1",
4
4
  "description": "TetraScience React UI",
5
5
  "type": "module",
6
6
  "author": "TetraScience",
@@ -30,6 +30,16 @@
30
30
  "default": "./dist/index.cjs"
31
31
  }
32
32
  },
33
+ "./telemetry": {
34
+ "import": {
35
+ "types": "./dist/telemetry/index.d.ts",
36
+ "default": "./dist/telemetry.js"
37
+ },
38
+ "require": {
39
+ "types": "./dist/telemetry/index.d.ts",
40
+ "default": "./dist/telemetry.cjs"
41
+ }
42
+ },
33
43
  "./server": {
34
44
  "import": {
35
45
  "types": "./dist/server/index.d.ts",
@@ -200,7 +210,9 @@
200
210
  "lint:fix": "eslint . --fix --max-warnings 0",
201
211
  "typecheck": "tsc --noEmit",
202
212
  "format": "prettier --write .",
203
- "format:check": "prettier --check ."
213
+ "format:check": "prettier --check .",
214
+ "test:e2e": "vitest run --config vitest.e2e.config.ts",
215
+ "typecheck:e2e": "tsc --noEmit -p tsconfig.e2e.json"
204
216
  },
205
217
  "dependencies": {
206
218
  "@aws-sdk/client-cloudwatch-logs": ">=3.0.0 <4.0.0",
@@ -250,6 +262,7 @@
250
262
  "@rdkit/rdkit": "2025.3.4-1.0.0",
251
263
  "@streamdown/math": "^1.0.2",
252
264
  "@streamdown/mermaid": "^1.0.2",
265
+ "@tetrascience-npm/request": ">=0.4.0",
253
266
  "plotly.js-dist": "^2.35.2",
254
267
  "react": "^19.0.0",
255
268
  "react-dom": "^19.0.0",
@@ -280,6 +293,9 @@
280
293
  },
281
294
  "devDependencies": {
282
295
  "@aws-sdk/client-athena": "^3.975.0",
296
+ "@aws-sdk/client-cloudwatch": "^3.699.0",
297
+ "@aws-sdk/client-cloudwatch-logs": "^3.699.0",
298
+ "@aws-sdk/client-ssm": "^3.699.0",
283
299
  "@commitlint/cli": "^20.5.0",
284
300
  "@commitlint/config-conventional": "^20.5.0",
285
301
  "@databricks/sql": "^1.12.0",
@@ -303,6 +319,7 @@
303
319
  "@streamdown/math": "^1.0.2",
304
320
  "@streamdown/mermaid": "^1.0.2",
305
321
  "@tailwindcss/vite": "^4.2.2",
322
+ "@tetrascience-npm/request": "0.4.0",
306
323
  "@types/eslint-plugin-jsx-a11y": "^6",
307
324
  "@types/jsdom": "^27",
308
325
  "@types/node": "^25.3.3",