logixia 1.13.3 → 2.0.4

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 (229) hide show
  1. package/README.md +117 -44
  2. package/dist/async-context-B05ArHgM.mjs +134 -0
  3. package/dist/async-context-B05ArHgM.mjs.map +1 -0
  4. package/dist/async-context-DTQgi8f7.js +150 -0
  5. package/dist/async-context-DTQgi8f7.js.map +1 -0
  6. package/dist/{azure-monitor.transport-CHnjVRF-.mjs → azure-monitor.transport-BPbgjATu.mjs} +27 -15
  7. package/dist/azure-monitor.transport-BPbgjATu.mjs.map +1 -0
  8. package/dist/{azure-monitor.transport-BDFO4F-i.d.ts → azure-monitor.transport-C7kYyZUW.d.mts} +3 -6
  9. package/dist/azure-monitor.transport-C7kYyZUW.d.mts.map +1 -0
  10. package/dist/{azure-monitor.transport-CrvfHd9C.js → azure-monitor.transport-CwbOLZHB.js} +32 -20
  11. package/dist/azure-monitor.transport-CwbOLZHB.js.map +1 -0
  12. package/dist/{azure-monitor.transport-D5wQEH04.d.mts → azure-monitor.transport-DV03Z5P4.d.ts} +3 -6
  13. package/dist/azure-monitor.transport-DV03Z5P4.d.ts.map +1 -0
  14. package/dist/azure.d.mts +1 -2
  15. package/dist/azure.d.ts +1 -2
  16. package/dist/azure.js +3 -6
  17. package/dist/azure.mjs +1 -6
  18. package/dist/{batch-buffer-PGvSgbmd.mjs → batch-buffer-0fgLmIPn.mjs} +21 -7
  19. package/dist/batch-buffer-0fgLmIPn.mjs.map +1 -0
  20. package/dist/{batch-buffer-BY-mRWIK.js → batch-buffer-DT4gdXmf.js} +53 -40
  21. package/dist/batch-buffer-DT4gdXmf.js.map +1 -0
  22. package/dist/browser.d.mts +52 -23
  23. package/dist/browser.d.mts.map +1 -1
  24. package/dist/browser.d.ts +52 -23
  25. package/dist/browser.d.ts.map +1 -1
  26. package/dist/browser.js +460 -62
  27. package/dist/browser.js.map +1 -1
  28. package/dist/browser.mjs +461 -65
  29. package/dist/browser.mjs.map +1 -1
  30. package/dist/{nestjs-extras-ImbuDhs1.mjs → builder-D70z6kjj.mjs} +279 -965
  31. package/dist/builder-D70z6kjj.mjs.map +1 -0
  32. package/dist/{nestjs-extras-DnJ6vrIC.js → builder-M7NoaugM.js} +421 -1177
  33. package/dist/builder-M7NoaugM.js.map +1 -0
  34. package/dist/cli/index.js +86 -67
  35. package/dist/cloudwatch.d.mts +1 -2
  36. package/dist/cloudwatch.d.ts +1 -2
  37. package/dist/cloudwatch.js +3 -7
  38. package/dist/cloudwatch.mjs +1 -7
  39. package/dist/{cloudwatch.transport-BO1ahxw-.js → cloudwatch.transport-B2wKQFF7.js} +61 -36
  40. package/dist/cloudwatch.transport-B2wKQFF7.js.map +1 -0
  41. package/dist/{cloudwatch.transport-DZ6ies7g.mjs → cloudwatch.transport-BTvYfLs9.mjs} +54 -28
  42. package/dist/cloudwatch.transport-BTvYfLs9.mjs.map +1 -0
  43. package/dist/{cloudwatch.transport-B00st6Xv.d.ts → cloudwatch.transport-Bf7L4I59.d.ts} +2 -6
  44. package/dist/cloudwatch.transport-Bf7L4I59.d.ts.map +1 -0
  45. package/dist/{cloudwatch.transport-Blc7Gtxt.d.mts → cloudwatch.transport-Rj4yTpRn.d.mts} +2 -6
  46. package/dist/cloudwatch.transport-Rj4yTpRn.d.mts.map +1 -0
  47. package/dist/{coerce.utils-Cu6IUTgX.mjs → coerce.utils-B_rEudCn.mjs} +3 -4
  48. package/dist/{coerce.utils-Cu6IUTgX.mjs.map → coerce.utils-B_rEudCn.mjs.map} +1 -1
  49. package/dist/{coerce.utils-B-LilukN.js → coerce.utils-C1C5sIX0.js} +8 -10
  50. package/dist/{coerce.utils-B-LilukN.js.map → coerce.utils-C1C5sIX0.js.map} +1 -1
  51. package/dist/correlation.d.mts +13 -16
  52. package/dist/correlation.d.mts.map +1 -1
  53. package/dist/correlation.d.ts +13 -16
  54. package/dist/correlation.d.ts.map +1 -1
  55. package/dist/correlation.js +58 -9
  56. package/dist/correlation.js.map +1 -1
  57. package/dist/correlation.mjs +56 -6
  58. package/dist/correlation.mjs.map +1 -1
  59. package/dist/{entry-fields-BJkuPnIg.mjs → entry-fields-CGuvT2R0.mjs} +5 -6
  60. package/dist/entry-fields-CGuvT2R0.mjs.map +1 -0
  61. package/dist/{entry-fields-BvOLFlJ4.js → entry-fields-RLd1jCoR.js} +10 -12
  62. package/dist/entry-fields-RLd1jCoR.js.map +1 -0
  63. package/dist/gcp.d.mts +1 -2
  64. package/dist/gcp.d.ts +1 -2
  65. package/dist/gcp.js +3 -7
  66. package/dist/gcp.mjs +1 -7
  67. package/dist/{gcp.transport-m4fVw0b1.d.ts → gcp.transport-C5D72b1g.d.mts} +3 -6
  68. package/dist/gcp.transport-C5D72b1g.d.mts.map +1 -0
  69. package/dist/{gcp.transport-zzNyIoih.d.mts → gcp.transport-CFQfdAOK.d.ts} +3 -6
  70. package/dist/gcp.transport-CFQfdAOK.d.ts.map +1 -0
  71. package/dist/{gcp.transport-DBX5TPIy.mjs → gcp.transport-CGKopAhQ.mjs} +55 -16
  72. package/dist/gcp.transport-CGKopAhQ.mjs.map +1 -0
  73. package/dist/{gcp.transport-CHV8MGgz.js → gcp.transport-KZe9hCAR.js} +60 -22
  74. package/dist/gcp.transport-KZe9hCAR.js.map +1 -0
  75. package/dist/{index-nZEeCr3X.d.ts → index-DwozbmB4.d.ts} +3 -8
  76. package/dist/index-DwozbmB4.d.ts.map +1 -0
  77. package/dist/{index-BPwEisy4.d.mts → index-xqaJtAZn.d.mts} +3 -8
  78. package/dist/index-xqaJtAZn.d.mts.map +1 -0
  79. package/dist/index.d.mts +141 -203
  80. package/dist/index.d.mts.map +1 -1
  81. package/dist/index.d.ts +140 -202
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +196 -381
  84. package/dist/index.js.map +1 -1
  85. package/dist/index.mjs +145 -294
  86. package/dist/index.mjs.map +1 -1
  87. package/dist/internal-log-b0Vp4qrB.js +183 -0
  88. package/dist/internal-log-b0Vp4qrB.js.map +1 -0
  89. package/dist/internal-log-uCSd1PMc.mjs +142 -0
  90. package/dist/internal-log-uCSd1PMc.mjs.map +1 -0
  91. package/dist/logitron-logger-D2B75Oc7.d.ts +365 -0
  92. package/dist/logitron-logger-D2B75Oc7.d.ts.map +1 -0
  93. package/dist/logitron-logger-oEd6wNhf.d.mts +365 -0
  94. package/dist/logitron-logger-oEd6wNhf.d.mts.map +1 -0
  95. package/dist/middleware.d.mts +10 -12
  96. package/dist/middleware.d.mts.map +1 -1
  97. package/dist/middleware.d.ts +10 -12
  98. package/dist/middleware.d.ts.map +1 -1
  99. package/dist/middleware.js +16 -47
  100. package/dist/middleware.js.map +1 -1
  101. package/dist/middleware.mjs +13 -45
  102. package/dist/middleware.mjs.map +1 -1
  103. package/dist/nest.d.mts +467 -10
  104. package/dist/nest.d.mts.map +1 -1
  105. package/dist/nest.d.ts +467 -10
  106. package/dist/nest.d.ts.map +1 -1
  107. package/dist/nest.js +889 -43
  108. package/dist/nest.js.map +1 -0
  109. package/dist/nest.mjs +850 -9
  110. package/dist/nest.mjs.map +1 -0
  111. package/dist/{trace.utils-CvXgaiQT.mjs → request-trace-C_fvIDv4.mjs} +44 -9
  112. package/dist/request-trace-C_fvIDv4.mjs.map +1 -0
  113. package/dist/{trace.utils-DpBOLDtq.js → request-trace-wTJdKp6-.js} +121 -77
  114. package/dist/request-trace-wTJdKp6-.js.map +1 -0
  115. package/dist/rolldown-runtime-D3PmRqjj.mjs +4 -0
  116. package/dist/{safe-stringify-DJ66tW5I.mjs → safe-stringify-BMRl3EX3.mjs} +3 -4
  117. package/dist/safe-stringify-BMRl3EX3.mjs.map +1 -0
  118. package/dist/{safe-stringify-DWo3aZp_.js → safe-stringify-DLz1Kz3o.js} +23 -25
  119. package/dist/safe-stringify-DLz1Kz3o.js.map +1 -0
  120. package/dist/{search-CHUZPl0h.d.ts → search-BH33XhuU.d.mts} +2 -22
  121. package/dist/search-BH33XhuU.d.mts.map +1 -0
  122. package/dist/{search-DjXSn6v5.mjs → search-CWMVYC-R.mjs} +34 -41
  123. package/dist/search-CWMVYC-R.mjs.map +1 -0
  124. package/dist/{search-BfBzDIT9.d.mts → search-CazwyX5Z.d.ts} +2 -22
  125. package/dist/search-CazwyX5Z.d.ts.map +1 -0
  126. package/dist/{search-B0MH1SJJ.js → search-DUfWuzFP.js} +64 -71
  127. package/dist/search-DUfWuzFP.js.map +1 -0
  128. package/dist/search.d.mts +2 -4
  129. package/dist/search.d.ts +2 -4
  130. package/dist/search.js +3 -4
  131. package/dist/search.mjs +1 -4
  132. package/dist/secret-config-BegMLkYl.mjs +209 -0
  133. package/dist/secret-config-BegMLkYl.mjs.map +1 -0
  134. package/dist/secret-config-CBZBs0p1.js +271 -0
  135. package/dist/secret-config-CBZBs0p1.js.map +1 -0
  136. package/dist/slack.transport-Ce96JBDk.mjs +824 -0
  137. package/dist/slack.transport-Ce96JBDk.mjs.map +1 -0
  138. package/dist/slack.transport-Dlx3xvb7.js +847 -0
  139. package/dist/slack.transport-Dlx3xvb7.js.map +1 -0
  140. package/dist/slack.transport-DrirgDNy.d.ts +159 -0
  141. package/dist/slack.transport-DrirgDNy.d.ts.map +1 -0
  142. package/dist/slack.transport-DxHOXlLu.d.mts +159 -0
  143. package/dist/slack.transport-DxHOXlLu.d.mts.map +1 -0
  144. package/dist/testing.d.mts +3 -8
  145. package/dist/testing.d.mts.map +1 -1
  146. package/dist/testing.d.ts +3 -8
  147. package/dist/testing.d.ts.map +1 -1
  148. package/dist/testing.js +3 -4
  149. package/dist/testing.js.map +1 -1
  150. package/dist/testing.mjs +2 -3
  151. package/dist/testing.mjs.map +1 -1
  152. package/dist/{transport.manager-Crau3Rle.js → transport.manager-DkcEOlqV.js} +331 -234
  153. package/dist/transport.manager-DkcEOlqV.js.map +1 -0
  154. package/dist/{transport.manager-CpdVHEGP.mjs → transport.manager-eZ_EeC00.mjs} +274 -178
  155. package/dist/transport.manager-eZ_EeC00.mjs.map +1 -0
  156. package/dist/{transport.types-CtNr3D7S.d.mts → transport.types-BJVL1QQl.d.mts} +5 -4
  157. package/dist/transport.types-BJVL1QQl.d.mts.map +1 -0
  158. package/dist/{transport.types-DqVLZTyf.d.ts → transport.types-BJVL1QQl.d.ts} +5 -4
  159. package/dist/transport.types-BJVL1QQl.d.ts.map +1 -0
  160. package/dist/transports.d.mts +32 -41
  161. package/dist/transports.d.mts.map +1 -1
  162. package/dist/transports.d.ts +32 -41
  163. package/dist/transports.d.ts.map +1 -1
  164. package/dist/transports.js +12 -12
  165. package/dist/transports.mjs +7 -12
  166. package/dist/worker-entry.js +337 -87
  167. package/dist/worker.d.mts +1 -2
  168. package/dist/worker.d.ts +1 -2
  169. package/dist/worker.js +3 -4
  170. package/dist/worker.mjs +1 -4
  171. package/dist/{worker.transport-Bj-i0P9V.d.ts → worker.transport-BuPwBi3d.d.ts} +2 -6
  172. package/dist/worker.transport-BuPwBi3d.d.ts.map +1 -0
  173. package/dist/{worker.transport-JP8YHBuU.js → worker.transport-C6GNl_r2.js} +44 -14
  174. package/dist/worker.transport-C6GNl_r2.js.map +1 -0
  175. package/dist/{worker.transport-otwhJ_Et.mjs → worker.transport-DLVA4y2e.mjs} +39 -9
  176. package/dist/worker.transport-DLVA4y2e.mjs.map +1 -0
  177. package/dist/{worker.transport-Bgv9heDI.d.mts → worker.transport-hJay1wxA.d.mts} +2 -6
  178. package/dist/worker.transport-hJay1wxA.d.mts.map +1 -0
  179. package/package.json +36 -19
  180. package/dist/async-context-BzJrbfQs.mjs +0 -72
  181. package/dist/async-context-BzJrbfQs.mjs.map +0 -1
  182. package/dist/async-context-DyQQYq3I.js +0 -92
  183. package/dist/async-context-DyQQYq3I.js.map +0 -1
  184. package/dist/azure-monitor.transport-BDFO4F-i.d.ts.map +0 -1
  185. package/dist/azure-monitor.transport-CHnjVRF-.mjs.map +0 -1
  186. package/dist/azure-monitor.transport-CrvfHd9C.js.map +0 -1
  187. package/dist/azure-monitor.transport-D5wQEH04.d.mts.map +0 -1
  188. package/dist/batch-buffer-BY-mRWIK.js.map +0 -1
  189. package/dist/batch-buffer-PGvSgbmd.mjs.map +0 -1
  190. package/dist/chunk-DLSkYcPk.mjs +0 -7
  191. package/dist/chunk-nOFOJqeH.js +0 -30
  192. package/dist/cloudwatch.transport-B00st6Xv.d.ts.map +0 -1
  193. package/dist/cloudwatch.transport-BO1ahxw-.js.map +0 -1
  194. package/dist/cloudwatch.transport-Blc7Gtxt.d.mts.map +0 -1
  195. package/dist/cloudwatch.transport-DZ6ies7g.mjs.map +0 -1
  196. package/dist/entry-fields-BJkuPnIg.mjs.map +0 -1
  197. package/dist/entry-fields-BvOLFlJ4.js.map +0 -1
  198. package/dist/gcp.transport-CHV8MGgz.js.map +0 -1
  199. package/dist/gcp.transport-DBX5TPIy.mjs.map +0 -1
  200. package/dist/gcp.transport-m4fVw0b1.d.ts.map +0 -1
  201. package/dist/gcp.transport-zzNyIoih.d.mts.map +0 -1
  202. package/dist/index-BPwEisy4.d.mts.map +0 -1
  203. package/dist/index-nZEeCr3X.d.ts.map +0 -1
  204. package/dist/internal-log-BUJYMZbE.js +0 -70
  205. package/dist/internal-log-BUJYMZbE.js.map +0 -1
  206. package/dist/internal-log-Cm8SOWSV.mjs +0 -52
  207. package/dist/internal-log-Cm8SOWSV.mjs.map +0 -1
  208. package/dist/nestjs-extras-DnJ6vrIC.js.map +0 -1
  209. package/dist/nestjs-extras-ImbuDhs1.mjs.map +0 -1
  210. package/dist/nestjs-extras-OkC796py.d.mts +0 -725
  211. package/dist/nestjs-extras-OkC796py.d.mts.map +0 -1
  212. package/dist/nestjs-extras-mKrHE9S6.d.ts +0 -725
  213. package/dist/nestjs-extras-mKrHE9S6.d.ts.map +0 -1
  214. package/dist/safe-stringify-DJ66tW5I.mjs.map +0 -1
  215. package/dist/safe-stringify-DWo3aZp_.js.map +0 -1
  216. package/dist/search-B0MH1SJJ.js.map +0 -1
  217. package/dist/search-BfBzDIT9.d.mts.map +0 -1
  218. package/dist/search-CHUZPl0h.d.ts.map +0 -1
  219. package/dist/search-DjXSn6v5.mjs.map +0 -1
  220. package/dist/trace.utils-CvXgaiQT.mjs.map +0 -1
  221. package/dist/trace.utils-DpBOLDtq.js.map +0 -1
  222. package/dist/transport.manager-CpdVHEGP.mjs.map +0 -1
  223. package/dist/transport.manager-Crau3Rle.js.map +0 -1
  224. package/dist/transport.types-CtNr3D7S.d.mts.map +0 -1
  225. package/dist/transport.types-DqVLZTyf.d.ts.map +0 -1
  226. package/dist/worker.transport-Bgv9heDI.d.mts.map +0 -1
  227. package/dist/worker.transport-Bj-i0P9V.d.ts.map +0 -1
  228. package/dist/worker.transport-JP8YHBuU.js.map +0 -1
  229. package/dist/worker.transport-otwhJ_Et.mjs.map +0 -1
@@ -0,0 +1,847 @@
1
+ const require_secret_config = require("./secret-config-CBZBs0p1.js");
2
+ const require_internal_log = require("./internal-log-b0Vp4qrB.js");
3
+ const require_safe_stringify = require("./safe-stringify-DLz1Kz3o.js");
4
+ const require_transport_manager = require("./transport.manager-DkcEOlqV.js");
5
+ const require_batch_buffer = require("./batch-buffer-DT4gdXmf.js");
6
+ // src/transports/otlp.transport.ts
7
+ const HEX_32 = /^[0-9a-f]{32}$/;
8
+ /**
9
+ * OTLP JSON wants a 32-char lowercase hex trace id. Dashed UUIDs are
10
+ * normalised; anything else (or all zeros) returns undefined.
11
+ */
12
+ function toOtlpTraceId(id) {
13
+ const hex = id.replace(/-/g, "").toLowerCase();
14
+ return HEX_32.test(hex) && hex !== "0".repeat(32) ? hex : void 0;
15
+ }
16
+ /**
17
+ * Map a logixia level name to an OTel SeverityNumber (1–24) and text.
18
+ * Per the OTel logs SDK spec: TRACE=1, DEBUG=5, INFO=9, WARN=13, ERROR=17,
19
+ * FATAL=21. Custom/unknown levels fall back to INFO (9).
20
+ */
21
+ function toOtelSeverity(level) {
22
+ switch (level.toLowerCase()) {
23
+ case "trace": return {
24
+ number: 1,
25
+ text: "TRACE"
26
+ };
27
+ case "verbose": return {
28
+ number: 5,
29
+ text: "DEBUG"
30
+ };
31
+ case "debug": return {
32
+ number: 5,
33
+ text: "DEBUG"
34
+ };
35
+ case "info": return {
36
+ number: 9,
37
+ text: "INFO"
38
+ };
39
+ case "warn":
40
+ case "warning": return {
41
+ number: 13,
42
+ text: "WARN"
43
+ };
44
+ case "error": return {
45
+ number: 17,
46
+ text: "ERROR"
47
+ };
48
+ case "fatal": return {
49
+ number: 21,
50
+ text: "FATAL"
51
+ };
52
+ default: return {
53
+ number: 9,
54
+ text: "INFO"
55
+ };
56
+ }
57
+ }
58
+ /** Coerce a JS value into an OTLP AnyValue. */
59
+ function toAnyValue(value) {
60
+ if (typeof value === "string") return { stringValue: value };
61
+ if (typeof value === "boolean") return { boolValue: value };
62
+ if (typeof value === "number") return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };
63
+ if (typeof value === "bigint") return { stringValue: value.toString() };
64
+ if (value === null || value === void 0) return { stringValue: "" };
65
+ try {
66
+ return { stringValue: require_safe_stringify.stringifyForLog(value) };
67
+ } catch {
68
+ return { stringValue: String(value) };
69
+ }
70
+ }
71
+ /** Build the OTLP KeyValue attribute list from a flat record. */
72
+ function toAttributes(rec) {
73
+ return Object.entries(rec).map(([key, value]) => ({
74
+ key,
75
+ value: toAnyValue(value)
76
+ }));
77
+ }
78
+ var OtlpLogTransport = class {
79
+ constructor(config) {
80
+ this.name = "otlp";
81
+ this.flushTimer = null;
82
+ this.url = config.url;
83
+ this.headers = config.headers ?? {};
84
+ this.batchSize = config.batchSize ?? 100;
85
+ this.flushIntervalMs = config.flushIntervalMs ?? 5e3;
86
+ this.requestTimeoutMs = config.requestTimeoutMs ?? 1e4;
87
+ this.level = config.level;
88
+ this.buffer = new require_batch_buffer.BatchBuffer({
89
+ name: "OtlpLogTransport",
90
+ batchSize: this.batchSize,
91
+ maxBufferSize: config.maxBufferSize ?? 1e4,
92
+ send: (entries) => this.send(entries),
93
+ secrets: () => this.secretValues()
94
+ });
95
+ const resource = {
96
+ "service.name": config.serviceName ?? "logixia",
97
+ ...config.serviceVersion ? { "service.version": config.serviceVersion } : {},
98
+ ...config.environment ? { "deployment.environment": config.environment } : {},
99
+ ...config.resourceAttributes ?? {}
100
+ };
101
+ this.resourceAttrs = toAttributes(resource);
102
+ require_secret_config.protectSecrets(this, ["headers", "url"]);
103
+ this.flushTimer = setInterval(() => {
104
+ this.flush().catch(() => {});
105
+ }, this.flushIntervalMs);
106
+ if (this.flushTimer.unref) this.flushTimer.unref();
107
+ }
108
+ write(entry) {
109
+ this.buffer.push(entry);
110
+ this.buffer.flushIfFull();
111
+ }
112
+ /** Convert one entry into an OTLP LogRecord. */
113
+ toLogRecord(entry) {
114
+ const sev = toOtelSeverity(entry.level);
115
+ const tsNanos = String(entry.timestamp.getTime() * 1e6);
116
+ const attrs = { ...entry.data ?? {} };
117
+ if (entry.context !== void 0) attrs["context"] = entry.context;
118
+ if (entry.appName !== void 0) attrs["app.name"] = entry.appName;
119
+ if (entry.environment !== void 0) attrs["deployment.environment"] = entry.environment;
120
+ const record = {
121
+ timeUnixNano: tsNanos,
122
+ observedTimeUnixNano: tsNanos,
123
+ severityNumber: sev.number,
124
+ severityText: sev.text,
125
+ body: { stringValue: entry.message },
126
+ attributes: toAttributes(attrs)
127
+ };
128
+ if (entry.traceId) {
129
+ const traceId = toOtlpTraceId(entry.traceId);
130
+ if (traceId) record["traceId"] = traceId;
131
+ else record["attributes"].push({
132
+ key: "trace_id",
133
+ value: { stringValue: entry.traceId }
134
+ });
135
+ }
136
+ return record;
137
+ }
138
+ buildPayload(entries) {
139
+ return require_safe_stringify.stringifyForLog({ resourceLogs: [{
140
+ resource: { attributes: this.resourceAttrs },
141
+ scopeLogs: [{
142
+ scope: { name: "logixia" },
143
+ logRecords: entries.map((e) => this.toLogRecord(e))
144
+ }]
145
+ }] });
146
+ }
147
+ flush() {
148
+ return this.buffer.flush();
149
+ }
150
+ secretValues() {
151
+ return require_secret_config.collectSecrets({
152
+ headers: this.headers,
153
+ url: this.url
154
+ });
155
+ }
156
+ async send(entries) {
157
+ if (typeof fetch !== "function") {
158
+ require_internal_log.internalWarn("OtlpLogTransport: global fetch unavailable — cannot export logs");
159
+ return;
160
+ }
161
+ const controller = new AbortController();
162
+ const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
163
+ try {
164
+ const res = await fetch(this.url, {
165
+ method: "POST",
166
+ headers: {
167
+ "Content-Type": "application/json",
168
+ ...this.headers
169
+ },
170
+ body: this.buildPayload(entries),
171
+ signal: controller.signal
172
+ });
173
+ if (!res.ok) throw new require_batch_buffer.HttpError(res.status, "", "OTLP export");
174
+ } catch (err) {
175
+ require_secret_config.scrubError(err, this.secretValues());
176
+ if (controller.signal.aborted) throw new Error(`OTLP export timed out after ${this.requestTimeoutMs}ms`, { cause: err });
177
+ throw err;
178
+ } finally {
179
+ clearTimeout(timer);
180
+ }
181
+ }
182
+ async close() {
183
+ if (this.flushTimer) {
184
+ clearInterval(this.flushTimer);
185
+ this.flushTimer = null;
186
+ }
187
+ await this.buffer.close();
188
+ }
189
+ };
190
+ // src/transports/webhook-queue.ts
191
+ const MAX_BACKOFF_MS = 3e4;
192
+ const DEFAULT_QUEUE_CAP = 1e3;
193
+ const CLOSE_TIMEOUT_MS = 3e3;
194
+ const OVERFLOW_WARN_MS = 5e3;
195
+ const DEFAULT_WEBHOOK_TIMEOUT_MS = 1e4;
196
+ const ZWSP = "​";
197
+ const LEVEL_RANK = {
198
+ fatal: 0,
199
+ error: 1,
200
+ warn: 2,
201
+ warning: 2,
202
+ info: 3,
203
+ debug: 4,
204
+ verbose: 5,
205
+ trace: 6
206
+ };
207
+ function resolveMinLevel(level, label) {
208
+ const resolved = (level ?? "error").toLowerCase();
209
+ if (!(resolved in LEVEL_RANK)) throw new TypeError(`${label}: unknown level "${String(level).slice(0, 40)}"`);
210
+ return resolved;
211
+ }
212
+ /** Unknown (custom) entry levels pass, like the TransportManager does. */
213
+ function meetsLevel(entryLevel, minLevel) {
214
+ const entryRank = LEVEL_RANK[entryLevel.toLowerCase()];
215
+ return entryRank === void 0 || entryRank <= (LEVEL_RANK[minLevel] ?? 1);
216
+ }
217
+ /** Validates the secret URL. Error messages never contain it. */
218
+ function parseWebhookUrl(raw, rules) {
219
+ const { label, allowCustomUrl, hostMatches, pathMatches, expected } = rules;
220
+ if (typeof raw !== "string" || raw.length === 0) throw new TypeError(`${label}: webhookUrl is required`);
221
+ let url;
222
+ try {
223
+ url = new URL(raw);
224
+ } catch {
225
+ throw new TypeError(`${label}: webhookUrl is not a valid URL`);
226
+ }
227
+ if (url.protocol !== "https:") throw new TypeError(`${label}: webhookUrl must use https:`);
228
+ if (url.username || url.password) throw new TypeError(`${label}: webhookUrl must not contain credentials`);
229
+ if (!allowCustomUrl) {
230
+ if (!hostMatches(url.hostname.toLowerCase()) || !pathMatches(url.pathname) || url.port) throw new TypeError(`${label}: webhookUrl must be a ${expected} webhook (set allowCustomUrl: true to use another https endpoint)`);
231
+ }
232
+ return url;
233
+ }
234
+ function retryAfterHeaderMs(res) {
235
+ var _res$headers;
236
+ const raw = (_res$headers = res.headers) === null || _res$headers === void 0 ? void 0 : _res$headers.get("retry-after");
237
+ if (raw === null || raw === void 0 || raw === "") return void 0;
238
+ const seconds = Number(raw);
239
+ return Number.isFinite(seconds) && seconds >= 0 ? seconds * 1e3 : void 0;
240
+ }
241
+ function nonNegative(value, fallback) {
242
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
243
+ }
244
+ function failureCode(err) {
245
+ const cause = err === null || err === void 0 ? void 0 : err.cause;
246
+ const code = (cause === null || cause === void 0 ? void 0 : cause.code) ?? (err === null || err === void 0 ? void 0 : err.code);
247
+ if (typeof code === "string") return code.slice(0, 40);
248
+ return err instanceof Error ? err.name : "error";
249
+ }
250
+ function sleep(ms, signal) {
251
+ return new Promise((resolve) => {
252
+ var _timer$unref;
253
+ if (signal.aborted) return resolve();
254
+ const done = () => {
255
+ clearTimeout(timer);
256
+ signal.removeEventListener("abort", done);
257
+ resolve();
258
+ };
259
+ const timer = setTimeout(done, ms);
260
+ (_timer$unref = timer.unref) === null || _timer$unref === void 0 || _timer$unref.call(timer);
261
+ signal.addEventListener("abort", done, { once: true });
262
+ });
263
+ }
264
+ async function cancelBody(res) {
265
+ try {
266
+ var _res$body;
267
+ await ((_res$body = res.body) === null || _res$body === void 0 ? void 0 : _res$body.cancel());
268
+ } catch {}
269
+ }
270
+ /**
271
+ * Throttled queue for chat webhooks. It is not built on BatchBuffer because the
272
+ * semantics differ: sends are spaced by `minIntervalMs` (leading edge, then one
273
+ * message per window), a window sends one summarized message and drops the
274
+ * overflow, 429 waits are cancellable by close(), and flush() surfaces send
275
+ * errors. HttpError / isPermanentHttpError are shared with BatchBuffer.
276
+ *
277
+ * Entries past `maxPerMessage` in one window are not sent: the message carries a
278
+ * "… and N more" line and an internal warning reports the count.
279
+ */
280
+ var WebhookQueue = class {
281
+ constructor(opts) {
282
+ this.opts = opts;
283
+ this.items = [];
284
+ this.timer = null;
285
+ this.nextAllowed = 0;
286
+ this.failures = 0;
287
+ this.closed = false;
288
+ this.shutdown = new AbortController();
289
+ this.overflowPending = 0;
290
+ this.lastOverflowWarn = 0;
291
+ this.fetchWarned = false;
292
+ this.host = opts.url.hostname;
293
+ this.httpLabel = `${opts.label} (${this.host})`;
294
+ this.minIntervalMs = nonNegative(opts.minIntervalMs, 1e3);
295
+ this.maxRetries = Math.floor(nonNegative(opts.maxRetries, 1));
296
+ this.timeoutMs = nonNegative(opts.requestTimeoutMs, DEFAULT_WEBHOOK_TIMEOUT_MS);
297
+ this.cap = new require_transport_manager.BufferCap(opts.label, opts.maxQueueSize, opts.maxPerMessage, DEFAULT_QUEUE_CAP);
298
+ }
299
+ get length() {
300
+ return this.items.length;
301
+ }
302
+ push(entry) {
303
+ if (this.closed) return;
304
+ this.items.push(entry);
305
+ this.cap.enforce(this.items);
306
+ this.schedule();
307
+ }
308
+ flush() {
309
+ this.inFlight ??= this.drain().finally(() => {
310
+ this.inFlight = void 0;
311
+ });
312
+ return this.inFlight;
313
+ }
314
+ /** Cancels rate-limit waits, then makes one best-effort send with a short timeout. */
315
+ async close() {
316
+ this.closed = true;
317
+ this.clearTimer();
318
+ this.shutdown.abort();
319
+ try {
320
+ await this.inFlight;
321
+ } catch {}
322
+ try {
323
+ await this.flush();
324
+ } catch (err) {
325
+ require_internal_log.internalError(`${this.opts.label} flush on close failed`, err);
326
+ }
327
+ this.reportOverflow(true);
328
+ if (this.items.length > 0) {
329
+ require_internal_log.internalError(`${this.opts.label} closing with ${this.items.length} undelivered entries`);
330
+ this.items = [];
331
+ }
332
+ }
333
+ clearTimer() {
334
+ if (this.timer) clearTimeout(this.timer);
335
+ this.timer = null;
336
+ }
337
+ schedule() {
338
+ var _this$timer$unref, _this$timer;
339
+ if (this.timer || this.closed || this.items.length === 0) return;
340
+ const delay = Math.max(0, this.nextAllowed - Date.now());
341
+ this.timer = setTimeout(() => {
342
+ this.timer = null;
343
+ this.flush().catch((err) => require_internal_log.internalError(`${this.opts.label} send failed`, err));
344
+ }, delay);
345
+ (_this$timer$unref = (_this$timer = this.timer).unref) === null || _this$timer$unref === void 0 || _this$timer$unref.call(_this$timer);
346
+ }
347
+ reportOverflow(force = false) {
348
+ const now = Date.now();
349
+ if (this.overflowPending === 0 || !force && now - this.lastOverflowWarn < OVERFLOW_WARN_MS) return;
350
+ const n = this.overflowPending;
351
+ require_internal_log.internalWarn(`${this.opts.label}: ${n} entr${n === 1 ? "y" : "ies"} over the per-message limit (${this.opts.maxPerMessage}) summarized in a "... and N more" line and not sent`);
352
+ this.overflowPending = 0;
353
+ this.lastOverflowWarn = now;
354
+ }
355
+ render(batch) {
356
+ const rendered = [];
357
+ let bad = 0;
358
+ for (const entry of batch) try {
359
+ const value = this.opts.render(entry);
360
+ JSON.stringify(value);
361
+ rendered.push(value);
362
+ } catch (err) {
363
+ bad += 1;
364
+ if (bad === 1) require_internal_log.internalError(`${this.opts.label}: dropping an entry that cannot be rendered`, err);
365
+ }
366
+ if (bad > 1) require_internal_log.internalError(`${this.opts.label}: dropped ${bad} entries that cannot be rendered`);
367
+ return rendered;
368
+ }
369
+ async drain() {
370
+ if (this.items.length === 0) return;
371
+ if (typeof fetch !== "function") {
372
+ if (!this.fetchWarned) {
373
+ this.fetchWarned = true;
374
+ require_internal_log.internalWarn(`${this.opts.label}: global fetch is unavailable (Node 18+ required); dropping entries`);
375
+ }
376
+ this.items = [];
377
+ return;
378
+ }
379
+ const all = this.items;
380
+ this.items = [];
381
+ const batch = all.slice(0, this.opts.maxPerMessage);
382
+ const overflow = all.length - batch.length;
383
+ if (overflow > 0) {
384
+ this.overflowPending += overflow;
385
+ this.reportOverflow();
386
+ }
387
+ this.nextAllowed = Date.now() + this.minIntervalMs;
388
+ try {
389
+ const rendered = this.render(batch);
390
+ if (rendered.length === 0 && overflow === 0) return;
391
+ let body;
392
+ try {
393
+ body = JSON.stringify(this.opts.assemble(rendered, overflow));
394
+ } catch (err) {
395
+ require_internal_log.internalError(`${this.opts.label}: dropping a batch that cannot be serialized`, err);
396
+ return;
397
+ }
398
+ try {
399
+ await this.sendWithRetry(body);
400
+ this.failures = 0;
401
+ } catch (err) {
402
+ if (!require_batch_buffer.isPermanentHttpError(err)) {
403
+ this.items.unshift(...batch);
404
+ this.cap.enforce(this.items);
405
+ this.failures += 1;
406
+ const backoff = Math.min(MAX_BACKOFF_MS, Math.max(1e3, this.minIntervalMs) * 2 ** this.failures);
407
+ this.nextAllowed = Math.max(this.nextAllowed, Date.now() + backoff);
408
+ }
409
+ throw err;
410
+ }
411
+ } finally {
412
+ this.schedule();
413
+ }
414
+ }
415
+ async sendWithRetry(body) {
416
+ for (let attempt = 0;; attempt += 1) {
417
+ const res = await this.post(body);
418
+ if (res.ok) return;
419
+ if (res.status !== 429) throw new require_batch_buffer.HttpError(res.status, "", this.httpLabel);
420
+ const waitMs = Math.min(MAX_BACKOFF_MS, Math.max(0, res.retryAfterMs ?? Math.max(1e3, this.minIntervalMs)));
421
+ this.nextAllowed = Math.max(this.nextAllowed, Date.now() + waitMs);
422
+ if (attempt >= this.maxRetries || this.closed) throw new require_batch_buffer.HttpError(429, "", this.httpLabel);
423
+ await sleep(waitMs, this.shutdown.signal);
424
+ if (this.shutdown.signal.aborted) throw new Error(`${this.opts.label}: rate-limit wait cancelled by close()`);
425
+ }
426
+ }
427
+ async readRetryAfter(res) {
428
+ try {
429
+ return this.opts.retryAfterMs ? await this.opts.retryAfterMs(res) : retryAfterHeaderMs(res);
430
+ } catch {
431
+ return;
432
+ }
433
+ }
434
+ async post(body) {
435
+ const controller = new AbortController();
436
+ const timeoutMs = this.closed ? Math.min(this.timeoutMs, CLOSE_TIMEOUT_MS) : this.timeoutMs;
437
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
438
+ try {
439
+ const res = await fetch(this.opts.url, {
440
+ method: "POST",
441
+ headers: { "Content-Type": "application/json" },
442
+ body,
443
+ redirect: "error",
444
+ signal: controller.signal
445
+ });
446
+ let retryAfterMs;
447
+ try {
448
+ if (res.status === 429) retryAfterMs = await this.readRetryAfter(res);
449
+ } finally {
450
+ await cancelBody(res);
451
+ }
452
+ return {
453
+ ok: res.ok,
454
+ status: res.status,
455
+ retryAfterMs
456
+ };
457
+ } catch (err) {
458
+ throw new Error(`${this.opts.label}: request to ${this.host} failed (${controller.signal.aborted ? "timeout" : failureCode(err)})`);
459
+ } finally {
460
+ clearTimeout(timer);
461
+ }
462
+ }
463
+ };
464
+ /** Cuts `text` to `max` chars without splitting a surrogate pair or a `&amp;`-style entity. */
465
+ function truncateText(text, max, ellipsis = "…") {
466
+ if (text.length <= max) return text;
467
+ let cut = text.slice(0, Math.max(0, max - ellipsis.length));
468
+ cut = cut.replace(/&[a-z]{0,3}$/, "");
469
+ if (/[\uD800-\uDBFF]$/.test(cut)) cut = cut.slice(0, -1);
470
+ return cut + ellipsis;
471
+ }
472
+ /** Breaks code-fence and broadcast-mention sequences with a zero-width space. */
473
+ function defuseText(text) {
474
+ return text.replace(/`{3,}/g, (m) => m.split("").join(ZWSP)).replace(/@(everyone|here|channel)/gi, `@${ZWSP}$1`);
475
+ }
476
+ /** Collapses control characters and newlines for single-line fields. */
477
+ function singleLine(text) {
478
+ return text.replace(/[\u0000-\u001f\u007f]+/g, " ").trim();
479
+ }
480
+ /** ISO string, or undefined for a missing or invalid timestamp. */
481
+ function safeIso(date) {
482
+ return date instanceof Date && !Number.isNaN(date.getTime()) ? date.toISOString() : void 0;
483
+ }
484
+ const DISCORD_DESCRIPTION_MAX = 4096;
485
+ const DISCORD_FIELD_VALUE_MAX = 1024;
486
+ const DISCORD_EMBED_TOTAL_MAX = 6e3;
487
+ const DISCORD_CONTENT_MAX = 2e3;
488
+ const FIELD_VALUE_CAP = 64;
489
+ const DISCORD_FOOTER_MAX = 2048;
490
+ const FENCE_OVERHEAD = 12;
491
+ const MIN_USEFUL_BODY = 20;
492
+ const DISCORD_LEVEL_COLORS = {
493
+ fatal: 10038562,
494
+ error: 15548997,
495
+ warn: 16705372,
496
+ warning: 16705372,
497
+ info: 3447003,
498
+ debug: 9807270,
499
+ verbose: 9807270,
500
+ trace: 9807270
501
+ };
502
+ const DEFAULT_COLOR = 9807270;
503
+ /** Escapes Discord markdown and defuses @everyone / @here and code fences. */
504
+ function escapeDiscord(text) {
505
+ return defuseText(text.replace(/[\\*_~`|>#[\]()]/g, "\\$&"));
506
+ }
507
+ function textLength(value, key) {
508
+ const text = value === null || value === void 0 ? void 0 : value[key];
509
+ return typeof text === "string" ? text.length : 0;
510
+ }
511
+ /** Counts every text field Discord counts toward the 6000-character limit. */
512
+ function embedChars(embed) {
513
+ var _embed$title, _embed$description;
514
+ return (((_embed$title = embed.title) === null || _embed$title === void 0 ? void 0 : _embed$title.length) ?? 0) + (((_embed$description = embed.description) === null || _embed$description === void 0 ? void 0 : _embed$description.length) ?? 0) + (embed.fields ?? []).reduce((n, f) => n + f.name.length + f.value.length, 0) + textLength(embed["footer"], "text") + textLength(embed["author"], "name");
515
+ }
516
+ /** Shrinks one embed to `limit` chars: description first, then fields, footer/author, title. */
517
+ function shrinkToFit(embed, limit) {
518
+ var _embed$fields;
519
+ const excess = () => embedChars(embed) - limit;
520
+ if (excess() > 0 && embed.description) {
521
+ const keep = embed.description.length - excess();
522
+ if (keep > 0) embed.description = truncateText(embed.description, keep);
523
+ else delete embed.description;
524
+ }
525
+ while (excess() > 0 && ((_embed$fields = embed.fields) === null || _embed$fields === void 0 ? void 0 : _embed$fields.length)) embed.fields.pop();
526
+ if (excess() > 0) {
527
+ delete embed["footer"];
528
+ delete embed["author"];
529
+ }
530
+ if (excess() > 0 && embed.title) embed.title = truncateText(embed.title, Math.max(1, embed.title.length - excess()));
531
+ }
532
+ function codeBlock(json, max) {
533
+ return `\`\`\`json\n${truncateText(json.replace(/`{3,}/g, (m) => m.split("").join("​")), max)}\n\`\`\``;
534
+ }
535
+ function clampCustomEmbed(embed) {
536
+ const out = { ...embed };
537
+ if (typeof out.title === "string") out.title = truncateText(out.title, 256);
538
+ if (typeof out.description === "string") out.description = truncateText(out.description, DISCORD_DESCRIPTION_MAX);
539
+ if (Array.isArray(out.fields)) out.fields = out.fields.slice(0, 25).map((f) => ({
540
+ ...f,
541
+ name: truncateText(String(f.name), 256),
542
+ value: truncateText(String(f.value), DISCORD_FIELD_VALUE_MAX)
543
+ }));
544
+ const footer = out["footer"];
545
+ if (footer && typeof footer.text === "string") out["footer"] = {
546
+ ...footer,
547
+ text: truncateText(footer.text, DISCORD_FOOTER_MAX)
548
+ };
549
+ const author = out["author"];
550
+ if (author && typeof author.name === "string") out["author"] = {
551
+ ...author,
552
+ name: truncateText(author.name, 256)
553
+ };
554
+ return out;
555
+ }
556
+ var DiscordTransport = class {
557
+ constructor(config) {
558
+ this.name = "discord";
559
+ const url = parseWebhookUrl(config.webhookUrl, {
560
+ label: "DiscordTransport",
561
+ hostMatches: (h) => [
562
+ "discord.com",
563
+ "discordapp.com",
564
+ "ptb.discord.com",
565
+ "canary.discord.com"
566
+ ].includes(h),
567
+ pathMatches: (p) => /^\/api(\/v\d+)?\/webhooks\//.test(p),
568
+ expected: "discord.com",
569
+ allowCustomUrl: config.allowCustomUrl
570
+ });
571
+ if (config.avatarUrl !== void 0 && !/^https:\/\//i.test(config.avatarUrl)) throw new TypeError("DiscordTransport: avatarUrl must use https:");
572
+ this.level = resolveMinLevel(config.level, "DiscordTransport");
573
+ if (config.filter) this.filter = config.filter;
574
+ this.username = config.username;
575
+ this.avatarUrl = config.avatarUrl;
576
+ this.formatter = config.formatter;
577
+ const queue = new WebhookQueue({
578
+ label: "DiscordTransport",
579
+ url,
580
+ minIntervalMs: config.minIntervalMs,
581
+ maxPerMessage: 10,
582
+ maxQueueSize: config.maxBufferSize,
583
+ maxRetries: config.maxRetries,
584
+ requestTimeoutMs: config.requestTimeoutMs ?? 1e4,
585
+ render: (entry) => this.draftFor(entry),
586
+ assemble: (drafts, overflow) => this.buildPayload(drafts, overflow),
587
+ retryAfterMs: readRetryAfter
588
+ });
589
+ Object.defineProperty(this, "queue", {
590
+ value: queue,
591
+ enumerable: false
592
+ });
593
+ }
594
+ write(entry) {
595
+ if (!meetsLevel(entry.level, this.level)) return;
596
+ this.queue.push(entry);
597
+ }
598
+ flush() {
599
+ return this.queue.flush();
600
+ }
601
+ close() {
602
+ return this.queue.close();
603
+ }
604
+ /** Never serialise the webhook URL (JSON.stringify, util.inspect). */
605
+ toJSON() {
606
+ return {
607
+ name: this.name,
608
+ level: this.level
609
+ };
610
+ }
611
+ /** `showHidden` would otherwise print the non-enumerable queue (and its URL). */
612
+ [Symbol.for("nodejs.util.inspect.custom")]() {
613
+ return this.toJSON();
614
+ }
615
+ buildPayload(drafts, overflow) {
616
+ const kept = this.fitToBudget(drafts);
617
+ const dropped = overflow + (drafts.length - kept.length);
618
+ const payload = {
619
+ embeds: kept.map((d) => d.embed),
620
+ allowed_mentions: { parse: [] }
621
+ };
622
+ if (dropped > 0) payload["content"] = truncateText(`… and ${dropped} more entr${dropped === 1 ? "y" : "ies"} not shown`, DISCORD_CONTENT_MAX);
623
+ if (this.username) payload["username"] = truncateText(this.username, 80);
624
+ if (this.avatarUrl) payload["avatar_url"] = this.avatarUrl;
625
+ return payload;
626
+ }
627
+ /** Enforces the 6000-char total: drops trailing embeds if needed, then shares the rest among descriptions. */
628
+ fitToBudget(drafts) {
629
+ const kept = [...drafts];
630
+ const base = () => kept.reduce((n, d) => n + embedChars(d.embed), 0);
631
+ while (kept.length > 1 && base() > 6e3) kept.pop();
632
+ const only = kept[0];
633
+ if (only && base() > 6e3) shrinkToFit(only.embed, DISCORD_EMBED_TOTAL_MAX);
634
+ const withData = kept.filter((d) => d.json !== void 0);
635
+ if (withData.length === 0) return kept;
636
+ const share = Math.floor((DISCORD_EMBED_TOTAL_MAX - base()) / withData.length);
637
+ const body = Math.min(DISCORD_DESCRIPTION_MAX, share) - FENCE_OVERHEAD;
638
+ for (const d of withData) if (body >= MIN_USEFUL_BODY) d.embed.description = codeBlock(d.json, body);
639
+ return kept;
640
+ }
641
+ draftFor(entry) {
642
+ if (this.formatter) try {
643
+ return {
644
+ embed: clampCustomEmbed(this.formatter(entry)),
645
+ json: void 0
646
+ };
647
+ } catch {
648
+ require_internal_log.internalWarn("DiscordTransport: formatter threw; using the default embed");
649
+ }
650
+ return defaultDraft(entry);
651
+ }
652
+ };
653
+ function defaultDraft(entry) {
654
+ const title = `[${singleLine(entry.level).toUpperCase()}] ${escapeDiscord(singleLine(entry.message))}`;
655
+ const fields = [
656
+ ["appName", entry.appName],
657
+ ["environment", entry.environment],
658
+ ["context", entry.context],
659
+ ["traceId", entry.traceId]
660
+ ].map(([name, value]) => [name, typeof value === "string" ? singleLine(value) : ""]).filter(([, value]) => value !== "").map(([name, value]) => ({
661
+ name,
662
+ value: truncateText(escapeDiscord(value), FIELD_VALUE_CAP),
663
+ inline: true
664
+ }));
665
+ const embed = {
666
+ title: truncateText(title, 256),
667
+ color: DISCORD_LEVEL_COLORS[entry.level.toLowerCase()] ?? DEFAULT_COLOR,
668
+ fields
669
+ };
670
+ const timestamp = safeIso(entry.timestamp);
671
+ if (timestamp) embed.timestamp = timestamp;
672
+ return {
673
+ embed,
674
+ json: entry.data && Object.keys(entry.data).length > 0 ? require_safe_stringify.stringifyForLog(entry.data) : void 0
675
+ };
676
+ }
677
+ async function readRetryAfter(res) {
678
+ try {
679
+ var _res$json;
680
+ const body = await ((_res$json = res.json) === null || _res$json === void 0 ? void 0 : _res$json.call(res));
681
+ const seconds = Number(body === null || body === void 0 ? void 0 : body.retry_after);
682
+ if ((body === null || body === void 0 ? void 0 : body.retry_after) !== void 0 && Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
683
+ } catch {}
684
+ return retryAfterHeaderMs(res);
685
+ }
686
+ const SLACK_DATA_MAX = 2500;
687
+ const SLACK_FIELD_MAX = 300;
688
+ const SLACK_TEXT_MAX = 1e3;
689
+ const SLACK_MAX_BLOCKS = 50;
690
+ const SLACK_MAX_PER_MESSAGE = 10;
691
+ /** Escapes the three characters Slack treats specially and defuses broadcast mentions. */
692
+ function escapeSlack(text) {
693
+ return defuseText(text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"));
694
+ }
695
+ const LEVEL_EMOJI = {
696
+ fatal: ":rotating_light:",
697
+ error: ":red_circle:",
698
+ warn: ":warning:",
699
+ warning: ":warning:",
700
+ info: ":information_source:"
701
+ };
702
+ var SlackTransport = class {
703
+ constructor(config) {
704
+ this.name = "slack";
705
+ const url = parseWebhookUrl(config.webhookUrl, {
706
+ label: "SlackTransport",
707
+ hostMatches: (h) => h === "hooks.slack.com" || h === "hooks.slack-gov.com",
708
+ pathMatches: (p) => p.startsWith("/services/") || p.startsWith("/triggers/"),
709
+ expected: "hooks.slack.com",
710
+ allowCustomUrl: config.allowCustomUrl
711
+ });
712
+ this.level = resolveMinLevel(config.level, "SlackTransport");
713
+ if (config.filter) this.filter = config.filter;
714
+ this.username = config.username;
715
+ this.iconEmoji = config.iconEmoji;
716
+ this.formatter = config.formatter;
717
+ const queue = new WebhookQueue({
718
+ label: "SlackTransport",
719
+ url,
720
+ minIntervalMs: config.minIntervalMs,
721
+ maxPerMessage: SLACK_MAX_PER_MESSAGE,
722
+ maxQueueSize: config.maxBufferSize,
723
+ maxRetries: config.maxRetries,
724
+ requestTimeoutMs: config.requestTimeoutMs ?? 1e4,
725
+ render: (entry) => this.messageFor(entry),
726
+ assemble: (messages, overflow) => this.buildPayload(messages, overflow)
727
+ });
728
+ Object.defineProperty(this, "queue", {
729
+ value: queue,
730
+ enumerable: false
731
+ });
732
+ }
733
+ write(entry) {
734
+ if (!meetsLevel(entry.level, this.level)) return;
735
+ this.queue.push(entry);
736
+ }
737
+ flush() {
738
+ return this.queue.flush();
739
+ }
740
+ close() {
741
+ return this.queue.close();
742
+ }
743
+ /** Never serialise the webhook URL (JSON.stringify, util.inspect). */
744
+ toJSON() {
745
+ return {
746
+ name: this.name,
747
+ level: this.level
748
+ };
749
+ }
750
+ /** `showHidden` would otherwise print the non-enumerable queue (and its URL). */
751
+ [Symbol.for("nodejs.util.inspect.custom")]() {
752
+ return this.toJSON();
753
+ }
754
+ buildPayload(messages, overflow) {
755
+ const blocks = messages.flatMap((m) => m.blocks ?? []);
756
+ const counter = overflow > 0 ? `… and ${overflow} more entr${overflow === 1 ? "y" : "ies"} not shown` : "";
757
+ if (counter) blocks.push({
758
+ type: "context",
759
+ elements: [{
760
+ type: "mrkdwn",
761
+ text: counter
762
+ }]
763
+ });
764
+ const payload = {
765
+ text: truncateText([...messages.map((m) => m.text ?? ""), counter].filter(Boolean).join("\n"), SLACK_TEXT_MAX),
766
+ blocks: blocks.slice(0, SLACK_MAX_BLOCKS)
767
+ };
768
+ if (this.username) payload["username"] = truncateText(this.username, 80);
769
+ if (this.iconEmoji) payload["icon_emoji"] = this.iconEmoji;
770
+ return payload;
771
+ }
772
+ messageFor(entry) {
773
+ if (this.formatter) try {
774
+ return this.formatter(entry);
775
+ } catch {
776
+ require_internal_log.internalWarn("SlackTransport: formatter threw; using the default message");
777
+ }
778
+ return defaultMessage(entry);
779
+ }
780
+ };
781
+ function defaultMessage(entry) {
782
+ const level = singleLine(entry.level).toUpperCase();
783
+ const emoji = LEVEL_EMOJI[entry.level.toLowerCase()] ?? ":white_circle:";
784
+ const headline = `[${level}] ${singleLine(entry.message)}`;
785
+ const blocks = [{
786
+ type: "header",
787
+ text: {
788
+ type: "plain_text",
789
+ text: truncateText(escapeSlack(headline), 150),
790
+ emoji: false
791
+ }
792
+ }];
793
+ const context = [
794
+ ["app", entry.appName],
795
+ ["env", entry.environment],
796
+ ["context", entry.context],
797
+ ["trace", entry.traceId],
798
+ ["time", safeIso(entry.timestamp)]
799
+ ].map(([label, value]) => [label, typeof value === "string" ? singleLine(value) : ""]).filter(([, value]) => value !== "").map(([label, value]) => ({
800
+ type: "mrkdwn",
801
+ text: `*${label}:* ${truncateText(escapeSlack(value), SLACK_FIELD_MAX)}`
802
+ }));
803
+ if (context.length > 0) blocks.push({
804
+ type: "context",
805
+ elements: context
806
+ });
807
+ if (entry.data && Object.keys(entry.data).length > 0) {
808
+ const json = truncateText(escapeSlack(require_safe_stringify.stringifyForLog(entry.data)), SLACK_DATA_MAX);
809
+ blocks.push({
810
+ type: "section",
811
+ text: {
812
+ type: "mrkdwn",
813
+ text: `\`\`\`${json}\`\`\``
814
+ }
815
+ });
816
+ }
817
+ return {
818
+ text: `${emoji} ${escapeSlack(truncateText(headline, 150))}`,
819
+ blocks
820
+ };
821
+ }
822
+ Object.defineProperty(exports, "DiscordTransport", {
823
+ enumerable: true,
824
+ get: function() {
825
+ return DiscordTransport;
826
+ }
827
+ });
828
+ Object.defineProperty(exports, "OtlpLogTransport", {
829
+ enumerable: true,
830
+ get: function() {
831
+ return OtlpLogTransport;
832
+ }
833
+ });
834
+ Object.defineProperty(exports, "SlackTransport", {
835
+ enumerable: true,
836
+ get: function() {
837
+ return SlackTransport;
838
+ }
839
+ });
840
+ Object.defineProperty(exports, "toOtelSeverity", {
841
+ enumerable: true,
842
+ get: function() {
843
+ return toOtelSeverity;
844
+ }
845
+ });
846
+
847
+ //# sourceMappingURL=slack.transport-Dlx3xvb7.js.map