@loggerjs/browser 0.5.0 → 0.5.2

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,107 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/http-capture-utils.ts
3
+ function nowMs() {
4
+ return typeof performance !== "undefined" ? performance.now() : Date.now();
5
+ }
6
+ function durationMs(started) {
7
+ return nowMs() - started;
8
+ }
9
+ function sanitizeHttpUrl(rawUrl, sanitizer) {
10
+ const stripped = stripSensitiveUrlParts(rawUrl);
11
+ return sanitizer ? sanitizer(stripped) : stripped;
12
+ }
13
+ function shouldSample(sampleRate, random) {
14
+ if (sampleRate >= 1) return true;
15
+ if (sampleRate <= 0) return false;
16
+ return random() < sampleRate;
17
+ }
18
+ function stripSensitiveUrlParts(rawUrl) {
19
+ if (!/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(rawUrl)) return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
20
+ try {
21
+ const url = new URL(rawUrl);
22
+ url.search = "";
23
+ url.hash = "";
24
+ return url.toString();
25
+ } catch {
26
+ return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
27
+ }
28
+ }
29
+ //#endregion
30
+ //#region src/xhr-integration.ts
31
+ const XHR_META = "__LOGGERJS_XHR_META__";
32
+ function captureXHRIntegration(options = {}) {
33
+ const minStatus = options.minStatus ?? 400;
34
+ const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
35
+ const sampleRate = options.sampleRate ?? 1;
36
+ const random = options.random ?? Math.random;
37
+ return {
38
+ name: "capture-xhr",
39
+ setup(api) {
40
+ if (typeof XMLHttpRequest === "undefined") return;
41
+ const proto = XMLHttpRequest.prototype;
42
+ const originalOpen = proto.open;
43
+ const originalSend = proto.send;
44
+ api.unpatched.XMLHttpRequest ??= XMLHttpRequest;
45
+ const capture = api.guard((input) => api.capture(input));
46
+ proto.open = function patchedOpen(method, url, ...rest) {
47
+ this[XHR_META] = {
48
+ method: method.toUpperCase(),
49
+ url: sanitizeHttpUrl(String(url), options.sanitizeUrl),
50
+ started: 0
51
+ };
52
+ return originalOpen.apply(this, [
53
+ method,
54
+ url,
55
+ ...rest
56
+ ]);
57
+ };
58
+ proto.send = function patchedSend(body) {
59
+ const meta = this[XHR_META];
60
+ if (meta) meta.started = nowMs();
61
+ let captured = false;
62
+ const onLoadEnd = () => {
63
+ if (!meta || captured) return;
64
+ if (this.status >= minStatus || captureAll && shouldSample(sampleRate, random)) {
65
+ captured = true;
66
+ capture({
67
+ level: this.status >= minStatus ? "warn" : "debug",
68
+ message: `XHR ${this.status} ${meta.method} ${meta.url}`,
69
+ props: { http: {
70
+ kind: "xhr",
71
+ method: meta.method,
72
+ url: meta.url,
73
+ status: this.status,
74
+ durationMs: durationMs(meta.started)
75
+ } }
76
+ });
77
+ }
78
+ };
79
+ const onError = () => {
80
+ if (!meta || captured) return;
81
+ captured = true;
82
+ capture({
83
+ level: "error",
84
+ message: `XHR network error ${meta.method} ${meta.url}`,
85
+ props: { http: {
86
+ kind: "xhr",
87
+ method: meta.method,
88
+ url: meta.url,
89
+ durationMs: durationMs(meta.started)
90
+ } }
91
+ });
92
+ };
93
+ this.addEventListener("loadend", onLoadEnd, { once: true });
94
+ this.addEventListener("error", onError, { once: true });
95
+ return originalSend.apply(this, [body]);
96
+ };
97
+ return () => {
98
+ proto.open = originalOpen;
99
+ proto.send = originalSend;
100
+ };
101
+ }
102
+ };
103
+ }
104
+ //#endregion
105
+ exports.captureXHRIntegration = captureXHRIntegration;
106
+
107
+ //# sourceMappingURL=xhr-integration.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xhr-integration.cjs","names":[],"sources":["../src/http-capture-utils.ts","../src/xhr-integration.ts"],"sourcesContent":["export function nowMs(): number {\n return typeof performance !== \"undefined\" ? performance.now() : Date.now();\n}\n\nexport function durationMs(started: number): number {\n return nowMs() - started;\n}\n\nexport function sanitizeHttpUrl(rawUrl: string, sanitizer?: (url: string) => string): string {\n const stripped = stripSensitiveUrlParts(rawUrl);\n return sanitizer ? sanitizer(stripped) : stripped;\n}\n\nexport function pickAllowedHeaders(\n headers: Headers | undefined,\n allowList: readonly string[] | undefined,\n): Record<string, string> | undefined {\n if (!headers || !allowList || allowList.length === 0) return undefined;\n const out: Record<string, string> = {};\n for (const name of allowList) {\n const value = headers.get(name);\n if (value !== null) out[name.toLowerCase()] = value;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nexport function headersFromInit(headers: HeadersInit | undefined): Headers | undefined {\n if (!headers) return undefined;\n try {\n return new Headers(headers);\n } catch {\n return undefined;\n }\n}\n\nexport function shouldSample(sampleRate: number, random: () => number): boolean {\n if (sampleRate >= 1) return true;\n if (sampleRate <= 0) return false;\n return random() < sampleRate;\n}\n\nfunction stripSensitiveUrlParts(rawUrl: string): string {\n if (!/^[a-zA-Z][a-zA-Z\\d+.-]*:/.test(rawUrl)) {\n return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;\n }\n\n try {\n const url = new URL(rawUrl);\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n } catch {\n return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;\n }\n}\n","import type { CaptureInput, Integration, IntegrationSetupContext } from \"@loggerjs/core\";\nimport { durationMs, nowMs, sanitizeHttpUrl, shouldSample } from \"./http-capture-utils\";\n\nexport interface CaptureXHROptions {\n minStatus?: number;\n captureAll?: boolean;\n captureSuccessful?: boolean;\n sampleRate?: number;\n random?: () => number;\n sanitizeUrl?: (url: string) => string;\n}\n\ninterface XHRMeta {\n method: string;\n url: string;\n started: number;\n}\n\nconst XHR_META = \"__LOGGERJS_XHR_META__\";\n\nexport function captureXHRIntegration(options: CaptureXHROptions = {}): Integration {\n const minStatus = options.minStatus ?? 400;\n const captureAll = options.captureAll ?? options.captureSuccessful ?? false;\n const sampleRate = options.sampleRate ?? 1;\n const random = options.random ?? Math.random;\n\n return {\n name: \"capture-xhr\",\n setup(api: IntegrationSetupContext) {\n if (typeof XMLHttpRequest === \"undefined\") return;\n const proto = XMLHttpRequest.prototype;\n const originalOpen = proto.open;\n const originalSend = proto.send;\n api.unpatched.XMLHttpRequest ??= XMLHttpRequest as unknown;\n const capture = api.guard((input: CaptureInput) => api.capture(input));\n\n proto.open = function patchedOpen(\n this: XMLHttpRequest,\n method: string,\n url: string | URL,\n ...rest: unknown[]\n ) {\n (this as unknown as Record<string, XHRMeta>)[XHR_META] = {\n method: method.toUpperCase(),\n url: sanitizeHttpUrl(String(url), options.sanitizeUrl),\n started: 0,\n };\n return (originalOpen as unknown as (...args: unknown[]) => void).apply(this, [\n method,\n url,\n ...rest,\n ]);\n };\n\n proto.send = function patchedSend(\n this: XMLHttpRequest,\n body?: Document | XMLHttpRequestBodyInit | null,\n ) {\n const meta = (this as unknown as Record<string, XHRMeta>)[XHR_META];\n if (meta) meta.started = nowMs();\n let captured = false;\n\n const onLoadEnd = () => {\n if (!meta || captured) return;\n const shouldCapture =\n this.status >= minStatus || (captureAll && shouldSample(sampleRate, random));\n if (shouldCapture) {\n captured = true;\n capture({\n level: this.status >= minStatus ? \"warn\" : \"debug\",\n message: `XHR ${this.status} ${meta.method} ${meta.url}`,\n props: {\n http: {\n kind: \"xhr\",\n method: meta.method,\n url: meta.url,\n status: this.status,\n durationMs: durationMs(meta.started),\n },\n },\n });\n }\n };\n\n const onError = () => {\n if (!meta || captured) return;\n captured = true;\n capture({\n level: \"error\",\n message: `XHR network error ${meta.method} ${meta.url}`,\n props: {\n http: {\n kind: \"xhr\",\n method: meta.method,\n url: meta.url,\n durationMs: durationMs(meta.started),\n },\n },\n });\n };\n\n this.addEventListener(\"loadend\", onLoadEnd, { once: true });\n this.addEventListener(\"error\", onError, { once: true });\n return (originalSend as unknown as (...args: unknown[]) => void).apply(this, [body]);\n };\n\n return () => {\n proto.open = originalOpen;\n proto.send = originalSend;\n };\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ //#region src/http-capture-utils.ts
2
+ function nowMs() {
3
+ return typeof performance !== "undefined" ? performance.now() : Date.now();
4
+ }
5
+ function durationMs(started) {
6
+ return nowMs() - started;
7
+ }
8
+ function sanitizeHttpUrl(rawUrl, sanitizer) {
9
+ const stripped = stripSensitiveUrlParts(rawUrl);
10
+ return sanitizer ? sanitizer(stripped) : stripped;
11
+ }
12
+ function shouldSample(sampleRate, random) {
13
+ if (sampleRate >= 1) return true;
14
+ if (sampleRate <= 0) return false;
15
+ return random() < sampleRate;
16
+ }
17
+ function stripSensitiveUrlParts(rawUrl) {
18
+ if (!/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(rawUrl)) return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
19
+ try {
20
+ const url = new URL(rawUrl);
21
+ url.search = "";
22
+ url.hash = "";
23
+ return url.toString();
24
+ } catch {
25
+ return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
26
+ }
27
+ }
28
+ //#endregion
29
+ //#region src/xhr-integration.ts
30
+ const XHR_META = "__LOGGERJS_XHR_META__";
31
+ function captureXHRIntegration(options = {}) {
32
+ const minStatus = options.minStatus ?? 400;
33
+ const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
34
+ const sampleRate = options.sampleRate ?? 1;
35
+ const random = options.random ?? Math.random;
36
+ return {
37
+ name: "capture-xhr",
38
+ setup(api) {
39
+ if (typeof XMLHttpRequest === "undefined") return;
40
+ const proto = XMLHttpRequest.prototype;
41
+ const originalOpen = proto.open;
42
+ const originalSend = proto.send;
43
+ api.unpatched.XMLHttpRequest ??= XMLHttpRequest;
44
+ const capture = api.guard((input) => api.capture(input));
45
+ proto.open = function patchedOpen(method, url, ...rest) {
46
+ this[XHR_META] = {
47
+ method: method.toUpperCase(),
48
+ url: sanitizeHttpUrl(String(url), options.sanitizeUrl),
49
+ started: 0
50
+ };
51
+ return originalOpen.apply(this, [
52
+ method,
53
+ url,
54
+ ...rest
55
+ ]);
56
+ };
57
+ proto.send = function patchedSend(body) {
58
+ const meta = this[XHR_META];
59
+ if (meta) meta.started = nowMs();
60
+ let captured = false;
61
+ const onLoadEnd = () => {
62
+ if (!meta || captured) return;
63
+ if (this.status >= minStatus || captureAll && shouldSample(sampleRate, random)) {
64
+ captured = true;
65
+ capture({
66
+ level: this.status >= minStatus ? "warn" : "debug",
67
+ message: `XHR ${this.status} ${meta.method} ${meta.url}`,
68
+ props: { http: {
69
+ kind: "xhr",
70
+ method: meta.method,
71
+ url: meta.url,
72
+ status: this.status,
73
+ durationMs: durationMs(meta.started)
74
+ } }
75
+ });
76
+ }
77
+ };
78
+ const onError = () => {
79
+ if (!meta || captured) return;
80
+ captured = true;
81
+ capture({
82
+ level: "error",
83
+ message: `XHR network error ${meta.method} ${meta.url}`,
84
+ props: { http: {
85
+ kind: "xhr",
86
+ method: meta.method,
87
+ url: meta.url,
88
+ durationMs: durationMs(meta.started)
89
+ } }
90
+ });
91
+ };
92
+ this.addEventListener("loadend", onLoadEnd, { once: true });
93
+ this.addEventListener("error", onError, { once: true });
94
+ return originalSend.apply(this, [body]);
95
+ };
96
+ return () => {
97
+ proto.open = originalOpen;
98
+ proto.send = originalSend;
99
+ };
100
+ }
101
+ };
102
+ }
103
+ //#endregion
104
+ export { captureXHRIntegration };
105
+
106
+ //# sourceMappingURL=xhr-integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xhr-integration.js","names":[],"sources":["../src/http-capture-utils.ts","../src/xhr-integration.ts"],"sourcesContent":["export function nowMs(): number {\n return typeof performance !== \"undefined\" ? performance.now() : Date.now();\n}\n\nexport function durationMs(started: number): number {\n return nowMs() - started;\n}\n\nexport function sanitizeHttpUrl(rawUrl: string, sanitizer?: (url: string) => string): string {\n const stripped = stripSensitiveUrlParts(rawUrl);\n return sanitizer ? sanitizer(stripped) : stripped;\n}\n\nexport function pickAllowedHeaders(\n headers: Headers | undefined,\n allowList: readonly string[] | undefined,\n): Record<string, string> | undefined {\n if (!headers || !allowList || allowList.length === 0) return undefined;\n const out: Record<string, string> = {};\n for (const name of allowList) {\n const value = headers.get(name);\n if (value !== null) out[name.toLowerCase()] = value;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nexport function headersFromInit(headers: HeadersInit | undefined): Headers | undefined {\n if (!headers) return undefined;\n try {\n return new Headers(headers);\n } catch {\n return undefined;\n }\n}\n\nexport function shouldSample(sampleRate: number, random: () => number): boolean {\n if (sampleRate >= 1) return true;\n if (sampleRate <= 0) return false;\n return random() < sampleRate;\n}\n\nfunction stripSensitiveUrlParts(rawUrl: string): string {\n if (!/^[a-zA-Z][a-zA-Z\\d+.-]*:/.test(rawUrl)) {\n return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;\n }\n\n try {\n const url = new URL(rawUrl);\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n } catch {\n return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;\n }\n}\n","import type { CaptureInput, Integration, IntegrationSetupContext } from \"@loggerjs/core\";\nimport { durationMs, nowMs, sanitizeHttpUrl, shouldSample } from \"./http-capture-utils\";\n\nexport interface CaptureXHROptions {\n minStatus?: number;\n captureAll?: boolean;\n captureSuccessful?: boolean;\n sampleRate?: number;\n random?: () => number;\n sanitizeUrl?: (url: string) => string;\n}\n\ninterface XHRMeta {\n method: string;\n url: string;\n started: number;\n}\n\nconst XHR_META = \"__LOGGERJS_XHR_META__\";\n\nexport function captureXHRIntegration(options: CaptureXHROptions = {}): Integration {\n const minStatus = options.minStatus ?? 400;\n const captureAll = options.captureAll ?? options.captureSuccessful ?? false;\n const sampleRate = options.sampleRate ?? 1;\n const random = options.random ?? Math.random;\n\n return {\n name: \"capture-xhr\",\n setup(api: IntegrationSetupContext) {\n if (typeof XMLHttpRequest === \"undefined\") return;\n const proto = XMLHttpRequest.prototype;\n const originalOpen = proto.open;\n const originalSend = proto.send;\n api.unpatched.XMLHttpRequest ??= XMLHttpRequest as unknown;\n const capture = api.guard((input: CaptureInput) => api.capture(input));\n\n proto.open = function patchedOpen(\n this: XMLHttpRequest,\n method: string,\n url: string | URL,\n ...rest: unknown[]\n ) {\n (this as unknown as Record<string, XHRMeta>)[XHR_META] = {\n method: method.toUpperCase(),\n url: sanitizeHttpUrl(String(url), options.sanitizeUrl),\n started: 0,\n };\n return (originalOpen as unknown as (...args: unknown[]) => void).apply(this, [\n method,\n url,\n ...rest,\n ]);\n };\n\n proto.send = function patchedSend(\n this: XMLHttpRequest,\n body?: Document | XMLHttpRequestBodyInit | null,\n ) {\n const meta = (this as unknown as Record<string, XHRMeta>)[XHR_META];\n if (meta) meta.started = nowMs();\n let captured = false;\n\n const onLoadEnd = () => {\n if (!meta || captured) return;\n const shouldCapture =\n this.status >= minStatus || (captureAll && shouldSample(sampleRate, random));\n if (shouldCapture) {\n captured = true;\n capture({\n level: this.status >= minStatus ? \"warn\" : \"debug\",\n message: `XHR ${this.status} ${meta.method} ${meta.url}`,\n props: {\n http: {\n kind: \"xhr\",\n method: meta.method,\n url: meta.url,\n status: this.status,\n durationMs: durationMs(meta.started),\n },\n },\n });\n }\n };\n\n const onError = () => {\n if (!meta || captured) return;\n captured = true;\n capture({\n level: \"error\",\n message: `XHR network error ${meta.method} ${meta.url}`,\n props: {\n http: {\n kind: \"xhr\",\n method: meta.method,\n url: meta.url,\n durationMs: durationMs(meta.started),\n },\n },\n });\n };\n\n this.addEventListener(\"loadend\", onLoadEnd, { once: true });\n this.addEventListener(\"error\", onError, { once: true });\n return (originalSend as unknown as (...args: unknown[]) => void).apply(this, [body]);\n };\n\n return () => {\n proto.open = originalOpen;\n proto.send = originalSend;\n };\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,241 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _loggerjs_core = require("@loggerjs/core");
3
+ //#region src/zip-export.ts
4
+ const ZIP_UTF8_FLAG = 2048;
5
+ const ZIP_STORE_METHOD = 0;
6
+ const ZIP_VERSION = 20;
7
+ const ZIP_MAX_UINT16 = 65535;
8
+ const ZIP_MAX_UINT32 = 4294967295;
9
+ const LOCAL_FILE_HEADER_SIGNATURE = 67324752;
10
+ const CENTRAL_DIRECTORY_SIGNATURE = 33639248;
11
+ const END_OF_CENTRAL_DIRECTORY_SIGNATURE = 101010256;
12
+ let crc32Table;
13
+ function getCrc32Table() {
14
+ if (crc32Table) return crc32Table;
15
+ const table = new Uint32Array(256);
16
+ for (let index = 0; index < table.length; index += 1) {
17
+ let crc = index;
18
+ for (let bit = 0; bit < 8; bit += 1) crc = crc & 1 ? 3988292384 ^ crc >>> 1 : crc >>> 1;
19
+ table[index] = crc >>> 0;
20
+ }
21
+ crc32Table = table;
22
+ return table;
23
+ }
24
+ function crc32(bytes) {
25
+ const table = getCrc32Table();
26
+ let crc = 4294967295;
27
+ for (const byte of bytes) crc = crc >>> 8 ^ (table[(crc ^ byte) & 255] ?? 0);
28
+ return (crc ^ 4294967295) >>> 0;
29
+ }
30
+ function encodeText(value) {
31
+ return new TextEncoder().encode(value);
32
+ }
33
+ function normalizeFileContent(content) {
34
+ if (typeof content === "string") return encodeText(content);
35
+ if (content instanceof Uint8Array) return content;
36
+ return new Uint8Array(content);
37
+ }
38
+ function normalizeZipFileName(name) {
39
+ const normalized = name.replace(/\\/g, "/").split("/").filter((part) => part.length > 0 && part !== "." && part !== "..").join("/");
40
+ if (!normalized) throw new Error("ZIP file name cannot be empty");
41
+ return normalized;
42
+ }
43
+ function toDosDateTime(timestamp) {
44
+ const date = new Date(Number.isFinite(timestamp) ? timestamp : Date.now());
45
+ const year = Math.min(2107, Math.max(1980, date.getFullYear()));
46
+ return {
47
+ time: date.getHours() << 11 | date.getMinutes() << 5 | Math.floor(date.getSeconds() / 2),
48
+ date: year - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()
49
+ };
50
+ }
51
+ function assertZipUint16(value, label) {
52
+ if (!Number.isSafeInteger(value) || value < 0 || value > ZIP_MAX_UINT16) throw new Error(`${label} exceeds ZIP32 limit`);
53
+ }
54
+ function assertZipUint32(value, label) {
55
+ if (!Number.isSafeInteger(value) || value < 0 || value > ZIP_MAX_UINT32) throw new Error(`${label} exceeds ZIP32 limit`);
56
+ }
57
+ function createLocalFileHeader(file) {
58
+ const header = new Uint8Array(30);
59
+ const view = new DataView(header.buffer);
60
+ view.setUint32(0, LOCAL_FILE_HEADER_SIGNATURE, true);
61
+ view.setUint16(4, ZIP_VERSION, true);
62
+ view.setUint16(6, ZIP_UTF8_FLAG, true);
63
+ view.setUint16(8, ZIP_STORE_METHOD, true);
64
+ view.setUint16(10, file.modifiedTime, true);
65
+ view.setUint16(12, file.modifiedDate, true);
66
+ view.setUint32(14, file.crc32, true);
67
+ view.setUint32(18, file.contentBytes.byteLength, true);
68
+ view.setUint32(22, file.contentBytes.byteLength, true);
69
+ view.setUint16(26, file.nameBytes.byteLength, true);
70
+ view.setUint16(28, 0, true);
71
+ return header;
72
+ }
73
+ function createCentralDirectoryHeader(file) {
74
+ const header = new Uint8Array(46);
75
+ const view = new DataView(header.buffer);
76
+ view.setUint32(0, CENTRAL_DIRECTORY_SIGNATURE, true);
77
+ view.setUint16(4, ZIP_VERSION, true);
78
+ view.setUint16(6, ZIP_VERSION, true);
79
+ view.setUint16(8, ZIP_UTF8_FLAG, true);
80
+ view.setUint16(10, ZIP_STORE_METHOD, true);
81
+ view.setUint16(12, file.modifiedTime, true);
82
+ view.setUint16(14, file.modifiedDate, true);
83
+ view.setUint32(16, file.crc32, true);
84
+ view.setUint32(20, file.contentBytes.byteLength, true);
85
+ view.setUint32(24, file.contentBytes.byteLength, true);
86
+ view.setUint16(28, file.nameBytes.byteLength, true);
87
+ view.setUint16(30, 0, true);
88
+ view.setUint16(32, 0, true);
89
+ view.setUint16(34, 0, true);
90
+ view.setUint16(36, 0, true);
91
+ view.setUint32(38, 0, true);
92
+ view.setUint32(42, file.localHeaderOffset, true);
93
+ return header;
94
+ }
95
+ function createEndOfCentralDirectory(fileCount, centralDirectorySize, centralDirectoryOffset) {
96
+ assertZipUint16(fileCount, "ZIP file count");
97
+ assertZipUint32(centralDirectorySize, "ZIP central directory size");
98
+ assertZipUint32(centralDirectoryOffset, "ZIP central directory offset");
99
+ const footer = new Uint8Array(22);
100
+ const view = new DataView(footer.buffer);
101
+ view.setUint32(0, END_OF_CENTRAL_DIRECTORY_SIGNATURE, true);
102
+ view.setUint16(4, 0, true);
103
+ view.setUint16(6, 0, true);
104
+ view.setUint16(8, fileCount, true);
105
+ view.setUint16(10, fileCount, true);
106
+ view.setUint32(12, centralDirectorySize, true);
107
+ view.setUint32(16, centralDirectoryOffset, true);
108
+ view.setUint16(20, 0, true);
109
+ return footer;
110
+ }
111
+ function createNormalizedZipFiles(files, options) {
112
+ const names = /* @__PURE__ */ new Set();
113
+ let offset = 0;
114
+ return files.map((file) => {
115
+ const name = normalizeZipFileName(file.name);
116
+ if (names.has(name)) throw new Error(`Duplicate ZIP file name: ${name}`);
117
+ names.add(name);
118
+ const nameBytes = encodeText(name);
119
+ const contentBytes = normalizeFileContent(file.content);
120
+ assertZipUint16(nameBytes.byteLength, `ZIP file name ${name}`);
121
+ assertZipUint32(contentBytes.byteLength, `ZIP file ${name}`);
122
+ assertZipUint32(offset, "ZIP local header offset");
123
+ const modified = toDosDateTime(file.lastModified ?? options.lastModified ?? Date.now());
124
+ const normalized = {
125
+ name,
126
+ nameBytes,
127
+ contentBytes,
128
+ crc32: crc32(contentBytes),
129
+ modifiedTime: modified.time,
130
+ modifiedDate: modified.date,
131
+ localHeaderOffset: offset
132
+ };
133
+ offset += 30 + nameBytes.byteLength + contentBytes.byteLength;
134
+ assertZipUint32(offset, "ZIP local data size");
135
+ return normalized;
136
+ });
137
+ }
138
+ function isQuerySource(source) {
139
+ return "query" in source && typeof source.query === "function";
140
+ }
141
+ function resolveSource(source, query) {
142
+ if (isQuerySource(source)) return source.query(query);
143
+ if (query) throw new Error("Log ZIP export query options require a source with query()");
144
+ return source;
145
+ }
146
+ function normalizeMaxEvents(value) {
147
+ if (value === void 0) return void 0;
148
+ if (!Number.isFinite(value)) return void 0;
149
+ return Math.max(0, Math.floor(value));
150
+ }
151
+ function createLogZipBlob(files, options = {}) {
152
+ const normalizedFiles = createNormalizedZipFiles(files, options);
153
+ const parts = [];
154
+ let centralDirectoryOffset = 0;
155
+ for (const file of normalizedFiles) {
156
+ parts.push(createLocalFileHeader(file), file.nameBytes, file.contentBytes);
157
+ centralDirectoryOffset += 30 + file.nameBytes.byteLength + file.contentBytes.byteLength;
158
+ }
159
+ let centralDirectorySize = 0;
160
+ for (const file of normalizedFiles) {
161
+ const header = createCentralDirectoryHeader(file);
162
+ parts.push(header, file.nameBytes);
163
+ centralDirectorySize += header.byteLength + file.nameBytes.byteLength;
164
+ }
165
+ parts.push(createEndOfCentralDirectory(normalizedFiles.length, centralDirectorySize, centralDirectoryOffset));
166
+ return new Blob(parts, { type: options.type ?? "application/zip" });
167
+ }
168
+ async function exportLogsToZip(source, options = {}) {
169
+ const format = options.format ?? "ndjson";
170
+ const createdAt = options.createdAt ?? Date.now();
171
+ const logFileName = options.logFileName ?? (format === "json" ? "logs.json" : "logs.ndjson");
172
+ const maxEvents = normalizeMaxEvents(options.maxEvents);
173
+ const serialize = options.serializeEvent ?? ((event) => (0, _loggerjs_core.safeJsonStringify)(event, options.stringify ?? {}));
174
+ const lines = [];
175
+ let skippedCount = 0;
176
+ let from;
177
+ let to;
178
+ for await (const event of resolveSource(source, options.query)) {
179
+ if (maxEvents !== void 0 && lines.length >= maxEvents) break;
180
+ const mapped = options.mapEvent ? options.mapEvent(event) : event;
181
+ if (!mapped) {
182
+ skippedCount += 1;
183
+ continue;
184
+ }
185
+ lines.push(serialize(mapped));
186
+ from = from === void 0 ? mapped.time : Math.min(from, mapped.time);
187
+ to = to === void 0 ? mapped.time : Math.max(to, mapped.time);
188
+ }
189
+ const files = [{
190
+ name: logFileName,
191
+ content: format === "json" ? `[${lines.join(",")}]` : lines.length > 0 ? `${lines.join("\n")}\n` : "",
192
+ lastModified: createdAt
193
+ }];
194
+ if (options.includeManifest ?? true) {
195
+ const manifest = {
196
+ schema: "loggerjs.log-export.v1",
197
+ createdAt: new Date(createdAt).toISOString(),
198
+ source: options.source,
199
+ format,
200
+ logFileName: normalizeZipFileName(logFileName),
201
+ logCount: lines.length,
202
+ skippedCount,
203
+ query: options.query,
204
+ timeRange: from === void 0 || to === void 0 ? void 0 : {
205
+ from,
206
+ to
207
+ }
208
+ };
209
+ files.push({
210
+ name: options.manifestFileName ?? "manifest.json",
211
+ content: `${(0, _loggerjs_core.safeJsonStringify)(manifest, { space: 2 })}\n`,
212
+ lastModified: createdAt
213
+ });
214
+ }
215
+ return createLogZipBlob(files, { lastModified: createdAt });
216
+ }
217
+ function downloadBlob(blob, filenameOrOptions = {}) {
218
+ const options = typeof filenameOrOptions === "string" ? { filename: filenameOrOptions } : filenameOrOptions;
219
+ const documentRef = options.document ?? globalThis.document;
220
+ const urlRef = options.url ?? globalThis.URL;
221
+ if (!documentRef?.body || !urlRef?.createObjectURL || !urlRef.revokeObjectURL) throw new Error("downloadBlob requires document and URL.createObjectURL");
222
+ const href = urlRef.createObjectURL(blob);
223
+ const anchor = documentRef.createElement("a");
224
+ anchor.href = href;
225
+ anchor.download = options.filename ?? "loggerjs-logs.zip";
226
+ anchor.style.display = "none";
227
+ documentRef.body.appendChild(anchor);
228
+ anchor.click();
229
+ anchor.remove();
230
+ const revoke = () => urlRef.revokeObjectURL(href);
231
+ const revokeDelayMs = options.revokeDelayMs ?? 1e3;
232
+ if (revokeDelayMs <= 0) revoke();
233
+ else setTimeout(revoke, revokeDelayMs);
234
+ return href;
235
+ }
236
+ //#endregion
237
+ exports.createLogZipBlob = createLogZipBlob;
238
+ exports.downloadBlob = downloadBlob;
239
+ exports.exportLogsToZip = exportLogsToZip;
240
+
241
+ //# sourceMappingURL=zip-export.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zip-export.cjs","names":[],"sources":["../src/zip-export.ts"],"sourcesContent":["import { safeJsonStringify, type LogEvent, type SafeStringifyOptions } from \"@loggerjs/core\";\nimport type { IndexedDbTransportQueryOptions } from \"./indexeddb-transport\";\n\nexport type LogZipExportFormat = \"ndjson\" | \"json\";\n\nexport interface ZipExportFile {\n name: string;\n content: string | Uint8Array | ArrayBuffer;\n lastModified?: number;\n}\n\nexport interface LogZipBlobOptions {\n lastModified?: number;\n type?: string;\n}\n\nexport interface LogZipExportQuerySource {\n query: (options?: IndexedDbTransportQueryOptions) => AsyncIterable<LogEvent>;\n}\n\nexport type LogZipExportSource = AsyncIterable<LogEvent> | LogZipExportQuerySource;\n\nexport interface LogZipExportManifest {\n schema: \"loggerjs.log-export.v1\";\n createdAt: string;\n source?: string;\n format: LogZipExportFormat;\n logFileName: string;\n logCount: number;\n skippedCount: number;\n query?: IndexedDbTransportQueryOptions;\n timeRange?: {\n from: number;\n to: number;\n };\n}\n\nexport interface LogZipExportOptions {\n query?: IndexedDbTransportQueryOptions;\n format?: LogZipExportFormat;\n logFileName?: string;\n includeManifest?: boolean;\n manifestFileName?: string;\n source?: string;\n createdAt?: number;\n maxEvents?: number;\n stringify?: SafeStringifyOptions;\n serializeEvent?: (event: LogEvent) => string;\n mapEvent?: (event: LogEvent) => LogEvent | false | null | undefined;\n}\n\nexport interface DownloadBlobOptions {\n filename?: string;\n document?: Document;\n url?: Pick<typeof URL, \"createObjectURL\" | \"revokeObjectURL\">;\n revokeDelayMs?: number;\n}\n\ninterface NormalizedZipFile {\n name: string;\n nameBytes: Uint8Array;\n contentBytes: Uint8Array;\n crc32: number;\n modifiedTime: number;\n modifiedDate: number;\n localHeaderOffset: number;\n}\n\nconst ZIP_UTF8_FLAG = 0x0800;\nconst ZIP_STORE_METHOD = 0;\nconst ZIP_VERSION = 20;\nconst ZIP_MAX_UINT16 = 0xffff;\nconst ZIP_MAX_UINT32 = 0xffffffff;\nconst LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;\nconst CENTRAL_DIRECTORY_SIGNATURE = 0x02014b50;\nconst END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;\n\nlet crc32Table: Uint32Array | undefined;\n\nfunction getCrc32Table(): Uint32Array {\n if (crc32Table) return crc32Table;\n const table = new Uint32Array(256);\n for (let index = 0; index < table.length; index += 1) {\n let crc = index;\n for (let bit = 0; bit < 8; bit += 1) {\n crc = crc & 1 ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1;\n }\n table[index] = crc >>> 0;\n }\n crc32Table = table;\n return table;\n}\n\nfunction crc32(bytes: Uint8Array): number {\n const table = getCrc32Table();\n let crc = 0xffffffff;\n for (const byte of bytes) {\n crc = (crc >>> 8) ^ (table[(crc ^ byte) & 0xff] ?? 0);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\nfunction encodeText(value: string): Uint8Array {\n return new TextEncoder().encode(value);\n}\n\nfunction normalizeFileContent(content: ZipExportFile[\"content\"]): Uint8Array {\n if (typeof content === \"string\") return encodeText(content);\n if (content instanceof Uint8Array) return content;\n return new Uint8Array(content);\n}\n\nfunction normalizeZipFileName(name: string): string {\n const normalized = name\n .replace(/\\\\/g, \"/\")\n .split(\"/\")\n .filter((part) => part.length > 0 && part !== \".\" && part !== \"..\")\n .join(\"/\");\n if (!normalized) throw new Error(\"ZIP file name cannot be empty\");\n return normalized;\n}\n\nfunction toDosDateTime(timestamp: number): { time: number; date: number } {\n const date = new Date(Number.isFinite(timestamp) ? timestamp : Date.now());\n const year = Math.min(2107, Math.max(1980, date.getFullYear()));\n return {\n time: (date.getHours() << 11) | (date.getMinutes() << 5) | Math.floor(date.getSeconds() / 2),\n date: ((year - 1980) << 9) | ((date.getMonth() + 1) << 5) | date.getDate(),\n };\n}\n\nfunction assertZipUint16(value: number, label: string) {\n if (!Number.isSafeInteger(value) || value < 0 || value > ZIP_MAX_UINT16) {\n throw new Error(`${label} exceeds ZIP32 limit`);\n }\n}\n\nfunction assertZipUint32(value: number, label: string) {\n if (!Number.isSafeInteger(value) || value < 0 || value > ZIP_MAX_UINT32) {\n throw new Error(`${label} exceeds ZIP32 limit`);\n }\n}\n\nfunction createLocalFileHeader(file: NormalizedZipFile): Uint8Array {\n const header = new Uint8Array(30);\n const view = new DataView(header.buffer);\n view.setUint32(0, LOCAL_FILE_HEADER_SIGNATURE, true);\n view.setUint16(4, ZIP_VERSION, true);\n view.setUint16(6, ZIP_UTF8_FLAG, true);\n view.setUint16(8, ZIP_STORE_METHOD, true);\n view.setUint16(10, file.modifiedTime, true);\n view.setUint16(12, file.modifiedDate, true);\n view.setUint32(14, file.crc32, true);\n view.setUint32(18, file.contentBytes.byteLength, true);\n view.setUint32(22, file.contentBytes.byteLength, true);\n view.setUint16(26, file.nameBytes.byteLength, true);\n view.setUint16(28, 0, true);\n return header;\n}\n\nfunction createCentralDirectoryHeader(file: NormalizedZipFile): Uint8Array {\n const header = new Uint8Array(46);\n const view = new DataView(header.buffer);\n view.setUint32(0, CENTRAL_DIRECTORY_SIGNATURE, true);\n view.setUint16(4, ZIP_VERSION, true);\n view.setUint16(6, ZIP_VERSION, true);\n view.setUint16(8, ZIP_UTF8_FLAG, true);\n view.setUint16(10, ZIP_STORE_METHOD, true);\n view.setUint16(12, file.modifiedTime, true);\n view.setUint16(14, file.modifiedDate, true);\n view.setUint32(16, file.crc32, true);\n view.setUint32(20, file.contentBytes.byteLength, true);\n view.setUint32(24, file.contentBytes.byteLength, true);\n view.setUint16(28, file.nameBytes.byteLength, true);\n view.setUint16(30, 0, true);\n view.setUint16(32, 0, true);\n view.setUint16(34, 0, true);\n view.setUint16(36, 0, true);\n view.setUint32(38, 0, true);\n view.setUint32(42, file.localHeaderOffset, true);\n return header;\n}\n\nfunction createEndOfCentralDirectory(\n fileCount: number,\n centralDirectorySize: number,\n centralDirectoryOffset: number,\n): Uint8Array {\n assertZipUint16(fileCount, \"ZIP file count\");\n assertZipUint32(centralDirectorySize, \"ZIP central directory size\");\n assertZipUint32(centralDirectoryOffset, \"ZIP central directory offset\");\n\n const footer = new Uint8Array(22);\n const view = new DataView(footer.buffer);\n view.setUint32(0, END_OF_CENTRAL_DIRECTORY_SIGNATURE, true);\n view.setUint16(4, 0, true);\n view.setUint16(6, 0, true);\n view.setUint16(8, fileCount, true);\n view.setUint16(10, fileCount, true);\n view.setUint32(12, centralDirectorySize, true);\n view.setUint32(16, centralDirectoryOffset, true);\n view.setUint16(20, 0, true);\n return footer;\n}\n\nfunction createNormalizedZipFiles(\n files: readonly ZipExportFile[],\n options: LogZipBlobOptions,\n): NormalizedZipFile[] {\n const names = new Set<string>();\n let offset = 0;\n return files.map((file) => {\n const name = normalizeZipFileName(file.name);\n if (names.has(name)) throw new Error(`Duplicate ZIP file name: ${name}`);\n names.add(name);\n\n const nameBytes = encodeText(name);\n const contentBytes = normalizeFileContent(file.content);\n assertZipUint16(nameBytes.byteLength, `ZIP file name ${name}`);\n assertZipUint32(contentBytes.byteLength, `ZIP file ${name}`);\n assertZipUint32(offset, \"ZIP local header offset\");\n\n const modified = toDosDateTime(file.lastModified ?? options.lastModified ?? Date.now());\n const normalized: NormalizedZipFile = {\n name,\n nameBytes,\n contentBytes,\n crc32: crc32(contentBytes),\n modifiedTime: modified.time,\n modifiedDate: modified.date,\n localHeaderOffset: offset,\n };\n offset += 30 + nameBytes.byteLength + contentBytes.byteLength;\n assertZipUint32(offset, \"ZIP local data size\");\n return normalized;\n });\n}\n\nfunction isQuerySource(source: LogZipExportSource): source is LogZipExportQuerySource {\n return \"query\" in source && typeof source.query === \"function\";\n}\n\nfunction resolveSource(\n source: LogZipExportSource,\n query: IndexedDbTransportQueryOptions | undefined,\n): AsyncIterable<LogEvent> {\n if (isQuerySource(source)) return source.query(query);\n if (query) throw new Error(\"Log ZIP export query options require a source with query()\");\n return source;\n}\n\nfunction normalizeMaxEvents(value: number | undefined): number | undefined {\n if (value === undefined) return undefined;\n if (!Number.isFinite(value)) return undefined;\n return Math.max(0, Math.floor(value));\n}\n\nexport function createLogZipBlob(\n files: readonly ZipExportFile[],\n options: LogZipBlobOptions = {},\n): Blob {\n const normalizedFiles = createNormalizedZipFiles(files, options);\n const parts: Uint8Array[] = [];\n let centralDirectoryOffset = 0;\n for (const file of normalizedFiles) {\n parts.push(createLocalFileHeader(file), file.nameBytes, file.contentBytes);\n centralDirectoryOffset += 30 + file.nameBytes.byteLength + file.contentBytes.byteLength;\n }\n\n let centralDirectorySize = 0;\n for (const file of normalizedFiles) {\n const header = createCentralDirectoryHeader(file);\n parts.push(header, file.nameBytes);\n centralDirectorySize += header.byteLength + file.nameBytes.byteLength;\n }\n parts.push(\n createEndOfCentralDirectory(\n normalizedFiles.length,\n centralDirectorySize,\n centralDirectoryOffset,\n ),\n );\n\n return new Blob(parts as unknown as BlobPart[], { type: options.type ?? \"application/zip\" });\n}\n\nexport async function exportLogsToZip(\n source: LogZipExportSource,\n options: LogZipExportOptions = {},\n): Promise<Blob> {\n const format = options.format ?? \"ndjson\";\n const createdAt = options.createdAt ?? Date.now();\n const logFileName = options.logFileName ?? (format === \"json\" ? \"logs.json\" : \"logs.ndjson\");\n const maxEvents = normalizeMaxEvents(options.maxEvents);\n const serialize =\n options.serializeEvent ??\n ((event: LogEvent) => safeJsonStringify(event, options.stringify ?? {}));\n const lines: string[] = [];\n let skippedCount = 0;\n let from: number | undefined;\n let to: number | undefined;\n\n for await (const event of resolveSource(source, options.query)) {\n if (maxEvents !== undefined && lines.length >= maxEvents) break;\n const mapped = options.mapEvent ? options.mapEvent(event) : event;\n if (!mapped) {\n skippedCount += 1;\n continue;\n }\n lines.push(serialize(mapped));\n from = from === undefined ? mapped.time : Math.min(from, mapped.time);\n to = to === undefined ? mapped.time : Math.max(to, mapped.time);\n }\n\n const logContent =\n format === \"json\" ? `[${lines.join(\",\")}]` : lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\";\n\n const files: ZipExportFile[] = [\n {\n name: logFileName,\n content: logContent,\n lastModified: createdAt,\n },\n ];\n\n if (options.includeManifest ?? true) {\n const manifest: LogZipExportManifest = {\n schema: \"loggerjs.log-export.v1\",\n createdAt: new Date(createdAt).toISOString(),\n source: options.source,\n format,\n logFileName: normalizeZipFileName(logFileName),\n logCount: lines.length,\n skippedCount,\n query: options.query,\n timeRange: from === undefined || to === undefined ? undefined : { from, to },\n };\n files.push({\n name: options.manifestFileName ?? \"manifest.json\",\n content: `${safeJsonStringify(manifest, { space: 2 })}\\n`,\n lastModified: createdAt,\n });\n }\n\n return createLogZipBlob(files, { lastModified: createdAt });\n}\n\nexport function downloadBlob(\n blob: Blob,\n filenameOrOptions: string | DownloadBlobOptions = {},\n): string {\n const options =\n typeof filenameOrOptions === \"string\" ? { filename: filenameOrOptions } : filenameOrOptions;\n const documentRef = options.document ?? globalThis.document;\n const urlRef = options.url ?? globalThis.URL;\n if (!documentRef?.body || !urlRef?.createObjectURL || !urlRef.revokeObjectURL) {\n throw new Error(\"downloadBlob requires document and URL.createObjectURL\");\n }\n\n const href = urlRef.createObjectURL(blob);\n const anchor = documentRef.createElement(\"a\");\n anchor.href = href;\n anchor.download = options.filename ?? \"loggerjs-logs.zip\";\n anchor.style.display = \"none\";\n documentRef.body.appendChild(anchor);\n anchor.click();\n anchor.remove();\n\n const revoke = () => urlRef.revokeObjectURL(href);\n const revokeDelayMs = options.revokeDelayMs ?? 1000;\n if (revokeDelayMs <= 0) {\n revoke();\n } else {\n setTimeout(revoke, revokeDelayMs);\n }\n\n return href;\n}\n"],"mappings":"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"}