@interfere/react 0.0.1 → 0.0.2-alpha.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/client.d.mts +15 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +75 -0
- package/dist/client.mjs.map +1 -0
- package/dist/core/events/event-registry.d.mts +23 -0
- package/dist/core/events/event-registry.d.mts.map +1 -0
- package/dist/core/events/event-registry.mjs +32 -0
- package/dist/core/events/event-registry.mjs.map +1 -0
- package/dist/core/events/plugin-event-types.d.mts +92 -0
- package/dist/core/events/plugin-event-types.d.mts.map +1 -0
- package/dist/core/events/plugin-event-types.mjs +25 -0
- package/dist/core/events/plugin-event-types.mjs.map +1 -0
- package/dist/core/plugins/dom-utils.d.mts +9 -0
- package/dist/core/plugins/dom-utils.d.mts.map +1 -0
- package/dist/core/plugins/dom-utils.mjs +25 -0
- package/dist/core/plugins/dom-utils.mjs.map +1 -0
- package/dist/core/plugins/impl/ai-summary.d.mts +6 -0
- package/dist/core/plugins/impl/ai-summary.d.mts.map +1 -0
- package/dist/core/plugins/impl/ai-summary.mjs +122 -0
- package/dist/core/plugins/impl/ai-summary.mjs.map +1 -0
- package/dist/core/plugins/impl/errors.d.mts +9 -0
- package/dist/core/plugins/impl/errors.d.mts.map +1 -0
- package/dist/core/plugins/impl/errors.mjs +153 -0
- package/dist/core/plugins/impl/errors.mjs.map +1 -0
- package/dist/core/plugins/impl/page-events.d.mts +15 -0
- package/dist/core/plugins/impl/page-events.d.mts.map +1 -0
- package/dist/core/plugins/impl/page-events.mjs +131 -0
- package/dist/core/plugins/impl/page-events.mjs.map +1 -0
- package/dist/core/plugins/impl/rage-click.d.mts +6 -0
- package/dist/core/plugins/impl/rage-click.d.mts.map +1 -0
- package/dist/core/plugins/impl/rage-click.mjs +53 -0
- package/dist/core/plugins/impl/rage-click.mjs.map +1 -0
- package/dist/core/plugins/impl/replay.d.mts +9 -0
- package/dist/core/plugins/impl/replay.d.mts.map +1 -0
- package/dist/core/plugins/impl/replay.mjs +144 -0
- package/dist/core/plugins/impl/replay.mjs.map +1 -0
- package/dist/core/plugins/impl/server-tracing.d.mts +7 -0
- package/dist/core/plugins/impl/server-tracing.d.mts.map +1 -0
- package/dist/core/plugins/impl/server-tracing.mjs +160 -0
- package/dist/core/plugins/impl/server-tracing.mjs.map +1 -0
- package/dist/core/plugins/plugin-event-system.d.mts +47 -0
- package/dist/core/plugins/plugin-event-system.d.mts.map +1 -0
- package/dist/core/plugins/plugin-event-system.mjs +75 -0
- package/dist/core/plugins/plugin-event-system.mjs.map +1 -0
- package/dist/core/plugins/plugin-loader.d.mts +22 -0
- package/dist/core/plugins/plugin-loader.d.mts.map +1 -0
- package/dist/core/plugins/plugin-loader.mjs +142 -0
- package/dist/core/plugins/plugin-loader.mjs.map +1 -0
- package/dist/core/runtime/config.d.mts +14 -0
- package/dist/core/runtime/config.d.mts.map +1 -0
- package/dist/core/runtime/config.mjs +39 -0
- package/dist/core/runtime/config.mjs.map +1 -0
- package/dist/core/runtime/context.d.mts +25 -0
- package/dist/core/runtime/context.d.mts.map +1 -0
- package/dist/core/runtime/context.mjs +48 -0
- package/dist/core/runtime/context.mjs.map +1 -0
- package/dist/core/runtime/ingest-target.d.mts +10 -0
- package/dist/core/runtime/ingest-target.d.mts.map +1 -0
- package/dist/core/runtime/ingest-target.mjs +15 -0
- package/dist/core/runtime/ingest-target.mjs.map +1 -0
- package/dist/core/schemas.d.mts +85 -0
- package/dist/core/schemas.d.mts.map +1 -0
- package/dist/core/schemas.mjs +1 -0
- package/dist/effect/build-envelope.d.mts +9 -0
- package/dist/effect/build-envelope.d.mts.map +1 -0
- package/dist/effect/build-envelope.mjs +29 -0
- package/dist/effect/build-envelope.mjs.map +1 -0
- package/dist/effect/errors.d.mts +36 -0
- package/dist/effect/errors.d.mts.map +1 -0
- package/dist/effect/errors.mjs +10 -0
- package/dist/effect/errors.mjs.map +1 -0
- package/dist/effect/layers/config.layer.d.mts +13 -0
- package/dist/effect/layers/config.layer.d.mts.map +1 -0
- package/dist/effect/layers/config.layer.mjs +21 -0
- package/dist/effect/layers/config.layer.mjs.map +1 -0
- package/dist/effect/layers/context.layer.d.mts +12 -0
- package/dist/effect/layers/context.layer.d.mts.map +1 -0
- package/dist/effect/layers/context.layer.mjs +14 -0
- package/dist/effect/layers/context.layer.mjs.map +1 -0
- package/dist/effect/layers/http.layer.d.mts +21 -0
- package/dist/effect/layers/http.layer.d.mts.map +1 -0
- package/dist/effect/layers/http.layer.mjs +113 -0
- package/dist/effect/layers/http.layer.mjs.map +1 -0
- package/dist/effect/layers/queue.layer.d.mts +30 -0
- package/dist/effect/layers/queue.layer.d.mts.map +1 -0
- package/dist/effect/layers/queue.layer.mjs +232 -0
- package/dist/effect/layers/queue.layer.mjs.map +1 -0
- package/dist/effect/layers/session.layer.d.mts +26 -0
- package/dist/effect/layers/session.layer.d.mts.map +1 -0
- package/dist/effect/layers/session.layer.mjs +126 -0
- package/dist/effect/layers/session.layer.mjs.map +1 -0
- package/dist/effect/layers/storage.layer.d.mts +19 -0
- package/dist/effect/layers/storage.layer.d.mts.map +1 -0
- package/dist/effect/layers/storage.layer.mjs +200 -0
- package/dist/effect/layers/storage.layer.mjs.map +1 -0
- package/dist/effect/layers/tracer.layer.d.mts +9 -0
- package/dist/effect/layers/tracer.layer.d.mts.map +1 -0
- package/dist/effect/layers/tracer.layer.mjs +11 -0
- package/dist/effect/layers/tracer.layer.mjs.map +1 -0
- package/dist/effect/runtime-services.d.mts +22 -0
- package/dist/effect/runtime-services.d.mts.map +1 -0
- package/dist/effect/runtime-services.mjs +76 -0
- package/dist/effect/runtime-services.mjs.map +1 -0
- package/dist/effect/tags.d.mts +50 -0
- package/dist/effect/tags.d.mts.map +1 -0
- package/dist/effect/tags.mjs +7 -0
- package/dist/effect/tags.mjs.map +1 -0
- package/dist/hooks/use-runtime-and-plugins.d.mts +7 -0
- package/dist/hooks/use-runtime-and-plugins.d.mts.map +1 -0
- package/dist/hooks/use-runtime-and-plugins.mjs +153 -0
- package/dist/hooks/use-runtime-and-plugins.mjs.map +1 -0
- package/dist/hooks/use-session.d.mts +40 -0
- package/dist/hooks/use-session.d.mts.map +1 -0
- package/dist/hooks/use-session.mjs +96 -0
- package/dist/hooks/use-session.mjs.map +1 -0
- package/dist/package.mjs +100 -0
- package/dist/package.mjs.map +1 -0
- package/dist/provider.d.mts +17 -0
- package/dist/provider.d.mts.map +1 -0
- package/dist/provider.mjs +26 -0
- package/dist/provider.mjs.map +1 -0
- package/dist/server/auth.d.mts +11 -0
- package/dist/server/auth.d.mts.map +1 -0
- package/dist/server/auth.mjs +36 -0
- package/dist/server/auth.mjs.map +1 -0
- package/dist/server/capture.d.mts +18 -0
- package/dist/server/capture.d.mts.map +1 -0
- package/dist/server/capture.mjs +105 -0
- package/dist/server/capture.mjs.map +1 -0
- package/package.json +60 -27
- package/dist/__tests__/client.test.d.ts +0 -2
- package/dist/__tests__/client.test.d.ts.map +0 -1
- package/dist/__tests__/client.test.js +0 -447
- package/dist/__tests__/client.test.js.map +0 -1
- package/dist/__tests__/lib/core/error-handlers.test.d.ts +0 -2
- package/dist/__tests__/lib/core/error-handlers.test.d.ts.map +0 -1
- package/dist/__tests__/lib/core/error-handlers.test.js +0 -596
- package/dist/__tests__/lib/core/error-handlers.test.js.map +0 -1
- package/dist/__tests__/lib/core/event-queue.test.d.ts +0 -2
- package/dist/__tests__/lib/core/event-queue.test.d.ts.map +0 -1
- package/dist/__tests__/lib/core/event-queue.test.js +0 -290
- package/dist/__tests__/lib/core/event-queue.test.js.map +0 -1
- package/dist/__tests__/lib/core/runtime.test.d.ts +0 -2
- package/dist/__tests__/lib/core/runtime.test.d.ts.map +0 -1
- package/dist/__tests__/lib/core/runtime.test.js +0 -133
- package/dist/__tests__/lib/core/runtime.test.js.map +0 -1
- package/dist/__tests__/lib/core/session-manager.test.d.ts +0 -2
- package/dist/__tests__/lib/core/session-manager.test.d.ts.map +0 -1
- package/dist/__tests__/lib/core/session-manager.test.js +0 -356
- package/dist/__tests__/lib/core/session-manager.test.js.map +0 -1
- package/dist/__tests__/provider.test.d.ts +0 -2
- package/dist/__tests__/provider.test.d.ts.map +0 -1
- package/dist/__tests__/provider.test.js +0 -143
- package/dist/__tests__/provider.test.js.map +0 -1
- package/dist/client.d.ts +0 -78
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -219
- package/dist/client.js.map +0 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -5
- package/dist/index.js.map +0 -1
- package/dist/lib/core/error-handlers.d.ts +0 -14
- package/dist/lib/core/error-handlers.d.ts.map +0 -1
- package/dist/lib/core/error-handlers.js +0 -191
- package/dist/lib/core/error-handlers.js.map +0 -1
- package/dist/lib/core/event-queue.d.ts +0 -90
- package/dist/lib/core/event-queue.d.ts.map +0 -1
- package/dist/lib/core/event-queue.js +0 -286
- package/dist/lib/core/event-queue.js.map +0 -1
- package/dist/lib/core/runtime.d.ts +0 -7
- package/dist/lib/core/runtime.d.ts.map +0 -1
- package/dist/lib/core/runtime.js +0 -16
- package/dist/lib/core/runtime.js.map +0 -1
- package/dist/lib/core/session-manager.d.ts +0 -96
- package/dist/lib/core/session-manager.d.ts.map +0 -1
- package/dist/lib/core/session-manager.js +0 -431
- package/dist/lib/core/session-manager.js.map +0 -1
- package/dist/lib/persistence/storage.d.ts +0 -5
- package/dist/lib/persistence/storage.d.ts.map +0 -1
- package/dist/lib/persistence/storage.js +0 -67
- package/dist/lib/persistence/storage.js.map +0 -1
- package/dist/lib/session/rage-click.d.ts +0 -2
- package/dist/lib/session/rage-click.d.ts.map +0 -1
- package/dist/lib/session/rage-click.js +0 -51
- package/dist/lib/session/rage-click.js.map +0 -1
- package/dist/lib/session/replay.d.ts +0 -3
- package/dist/lib/session/replay.d.ts.map +0 -1
- package/dist/lib/session/replay.js +0 -106
- package/dist/lib/session/replay.js.map +0 -1
- package/dist/lib/session/session-summary.d.ts +0 -3
- package/dist/lib/session/session-summary.d.ts.map +0 -1
- package/dist/lib/session/session-summary.js +0 -79
- package/dist/lib/session/session-summary.js.map +0 -1
- package/dist/provider.d.ts +0 -76
- package/dist/provider.d.ts.map +0 -1
- package/dist/provider.js +0 -138
- package/dist/provider.js.map +0 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/hooks/use-session.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* React hook to get the current session ID.
|
|
4
|
+
* Automatically handles cleanup on unmount.
|
|
5
|
+
*
|
|
6
|
+
* @returns Object with sessionId (string | null) and loading state
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* function MyComponent() {
|
|
10
|
+
* const { sessionId, loading, error } = useSession();
|
|
11
|
+
*
|
|
12
|
+
* if (loading) return <div>Loading session...</div>;
|
|
13
|
+
* if (error) return <div>Error: {error.message}</div>;
|
|
14
|
+
*
|
|
15
|
+
* return <div>Session ID: {sessionId}</div>;
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
declare function useSession(): {
|
|
19
|
+
sessionId: string | null;
|
|
20
|
+
loading: boolean;
|
|
21
|
+
error: Error | null;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* React hook to execute a callback when the session is ready.
|
|
25
|
+
* Automatically handles cleanup on unmount.
|
|
26
|
+
*
|
|
27
|
+
* @param callback Function to call with the session ID
|
|
28
|
+
* @param deps Dependency array for the callback
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* function MyComponent() {
|
|
32
|
+
* useSessionReady((sessionId) => {
|
|
33
|
+
* console.log('Session is ready:', sessionId);
|
|
34
|
+
* // Send analytics event, initialize tracking, etc.
|
|
35
|
+
* }, []);
|
|
36
|
+
* }
|
|
37
|
+
*/
|
|
38
|
+
declare function useSessionReady(callback: (sessionId: string) => void, deps?: React.DependencyList): void;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { useSession, useSessionReady };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-session.d.mts","names":[],"sources":["../../src/hooks/use-session.ts"],"sourcesContent":[],"mappings":";;AAsBA;AA2EA;;;;;;;;;;;;;;iBA3EgB,UAAA,CAAA;;;SAAU;;;;;;;;;;;;;;;;;iBA2EV,eAAA,+CAER,KAAA,CAAM"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { SessionServiceTag } from "../effect/layers/session.layer.mjs";
|
|
2
|
+
import { runWithSDK } from "../effect/runtime-services.mjs";
|
|
3
|
+
import { Effect, Option } from "effect";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
|
|
6
|
+
//#region src/hooks/use-session.ts
|
|
7
|
+
/**
|
|
8
|
+
* React hook to get the current session ID.
|
|
9
|
+
* Automatically handles cleanup on unmount.
|
|
10
|
+
*
|
|
11
|
+
* @returns Object with sessionId (string | null) and loading state
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* function MyComponent() {
|
|
15
|
+
* const { sessionId, loading, error } = useSession();
|
|
16
|
+
*
|
|
17
|
+
* if (loading) return <div>Loading session...</div>;
|
|
18
|
+
* if (error) return <div>Error: {error.message}</div>;
|
|
19
|
+
*
|
|
20
|
+
* return <div>Session ID: {sessionId}</div>;
|
|
21
|
+
* }
|
|
22
|
+
*/
|
|
23
|
+
function useSession() {
|
|
24
|
+
const [sessionId, setSessionId] = useState(null);
|
|
25
|
+
const [loading, setLoading] = useState(true);
|
|
26
|
+
const [error, setError] = useState(null);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
runWithSDK(Effect.gen(function* () {
|
|
29
|
+
const sidOpt = yield* (yield* SessionServiceTag).getSessionId();
|
|
30
|
+
if (Option.isSome(sidOpt)) {
|
|
31
|
+
setSessionId(sidOpt.value);
|
|
32
|
+
setLoading(false);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
})).then((has) => {
|
|
37
|
+
if (has) return;
|
|
38
|
+
});
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
runWithSDK(Effect.gen(function* () {
|
|
41
|
+
return yield* (yield* SessionServiceTag).whenSessionReady({
|
|
42
|
+
signal: controller.signal,
|
|
43
|
+
timeout: 3e4
|
|
44
|
+
});
|
|
45
|
+
})).then((id) => {
|
|
46
|
+
setSessionId(id);
|
|
47
|
+
setLoading(false);
|
|
48
|
+
}).catch((err) => {
|
|
49
|
+
if (!controller.signal.aborted) {
|
|
50
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
51
|
+
setLoading(false);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return () => {
|
|
55
|
+
controller.abort();
|
|
56
|
+
};
|
|
57
|
+
}, []);
|
|
58
|
+
return {
|
|
59
|
+
sessionId,
|
|
60
|
+
loading,
|
|
61
|
+
error
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* React hook to execute a callback when the session is ready.
|
|
66
|
+
* Automatically handles cleanup on unmount.
|
|
67
|
+
*
|
|
68
|
+
* @param callback Function to call with the session ID
|
|
69
|
+
* @param deps Dependency array for the callback
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* function MyComponent() {
|
|
73
|
+
* useSessionReady((sessionId) => {
|
|
74
|
+
* console.log('Session is ready:', sessionId);
|
|
75
|
+
* // Send analytics event, initialize tracking, etc.
|
|
76
|
+
* }, []);
|
|
77
|
+
* }
|
|
78
|
+
*/
|
|
79
|
+
function useSessionReady(callback, deps = []) {
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
const controller = new AbortController();
|
|
82
|
+
runWithSDK(Effect.gen(function* () {
|
|
83
|
+
return yield* (yield* SessionServiceTag).whenSessionReady({ signal: controller.signal });
|
|
84
|
+
})).then((sessionId) => {
|
|
85
|
+
if (!controller.signal.aborted) callback(sessionId);
|
|
86
|
+
}).catch((err) => {
|
|
87
|
+
if (!controller.signal.aborted) runWithSDK(Effect.logError("Failed to get session", { error: String(err) }));
|
|
88
|
+
});
|
|
89
|
+
return () => {
|
|
90
|
+
controller.abort();
|
|
91
|
+
};
|
|
92
|
+
}, deps);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
//#endregion
|
|
96
|
+
export { useSession, useSessionReady };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-session.mjs","names":[],"sources":["../../src/hooks/use-session.ts"],"sourcesContent":["import { Effect, Option } from \"effect\";\nimport { useEffect, useState } from \"react\";\n\nimport { SessionServiceTag } from \"../effect/layers/session.layer.js\";\nimport { runWithSDK } from \"../effect/runtime-services.js\";\n\n/**\n * React hook to get the current session ID.\n * Automatically handles cleanup on unmount.\n *\n * @returns Object with sessionId (string | null) and loading state\n *\n * @example\n * function MyComponent() {\n * const { sessionId, loading, error } = useSession();\n *\n * if (loading) return <div>Loading session...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return <div>Session ID: {sessionId}</div>;\n * }\n */\nexport function useSession() {\n const [sessionId, setSessionId] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n // Check if session already exists\n // Try to read session id from SessionService\n runWithSDK(\n Effect.gen(function* () {\n const session = yield* SessionServiceTag;\n const sidOpt = yield* session.getSessionId();\n if (Option.isSome(sidOpt)) {\n setSessionId(sidOpt.value);\n setLoading(false);\n return true as const;\n }\n return false as const;\n })\n ).then((has) => {\n if (has) {\n return;\n }\n });\n\n // Create AbortController for cleanup\n const controller = new AbortController();\n\n // Wait for session to be ready\n runWithSDK(\n Effect.gen(function* () {\n const session = yield* SessionServiceTag;\n const sid = yield* session.whenSessionReady({\n signal: controller.signal,\n timeout: 30_000,\n });\n return sid;\n })\n )\n .then((id) => {\n setSessionId(id);\n setLoading(false);\n })\n .catch((err) => {\n // Only set error if not aborted (aborted = component unmounted)\n if (!controller.signal.aborted) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setLoading(false);\n }\n });\n\n // Cleanup function - abort the polling on unmount\n return () => {\n controller.abort();\n };\n }, []);\n\n return { sessionId, loading, error };\n}\n\n/**\n * React hook to execute a callback when the session is ready.\n * Automatically handles cleanup on unmount.\n *\n * @param callback Function to call with the session ID\n * @param deps Dependency array for the callback\n *\n * @example\n * function MyComponent() {\n * useSessionReady((sessionId) => {\n * console.log('Session is ready:', sessionId);\n * // Send analytics event, initialize tracking, etc.\n * }, []);\n * }\n */\nexport function useSessionReady(\n callback: (sessionId: string) => void,\n deps: React.DependencyList = []\n) {\n useEffect(() => {\n const controller = new AbortController();\n\n runWithSDK(\n Effect.gen(function* () {\n const session = yield* SessionServiceTag;\n const sid = yield* session.whenSessionReady({\n signal: controller.signal,\n });\n return sid;\n })\n )\n .then((sessionId: string) => {\n if (!controller.signal.aborted) {\n callback(sessionId);\n }\n })\n .catch((err: unknown) => {\n // Silently ignore abort errors (component unmounted)\n if (!controller.signal.aborted) {\n // biome-ignore lint/complexity/noVoid: necessary to avoid blocking\n void runWithSDK(\n Effect.logError(\"Failed to get session\", {\n error: String(err),\n })\n );\n }\n });\n\n return () => {\n controller.abort();\n };\n // biome-ignore lint/correctness/useExhaustiveDependencies: we want to pass the deps to the effect\n }, deps);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,aAAa;CAC3B,MAAM,CAAC,WAAW,gBAAgB,SAAwB,KAAK;CAC/D,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;AAEtD,iBAAgB;AAGd,aACE,OAAO,IAAI,aAAa;GAEtB,MAAM,SAAS,QADC,OAAO,mBACO,cAAc;AAC5C,OAAI,OAAO,OAAO,OAAO,EAAE;AACzB,iBAAa,OAAO,MAAM;AAC1B,eAAW,MAAM;AACjB,WAAO;;AAET,UAAO;IACP,CACH,CAAC,MAAM,QAAQ;AACd,OAAI,IACF;IAEF;EAGF,MAAM,aAAa,IAAI,iBAAiB;AAGxC,aACE,OAAO,IAAI,aAAa;AAMtB,UAJY,QADI,OAAO,mBACI,iBAAiB;IAC1C,QAAQ,WAAW;IACnB,SAAS;IACV,CAAC;IAEF,CACH,CACE,MAAM,OAAO;AACZ,gBAAa,GAAG;AAChB,cAAW,MAAM;IACjB,CACD,OAAO,QAAQ;AAEd,OAAI,CAAC,WAAW,OAAO,SAAS;AAC9B,aAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC,CAAC;AAC7D,eAAW,MAAM;;IAEnB;AAGJ,eAAa;AACX,cAAW,OAAO;;IAEnB,EAAE,CAAC;AAEN,QAAO;EAAE;EAAW;EAAS;EAAO;;;;;;;;;;;;;;;;;AAkBtC,SAAgB,gBACd,UACA,OAA6B,EAAE,EAC/B;AACA,iBAAgB;EACd,MAAM,aAAa,IAAI,iBAAiB;AAExC,aACE,OAAO,IAAI,aAAa;AAKtB,UAHY,QADI,OAAO,mBACI,iBAAiB,EAC1C,QAAQ,WAAW,QACpB,CAAC;IAEF,CACH,CACE,MAAM,cAAsB;AAC3B,OAAI,CAAC,WAAW,OAAO,QACrB,UAAS,UAAU;IAErB,CACD,OAAO,QAAiB;AAEvB,OAAI,CAAC,WAAW,OAAO,QAErB,CAAK,WACH,OAAO,SAAS,yBAAyB,EACvC,OAAO,OAAO,IAAI,EACnB,CAAC,CACH;IAEH;AAEJ,eAAa;AACX,cAAW,OAAO;;IAGnB,KAAK"}
|
package/dist/package.mjs
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
//#region package.json
|
|
2
|
+
var name = "@interfere/react";
|
|
3
|
+
var version = "0.0.2-alpha.1";
|
|
4
|
+
var package_default = {
|
|
5
|
+
name,
|
|
6
|
+
version,
|
|
7
|
+
license: "MIT",
|
|
8
|
+
description: "Build apps that never break.",
|
|
9
|
+
keywords: [
|
|
10
|
+
"observability",
|
|
11
|
+
"typescript",
|
|
12
|
+
"react",
|
|
13
|
+
"logging",
|
|
14
|
+
"error-tracking",
|
|
15
|
+
"session-replay"
|
|
16
|
+
],
|
|
17
|
+
homepage: "https://interfere.com",
|
|
18
|
+
bugs: { "url": "mailto:support@interfere.com" },
|
|
19
|
+
author: "Interfere <support@interfere.com> (https://interfere.com)",
|
|
20
|
+
files: ["dist"],
|
|
21
|
+
type: "module",
|
|
22
|
+
main: "./dist/index.mjs",
|
|
23
|
+
types: "./dist/index.d.mts",
|
|
24
|
+
typesVersions: { "*": {
|
|
25
|
+
"server/*": ["dist/server/*.d.mts"],
|
|
26
|
+
"*": ["dist/*.d.mts"]
|
|
27
|
+
} },
|
|
28
|
+
exports: {
|
|
29
|
+
"./provider": {
|
|
30
|
+
"@source": "./src/provider.tsx",
|
|
31
|
+
"types": "./dist/provider.d.mts",
|
|
32
|
+
"default": "./dist/provider.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./server/*": {
|
|
35
|
+
"@source": "./src/server/*.ts",
|
|
36
|
+
"types": "./dist/server/*.d.mts",
|
|
37
|
+
"default": "./dist/server/*.mjs"
|
|
38
|
+
},
|
|
39
|
+
"./core/runtime/*": {
|
|
40
|
+
"@source": "./src/core/runtime/*.ts",
|
|
41
|
+
"types": "./dist/core/runtime/*.d.mts",
|
|
42
|
+
"default": "./dist/core/runtime/*.mjs"
|
|
43
|
+
},
|
|
44
|
+
"./core/*": {
|
|
45
|
+
"@source": "./src/core/*.ts",
|
|
46
|
+
"types": "./dist/core/*.d.mts",
|
|
47
|
+
"default": "./dist/core/*.mjs"
|
|
48
|
+
},
|
|
49
|
+
"./*": {
|
|
50
|
+
"@source": "./src/*.ts",
|
|
51
|
+
"types": "./dist/*.d.mts",
|
|
52
|
+
"default": "./dist/*.mjs"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
sideEffects: false,
|
|
56
|
+
publishConfig: { "access": "public" },
|
|
57
|
+
scripts: {
|
|
58
|
+
"build": "tsdown",
|
|
59
|
+
"dev": "tsdown --watch",
|
|
60
|
+
"typecheck": "tsc --noEmit --incremental",
|
|
61
|
+
"test": "vitest run --coverage"
|
|
62
|
+
},
|
|
63
|
+
dependencies: {
|
|
64
|
+
"@effect/platform": "catalog:",
|
|
65
|
+
"@interfere/constants": "workspace:*",
|
|
66
|
+
"@interfere/effect-utils": "workspace:*",
|
|
67
|
+
"@interfere/types": "workspace:*",
|
|
68
|
+
"@rrweb/packer": "catalog:",
|
|
69
|
+
"@ua-parser-js/pro-enterprise": "catalog:",
|
|
70
|
+
"effect": "catalog:",
|
|
71
|
+
"nanoid": "catalog:",
|
|
72
|
+
"rrweb": "catalog:",
|
|
73
|
+
"uuid": "catalog:",
|
|
74
|
+
"zod": "catalog:"
|
|
75
|
+
},
|
|
76
|
+
peerDependencies: {
|
|
77
|
+
"react": ">=18",
|
|
78
|
+
"react-dom": ">=18"
|
|
79
|
+
},
|
|
80
|
+
devDependencies: {
|
|
81
|
+
"@interfere/test-utils": "workspace:*",
|
|
82
|
+
"@interfere/typescript-config": "workspace:*",
|
|
83
|
+
"@interfere/vitest-config": "workspace:*",
|
|
84
|
+
"@rrweb/types": "catalog:",
|
|
85
|
+
"@types/node": "catalog:",
|
|
86
|
+
"@types/react": "catalog:",
|
|
87
|
+
"@types/react-dom": "catalog:",
|
|
88
|
+
"@vitest/coverage-v8": "catalog:",
|
|
89
|
+
"jsdom": "catalog:",
|
|
90
|
+
"msw": "catalog:",
|
|
91
|
+
"react": "catalog:",
|
|
92
|
+
"react-dom": "catalog:",
|
|
93
|
+
"tsdown": "catalog:",
|
|
94
|
+
"typescript": "catalog:",
|
|
95
|
+
"vitest": "catalog:"
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
//#endregion
|
|
100
|
+
export { package_default as default, name, version };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.mjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { InterfereProviderValue, Plugin } from "./core/schemas.mjs";
|
|
2
|
+
import { Config } from "@interfere/types/sdk/config";
|
|
3
|
+
import { PropsWithChildren, ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/provider.d.ts
|
|
6
|
+
interface InterfereProviderProps extends PropsWithChildren {
|
|
7
|
+
config: Config;
|
|
8
|
+
plugins?: Plugin[];
|
|
9
|
+
}
|
|
10
|
+
declare function InterfereProvider({
|
|
11
|
+
children,
|
|
12
|
+
config,
|
|
13
|
+
plugins
|
|
14
|
+
}: InterfereProviderProps): ReactNode;
|
|
15
|
+
declare function useInterfere(): InterfereProviderValue;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { InterfereProvider, InterfereProviderProps, useInterfere };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.mts","names":[],"sources":["../src/provider.tsx"],"sourcesContent":[],"mappings":";;;;;UAwBiB,sBAAA,SAA+B;UACtC;EADO,OAAA,CAAA,EAEL,MAFK,EAAA;;AAEL,iBAGI,iBAAA,CAHJ;EAAA,QAAA;EAAA,MAAA;EAAA;AAAA,CAAA,EAOT,sBAPS,CAAA,EAOgB,SAPhB;AAFoC,iBA2BhC,YAAA,CAAA,CA3BgC,EA2BhB,sBA3BgB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { capture } from "./client.mjs";
|
|
4
|
+
import { useRuntimeAndPlugins } from "./hooks/use-runtime-and-plugins.mjs";
|
|
5
|
+
import { createContext, useContext, useMemo } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
|
|
8
|
+
//#region src/provider.tsx
|
|
9
|
+
const InterfereContext = createContext({ capture });
|
|
10
|
+
function InterfereProvider({ children, config, plugins }) {
|
|
11
|
+
const pluginApis = useRuntimeAndPlugins(config, plugins);
|
|
12
|
+
const contextValue = useMemo(() => ({
|
|
13
|
+
capture,
|
|
14
|
+
...pluginApis
|
|
15
|
+
}), [pluginApis]);
|
|
16
|
+
return /* @__PURE__ */ jsx(InterfereContext.Provider, {
|
|
17
|
+
value: contextValue,
|
|
18
|
+
children
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function useInterfere() {
|
|
22
|
+
return useContext(InterfereContext);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
export { InterfereProvider, useInterfere };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.mjs","names":[],"sources":["../src/provider.tsx"],"sourcesContent":["\"use client\";\n\nimport type { Config } from \"@interfere/types/sdk/config\";\n\nimport {\n createContext,\n type PropsWithChildren,\n type ReactNode,\n useContext,\n useMemo,\n} from \"react\";\n\nimport { capture } from \"./client.js\";\nimport type { InterfereProviderValue, Plugin } from \"./core/schemas.js\";\nimport { useRuntimeAndPlugins } from \"./hooks/use-runtime-and-plugins.js\";\n\nconst interfereContextDefaultValue: InterfereProviderValue = {\n capture,\n};\n\nconst InterfereContext = createContext<InterfereProviderValue>(\n interfereContextDefaultValue\n);\n\nexport interface InterfereProviderProps extends PropsWithChildren {\n config: Config;\n plugins?: Plugin[];\n}\n\nexport function InterfereProvider({\n children,\n config,\n plugins,\n}: InterfereProviderProps): ReactNode {\n const pluginApis = useRuntimeAndPlugins(config, plugins);\n\n const contextValue = useMemo(\n () => ({\n capture,\n ...pluginApis,\n }),\n [pluginApis]\n );\n\n return (\n <InterfereContext.Provider value={contextValue}>\n {children}\n </InterfereContext.Provider>\n );\n}\n\nexport function useInterfere(): InterfereProviderValue {\n const context = useContext(InterfereContext);\n\n return context;\n}\n"],"mappings":";;;;;;;;AAoBA,MAAM,mBAAmB,cAJoC,EAC3D,SACD,CAIA;AAOD,SAAgB,kBAAkB,EAChC,UACA,QACA,WACoC;CACpC,MAAM,aAAa,qBAAqB,QAAQ,QAAQ;CAExD,MAAM,eAAe,eACZ;EACL;EACA,GAAG;EACJ,GACD,CAAC,WAAW,CACb;AAED,QACE,oBAAC,iBAAiB;EAAS,OAAO;EAC/B;GACyB;;AAIhC,SAAgB,eAAuC;AAGrD,QAFgB,WAAW,iBAAiB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/server/auth.d.ts
|
|
2
|
+
type FetchImpl = typeof fetch;
|
|
3
|
+
declare function exchangeSecretForPublicToken(apiUrl: string, secret: string, fetchImpl?: FetchImpl): Promise<string | null>;
|
|
4
|
+
declare function getPublicToken(params: {
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
secret: string;
|
|
7
|
+
cacheTtlMs?: number;
|
|
8
|
+
fetchImpl?: FetchImpl;
|
|
9
|
+
}): Promise<string | null>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { exchangeSecretForPublicToken, getPublicToken };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.mts","names":[],"sources":["../../src/server/auth.ts"],"sourcesContent":[],"mappings":";KAAK,SAAA,UAAmB;AAAnB,iBAMiB,4BAAA,CANO,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,SAAA,CAAA,EAShB,SATgB,CAAA,EAU1B,OAV0B,CAAA,MAAA,GAAA,IAAA,CAAA;AAMP,iBAkCA,cAAA,CAlC4B,MAAA,EAGrC;EA+BS,MAAA,EAAA,MAAA;;;cAIR;IACV"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/server/auth.ts
|
|
2
|
+
async function exchangeSecretForPublicToken(apiUrl, secret, fetchImpl = fetch) {
|
|
3
|
+
const res = await fetchImpl(`${apiUrl}/auth/exchange`, {
|
|
4
|
+
method: "POST",
|
|
5
|
+
headers: {
|
|
6
|
+
Authorization: `Bearer ${secret}`,
|
|
7
|
+
"Content-Type": "application/json"
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
if (!res.ok) return null;
|
|
11
|
+
const data = await res.json();
|
|
12
|
+
if (typeof data.publicToken === "string" && data.publicToken.length > 0) return data.publicToken;
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const tokenCache = /* @__PURE__ */ new Map();
|
|
16
|
+
function getCacheKey(apiUrl, secret) {
|
|
17
|
+
return `${apiUrl}::${secret}`;
|
|
18
|
+
}
|
|
19
|
+
async function getPublicToken(params) {
|
|
20
|
+
const { apiUrl, secret, cacheTtlMs = 36e5, fetchImpl = fetch } = params;
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
const cacheKey = getCacheKey(apiUrl, secret);
|
|
23
|
+
const cached = tokenCache.get(cacheKey);
|
|
24
|
+
if (cached && cached.expiresAt > now) return cached.token;
|
|
25
|
+
const token = await exchangeSecretForPublicToken(apiUrl, secret, fetchImpl);
|
|
26
|
+
if (!token) return null;
|
|
27
|
+
tokenCache.set(cacheKey, {
|
|
28
|
+
token,
|
|
29
|
+
expiresAt: now + cacheTtlMs
|
|
30
|
+
});
|
|
31
|
+
for (const [key, value] of tokenCache.entries()) if (value.expiresAt <= now) tokenCache.delete(key);
|
|
32
|
+
return token;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { exchangeSecretForPublicToken, getPublicToken };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.mjs","names":[],"sources":["../../src/server/auth.ts"],"sourcesContent":["type FetchImpl = typeof fetch;\n\ninterface ExchangeResponse {\n publicToken: string;\n}\n\nexport async function exchangeSecretForPublicToken(\n apiUrl: string,\n secret: string,\n fetchImpl: FetchImpl = fetch\n): Promise<string | null> {\n const res = await fetchImpl(`${apiUrl}/auth/exchange`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${secret}`,\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) {\n return null;\n }\n\n const data = (await res.json()) as Partial<ExchangeResponse>;\n if (typeof data.publicToken === \"string\" && data.publicToken.length > 0) {\n return data.publicToken;\n }\n\n return null;\n}\n\n// Use a Map to cache tokens keyed by apiUrl + secret to prevent token leakage between different configurations\nconst tokenCache = new Map<string, { token: string; expiresAt: number }>();\n\nfunction getCacheKey(apiUrl: string, secret: string): string {\n // Create a unique cache key from apiUrl and secret\n // Using a separator that's unlikely to appear in URLs or secrets\n return `${apiUrl}::${secret}`;\n}\n\nexport async function getPublicToken(params: {\n apiUrl: string;\n secret: string;\n cacheTtlMs?: number;\n fetchImpl?: FetchImpl;\n}): Promise<string | null> {\n const { apiUrl, secret, cacheTtlMs = 3_600_000, fetchImpl = fetch } = params;\n\n const now = Date.now();\n const cacheKey = getCacheKey(apiUrl, secret);\n\n const cached = tokenCache.get(cacheKey);\n if (cached && cached.expiresAt > now) {\n return cached.token;\n }\n\n const token = await exchangeSecretForPublicToken(apiUrl, secret, fetchImpl);\n\n if (!token) {\n return null;\n }\n\n tokenCache.set(cacheKey, { token, expiresAt: now + cacheTtlMs });\n\n // Clean up expired entries to prevent memory leak\n for (const [key, value] of tokenCache.entries()) {\n if (value.expiresAt <= now) {\n tokenCache.delete(key);\n }\n }\n\n return token;\n}\n"],"mappings":";AAMA,eAAsB,6BACpB,QACA,QACA,YAAuB,OACC;CACxB,MAAM,MAAM,MAAM,UAAU,GAAG,OAAO,iBAAiB;EACrD,QAAQ;EACR,SAAS;GACP,eAAe,UAAU;GACzB,gBAAgB;GACjB;EACF,CAAC;AAEF,KAAI,CAAC,IAAI,GACP,QAAO;CAGT,MAAM,OAAQ,MAAM,IAAI,MAAM;AAC9B,KAAI,OAAO,KAAK,gBAAgB,YAAY,KAAK,YAAY,SAAS,EACpE,QAAO,KAAK;AAGd,QAAO;;AAIT,MAAM,6BAAa,IAAI,KAAmD;AAE1E,SAAS,YAAY,QAAgB,QAAwB;AAG3D,QAAO,GAAG,OAAO,IAAI;;AAGvB,eAAsB,eAAe,QAKV;CACzB,MAAM,EAAE,QAAQ,QAAQ,aAAa,MAAW,YAAY,UAAU;CAEtE,MAAM,MAAM,KAAK,KAAK;CACtB,MAAM,WAAW,YAAY,QAAQ,OAAO;CAE5C,MAAM,SAAS,WAAW,IAAI,SAAS;AACvC,KAAI,UAAU,OAAO,YAAY,IAC/B,QAAO,OAAO;CAGhB,MAAM,QAAQ,MAAM,6BAA6B,QAAQ,QAAQ,UAAU;AAE3E,KAAI,CAAC,MACH,QAAO;AAGT,YAAW,IAAI,UAAU;EAAE;EAAO,WAAW,MAAM;EAAY,CAAC;AAGhE,MAAK,MAAM,CAAC,KAAK,UAAU,WAAW,SAAS,CAC7C,KAAI,MAAM,aAAa,IACrB,YAAW,OAAO,IAAI;AAI1B,QAAO"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Config } from "@interfere/types/sdk/config";
|
|
2
|
+
import { Env, Runtime } from "@interfere/types/sdk/runtime";
|
|
3
|
+
import { Envelope } from "@interfere/types/sdk/envelope";
|
|
4
|
+
|
|
5
|
+
//#region src/server/capture.d.ts
|
|
6
|
+
declare function toJsonBytes(obj: unknown): number[];
|
|
7
|
+
type ServerErrorMeta = Pick<Config["metadata"], "buildId" | "releaseId">;
|
|
8
|
+
type ServerErrorEnvelopeInput = ServerErrorMeta & {
|
|
9
|
+
error: unknown;
|
|
10
|
+
request?: Request;
|
|
11
|
+
context?: Record<string, unknown>;
|
|
12
|
+
environment: string | Env;
|
|
13
|
+
runtime?: string | Runtime;
|
|
14
|
+
};
|
|
15
|
+
declare function buildServerErrorEnvelope(input: ServerErrorEnvelopeInput): Envelope;
|
|
16
|
+
declare function sendEnvelopesToIngest(envelopes: Envelope[], ingestUrl: string, surfaceToken: string): Promise<void>;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { ServerErrorEnvelopeInput, buildServerErrorEnvelope, sendEnvelopesToIngest, toJsonBytes };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.d.mts","names":[],"sources":["../../src/server/capture.ts"],"sourcesContent":[],"mappings":";;;;;iBAiBgB,WAAA;KA4EX,eAAA,GAAkB,KAAK;AA5EZ,KA8EJ,wBAAA,GAA2B,eA9EZ,GAAA;EA4EtB,KAAA,EAAA,OAAA;EAEO,OAAA,CAAA,EAEA,OAFA;EAA2B,OAAA,CAAA,EAG3B,MAH2B,CAAA,MAAA,EAAA,OAAA,CAAA;EAE3B,WAAA,EAAA,MAAA,GAEY,GAFZ;EACA,OAAA,CAAA,EAAA,MAAA,GAES,OAFT;CACY;AACH,iBAGL,wBAAA,CAHK,KAAA,EAIZ,wBAJY,CAAA,EAKlB,QALkB;AAAO,iBA2CN,qBAAA,CA3CM,SAAA,EA4Cf,QA5Ce,EAAA,EAAA,SAAA,EAAA,MAAA,EAAA,YAAA,EAAA,MAAA,CAAA,EA+CzB,OA/CyB,CAAA,IAAA,CAAA"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { withSpan } from "../effect/layers/tracer.layer.mjs";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { v7 } from "uuid";
|
|
4
|
+
import { withInterfereLogger } from "@interfere/effect-utils/observability";
|
|
5
|
+
import { normalizeEnv, normalizeRuntime } from "@interfere/types/sdk/runtime";
|
|
6
|
+
|
|
7
|
+
//#region src/server/capture.ts
|
|
8
|
+
function toJsonBytes(obj) {
|
|
9
|
+
const encoder = new TextEncoder();
|
|
10
|
+
return Array.from(encoder.encode(JSON.stringify(obj)));
|
|
11
|
+
}
|
|
12
|
+
const X_INTERFERE_REQUEST_HEADER = "x-interfere-request";
|
|
13
|
+
/**
|
|
14
|
+
* Parse x-interfere-request header to extract session ID and request ID
|
|
15
|
+
* Format: {sessionId}/{requestId}
|
|
16
|
+
*/
|
|
17
|
+
function parseInterfereRequestHeader(headerValue) {
|
|
18
|
+
if (!headerValue) return {
|
|
19
|
+
sessionId: null,
|
|
20
|
+
requestId: null
|
|
21
|
+
};
|
|
22
|
+
const parts = headerValue.split("/");
|
|
23
|
+
if (parts.length !== 2) return {
|
|
24
|
+
sessionId: null,
|
|
25
|
+
requestId: null
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
sessionId: parts[0] || null,
|
|
29
|
+
requestId: parts[1] || null
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parse stack trace to extract frames
|
|
34
|
+
* Similar logic to client-side error parsing
|
|
35
|
+
*/
|
|
36
|
+
const STACK_TRACE_REGEX = /at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/;
|
|
37
|
+
function parseStackTrace(stack) {
|
|
38
|
+
if (!stack) return [{
|
|
39
|
+
file: { reported: null },
|
|
40
|
+
line: { reported: null },
|
|
41
|
+
column: { reported: null },
|
|
42
|
+
fn: { reported: null }
|
|
43
|
+
}];
|
|
44
|
+
const lines = stack.split("\n");
|
|
45
|
+
const frames = [];
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
const match = line.match(STACK_TRACE_REGEX);
|
|
48
|
+
if (match) frames.push({
|
|
49
|
+
file: { reported: match[2] || null },
|
|
50
|
+
line: { reported: match[3] ? Number.parseInt(match[3], 10) : null },
|
|
51
|
+
column: { reported: match[4] ? Number.parseInt(match[4], 10) : null },
|
|
52
|
+
fn: { reported: match[1] || null }
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return frames.length > 0 ? frames : [{
|
|
56
|
+
file: { reported: null },
|
|
57
|
+
line: { reported: null },
|
|
58
|
+
column: { reported: null },
|
|
59
|
+
fn: { reported: null }
|
|
60
|
+
}];
|
|
61
|
+
}
|
|
62
|
+
function buildServerErrorEnvelope(input) {
|
|
63
|
+
const { error, request, buildId, releaseId, environment, runtime } = input;
|
|
64
|
+
let sessionId;
|
|
65
|
+
if (request) sessionId = parseInterfereRequestHeader(request.headers.get(X_INTERFERE_REQUEST_HEADER)).sessionId ?? `server_${v7()}`;
|
|
66
|
+
else sessionId = `server_${v7()}`;
|
|
67
|
+
const errorObj = error;
|
|
68
|
+
const errorPayload = {
|
|
69
|
+
frames: parseStackTrace(errorObj?.stack),
|
|
70
|
+
message: errorObj?.message ?? String(error ?? "Unknown error"),
|
|
71
|
+
name: errorObj?.name ?? "Error",
|
|
72
|
+
stack: errorObj?.stack ?? null,
|
|
73
|
+
errorSource: "server"
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
v: 0,
|
|
77
|
+
runtime: normalizeRuntime(runtime),
|
|
78
|
+
environment: normalizeEnv(environment),
|
|
79
|
+
clientTs: Date.now(),
|
|
80
|
+
sessionId,
|
|
81
|
+
type: "error",
|
|
82
|
+
payload: errorPayload,
|
|
83
|
+
buildId: buildId ?? "unknown",
|
|
84
|
+
releaseId: releaseId ?? "unknown",
|
|
85
|
+
uuid: v7()
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function sendEnvelopesToIngest(envelopes, ingestUrl, surfaceToken) {
|
|
89
|
+
const response = await Effect.runPromise(withInterfereLogger("react", withSpan("server.sendEnvelopes", Effect.promise(() => fetch(ingestUrl, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: {
|
|
92
|
+
"Content-Type": "application/json",
|
|
93
|
+
"x-interfere-surface": surfaceToken
|
|
94
|
+
},
|
|
95
|
+
body: JSON.stringify(envelopes),
|
|
96
|
+
keepalive: true
|
|
97
|
+
})))));
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
const errorText = await response.text().catch(() => "");
|
|
100
|
+
throw new Error(`Failed to send envelopes to ingest: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
export { buildServerErrorEnvelope, sendEnvelopesToIngest, toJsonBytes };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.mjs","names":["frames: IngestedFrame[]","sessionId: string","uuidv7","errorPayload: ErrorEnvelopePayload"],"sources":["../../src/server/capture.ts"],"sourcesContent":["import { withInterfereLogger } from \"@interfere/effect-utils/observability\";\nimport type { IngestedFrame } from \"@interfere/types/data/frame\";\nimport type { Config } from \"@interfere/types/sdk/config\";\nimport type { Envelope } from \"@interfere/types/sdk/envelope\";\nimport type { ErrorEnvelopePayload } from \"@interfere/types/sdk/plugins/payload/errors\";\nimport {\n type Env,\n normalizeEnv,\n normalizeRuntime,\n type Runtime,\n} from \"@interfere/types/sdk/runtime\";\n\nimport { Effect } from \"effect\";\nimport { v7 as uuidv7 } from \"uuid\";\n\nimport { withSpan } from \"../effect/layers/tracer.layer.js\";\n\nexport function toJsonBytes(obj: unknown): number[] {\n const encoder = new TextEncoder();\n return Array.from(encoder.encode(JSON.stringify(obj)));\n}\n\nconst X_INTERFERE_REQUEST_HEADER = \"x-interfere-request\";\n\n/**\n * Parse x-interfere-request header to extract session ID and request ID\n * Format: {sessionId}/{requestId}\n */\nfunction parseInterfereRequestHeader(headerValue: string | null): {\n sessionId: string | null;\n requestId: string | null;\n} {\n if (!headerValue) {\n return { sessionId: null, requestId: null };\n }\n\n const parts = headerValue.split(\"/\");\n if (parts.length !== 2) {\n return { sessionId: null, requestId: null };\n }\n\n return {\n sessionId: parts[0] || null,\n requestId: parts[1] || null,\n };\n}\n\n/**\n * Parse stack trace to extract frames\n * Similar logic to client-side error parsing\n */\nconst STACK_TRACE_REGEX = /at\\s+(?:(.+?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?/;\n\nfunction parseStackTrace(stack?: string): IngestedFrame[] {\n if (!stack) {\n return [\n {\n file: { reported: null },\n line: { reported: null },\n column: { reported: null },\n fn: { reported: null },\n },\n ];\n }\n\n const lines = stack.split(\"\\n\");\n const frames: IngestedFrame[] = [];\n\n for (const line of lines) {\n const match = line.match(STACK_TRACE_REGEX);\n\n if (match) {\n frames.push({\n file: { reported: match[2] || null },\n line: { reported: match[3] ? Number.parseInt(match[3], 10) : null },\n column: { reported: match[4] ? Number.parseInt(match[4], 10) : null },\n fn: { reported: match[1] || null },\n });\n }\n }\n\n return frames.length > 0\n ? frames\n : [\n {\n file: { reported: null },\n line: { reported: null },\n column: { reported: null },\n fn: { reported: null },\n },\n ];\n}\n\ntype ServerErrorMeta = Pick<Config[\"metadata\"], \"buildId\" | \"releaseId\">;\n\nexport type ServerErrorEnvelopeInput = ServerErrorMeta & {\n error: unknown;\n request?: Request;\n context?: Record<string, unknown>;\n environment: string | Env;\n runtime?: string | Runtime;\n};\n\nexport function buildServerErrorEnvelope(\n input: ServerErrorEnvelopeInput\n): Envelope {\n const { error, request, buildId, releaseId, environment, runtime } = input;\n\n // Extract session ID from x-interfere-request header\n let sessionId: string;\n if (request) {\n const headerValue = request.headers.get(X_INTERFERE_REQUEST_HEADER);\n const parsed = parseInterfereRequestHeader(headerValue);\n sessionId = parsed.sessionId ?? `server_${uuidv7()}`;\n } else {\n // Generate placeholder session ID if no request provided\n sessionId = `server_${uuidv7()}`;\n }\n\n // Build error payload matching ErrorEnvelopePayload schema\n const errorObj = error as Error;\n const errorPayload: ErrorEnvelopePayload = {\n frames: parseStackTrace(errorObj?.stack),\n message: errorObj?.message ?? String(error ?? \"Unknown error\"),\n name: errorObj?.name ?? \"Error\",\n stack: errorObj?.stack ?? null,\n errorSource: \"server\",\n };\n\n return {\n v: 0,\n runtime: normalizeRuntime(runtime),\n environment: normalizeEnv(environment),\n clientTs: Date.now(),\n sessionId,\n type: \"error\",\n payload: errorPayload,\n buildId: buildId ?? \"unknown\",\n releaseId: releaseId ?? \"unknown\",\n uuid: uuidv7(),\n };\n}\n\nexport async function sendEnvelopesToIngest(\n envelopes: Envelope[],\n ingestUrl: string,\n surfaceToken: string\n): Promise<void> {\n const response = await Effect.runPromise(\n withInterfereLogger(\n \"react\",\n withSpan(\n \"server.sendEnvelopes\",\n Effect.promise(() =>\n fetch(ingestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-interfere-surface\": surfaceToken,\n },\n body: JSON.stringify(envelopes),\n keepalive: true,\n })\n )\n )\n )\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"\");\n throw new Error(\n `Failed to send envelopes to ingest: ${response.status} ${response.statusText}${\n errorText ? ` - ${errorText}` : \"\"\n }`\n );\n }\n}\n"],"mappings":";;;;;;;AAiBA,SAAgB,YAAY,KAAwB;CAClD,MAAM,UAAU,IAAI,aAAa;AACjC,QAAO,MAAM,KAAK,QAAQ,OAAO,KAAK,UAAU,IAAI,CAAC,CAAC;;AAGxD,MAAM,6BAA6B;;;;;AAMnC,SAAS,4BAA4B,aAGnC;AACA,KAAI,CAAC,YACH,QAAO;EAAE,WAAW;EAAM,WAAW;EAAM;CAG7C,MAAM,QAAQ,YAAY,MAAM,IAAI;AACpC,KAAI,MAAM,WAAW,EACnB,QAAO;EAAE,WAAW;EAAM,WAAW;EAAM;AAG7C,QAAO;EACL,WAAW,MAAM,MAAM;EACvB,WAAW,MAAM,MAAM;EACxB;;;;;;AAOH,MAAM,oBAAoB;AAE1B,SAAS,gBAAgB,OAAiC;AACxD,KAAI,CAAC,MACH,QAAO,CACL;EACE,MAAM,EAAE,UAAU,MAAM;EACxB,MAAM,EAAE,UAAU,MAAM;EACxB,QAAQ,EAAE,UAAU,MAAM;EAC1B,IAAI,EAAE,UAAU,MAAM;EACvB,CACF;CAGH,MAAM,QAAQ,MAAM,MAAM,KAAK;CAC/B,MAAMA,SAA0B,EAAE;AAElC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,KAAK,MAAM,kBAAkB;AAE3C,MAAI,MACF,QAAO,KAAK;GACV,MAAM,EAAE,UAAU,MAAM,MAAM,MAAM;GACpC,MAAM,EAAE,UAAU,MAAM,KAAK,OAAO,SAAS,MAAM,IAAI,GAAG,GAAG,MAAM;GACnE,QAAQ,EAAE,UAAU,MAAM,KAAK,OAAO,SAAS,MAAM,IAAI,GAAG,GAAG,MAAM;GACrE,IAAI,EAAE,UAAU,MAAM,MAAM,MAAM;GACnC,CAAC;;AAIN,QAAO,OAAO,SAAS,IACnB,SACA,CACE;EACE,MAAM,EAAE,UAAU,MAAM;EACxB,MAAM,EAAE,UAAU,MAAM;EACxB,QAAQ,EAAE,UAAU,MAAM;EAC1B,IAAI,EAAE,UAAU,MAAM;EACvB,CACF;;AAaP,SAAgB,yBACd,OACU;CACV,MAAM,EAAE,OAAO,SAAS,SAAS,WAAW,aAAa,YAAY;CAGrE,IAAIC;AACJ,KAAI,QAGF,aADe,4BADK,QAAQ,QAAQ,IAAI,2BAA2B,CACZ,CACpC,aAAa,UAAUC,IAAQ;KAGlD,aAAY,UAAUA,IAAQ;CAIhC,MAAM,WAAW;CACjB,MAAMC,eAAqC;EACzC,QAAQ,gBAAgB,UAAU,MAAM;EACxC,SAAS,UAAU,WAAW,OAAO,SAAS,gBAAgB;EAC9D,MAAM,UAAU,QAAQ;EACxB,OAAO,UAAU,SAAS;EAC1B,aAAa;EACd;AAED,QAAO;EACL,GAAG;EACH,SAAS,iBAAiB,QAAQ;EAClC,aAAa,aAAa,YAAY;EACtC,UAAU,KAAK,KAAK;EACpB;EACA,MAAM;EACN,SAAS;EACT,SAAS,WAAW;EACpB,WAAW,aAAa;EACxB,MAAMD,IAAQ;EACf;;AAGH,eAAsB,sBACpB,WACA,WACA,cACe;CACf,MAAM,WAAW,MAAM,OAAO,WAC5B,oBACE,SACA,SACE,wBACA,OAAO,cACL,MAAM,WAAW;EACf,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,uBAAuB;GACxB;EACD,MAAM,KAAK,UAAU,UAAU;EAC/B,WAAW;EACZ,CAAC,CACH,CACF,CACF,CACF;AAED,KAAI,CAAC,SAAS,IAAI;EAChB,MAAM,YAAY,MAAM,SAAS,MAAM,CAAC,YAAY,GAAG;AACvD,QAAM,IAAI,MACR,uCAAuC,SAAS,OAAO,GAAG,SAAS,aACjE,YAAY,MAAM,cAAc,KAEnC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interfere/react",
|
|
3
|
-
"version": "0.0.1",
|
|
3
|
+
"version": "0.0.2-alpha.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Build apps that never break.",
|
|
6
6
|
"keywords": [
|
|
@@ -20,54 +20,87 @@
|
|
|
20
20
|
"dist"
|
|
21
21
|
],
|
|
22
22
|
"type": "module",
|
|
23
|
-
"main": "./dist/index.
|
|
24
|
-
"types": "./dist/index.d.
|
|
23
|
+
"main": "./dist/index.mjs",
|
|
24
|
+
"types": "./dist/index.d.mts",
|
|
25
|
+
"typesVersions": {
|
|
26
|
+
"*": {
|
|
27
|
+
"server/*": [
|
|
28
|
+
"dist/server/*.d.mts"
|
|
29
|
+
],
|
|
30
|
+
"*": [
|
|
31
|
+
"dist/*.d.mts"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
25
35
|
"exports": {
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
36
|
+
"./provider": {
|
|
37
|
+
"@source": "./src/provider.tsx",
|
|
38
|
+
"types": "./dist/provider.d.mts",
|
|
39
|
+
"default": "./dist/provider.mjs"
|
|
29
40
|
},
|
|
30
|
-
"./
|
|
31
|
-
"
|
|
32
|
-
"
|
|
41
|
+
"./server/*": {
|
|
42
|
+
"@source": "./src/server/*.ts",
|
|
43
|
+
"types": "./dist/server/*.d.mts",
|
|
44
|
+
"default": "./dist/server/*.mjs"
|
|
33
45
|
},
|
|
34
|
-
"./
|
|
35
|
-
"
|
|
36
|
-
"
|
|
46
|
+
"./core/runtime/*": {
|
|
47
|
+
"@source": "./src/core/runtime/*.ts",
|
|
48
|
+
"types": "./dist/core/runtime/*.d.mts",
|
|
49
|
+
"default": "./dist/core/runtime/*.mjs"
|
|
50
|
+
},
|
|
51
|
+
"./core/*": {
|
|
52
|
+
"@source": "./src/core/*.ts",
|
|
53
|
+
"types": "./dist/core/*.d.mts",
|
|
54
|
+
"default": "./dist/core/*.mjs"
|
|
55
|
+
},
|
|
56
|
+
"./*": {
|
|
57
|
+
"@source": "./src/*.ts",
|
|
58
|
+
"types": "./dist/*.d.mts",
|
|
59
|
+
"default": "./dist/*.mjs"
|
|
37
60
|
}
|
|
38
61
|
},
|
|
62
|
+
"sideEffects": false,
|
|
39
63
|
"publishConfig": {
|
|
40
64
|
"access": "public"
|
|
41
65
|
},
|
|
42
66
|
"dependencies": {
|
|
67
|
+
"@effect/platform": "^0.94.0",
|
|
43
68
|
"@rrweb/packer": "2.0.0-alpha.18",
|
|
44
|
-
"@
|
|
69
|
+
"@ua-parser-js/pro-enterprise": "^2.0.6",
|
|
70
|
+
"effect": "^3.19.13",
|
|
45
71
|
"nanoid": "^5.1.6",
|
|
46
72
|
"rrweb": "2.0.0-alpha.18",
|
|
47
73
|
"uuid": "^13.0.0",
|
|
48
|
-
"
|
|
49
|
-
"@interfere/
|
|
74
|
+
"zod": "^4.2.1",
|
|
75
|
+
"@interfere/constants": "0.0.2-alpha.0",
|
|
76
|
+
"@interfere/effect-utils": "0.0.2-alpha.0",
|
|
77
|
+
"@interfere/types": "0.0.2-alpha.0"
|
|
50
78
|
},
|
|
51
79
|
"peerDependencies": {
|
|
52
80
|
"react": ">=18",
|
|
53
81
|
"react-dom": ">=18"
|
|
54
82
|
},
|
|
55
83
|
"devDependencies": {
|
|
56
|
-
"@types
|
|
57
|
-
"@types/
|
|
58
|
-
"@types/react
|
|
59
|
-
"@
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
84
|
+
"@rrweb/types": "2.0.0-alpha.18",
|
|
85
|
+
"@types/node": "^22.19.3",
|
|
86
|
+
"@types/react": "19.2.7",
|
|
87
|
+
"@types/react-dom": "19.2.3",
|
|
88
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
89
|
+
"jsdom": "^27.4.0",
|
|
90
|
+
"msw": "^2.12.7",
|
|
91
|
+
"react": "^19.2.3",
|
|
92
|
+
"react-dom": "^19.2.3",
|
|
93
|
+
"tsdown": "^0.18.3",
|
|
63
94
|
"typescript": "5.9.3",
|
|
64
|
-
"vitest": "^
|
|
65
|
-
"@interfere/
|
|
95
|
+
"vitest": "^4.0.16",
|
|
96
|
+
"@interfere/test-utils": "0.0.0",
|
|
97
|
+
"@interfere/typescript-config": "1.0.3-alpha.0",
|
|
98
|
+
"@interfere/vitest-config": "1.0.1-alpha.0"
|
|
66
99
|
},
|
|
67
100
|
"scripts": {
|
|
68
|
-
"build": "
|
|
69
|
-
"dev": "
|
|
70
|
-
"
|
|
101
|
+
"build": "tsdown",
|
|
102
|
+
"dev": "tsdown --watch",
|
|
103
|
+
"typecheck": "tsc --noEmit --incremental",
|
|
71
104
|
"test": "vitest run --coverage"
|
|
72
105
|
}
|
|
73
106
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/client.test.ts"],"names":[],"mappings":""}
|