@loggerjs/browser 0.4.0 → 0.5.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/broadcast-channel-transport.cjs +115 -0
- package/dist/broadcast-channel-transport.cjs.map +1 -0
- package/dist/broadcast-channel-transport.js +114 -0
- package/dist/broadcast-channel-transport.js.map +1 -0
- package/dist/console-integration.cjs +98 -0
- package/dist/console-integration.cjs.map +1 -0
- package/dist/console-integration.js +97 -0
- package/dist/console-integration.js.map +1 -0
- package/dist/context-propagation-integration.cjs +78 -0
- package/dist/context-propagation-integration.cjs.map +1 -0
- package/dist/context-propagation-integration.js +77 -0
- package/dist/context-propagation-integration.js.map +1 -0
- package/dist/error-integration.cjs +128 -0
- package/dist/error-integration.cjs.map +1 -0
- package/dist/error-integration.js +127 -0
- package/dist/error-integration.js.map +1 -0
- package/dist/fetch-integration.cjs +120 -0
- package/dist/fetch-integration.cjs.map +1 -0
- package/dist/fetch-integration.js +119 -0
- package/dist/fetch-integration.js.map +1 -0
- package/dist/framework-error-integration.cjs +102 -0
- package/dist/framework-error-integration.cjs.map +1 -0
- package/dist/framework-error-integration.js +101 -0
- package/dist/framework-error-integration.js.map +1 -0
- package/dist/framework-router-integrations.cjs +88 -0
- package/dist/framework-router-integrations.cjs.map +1 -0
- package/dist/framework-router-integrations.js +84 -0
- package/dist/framework-router-integrations.js.map +1 -0
- package/dist/http-transport.cjs +301 -0
- package/dist/http-transport.cjs.map +1 -0
- package/dist/http-transport.js +299 -0
- package/dist/http-transport.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/indexeddb-offline-queue.cjs +105 -0
- package/dist/indexeddb-offline-queue.cjs.map +1 -0
- package/dist/indexeddb-offline-queue.js +104 -0
- package/dist/indexeddb-offline-queue.js.map +1 -0
- package/dist/indexeddb-transport.cjs +531 -0
- package/dist/indexeddb-transport.cjs.map +1 -0
- package/dist/indexeddb-transport.js +530 -0
- package/dist/indexeddb-transport.js.map +1 -0
- package/dist/offline-first-transport.cjs +675 -0
- package/dist/offline-first-transport.cjs.map +1 -0
- package/dist/offline-first-transport.js +674 -0
- package/dist/offline-first-transport.js.map +1 -0
- package/dist/page-lifecycle.cjs +39 -0
- package/dist/page-lifecycle.cjs.map +1 -0
- package/dist/page-lifecycle.js +38 -0
- package/dist/page-lifecycle.js.map +1 -0
- package/dist/payload-transforms.cjs +30 -0
- package/dist/payload-transforms.cjs.map +1 -0
- package/dist/payload-transforms.js +29 -0
- package/dist/payload-transforms.js.map +1 -0
- package/dist/performance-integration.cjs +135 -0
- package/dist/performance-integration.cjs.map +1 -0
- package/dist/performance-integration.js +133 -0
- package/dist/performance-integration.js.map +1 -0
- package/dist/reporting-integration.cjs +110 -0
- package/dist/reporting-integration.cjs.map +1 -0
- package/dist/reporting-integration.js +109 -0
- package/dist/reporting-integration.js.map +1 -0
- package/dist/router-integration.cjs +97 -0
- package/dist/router-integration.cjs.map +1 -0
- package/dist/router-integration.js +96 -0
- package/dist/router-integration.js.map +1 -0
- package/dist/runtime-host-integration.cjs +181 -0
- package/dist/runtime-host-integration.cjs.map +1 -0
- package/dist/runtime-host-integration.js +180 -0
- package/dist/runtime-host-integration.js.map +1 -0
- package/dist/service-worker-integration.cjs +109 -0
- package/dist/service-worker-integration.cjs.map +1 -0
- package/dist/service-worker-integration.js +108 -0
- package/dist/service-worker-integration.js.map +1 -0
- package/dist/service-worker-transport.cjs +184 -0
- package/dist/service-worker-transport.cjs.map +1 -0
- package/dist/service-worker-transport.js +183 -0
- package/dist/service-worker-transport.js.map +1 -0
- package/dist/user-action-integration.cjs +100 -0
- package/dist/user-action-integration.cjs.map +1 -0
- package/dist/user-action-integration.js +99 -0
- package/dist/user-action-integration.js.map +1 -0
- package/dist/web-vitals-integration.cjs +157 -0
- package/dist/web-vitals-integration.cjs.map +1 -0
- package/dist/web-vitals-integration.js +156 -0
- package/dist/web-vitals-integration.js.map +1 -0
- package/dist/websocket-integration.cjs +158 -0
- package/dist/websocket-integration.cjs.map +1 -0
- package/dist/websocket-integration.js +157 -0
- package/dist/websocket-integration.js.map +1 -0
- package/dist/websocket-transport.cjs +187 -0
- package/dist/websocket-transport.cjs.map +1 -0
- package/dist/websocket-transport.js +186 -0
- package/dist/websocket-transport.js.map +1 -0
- package/dist/xhr-integration.cjs +107 -0
- package/dist/xhr-integration.cjs.map +1 -0
- package/dist/xhr-integration.js +106 -0
- package/dist/xhr-integration.js.map +1 -0
- package/dist/zip-export.cjs +241 -0
- package/dist/zip-export.cjs.map +1 -0
- package/dist/zip-export.js +238 -0
- package/dist/zip-export.js.map +1 -0
- package/package.json +104 -77
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _loggerjs_core = require("@loggerjs/core");
|
|
3
|
+
//#region src/runtime-host-integration.ts
|
|
4
|
+
const textEncoder = new TextEncoder();
|
|
5
|
+
function byteLength(value) {
|
|
6
|
+
if (typeof value === "string") return textEncoder.encode(value).byteLength;
|
|
7
|
+
if (value instanceof ArrayBuffer) return value.byteLength;
|
|
8
|
+
if (ArrayBuffer.isView(value)) return value.byteLength;
|
|
9
|
+
}
|
|
10
|
+
function valueSummary(value, includeData) {
|
|
11
|
+
return {
|
|
12
|
+
dataType: typeof value === "object" && value ? value.constructor?.name ?? "object" : typeof value,
|
|
13
|
+
byteLength: byteLength(value),
|
|
14
|
+
data: includeData ? (0, _loggerjs_core.normalizeValue)(value, {
|
|
15
|
+
maxDepth: 4,
|
|
16
|
+
maxObjectKeys: 80
|
|
17
|
+
}) : void 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function senderInfo(sender, sanitizeUrl) {
|
|
21
|
+
if (!sender) return void 0;
|
|
22
|
+
const sanitize = (url) => url && sanitizeUrl ? sanitizeUrl(url) : url;
|
|
23
|
+
return {
|
|
24
|
+
id: sender.id,
|
|
25
|
+
origin: sender.origin,
|
|
26
|
+
url: sanitize(sender.url),
|
|
27
|
+
tab: sender.tab ? {
|
|
28
|
+
id: sender.tab.id,
|
|
29
|
+
url: sanitize(sender.tab.url)
|
|
30
|
+
} : void 0
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function defaultExtensionRuntime() {
|
|
34
|
+
const global = globalThis;
|
|
35
|
+
return global.browser?.runtime ?? global.chrome?.runtime;
|
|
36
|
+
}
|
|
37
|
+
function defaultIpcRenderer() {
|
|
38
|
+
const global = globalThis;
|
|
39
|
+
if (global.electron?.ipcRenderer) return global.electron.ipcRenderer;
|
|
40
|
+
try {
|
|
41
|
+
return (global.require?.("electron"))?.ipcRenderer;
|
|
42
|
+
} catch {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function captureRuntimeHostIntegration(options = {}) {
|
|
47
|
+
const level = options.level ?? "debug";
|
|
48
|
+
const captureExtensionMessages = options.captureExtensionMessages ?? true;
|
|
49
|
+
const captureExtensionInstalled = options.captureExtensionInstalled ?? true;
|
|
50
|
+
const captureExtensionMessageData = options.captureExtensionMessageData ?? false;
|
|
51
|
+
const captureElectronMessages = options.captureElectronMessages ?? true;
|
|
52
|
+
const captureElectronSend = options.captureElectronSend ?? true;
|
|
53
|
+
const captureElectronInvoke = options.captureElectronInvoke ?? true;
|
|
54
|
+
const captureElectronMessageData = options.captureElectronMessageData ?? false;
|
|
55
|
+
const electronChannels = options.electronChannels ?? [];
|
|
56
|
+
return {
|
|
57
|
+
name: "capture-runtime-host",
|
|
58
|
+
setup(api) {
|
|
59
|
+
const extensionRuntime = options.extensionRuntime ?? defaultExtensionRuntime();
|
|
60
|
+
const ipcRenderer = options.ipcRenderer ?? defaultIpcRenderer();
|
|
61
|
+
const disposers = [];
|
|
62
|
+
let disposed = false;
|
|
63
|
+
const capture = api.guard((input) => {
|
|
64
|
+
if (!disposed) api.capture(input);
|
|
65
|
+
});
|
|
66
|
+
if (extensionRuntime) {
|
|
67
|
+
const manifest = extensionRuntime.getManifest?.();
|
|
68
|
+
if (captureExtensionMessages && extensionRuntime.onMessage?.addListener) {
|
|
69
|
+
const onMessage = (message, sender) => {
|
|
70
|
+
capture({
|
|
71
|
+
level,
|
|
72
|
+
message: "Browser extension message",
|
|
73
|
+
props: { browser: {
|
|
74
|
+
kind: "extension-message",
|
|
75
|
+
extension: {
|
|
76
|
+
id: extensionRuntime.id,
|
|
77
|
+
name: manifest?.name,
|
|
78
|
+
version: manifest?.version
|
|
79
|
+
},
|
|
80
|
+
message: valueSummary(message, captureExtensionMessageData),
|
|
81
|
+
sender: senderInfo(sender, options.sanitizeUrl)
|
|
82
|
+
} }
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
extensionRuntime.onMessage.addListener(onMessage);
|
|
86
|
+
disposers.push(() => extensionRuntime.onMessage?.removeListener?.(onMessage));
|
|
87
|
+
}
|
|
88
|
+
if (captureExtensionInstalled && extensionRuntime.onInstalled?.addListener) {
|
|
89
|
+
const onInstalled = (details) => {
|
|
90
|
+
capture({
|
|
91
|
+
level,
|
|
92
|
+
message: "Browser extension installed",
|
|
93
|
+
props: { browser: {
|
|
94
|
+
kind: "extension-installed",
|
|
95
|
+
extension: {
|
|
96
|
+
id: extensionRuntime.id,
|
|
97
|
+
name: manifest?.name,
|
|
98
|
+
version: manifest?.version
|
|
99
|
+
},
|
|
100
|
+
details: (0, _loggerjs_core.normalizeValue)(details, {
|
|
101
|
+
maxDepth: 4,
|
|
102
|
+
maxObjectKeys: 80
|
|
103
|
+
})
|
|
104
|
+
} }
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
extensionRuntime.onInstalled.addListener(onInstalled);
|
|
108
|
+
disposers.push(() => extensionRuntime.onInstalled?.removeListener?.(onInstalled));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (ipcRenderer) {
|
|
112
|
+
if (captureElectronMessages && ipcRenderer.on) for (const channel of electronChannels) {
|
|
113
|
+
const listener = (_event, ...args) => {
|
|
114
|
+
capture({
|
|
115
|
+
level,
|
|
116
|
+
message: `Electron IPC message ${channel}`,
|
|
117
|
+
props: { browser: {
|
|
118
|
+
kind: "electron-ipc",
|
|
119
|
+
direction: "incoming",
|
|
120
|
+
channel,
|
|
121
|
+
args: args.map((arg) => valueSummary(arg, captureElectronMessageData))
|
|
122
|
+
} }
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
ipcRenderer.on(channel, listener);
|
|
126
|
+
disposers.push(() => {
|
|
127
|
+
if (ipcRenderer.off) ipcRenderer.off(channel, listener);
|
|
128
|
+
else ipcRenderer.removeListener?.(channel, listener);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (captureElectronSend && ipcRenderer.send) {
|
|
132
|
+
const originalSend = ipcRenderer.send;
|
|
133
|
+
ipcRenderer.send = (channel, ...args) => {
|
|
134
|
+
capture({
|
|
135
|
+
level,
|
|
136
|
+
message: `Electron IPC send ${channel}`,
|
|
137
|
+
props: { browser: {
|
|
138
|
+
kind: "electron-ipc",
|
|
139
|
+
direction: "outgoing",
|
|
140
|
+
channel,
|
|
141
|
+
args: args.map((arg) => valueSummary(arg, captureElectronMessageData))
|
|
142
|
+
} }
|
|
143
|
+
});
|
|
144
|
+
return originalSend.call(ipcRenderer, channel, ...args);
|
|
145
|
+
};
|
|
146
|
+
disposers.push(() => {
|
|
147
|
+
ipcRenderer.send = originalSend;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (captureElectronInvoke && ipcRenderer.invoke) {
|
|
151
|
+
const originalInvoke = ipcRenderer.invoke;
|
|
152
|
+
ipcRenderer.invoke = (channel, ...args) => {
|
|
153
|
+
capture({
|
|
154
|
+
level,
|
|
155
|
+
message: `Electron IPC invoke ${channel}`,
|
|
156
|
+
props: { browser: {
|
|
157
|
+
kind: "electron-ipc",
|
|
158
|
+
direction: "invoke",
|
|
159
|
+
channel,
|
|
160
|
+
args: args.map((arg) => valueSummary(arg, captureElectronMessageData))
|
|
161
|
+
} }
|
|
162
|
+
});
|
|
163
|
+
return originalInvoke.call(ipcRenderer, channel, ...args);
|
|
164
|
+
};
|
|
165
|
+
disposers.push(() => {
|
|
166
|
+
ipcRenderer.invoke = originalInvoke;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return () => {
|
|
171
|
+
if (disposed) return;
|
|
172
|
+
disposed = true;
|
|
173
|
+
for (const dispose of disposers) dispose();
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
exports.captureRuntimeHostIntegration = captureRuntimeHostIntegration;
|
|
180
|
+
|
|
181
|
+
//# sourceMappingURL=runtime-host-integration.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-host-integration.cjs","names":[],"sources":["../src/runtime-host-integration.ts"],"sourcesContent":["import {\n normalizeValue,\n type Integration,\n type IntegrationSetupContext,\n type LoggerLevel,\n} from \"@loggerjs/core\";\n\nexport interface BrowserExtensionEventLike<TListener extends (...args: never[]) => unknown> {\n addListener?: (listener: TListener) => void;\n removeListener?: (listener: TListener) => void;\n}\n\nexport interface BrowserExtensionRuntimeLike {\n id?: string;\n getManifest?: () => { name?: string; version?: string };\n onMessage?: BrowserExtensionEventLike<\n (message: unknown, sender?: BrowserExtensionMessageSenderLike) => unknown\n >;\n onInstalled?: BrowserExtensionEventLike<(details: unknown) => void>;\n}\n\nexport interface BrowserExtensionMessageSenderLike {\n id?: string;\n origin?: string;\n url?: string;\n tab?: {\n id?: number;\n url?: string;\n };\n}\n\nexport interface ElectronIpcRendererLike {\n on?: (channel: string, listener: (...args: unknown[]) => void) => unknown;\n off?: (channel: string, listener: (...args: unknown[]) => void) => unknown;\n removeListener?: (channel: string, listener: (...args: unknown[]) => void) => unknown;\n send?: (channel: string, ...args: unknown[]) => void;\n invoke?: (channel: string, ...args: unknown[]) => Promise<unknown>;\n}\n\nexport interface CaptureRuntimeHostOptions {\n level?: LoggerLevel;\n captureExtensionMessages?: boolean;\n captureExtensionInstalled?: boolean;\n captureExtensionMessageData?: boolean;\n captureElectronMessages?: boolean;\n captureElectronSend?: boolean;\n captureElectronInvoke?: boolean;\n captureElectronMessageData?: boolean;\n electronChannels?: readonly string[];\n extensionRuntime?: BrowserExtensionRuntimeLike;\n ipcRenderer?: ElectronIpcRendererLike;\n sanitizeUrl?: (url: string) => string;\n}\n\nconst textEncoder = new TextEncoder();\n\nfunction byteLength(value: unknown): number | undefined {\n if (typeof value === \"string\") return textEncoder.encode(value).byteLength;\n if (value instanceof ArrayBuffer) return value.byteLength;\n if (ArrayBuffer.isView(value)) return value.byteLength;\n return undefined;\n}\n\nfunction valueSummary(value: unknown, includeData: boolean): Record<string, unknown> {\n return {\n dataType:\n typeof value === \"object\" && value\n ? ((value as { constructor?: { name?: string } }).constructor?.name ?? \"object\")\n : typeof value,\n byteLength: byteLength(value),\n data: includeData ? normalizeValue(value, { maxDepth: 4, maxObjectKeys: 80 }) : undefined,\n };\n}\n\nfunction senderInfo(\n sender: BrowserExtensionMessageSenderLike | undefined,\n sanitizeUrl: ((url: string) => string) | undefined,\n) {\n if (!sender) return undefined;\n const sanitize = (url: string | undefined) => (url && sanitizeUrl ? sanitizeUrl(url) : url);\n return {\n id: sender.id,\n origin: sender.origin,\n url: sanitize(sender.url),\n tab: sender.tab\n ? {\n id: sender.tab.id,\n url: sanitize(sender.tab.url),\n }\n : undefined,\n };\n}\n\nfunction defaultExtensionRuntime(): BrowserExtensionRuntimeLike | undefined {\n const global = globalThis as unknown as {\n browser?: { runtime?: BrowserExtensionRuntimeLike };\n chrome?: { runtime?: BrowserExtensionRuntimeLike };\n };\n return global.browser?.runtime ?? global.chrome?.runtime;\n}\n\nfunction defaultIpcRenderer(): ElectronIpcRendererLike | undefined {\n const global = globalThis as unknown as {\n electron?: { ipcRenderer?: ElectronIpcRendererLike };\n require?: (id: string) => unknown;\n };\n if (global.electron?.ipcRenderer) return global.electron.ipcRenderer;\n try {\n return (global.require?.(\"electron\") as { ipcRenderer?: ElectronIpcRendererLike } | undefined)\n ?.ipcRenderer;\n } catch {\n return undefined;\n }\n}\n\nexport function captureRuntimeHostIntegration(\n options: CaptureRuntimeHostOptions = {},\n): Integration {\n const level = options.level ?? \"debug\";\n const captureExtensionMessages = options.captureExtensionMessages ?? true;\n const captureExtensionInstalled = options.captureExtensionInstalled ?? true;\n const captureExtensionMessageData = options.captureExtensionMessageData ?? false;\n const captureElectronMessages = options.captureElectronMessages ?? true;\n const captureElectronSend = options.captureElectronSend ?? true;\n const captureElectronInvoke = options.captureElectronInvoke ?? true;\n const captureElectronMessageData = options.captureElectronMessageData ?? false;\n const electronChannels = options.electronChannels ?? [];\n\n return {\n name: \"capture-runtime-host\",\n setup(api: IntegrationSetupContext) {\n const extensionRuntime = options.extensionRuntime ?? defaultExtensionRuntime();\n const ipcRenderer = options.ipcRenderer ?? defaultIpcRenderer();\n const disposers: Array<() => void> = [];\n let disposed = false;\n\n const capture = api.guard((input: Parameters<IntegrationSetupContext[\"capture\"]>[0]) => {\n if (!disposed) api.capture(input);\n });\n\n if (extensionRuntime) {\n const manifest = extensionRuntime.getManifest?.();\n\n if (captureExtensionMessages && extensionRuntime.onMessage?.addListener) {\n const onMessage = (message: unknown, sender?: BrowserExtensionMessageSenderLike) => {\n capture({\n level,\n message: \"Browser extension message\",\n props: {\n browser: {\n kind: \"extension-message\",\n extension: {\n id: extensionRuntime.id,\n name: manifest?.name,\n version: manifest?.version,\n },\n message: valueSummary(message, captureExtensionMessageData),\n sender: senderInfo(sender, options.sanitizeUrl),\n },\n },\n });\n };\n extensionRuntime.onMessage.addListener(onMessage as never);\n disposers.push(() => extensionRuntime.onMessage?.removeListener?.(onMessage as never));\n }\n\n if (captureExtensionInstalled && extensionRuntime.onInstalled?.addListener) {\n const onInstalled = (details: unknown) => {\n capture({\n level,\n message: \"Browser extension installed\",\n props: {\n browser: {\n kind: \"extension-installed\",\n extension: {\n id: extensionRuntime.id,\n name: manifest?.name,\n version: manifest?.version,\n },\n details: normalizeValue(details, { maxDepth: 4, maxObjectKeys: 80 }),\n },\n },\n });\n };\n extensionRuntime.onInstalled.addListener(onInstalled as never);\n disposers.push(() =>\n extensionRuntime.onInstalled?.removeListener?.(onInstalled as never),\n );\n }\n }\n\n if (ipcRenderer) {\n if (captureElectronMessages && ipcRenderer.on) {\n for (const channel of electronChannels) {\n const listener = (_event: unknown, ...args: unknown[]) => {\n capture({\n level,\n message: `Electron IPC message ${channel}`,\n props: {\n browser: {\n kind: \"electron-ipc\",\n direction: \"incoming\",\n channel,\n args: args.map((arg) => valueSummary(arg, captureElectronMessageData)),\n },\n },\n });\n };\n ipcRenderer.on(channel, listener);\n disposers.push(() => {\n if (ipcRenderer.off) ipcRenderer.off(channel, listener);\n else ipcRenderer.removeListener?.(channel, listener);\n });\n }\n }\n\n if (captureElectronSend && ipcRenderer.send) {\n const originalSend = ipcRenderer.send;\n ipcRenderer.send = (channel: string, ...args: unknown[]) => {\n capture({\n level,\n message: `Electron IPC send ${channel}`,\n props: {\n browser: {\n kind: \"electron-ipc\",\n direction: \"outgoing\",\n channel,\n args: args.map((arg) => valueSummary(arg, captureElectronMessageData)),\n },\n },\n });\n return originalSend.call(ipcRenderer, channel, ...args);\n };\n disposers.push(() => {\n ipcRenderer.send = originalSend;\n });\n }\n\n if (captureElectronInvoke && ipcRenderer.invoke) {\n const originalInvoke = ipcRenderer.invoke;\n ipcRenderer.invoke = (channel: string, ...args: unknown[]) => {\n capture({\n level,\n message: `Electron IPC invoke ${channel}`,\n props: {\n browser: {\n kind: \"electron-ipc\",\n direction: \"invoke\",\n channel,\n args: args.map((arg) => valueSummary(arg, captureElectronMessageData)),\n },\n },\n });\n return originalInvoke.call(ipcRenderer, channel, ...args);\n };\n disposers.push(() => {\n ipcRenderer.invoke = originalInvoke;\n });\n }\n }\n\n return () => {\n if (disposed) return;\n disposed = true;\n for (const dispose of disposers) dispose();\n };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { normalizeValue } from "@loggerjs/core";
|
|
2
|
+
//#region src/runtime-host-integration.ts
|
|
3
|
+
const textEncoder = new TextEncoder();
|
|
4
|
+
function byteLength(value) {
|
|
5
|
+
if (typeof value === "string") return textEncoder.encode(value).byteLength;
|
|
6
|
+
if (value instanceof ArrayBuffer) return value.byteLength;
|
|
7
|
+
if (ArrayBuffer.isView(value)) return value.byteLength;
|
|
8
|
+
}
|
|
9
|
+
function valueSummary(value, includeData) {
|
|
10
|
+
return {
|
|
11
|
+
dataType: typeof value === "object" && value ? value.constructor?.name ?? "object" : typeof value,
|
|
12
|
+
byteLength: byteLength(value),
|
|
13
|
+
data: includeData ? normalizeValue(value, {
|
|
14
|
+
maxDepth: 4,
|
|
15
|
+
maxObjectKeys: 80
|
|
16
|
+
}) : void 0
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function senderInfo(sender, sanitizeUrl) {
|
|
20
|
+
if (!sender) return void 0;
|
|
21
|
+
const sanitize = (url) => url && sanitizeUrl ? sanitizeUrl(url) : url;
|
|
22
|
+
return {
|
|
23
|
+
id: sender.id,
|
|
24
|
+
origin: sender.origin,
|
|
25
|
+
url: sanitize(sender.url),
|
|
26
|
+
tab: sender.tab ? {
|
|
27
|
+
id: sender.tab.id,
|
|
28
|
+
url: sanitize(sender.tab.url)
|
|
29
|
+
} : void 0
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function defaultExtensionRuntime() {
|
|
33
|
+
const global = globalThis;
|
|
34
|
+
return global.browser?.runtime ?? global.chrome?.runtime;
|
|
35
|
+
}
|
|
36
|
+
function defaultIpcRenderer() {
|
|
37
|
+
const global = globalThis;
|
|
38
|
+
if (global.electron?.ipcRenderer) return global.electron.ipcRenderer;
|
|
39
|
+
try {
|
|
40
|
+
return (global.require?.("electron"))?.ipcRenderer;
|
|
41
|
+
} catch {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function captureRuntimeHostIntegration(options = {}) {
|
|
46
|
+
const level = options.level ?? "debug";
|
|
47
|
+
const captureExtensionMessages = options.captureExtensionMessages ?? true;
|
|
48
|
+
const captureExtensionInstalled = options.captureExtensionInstalled ?? true;
|
|
49
|
+
const captureExtensionMessageData = options.captureExtensionMessageData ?? false;
|
|
50
|
+
const captureElectronMessages = options.captureElectronMessages ?? true;
|
|
51
|
+
const captureElectronSend = options.captureElectronSend ?? true;
|
|
52
|
+
const captureElectronInvoke = options.captureElectronInvoke ?? true;
|
|
53
|
+
const captureElectronMessageData = options.captureElectronMessageData ?? false;
|
|
54
|
+
const electronChannels = options.electronChannels ?? [];
|
|
55
|
+
return {
|
|
56
|
+
name: "capture-runtime-host",
|
|
57
|
+
setup(api) {
|
|
58
|
+
const extensionRuntime = options.extensionRuntime ?? defaultExtensionRuntime();
|
|
59
|
+
const ipcRenderer = options.ipcRenderer ?? defaultIpcRenderer();
|
|
60
|
+
const disposers = [];
|
|
61
|
+
let disposed = false;
|
|
62
|
+
const capture = api.guard((input) => {
|
|
63
|
+
if (!disposed) api.capture(input);
|
|
64
|
+
});
|
|
65
|
+
if (extensionRuntime) {
|
|
66
|
+
const manifest = extensionRuntime.getManifest?.();
|
|
67
|
+
if (captureExtensionMessages && extensionRuntime.onMessage?.addListener) {
|
|
68
|
+
const onMessage = (message, sender) => {
|
|
69
|
+
capture({
|
|
70
|
+
level,
|
|
71
|
+
message: "Browser extension message",
|
|
72
|
+
props: { browser: {
|
|
73
|
+
kind: "extension-message",
|
|
74
|
+
extension: {
|
|
75
|
+
id: extensionRuntime.id,
|
|
76
|
+
name: manifest?.name,
|
|
77
|
+
version: manifest?.version
|
|
78
|
+
},
|
|
79
|
+
message: valueSummary(message, captureExtensionMessageData),
|
|
80
|
+
sender: senderInfo(sender, options.sanitizeUrl)
|
|
81
|
+
} }
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
extensionRuntime.onMessage.addListener(onMessage);
|
|
85
|
+
disposers.push(() => extensionRuntime.onMessage?.removeListener?.(onMessage));
|
|
86
|
+
}
|
|
87
|
+
if (captureExtensionInstalled && extensionRuntime.onInstalled?.addListener) {
|
|
88
|
+
const onInstalled = (details) => {
|
|
89
|
+
capture({
|
|
90
|
+
level,
|
|
91
|
+
message: "Browser extension installed",
|
|
92
|
+
props: { browser: {
|
|
93
|
+
kind: "extension-installed",
|
|
94
|
+
extension: {
|
|
95
|
+
id: extensionRuntime.id,
|
|
96
|
+
name: manifest?.name,
|
|
97
|
+
version: manifest?.version
|
|
98
|
+
},
|
|
99
|
+
details: normalizeValue(details, {
|
|
100
|
+
maxDepth: 4,
|
|
101
|
+
maxObjectKeys: 80
|
|
102
|
+
})
|
|
103
|
+
} }
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
extensionRuntime.onInstalled.addListener(onInstalled);
|
|
107
|
+
disposers.push(() => extensionRuntime.onInstalled?.removeListener?.(onInstalled));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (ipcRenderer) {
|
|
111
|
+
if (captureElectronMessages && ipcRenderer.on) for (const channel of electronChannels) {
|
|
112
|
+
const listener = (_event, ...args) => {
|
|
113
|
+
capture({
|
|
114
|
+
level,
|
|
115
|
+
message: `Electron IPC message ${channel}`,
|
|
116
|
+
props: { browser: {
|
|
117
|
+
kind: "electron-ipc",
|
|
118
|
+
direction: "incoming",
|
|
119
|
+
channel,
|
|
120
|
+
args: args.map((arg) => valueSummary(arg, captureElectronMessageData))
|
|
121
|
+
} }
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
ipcRenderer.on(channel, listener);
|
|
125
|
+
disposers.push(() => {
|
|
126
|
+
if (ipcRenderer.off) ipcRenderer.off(channel, listener);
|
|
127
|
+
else ipcRenderer.removeListener?.(channel, listener);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (captureElectronSend && ipcRenderer.send) {
|
|
131
|
+
const originalSend = ipcRenderer.send;
|
|
132
|
+
ipcRenderer.send = (channel, ...args) => {
|
|
133
|
+
capture({
|
|
134
|
+
level,
|
|
135
|
+
message: `Electron IPC send ${channel}`,
|
|
136
|
+
props: { browser: {
|
|
137
|
+
kind: "electron-ipc",
|
|
138
|
+
direction: "outgoing",
|
|
139
|
+
channel,
|
|
140
|
+
args: args.map((arg) => valueSummary(arg, captureElectronMessageData))
|
|
141
|
+
} }
|
|
142
|
+
});
|
|
143
|
+
return originalSend.call(ipcRenderer, channel, ...args);
|
|
144
|
+
};
|
|
145
|
+
disposers.push(() => {
|
|
146
|
+
ipcRenderer.send = originalSend;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (captureElectronInvoke && ipcRenderer.invoke) {
|
|
150
|
+
const originalInvoke = ipcRenderer.invoke;
|
|
151
|
+
ipcRenderer.invoke = (channel, ...args) => {
|
|
152
|
+
capture({
|
|
153
|
+
level,
|
|
154
|
+
message: `Electron IPC invoke ${channel}`,
|
|
155
|
+
props: { browser: {
|
|
156
|
+
kind: "electron-ipc",
|
|
157
|
+
direction: "invoke",
|
|
158
|
+
channel,
|
|
159
|
+
args: args.map((arg) => valueSummary(arg, captureElectronMessageData))
|
|
160
|
+
} }
|
|
161
|
+
});
|
|
162
|
+
return originalInvoke.call(ipcRenderer, channel, ...args);
|
|
163
|
+
};
|
|
164
|
+
disposers.push(() => {
|
|
165
|
+
ipcRenderer.invoke = originalInvoke;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return () => {
|
|
170
|
+
if (disposed) return;
|
|
171
|
+
disposed = true;
|
|
172
|
+
for (const dispose of disposers) dispose();
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
//#endregion
|
|
178
|
+
export { captureRuntimeHostIntegration };
|
|
179
|
+
|
|
180
|
+
//# sourceMappingURL=runtime-host-integration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-host-integration.js","names":[],"sources":["../src/runtime-host-integration.ts"],"sourcesContent":["import {\n normalizeValue,\n type Integration,\n type IntegrationSetupContext,\n type LoggerLevel,\n} from \"@loggerjs/core\";\n\nexport interface BrowserExtensionEventLike<TListener extends (...args: never[]) => unknown> {\n addListener?: (listener: TListener) => void;\n removeListener?: (listener: TListener) => void;\n}\n\nexport interface BrowserExtensionRuntimeLike {\n id?: string;\n getManifest?: () => { name?: string; version?: string };\n onMessage?: BrowserExtensionEventLike<\n (message: unknown, sender?: BrowserExtensionMessageSenderLike) => unknown\n >;\n onInstalled?: BrowserExtensionEventLike<(details: unknown) => void>;\n}\n\nexport interface BrowserExtensionMessageSenderLike {\n id?: string;\n origin?: string;\n url?: string;\n tab?: {\n id?: number;\n url?: string;\n };\n}\n\nexport interface ElectronIpcRendererLike {\n on?: (channel: string, listener: (...args: unknown[]) => void) => unknown;\n off?: (channel: string, listener: (...args: unknown[]) => void) => unknown;\n removeListener?: (channel: string, listener: (...args: unknown[]) => void) => unknown;\n send?: (channel: string, ...args: unknown[]) => void;\n invoke?: (channel: string, ...args: unknown[]) => Promise<unknown>;\n}\n\nexport interface CaptureRuntimeHostOptions {\n level?: LoggerLevel;\n captureExtensionMessages?: boolean;\n captureExtensionInstalled?: boolean;\n captureExtensionMessageData?: boolean;\n captureElectronMessages?: boolean;\n captureElectronSend?: boolean;\n captureElectronInvoke?: boolean;\n captureElectronMessageData?: boolean;\n electronChannels?: readonly string[];\n extensionRuntime?: BrowserExtensionRuntimeLike;\n ipcRenderer?: ElectronIpcRendererLike;\n sanitizeUrl?: (url: string) => string;\n}\n\nconst textEncoder = new TextEncoder();\n\nfunction byteLength(value: unknown): number | undefined {\n if (typeof value === \"string\") return textEncoder.encode(value).byteLength;\n if (value instanceof ArrayBuffer) return value.byteLength;\n if (ArrayBuffer.isView(value)) return value.byteLength;\n return undefined;\n}\n\nfunction valueSummary(value: unknown, includeData: boolean): Record<string, unknown> {\n return {\n dataType:\n typeof value === \"object\" && value\n ? ((value as { constructor?: { name?: string } }).constructor?.name ?? \"object\")\n : typeof value,\n byteLength: byteLength(value),\n data: includeData ? normalizeValue(value, { maxDepth: 4, maxObjectKeys: 80 }) : undefined,\n };\n}\n\nfunction senderInfo(\n sender: BrowserExtensionMessageSenderLike | undefined,\n sanitizeUrl: ((url: string) => string) | undefined,\n) {\n if (!sender) return undefined;\n const sanitize = (url: string | undefined) => (url && sanitizeUrl ? sanitizeUrl(url) : url);\n return {\n id: sender.id,\n origin: sender.origin,\n url: sanitize(sender.url),\n tab: sender.tab\n ? {\n id: sender.tab.id,\n url: sanitize(sender.tab.url),\n }\n : undefined,\n };\n}\n\nfunction defaultExtensionRuntime(): BrowserExtensionRuntimeLike | undefined {\n const global = globalThis as unknown as {\n browser?: { runtime?: BrowserExtensionRuntimeLike };\n chrome?: { runtime?: BrowserExtensionRuntimeLike };\n };\n return global.browser?.runtime ?? global.chrome?.runtime;\n}\n\nfunction defaultIpcRenderer(): ElectronIpcRendererLike | undefined {\n const global = globalThis as unknown as {\n electron?: { ipcRenderer?: ElectronIpcRendererLike };\n require?: (id: string) => unknown;\n };\n if (global.electron?.ipcRenderer) return global.electron.ipcRenderer;\n try {\n return (global.require?.(\"electron\") as { ipcRenderer?: ElectronIpcRendererLike } | undefined)\n ?.ipcRenderer;\n } catch {\n return undefined;\n }\n}\n\nexport function captureRuntimeHostIntegration(\n options: CaptureRuntimeHostOptions = {},\n): Integration {\n const level = options.level ?? \"debug\";\n const captureExtensionMessages = options.captureExtensionMessages ?? true;\n const captureExtensionInstalled = options.captureExtensionInstalled ?? true;\n const captureExtensionMessageData = options.captureExtensionMessageData ?? false;\n const captureElectronMessages = options.captureElectronMessages ?? true;\n const captureElectronSend = options.captureElectronSend ?? true;\n const captureElectronInvoke = options.captureElectronInvoke ?? true;\n const captureElectronMessageData = options.captureElectronMessageData ?? false;\n const electronChannels = options.electronChannels ?? [];\n\n return {\n name: \"capture-runtime-host\",\n setup(api: IntegrationSetupContext) {\n const extensionRuntime = options.extensionRuntime ?? defaultExtensionRuntime();\n const ipcRenderer = options.ipcRenderer ?? defaultIpcRenderer();\n const disposers: Array<() => void> = [];\n let disposed = false;\n\n const capture = api.guard((input: Parameters<IntegrationSetupContext[\"capture\"]>[0]) => {\n if (!disposed) api.capture(input);\n });\n\n if (extensionRuntime) {\n const manifest = extensionRuntime.getManifest?.();\n\n if (captureExtensionMessages && extensionRuntime.onMessage?.addListener) {\n const onMessage = (message: unknown, sender?: BrowserExtensionMessageSenderLike) => {\n capture({\n level,\n message: \"Browser extension message\",\n props: {\n browser: {\n kind: \"extension-message\",\n extension: {\n id: extensionRuntime.id,\n name: manifest?.name,\n version: manifest?.version,\n },\n message: valueSummary(message, captureExtensionMessageData),\n sender: senderInfo(sender, options.sanitizeUrl),\n },\n },\n });\n };\n extensionRuntime.onMessage.addListener(onMessage as never);\n disposers.push(() => extensionRuntime.onMessage?.removeListener?.(onMessage as never));\n }\n\n if (captureExtensionInstalled && extensionRuntime.onInstalled?.addListener) {\n const onInstalled = (details: unknown) => {\n capture({\n level,\n message: \"Browser extension installed\",\n props: {\n browser: {\n kind: \"extension-installed\",\n extension: {\n id: extensionRuntime.id,\n name: manifest?.name,\n version: manifest?.version,\n },\n details: normalizeValue(details, { maxDepth: 4, maxObjectKeys: 80 }),\n },\n },\n });\n };\n extensionRuntime.onInstalled.addListener(onInstalled as never);\n disposers.push(() =>\n extensionRuntime.onInstalled?.removeListener?.(onInstalled as never),\n );\n }\n }\n\n if (ipcRenderer) {\n if (captureElectronMessages && ipcRenderer.on) {\n for (const channel of electronChannels) {\n const listener = (_event: unknown, ...args: unknown[]) => {\n capture({\n level,\n message: `Electron IPC message ${channel}`,\n props: {\n browser: {\n kind: \"electron-ipc\",\n direction: \"incoming\",\n channel,\n args: args.map((arg) => valueSummary(arg, captureElectronMessageData)),\n },\n },\n });\n };\n ipcRenderer.on(channel, listener);\n disposers.push(() => {\n if (ipcRenderer.off) ipcRenderer.off(channel, listener);\n else ipcRenderer.removeListener?.(channel, listener);\n });\n }\n }\n\n if (captureElectronSend && ipcRenderer.send) {\n const originalSend = ipcRenderer.send;\n ipcRenderer.send = (channel: string, ...args: unknown[]) => {\n capture({\n level,\n message: `Electron IPC send ${channel}`,\n props: {\n browser: {\n kind: \"electron-ipc\",\n direction: \"outgoing\",\n channel,\n args: args.map((arg) => valueSummary(arg, captureElectronMessageData)),\n },\n },\n });\n return originalSend.call(ipcRenderer, channel, ...args);\n };\n disposers.push(() => {\n ipcRenderer.send = originalSend;\n });\n }\n\n if (captureElectronInvoke && ipcRenderer.invoke) {\n const originalInvoke = ipcRenderer.invoke;\n ipcRenderer.invoke = (channel: string, ...args: unknown[]) => {\n capture({\n level,\n message: `Electron IPC invoke ${channel}`,\n props: {\n browser: {\n kind: \"electron-ipc\",\n direction: \"invoke\",\n channel,\n args: args.map((arg) => valueSummary(arg, captureElectronMessageData)),\n },\n },\n });\n return originalInvoke.call(ipcRenderer, channel, ...args);\n };\n disposers.push(() => {\n ipcRenderer.invoke = originalInvoke;\n });\n }\n }\n\n return () => {\n if (disposed) return;\n disposed = true;\n for (const dispose of disposers) dispose();\n };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _loggerjs_core = require("@loggerjs/core");
|
|
3
|
+
//#region src/service-worker-integration.ts
|
|
4
|
+
const textEncoder = new TextEncoder();
|
|
5
|
+
function dataType(data) {
|
|
6
|
+
if (typeof data === "string") return "string";
|
|
7
|
+
if (data instanceof ArrayBuffer) return "arraybuffer";
|
|
8
|
+
if (ArrayBuffer.isView(data)) return data.constructor.name;
|
|
9
|
+
return data?.constructor?.name ?? typeof data;
|
|
10
|
+
}
|
|
11
|
+
function dataByteLength(data) {
|
|
12
|
+
if (typeof data === "string") return textEncoder.encode(data).byteLength;
|
|
13
|
+
if (data instanceof ArrayBuffer) return data.byteLength;
|
|
14
|
+
if (ArrayBuffer.isView(data)) return data.byteLength;
|
|
15
|
+
const size = data?.size;
|
|
16
|
+
return typeof size === "number" && Number.isFinite(size) ? size : void 0;
|
|
17
|
+
}
|
|
18
|
+
function controllerInfo(controller, sanitizeUrl) {
|
|
19
|
+
if (!controller) return void 0;
|
|
20
|
+
return {
|
|
21
|
+
scriptURL: controller.scriptURL && sanitizeUrl ? sanitizeUrl(controller.scriptURL) : controller.scriptURL,
|
|
22
|
+
state: controller.state
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function messagePayload(event, captureMessageData) {
|
|
26
|
+
return {
|
|
27
|
+
dataType: dataType(event.data),
|
|
28
|
+
byteLength: dataByteLength(event.data),
|
|
29
|
+
data: captureMessageData ? (0, _loggerjs_core.normalizeValue)(event.data, {
|
|
30
|
+
maxDepth: 4,
|
|
31
|
+
maxObjectKeys: 80
|
|
32
|
+
}) : void 0,
|
|
33
|
+
origin: event.origin,
|
|
34
|
+
lastEventId: event.lastEventId
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function captureServiceWorkerIntegration(options = {}) {
|
|
38
|
+
const level = options.level ?? "debug";
|
|
39
|
+
const captureControllerChange = options.captureControllerChange ?? true;
|
|
40
|
+
const captureMessages = options.captureMessages ?? true;
|
|
41
|
+
const captureMessageErrors = options.captureMessageErrors ?? true;
|
|
42
|
+
const captureMessageData = options.captureMessageData ?? false;
|
|
43
|
+
return {
|
|
44
|
+
name: "capture-service-worker",
|
|
45
|
+
setup(api) {
|
|
46
|
+
const serviceWorker = options.serviceWorker ?? (typeof navigator === "undefined" ? void 0 : navigator.serviceWorker);
|
|
47
|
+
if (!serviceWorker?.addEventListener || !serviceWorker.removeEventListener) return;
|
|
48
|
+
const disposers = [];
|
|
49
|
+
let disposed = false;
|
|
50
|
+
const capture = api.guard((input) => {
|
|
51
|
+
if (!disposed) api.capture(input);
|
|
52
|
+
});
|
|
53
|
+
if (captureControllerChange) {
|
|
54
|
+
const onControllerChange = () => {
|
|
55
|
+
capture({
|
|
56
|
+
level,
|
|
57
|
+
message: "Service worker controller change",
|
|
58
|
+
props: { browser: {
|
|
59
|
+
kind: "service-worker",
|
|
60
|
+
event: "controllerchange",
|
|
61
|
+
controller: controllerInfo(serviceWorker.controller, options.sanitizeUrl)
|
|
62
|
+
} }
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
serviceWorker.addEventListener("controllerchange", onControllerChange);
|
|
66
|
+
disposers.push(() => serviceWorker.removeEventListener("controllerchange", onControllerChange));
|
|
67
|
+
}
|
|
68
|
+
if (captureMessages) {
|
|
69
|
+
const onMessage = (event) => {
|
|
70
|
+
capture({
|
|
71
|
+
level,
|
|
72
|
+
message: "Service worker message",
|
|
73
|
+
props: { browser: {
|
|
74
|
+
kind: "service-worker",
|
|
75
|
+
event: "message",
|
|
76
|
+
message: messagePayload(event, captureMessageData)
|
|
77
|
+
} }
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
serviceWorker.addEventListener("message", onMessage);
|
|
81
|
+
disposers.push(() => serviceWorker.removeEventListener("message", onMessage));
|
|
82
|
+
}
|
|
83
|
+
if (captureMessageErrors) {
|
|
84
|
+
const onMessageError = (event) => {
|
|
85
|
+
capture({
|
|
86
|
+
level: "warn",
|
|
87
|
+
message: "Service worker message error",
|
|
88
|
+
props: { browser: {
|
|
89
|
+
kind: "service-worker",
|
|
90
|
+
event: "messageerror",
|
|
91
|
+
type: event.type
|
|
92
|
+
} }
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
serviceWorker.addEventListener("messageerror", onMessageError);
|
|
96
|
+
disposers.push(() => serviceWorker.removeEventListener("messageerror", onMessageError));
|
|
97
|
+
}
|
|
98
|
+
return () => {
|
|
99
|
+
if (disposed) return;
|
|
100
|
+
disposed = true;
|
|
101
|
+
for (const dispose of disposers) dispose();
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
exports.captureServiceWorkerIntegration = captureServiceWorkerIntegration;
|
|
108
|
+
|
|
109
|
+
//# sourceMappingURL=service-worker-integration.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-worker-integration.cjs","names":["record","message"],"sources":["../src/service-worker-integration.ts"],"sourcesContent":["import {\n normalizeValue,\n type Integration,\n type IntegrationSetupContext,\n type LoggerLevel,\n} from \"@loggerjs/core\";\n\nexport interface BrowserServiceWorkerControllerLike {\n scriptURL?: string;\n state?: string;\n}\n\nexport interface BrowserServiceWorkerContainerEventsLike {\n controller?: BrowserServiceWorkerControllerLike | null;\n addEventListener: (type: string, listener: EventListenerOrEventListenerObject) => void;\n removeEventListener: (type: string, listener: EventListenerOrEventListenerObject) => void;\n}\n\nexport interface BrowserServiceWorkerMessagePayload {\n dataType: string;\n byteLength?: number;\n data?: unknown;\n origin?: string;\n lastEventId?: string;\n}\n\nexport interface CaptureServiceWorkerOptions {\n level?: LoggerLevel;\n captureControllerChange?: boolean;\n captureMessages?: boolean;\n captureMessageErrors?: boolean;\n captureMessageData?: boolean;\n serviceWorker?: BrowserServiceWorkerContainerEventsLike;\n sanitizeUrl?: (url: string) => string;\n}\n\nconst textEncoder = new TextEncoder();\n\nfunction dataType(data: unknown): string {\n if (typeof data === \"string\") return \"string\";\n if (data instanceof ArrayBuffer) return \"arraybuffer\";\n if (ArrayBuffer.isView(data)) return data.constructor.name;\n const record = data as { constructor?: { name?: string } } | undefined;\n return record?.constructor?.name ?? typeof data;\n}\n\nfunction dataByteLength(data: unknown): number | undefined {\n if (typeof data === \"string\") return textEncoder.encode(data).byteLength;\n if (data instanceof ArrayBuffer) return data.byteLength;\n if (ArrayBuffer.isView(data)) return data.byteLength;\n const size = (data as { size?: unknown } | undefined)?.size;\n return typeof size === \"number\" && Number.isFinite(size) ? size : undefined;\n}\n\nfunction controllerInfo(\n controller: BrowserServiceWorkerControllerLike | null | undefined,\n sanitizeUrl: ((url: string) => string) | undefined,\n) {\n if (!controller) return undefined;\n return {\n scriptURL:\n controller.scriptURL && sanitizeUrl\n ? sanitizeUrl(controller.scriptURL)\n : controller.scriptURL,\n state: controller.state,\n };\n}\n\nfunction messagePayload(\n event: MessageEvent,\n captureMessageData: boolean,\n): BrowserServiceWorkerMessagePayload {\n return {\n dataType: dataType(event.data),\n byteLength: dataByteLength(event.data),\n data: captureMessageData\n ? normalizeValue(event.data, { maxDepth: 4, maxObjectKeys: 80 })\n : undefined,\n origin: event.origin,\n lastEventId: event.lastEventId,\n };\n}\n\nexport function captureServiceWorkerIntegration(\n options: CaptureServiceWorkerOptions = {},\n): Integration {\n const level = options.level ?? \"debug\";\n const captureControllerChange = options.captureControllerChange ?? true;\n const captureMessages = options.captureMessages ?? true;\n const captureMessageErrors = options.captureMessageErrors ?? true;\n const captureMessageData = options.captureMessageData ?? false;\n\n return {\n name: \"capture-service-worker\",\n setup(api: IntegrationSetupContext) {\n const serviceWorker =\n options.serviceWorker ??\n (typeof navigator === \"undefined\" ? undefined : navigator.serviceWorker);\n if (!serviceWorker?.addEventListener || !serviceWorker.removeEventListener) return;\n const disposers: Array<() => void> = [];\n let disposed = false;\n\n const capture = api.guard((input: Parameters<IntegrationSetupContext[\"capture\"]>[0]) => {\n if (!disposed) api.capture(input);\n });\n\n if (captureControllerChange) {\n const onControllerChange = () => {\n capture({\n level,\n message: \"Service worker controller change\",\n props: {\n browser: {\n kind: \"service-worker\",\n event: \"controllerchange\",\n controller: controllerInfo(serviceWorker.controller, options.sanitizeUrl),\n },\n },\n });\n };\n serviceWorker.addEventListener(\"controllerchange\", onControllerChange);\n disposers.push(() =>\n serviceWorker.removeEventListener(\"controllerchange\", onControllerChange),\n );\n }\n\n if (captureMessages) {\n const onMessage = (event: Event) => {\n const message = event as MessageEvent;\n capture({\n level,\n message: \"Service worker message\",\n props: {\n browser: {\n kind: \"service-worker\",\n event: \"message\",\n message: messagePayload(message, captureMessageData),\n },\n },\n });\n };\n serviceWorker.addEventListener(\"message\", onMessage);\n disposers.push(() => serviceWorker.removeEventListener(\"message\", onMessage));\n }\n\n if (captureMessageErrors) {\n const onMessageError = (event: Event) => {\n capture({\n level: \"warn\",\n message: \"Service worker message error\",\n props: {\n browser: {\n kind: \"service-worker\",\n event: \"messageerror\",\n type: event.type,\n },\n },\n });\n };\n serviceWorker.addEventListener(\"messageerror\", onMessageError);\n disposers.push(() => serviceWorker.removeEventListener(\"messageerror\", onMessageError));\n }\n\n return () => {\n if (disposed) return;\n disposed = true;\n for (const dispose of disposers) dispose();\n };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { normalizeValue } from "@loggerjs/core";
|
|
2
|
+
//#region src/service-worker-integration.ts
|
|
3
|
+
const textEncoder = new TextEncoder();
|
|
4
|
+
function dataType(data) {
|
|
5
|
+
if (typeof data === "string") return "string";
|
|
6
|
+
if (data instanceof ArrayBuffer) return "arraybuffer";
|
|
7
|
+
if (ArrayBuffer.isView(data)) return data.constructor.name;
|
|
8
|
+
return data?.constructor?.name ?? typeof data;
|
|
9
|
+
}
|
|
10
|
+
function dataByteLength(data) {
|
|
11
|
+
if (typeof data === "string") return textEncoder.encode(data).byteLength;
|
|
12
|
+
if (data instanceof ArrayBuffer) return data.byteLength;
|
|
13
|
+
if (ArrayBuffer.isView(data)) return data.byteLength;
|
|
14
|
+
const size = data?.size;
|
|
15
|
+
return typeof size === "number" && Number.isFinite(size) ? size : void 0;
|
|
16
|
+
}
|
|
17
|
+
function controllerInfo(controller, sanitizeUrl) {
|
|
18
|
+
if (!controller) return void 0;
|
|
19
|
+
return {
|
|
20
|
+
scriptURL: controller.scriptURL && sanitizeUrl ? sanitizeUrl(controller.scriptURL) : controller.scriptURL,
|
|
21
|
+
state: controller.state
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function messagePayload(event, captureMessageData) {
|
|
25
|
+
return {
|
|
26
|
+
dataType: dataType(event.data),
|
|
27
|
+
byteLength: dataByteLength(event.data),
|
|
28
|
+
data: captureMessageData ? normalizeValue(event.data, {
|
|
29
|
+
maxDepth: 4,
|
|
30
|
+
maxObjectKeys: 80
|
|
31
|
+
}) : void 0,
|
|
32
|
+
origin: event.origin,
|
|
33
|
+
lastEventId: event.lastEventId
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function captureServiceWorkerIntegration(options = {}) {
|
|
37
|
+
const level = options.level ?? "debug";
|
|
38
|
+
const captureControllerChange = options.captureControllerChange ?? true;
|
|
39
|
+
const captureMessages = options.captureMessages ?? true;
|
|
40
|
+
const captureMessageErrors = options.captureMessageErrors ?? true;
|
|
41
|
+
const captureMessageData = options.captureMessageData ?? false;
|
|
42
|
+
return {
|
|
43
|
+
name: "capture-service-worker",
|
|
44
|
+
setup(api) {
|
|
45
|
+
const serviceWorker = options.serviceWorker ?? (typeof navigator === "undefined" ? void 0 : navigator.serviceWorker);
|
|
46
|
+
if (!serviceWorker?.addEventListener || !serviceWorker.removeEventListener) return;
|
|
47
|
+
const disposers = [];
|
|
48
|
+
let disposed = false;
|
|
49
|
+
const capture = api.guard((input) => {
|
|
50
|
+
if (!disposed) api.capture(input);
|
|
51
|
+
});
|
|
52
|
+
if (captureControllerChange) {
|
|
53
|
+
const onControllerChange = () => {
|
|
54
|
+
capture({
|
|
55
|
+
level,
|
|
56
|
+
message: "Service worker controller change",
|
|
57
|
+
props: { browser: {
|
|
58
|
+
kind: "service-worker",
|
|
59
|
+
event: "controllerchange",
|
|
60
|
+
controller: controllerInfo(serviceWorker.controller, options.sanitizeUrl)
|
|
61
|
+
} }
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
serviceWorker.addEventListener("controllerchange", onControllerChange);
|
|
65
|
+
disposers.push(() => serviceWorker.removeEventListener("controllerchange", onControllerChange));
|
|
66
|
+
}
|
|
67
|
+
if (captureMessages) {
|
|
68
|
+
const onMessage = (event) => {
|
|
69
|
+
capture({
|
|
70
|
+
level,
|
|
71
|
+
message: "Service worker message",
|
|
72
|
+
props: { browser: {
|
|
73
|
+
kind: "service-worker",
|
|
74
|
+
event: "message",
|
|
75
|
+
message: messagePayload(event, captureMessageData)
|
|
76
|
+
} }
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
serviceWorker.addEventListener("message", onMessage);
|
|
80
|
+
disposers.push(() => serviceWorker.removeEventListener("message", onMessage));
|
|
81
|
+
}
|
|
82
|
+
if (captureMessageErrors) {
|
|
83
|
+
const onMessageError = (event) => {
|
|
84
|
+
capture({
|
|
85
|
+
level: "warn",
|
|
86
|
+
message: "Service worker message error",
|
|
87
|
+
props: { browser: {
|
|
88
|
+
kind: "service-worker",
|
|
89
|
+
event: "messageerror",
|
|
90
|
+
type: event.type
|
|
91
|
+
} }
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
serviceWorker.addEventListener("messageerror", onMessageError);
|
|
95
|
+
disposers.push(() => serviceWorker.removeEventListener("messageerror", onMessageError));
|
|
96
|
+
}
|
|
97
|
+
return () => {
|
|
98
|
+
if (disposed) return;
|
|
99
|
+
disposed = true;
|
|
100
|
+
for (const dispose of disposers) dispose();
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
export { captureServiceWorkerIntegration };
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=service-worker-integration.js.map
|