@tetrascience-npm/tetrascience-react-ui 1.0.0-beta.134.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.
- package/dist/components/ui/text.d.ts +63 -0
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +263 -260
- package/dist/index.js.map +1 -1
- package/dist/index.tailwind.css +1 -1
- package/dist/telemetry/context.cjs +2 -0
- package/dist/telemetry/context.cjs.map +1 -0
- package/dist/telemetry/context.d.ts +6 -0
- package/dist/telemetry/context.js +6 -0
- package/dist/telemetry/context.js.map +1 -0
- package/dist/telemetry/error-boundary.cjs +2 -0
- package/dist/telemetry/error-boundary.cjs.map +1 -0
- package/dist/telemetry/error-boundary.d.ts +39 -0
- package/dist/telemetry/error-boundary.js +33 -0
- package/dist/telemetry/error-boundary.js.map +1 -0
- package/dist/telemetry/events.cjs +2 -0
- package/dist/telemetry/events.cjs.map +1 -0
- package/dist/telemetry/events.d.ts +15 -0
- package/dist/telemetry/events.js +13 -0
- package/dist/telemetry/events.js.map +1 -0
- package/dist/telemetry/facade.cjs +2 -0
- package/dist/telemetry/facade.cjs.map +1 -0
- package/dist/telemetry/facade.d.ts +15 -0
- package/dist/telemetry/facade.js +86 -0
- package/dist/telemetry/facade.js.map +1 -0
- package/dist/telemetry/global-handlers.cjs +2 -0
- package/dist/telemetry/global-handlers.cjs.map +1 -0
- package/dist/telemetry/global-handlers.d.ts +25 -0
- package/dist/telemetry/global-handlers.js +31 -0
- package/dist/telemetry/global-handlers.js.map +1 -0
- package/dist/telemetry/index.d.ts +27 -0
- package/dist/telemetry/telemetry-provider.cjs +2 -0
- package/dist/telemetry/telemetry-provider.cjs.map +1 -0
- package/dist/telemetry/telemetry-provider.d.ts +36 -0
- package/dist/telemetry/telemetry-provider.js +65 -0
- package/dist/telemetry/telemetry-provider.js.map +1 -0
- package/dist/telemetry/types.d.ts +140 -0
- package/dist/telemetry/use-tetra-events.cjs +2 -0
- package/dist/telemetry/use-tetra-events.cjs.map +1 -0
- package/dist/telemetry/use-tetra-events.d.ts +19 -0
- package/dist/telemetry/use-tetra-events.js +55 -0
- package/dist/telemetry/use-tetra-events.js.map +1 -0
- package/dist/telemetry.cjs +2 -0
- package/dist/telemetry.cjs.map +1 -0
- package/dist/telemetry.js +24 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/ui/text.cjs +2 -0
- package/dist/ui/text.cjs.map +1 -0
- package/dist/ui/text.js +88 -0
- package/dist/ui/text.js.map +1 -0
- package/package.json +19 -2
|
@@ -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 @@
|
|
|
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;"}
|