@loggerjs/browser 0.5.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.
Files changed (103) hide show
  1. package/dist/broadcast-channel-transport.cjs +115 -0
  2. package/dist/broadcast-channel-transport.cjs.map +1 -0
  3. package/dist/broadcast-channel-transport.js +114 -0
  4. package/dist/broadcast-channel-transport.js.map +1 -0
  5. package/dist/console-integration.cjs +98 -0
  6. package/dist/console-integration.cjs.map +1 -0
  7. package/dist/console-integration.js +97 -0
  8. package/dist/console-integration.js.map +1 -0
  9. package/dist/context-propagation-integration.cjs +78 -0
  10. package/dist/context-propagation-integration.cjs.map +1 -0
  11. package/dist/context-propagation-integration.js +77 -0
  12. package/dist/context-propagation-integration.js.map +1 -0
  13. package/dist/error-integration.cjs +128 -0
  14. package/dist/error-integration.cjs.map +1 -0
  15. package/dist/error-integration.js +127 -0
  16. package/dist/error-integration.js.map +1 -0
  17. package/dist/fetch-integration.cjs +120 -0
  18. package/dist/fetch-integration.cjs.map +1 -0
  19. package/dist/fetch-integration.js +119 -0
  20. package/dist/fetch-integration.js.map +1 -0
  21. package/dist/framework-error-integration.cjs +102 -0
  22. package/dist/framework-error-integration.cjs.map +1 -0
  23. package/dist/framework-error-integration.js +101 -0
  24. package/dist/framework-error-integration.js.map +1 -0
  25. package/dist/framework-router-integrations.cjs +88 -0
  26. package/dist/framework-router-integrations.cjs.map +1 -0
  27. package/dist/framework-router-integrations.js +84 -0
  28. package/dist/framework-router-integrations.js.map +1 -0
  29. package/dist/http-transport.cjs +301 -0
  30. package/dist/http-transport.cjs.map +1 -0
  31. package/dist/http-transport.js +299 -0
  32. package/dist/http-transport.js.map +1 -0
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/indexeddb-offline-queue.cjs +105 -0
  36. package/dist/indexeddb-offline-queue.cjs.map +1 -0
  37. package/dist/indexeddb-offline-queue.js +104 -0
  38. package/dist/indexeddb-offline-queue.js.map +1 -0
  39. package/dist/indexeddb-transport.cjs +531 -0
  40. package/dist/indexeddb-transport.cjs.map +1 -0
  41. package/dist/indexeddb-transport.js +530 -0
  42. package/dist/indexeddb-transport.js.map +1 -0
  43. package/dist/offline-first-transport.cjs +675 -0
  44. package/dist/offline-first-transport.cjs.map +1 -0
  45. package/dist/offline-first-transport.js +674 -0
  46. package/dist/offline-first-transport.js.map +1 -0
  47. package/dist/page-lifecycle.cjs +39 -0
  48. package/dist/page-lifecycle.cjs.map +1 -0
  49. package/dist/page-lifecycle.js +38 -0
  50. package/dist/page-lifecycle.js.map +1 -0
  51. package/dist/payload-transforms.cjs +30 -0
  52. package/dist/payload-transforms.cjs.map +1 -0
  53. package/dist/payload-transforms.js +29 -0
  54. package/dist/payload-transforms.js.map +1 -0
  55. package/dist/performance-integration.cjs +135 -0
  56. package/dist/performance-integration.cjs.map +1 -0
  57. package/dist/performance-integration.js +133 -0
  58. package/dist/performance-integration.js.map +1 -0
  59. package/dist/reporting-integration.cjs +110 -0
  60. package/dist/reporting-integration.cjs.map +1 -0
  61. package/dist/reporting-integration.js +109 -0
  62. package/dist/reporting-integration.js.map +1 -0
  63. package/dist/router-integration.cjs +97 -0
  64. package/dist/router-integration.cjs.map +1 -0
  65. package/dist/router-integration.js +96 -0
  66. package/dist/router-integration.js.map +1 -0
  67. package/dist/runtime-host-integration.cjs +181 -0
  68. package/dist/runtime-host-integration.cjs.map +1 -0
  69. package/dist/runtime-host-integration.js +180 -0
  70. package/dist/runtime-host-integration.js.map +1 -0
  71. package/dist/service-worker-integration.cjs +109 -0
  72. package/dist/service-worker-integration.cjs.map +1 -0
  73. package/dist/service-worker-integration.js +108 -0
  74. package/dist/service-worker-integration.js.map +1 -0
  75. package/dist/service-worker-transport.cjs +184 -0
  76. package/dist/service-worker-transport.cjs.map +1 -0
  77. package/dist/service-worker-transport.js +183 -0
  78. package/dist/service-worker-transport.js.map +1 -0
  79. package/dist/user-action-integration.cjs +100 -0
  80. package/dist/user-action-integration.cjs.map +1 -0
  81. package/dist/user-action-integration.js +99 -0
  82. package/dist/user-action-integration.js.map +1 -0
  83. package/dist/web-vitals-integration.cjs +157 -0
  84. package/dist/web-vitals-integration.cjs.map +1 -0
  85. package/dist/web-vitals-integration.js +156 -0
  86. package/dist/web-vitals-integration.js.map +1 -0
  87. package/dist/websocket-integration.cjs +158 -0
  88. package/dist/websocket-integration.cjs.map +1 -0
  89. package/dist/websocket-integration.js +157 -0
  90. package/dist/websocket-integration.js.map +1 -0
  91. package/dist/websocket-transport.cjs +187 -0
  92. package/dist/websocket-transport.cjs.map +1 -0
  93. package/dist/websocket-transport.js +186 -0
  94. package/dist/websocket-transport.js.map +1 -0
  95. package/dist/xhr-integration.cjs +107 -0
  96. package/dist/xhr-integration.cjs.map +1 -0
  97. package/dist/xhr-integration.js +106 -0
  98. package/dist/xhr-integration.js.map +1 -0
  99. package/dist/zip-export.cjs +241 -0
  100. package/dist/zip-export.cjs.map +1 -0
  101. package/dist/zip-export.js +238 -0
  102. package/dist/zip-export.js.map +1 -0
  103. 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