logixia 2.0.9 → 2.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{async-context-COuvmwsS.mjs → async-context-BGUCdYAa.mjs} +3 -3
- package/dist/{async-context-COuvmwsS.mjs.map → async-context-BGUCdYAa.mjs.map} +1 -1
- package/dist/{async-context-Dv0bmPC-.js → async-context-D8um9I5V.js} +3 -3
- package/dist/{async-context-Dv0bmPC-.js.map → async-context-D8um9I5V.js.map} +1 -1
- package/dist/{azure-monitor.transport-D6ryi8rA.js → azure-monitor.transport-C8KdmsSw.js} +19 -8
- package/dist/{azure-monitor.transport-D6ryi8rA.js.map → azure-monitor.transport-C8KdmsSw.js.map} +1 -1
- package/dist/{azure-monitor.transport-Coz5Gesu.mjs → azure-monitor.transport-CTnfwA0F.mjs} +19 -8
- package/dist/{azure-monitor.transport-Coz5Gesu.mjs.map → azure-monitor.transport-CTnfwA0F.mjs.map} +1 -1
- package/dist/azure.js +1 -1
- package/dist/azure.mjs +1 -1
- package/dist/{batch-buffer-CQUWDMYq.mjs → batch-buffer-BJqzpfpT.mjs} +11 -7
- package/dist/{batch-buffer-CQUWDMYq.mjs.map → batch-buffer-BJqzpfpT.mjs.map} +1 -1
- package/dist/{batch-buffer-CFzKnuNZ.js → batch-buffer-BTQdAXwC.js} +11 -7
- package/dist/{batch-buffer-CFzKnuNZ.js.map → batch-buffer-BTQdAXwC.js.map} +1 -1
- package/dist/browser.js +24 -9
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +24 -9
- package/dist/browser.mjs.map +1 -1
- package/dist/{builder-BlIJNIuk.mjs → builder-C6kKEVrU.mjs} +154 -52
- package/dist/builder-C6kKEVrU.mjs.map +1 -0
- package/dist/{builder-Bs0qkfkY.js → builder-Up4YxX7l.js} +157 -55
- package/dist/builder-Up4YxX7l.js.map +1 -0
- package/dist/cli/index.js +21 -16
- package/dist/cloudwatch.js +1 -1
- package/dist/cloudwatch.mjs +1 -1
- package/dist/{cloudwatch.transport-Cr1aeTGj.mjs → cloudwatch.transport-Bl1qzdBx.mjs} +24 -9
- package/dist/{cloudwatch.transport-Cr1aeTGj.mjs.map → cloudwatch.transport-Bl1qzdBx.mjs.map} +1 -1
- package/dist/{cloudwatch.transport-D4G5xYfG.js → cloudwatch.transport-BoCyxNKO.js} +24 -9
- package/dist/{cloudwatch.transport-D4G5xYfG.js.map → cloudwatch.transport-BoCyxNKO.js.map} +1 -1
- package/dist/correlation.js +3 -3
- package/dist/correlation.mjs +3 -3
- package/dist/{coerce.utils-C1C5sIX0.js → csv.utils-CbVHY1pd.js} +24 -1
- package/dist/csv.utils-CbVHY1pd.js.map +1 -0
- package/dist/{coerce.utils-B_rEudCn.mjs → csv.utils-ZdxSod5g.mjs} +19 -2
- package/dist/csv.utils-ZdxSod5g.mjs.map +1 -0
- package/dist/{entry-fields-CVwoQplO.js → entry-fields-CJbeaoBs.js} +2 -2
- package/dist/{entry-fields-CVwoQplO.js.map → entry-fields-CJbeaoBs.js.map} +1 -1
- package/dist/{entry-fields-DNTKQxoU.mjs → entry-fields-D_aNnjg0.mjs} +2 -2
- package/dist/{entry-fields-DNTKQxoU.mjs.map → entry-fields-D_aNnjg0.mjs.map} +1 -1
- package/dist/gcp.js +1 -1
- package/dist/gcp.mjs +1 -1
- package/dist/{gcp.transport-LLK2bw2-.mjs → gcp.transport-DOH4bpWM.mjs} +18 -9
- package/dist/{gcp.transport-LLK2bw2-.mjs.map → gcp.transport-DOH4bpWM.mjs.map} +1 -1
- package/dist/{gcp.transport-CVuRpchl.js → gcp.transport-D_NHVZrw.js} +18 -9
- package/dist/{gcp.transport-CVuRpchl.js.map → gcp.transport-D_NHVZrw.js.map} +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -16
- package/dist/index.mjs.map +1 -1
- package/dist/logitron-logger-DVUdgmBY.d.ts.map +1 -1
- package/dist/logitron-logger-DoWmMjYB.d.mts.map +1 -1
- package/dist/middleware.d.mts.map +1 -1
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +10 -6
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +10 -6
- package/dist/middleware.mjs.map +1 -1
- package/dist/nest.d.mts +4 -3
- package/dist/nest.d.mts.map +1 -1
- package/dist/nest.d.ts +4 -3
- package/dist/nest.d.ts.map +1 -1
- package/dist/nest.js +41 -20
- package/dist/nest.js.map +1 -1
- package/dist/nest.mjs +39 -18
- package/dist/nest.mjs.map +1 -1
- package/dist/{otel-DOuwGPCd.mjs → otel-DAVxpArV.mjs} +135 -56
- package/dist/otel-DAVxpArV.mjs.map +1 -0
- package/dist/{otel-ChnVYRLA.js → otel-DH6-G6DR.js} +137 -58
- package/dist/otel-DH6-G6DR.js.map +1 -0
- package/dist/{request-trace-ByujsUZk.js → request-trace-C7kXMjx5.js} +5 -6
- package/dist/{request-trace-ByujsUZk.js.map → request-trace-C7kXMjx5.js.map} +1 -1
- package/dist/{request-trace-BgNykH9M.mjs → request-trace-Dekcnenb.mjs} +5 -6
- package/dist/{request-trace-BgNykH9M.mjs.map → request-trace-Dekcnenb.mjs.map} +1 -1
- package/dist/{safe-stringify-CN7icp8S.js → safe-stringify-CJisLyZ3.js} +2 -2
- package/dist/{safe-stringify-CN7icp8S.js.map → safe-stringify-CJisLyZ3.js.map} +1 -1
- package/dist/{safe-stringify--7gbe95n.mjs → safe-stringify-pZOnzGRJ.mjs} +2 -2
- package/dist/{safe-stringify--7gbe95n.mjs.map → safe-stringify-pZOnzGRJ.mjs.map} +1 -1
- package/dist/{search-DPwTKPve.d.ts → search-B4eEzrhM.d.ts} +3 -6
- package/dist/{search-DPwTKPve.d.ts.map → search-B4eEzrhM.d.ts.map} +1 -1
- package/dist/{search-CRjhHxe9.js → search-CJw_xoQl.js} +44 -35
- package/dist/search-CJw_xoQl.js.map +1 -0
- package/dist/{search-DAXKE-P9.mjs → search-CjYl-mo5.mjs} +41 -32
- package/dist/search-CjYl-mo5.mjs.map +1 -0
- package/dist/{search-D-8HJADE.d.mts → search-Crf2IEoA.d.mts} +3 -6
- package/dist/{search-D-8HJADE.d.mts.map → search-Crf2IEoA.d.mts.map} +1 -1
- package/dist/search.d.mts +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.js +1 -1
- package/dist/search.mjs +1 -1
- package/dist/{secret-config-BvFl2dC8.js → secret-config-6hdODxBb.js} +2 -2
- package/dist/{secret-config-BvFl2dC8.js.map → secret-config-6hdODxBb.js.map} +1 -1
- package/dist/{secret-config-ByGq7kJd.mjs → secret-config-D1F4MW69.mjs} +2 -2
- package/dist/{secret-config-ByGq7kJd.mjs.map → secret-config-D1F4MW69.mjs.map} +1 -1
- package/dist/{sensitive-names-CHMowujD.js → sensitive-names-CNgHaqv9.js} +7 -1
- package/dist/{sensitive-names-CHMowujD.js.map → sensitive-names-CNgHaqv9.js.map} +1 -1
- package/dist/{sensitive-names-o__1PUUR.mjs → sensitive-names-wv27qBQC.mjs} +2 -2
- package/dist/{sensitive-names-o__1PUUR.mjs.map → sensitive-names-wv27qBQC.mjs.map} +1 -1
- package/dist/{slack.transport-zLQ9MGxS.js → slack.transport-CEYBVzUj.js} +46 -18
- package/dist/{slack.transport-zLQ9MGxS.js.map → slack.transport-CEYBVzUj.js.map} +1 -1
- package/dist/{slack.transport-CSj1j0_X.mjs → slack.transport-YvaLJxNv.mjs} +46 -18
- package/dist/{slack.transport-CSj1j0_X.mjs.map → slack.transport-YvaLJxNv.mjs.map} +1 -1
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +6 -6
- package/dist/transports.mjs +6 -6
- package/dist/worker-entry.js +109 -41
- package/dist/worker.js +1 -1
- package/dist/worker.mjs +1 -1
- package/dist/{worker.transport-DQDv1nAJ.js → worker.transport-CwNpH98o.js} +21 -13
- package/dist/{worker.transport-DQDv1nAJ.js.map → worker.transport-CwNpH98o.js.map} +1 -1
- package/dist/{worker.transport-D78xhNhB.mjs → worker.transport-DgK_ZXlB.mjs} +21 -13
- package/dist/{worker.transport-D78xhNhB.mjs.map → worker.transport-DgK_ZXlB.mjs.map} +1 -1
- package/package.json +10 -3
- package/dist/builder-BlIJNIuk.mjs.map +0 -1
- package/dist/builder-Bs0qkfkY.js.map +0 -1
- package/dist/coerce.utils-B_rEudCn.mjs.map +0 -1
- package/dist/coerce.utils-C1C5sIX0.js.map +0 -1
- package/dist/otel-ChnVYRLA.js.map +0 -1
- package/dist/otel-DOuwGPCd.mjs.map +0 -1
- package/dist/search-CRjhHxe9.js.map +0 -1
- package/dist/search-DAXKE-P9.mjs.map +0 -1
|
@@ -260,6 +260,12 @@ Object.defineProperty(exports, "redactUrlQuery", {
|
|
|
260
260
|
return redactUrlQuery;
|
|
261
261
|
}
|
|
262
262
|
});
|
|
263
|
+
Object.defineProperty(exports, "redactUrlsInText", {
|
|
264
|
+
enumerable: true,
|
|
265
|
+
get: function() {
|
|
266
|
+
return redactUrlsInText;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
263
269
|
Object.defineProperty(exports, "sanitizeText", {
|
|
264
270
|
enumerable: true,
|
|
265
271
|
get: function() {
|
|
@@ -273,4 +279,4 @@ Object.defineProperty(exports, "sensitiveQueryValues", {
|
|
|
273
279
|
}
|
|
274
280
|
});
|
|
275
281
|
|
|
276
|
-
//# sourceMappingURL=sensitive-names-
|
|
282
|
+
//# sourceMappingURL=sensitive-names-CNgHaqv9.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sensitive-names-CHMowujD.js","names":[],"sources":["../src/utils/own-property.ts","../src/utils/sensitive-names.ts"],"sourcesContent":["/**\n * True for the one key that must never be assigned with `target[key] = value`.\n *\n * Assigning `constructor` or `prototype` on a fresh plain object only creates an\n * own property, which is harmless. Only `__proto__` invokes the prototype setter.\n */\nexport function isProtoKey(key: string): boolean {\n return key === '__proto__';\n}\n\n/**\n * Copy `value` onto `target[key]` as an own, enumerable data property.\n *\n * Normal keys use a plain assignment, which is the fast path. `__proto__` goes\n * through `Object.defineProperty` so that a key taken from logged or parsed data\n * becomes an own property instead of replacing the object's prototype.\n */\nexport function assignOwn(target: object, key: string, value: unknown): void {\n if (key === '__proto__') {\n Object.defineProperty(target, key, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n } else {\n (target as Record<string, unknown>)[key] = value;\n }\n}\n","/**\n * The one list of sensitive names. Log redaction (redact.utils), the HTTP logger\n * middleware and transport secret handling (transports/secret-config) all read it,\n * so the lists cannot drift. No imports: safe from any module, including browser code.\n */\n\n/** Field names redacted by name, wherever they appear in logged data. */\nexport const SENSITIVE_KEY_NAMES: readonly string[] = [\n 'password',\n 'passwd',\n 'secret',\n 'token',\n 'apiKey',\n 'api_key',\n 'accessToken',\n 'access_token',\n 'refreshToken',\n 'refresh_token',\n 'authorization',\n 'credentials',\n 'privateKey',\n 'private_key',\n 'clientSecret',\n 'client_secret',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'session',\n 'proxy-authorization',\n 'x-auth-token',\n 'x-access-token',\n 'aws_secret_access_key',\n 'awsSecretAccessKey',\n 'secretAccessKey',\n];\n\n/**\n * Extra names that only mean \"secret\" inside transport configuration, where\n * short words like `key` or `signature` are credentials, not log fields.\n */\nexport const TRANSPORT_CONFIG_SECRET_NAMES: readonly string[] = [\n 'writeKey',\n 'apiSecret',\n 'connectionString',\n 'sessionToken',\n 'passphrase',\n 'signature',\n 'pwd',\n 'pfx',\n 'cert',\n 'key',\n 'auth',\n];\n\n/** Request headers whose values are never logged. */\nexport const DEFAULT_REDACT_HEADERS: readonly string[] = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'proxy-authorization',\n 'x-auth-token',\n 'x-access-token',\n 'x-csrf-token',\n 'x-amz-security-token',\n];\n\n/** Query-string parameters whose values are never logged. */\nexport const DEFAULT_REDACT_QUERY_PARAMS: readonly string[] = [\n 'token',\n 'access_token',\n 'refresh_token',\n 'code',\n 'password',\n 'secret',\n 'api_key',\n 'apikey',\n 'key',\n 'signature',\n 'sig',\n 'auth',\n 'accesstoken',\n 'refreshtoken',\n 'id_token',\n 'idtoken',\n 'client_secret',\n 'clientsecret',\n 'session',\n 'sessionid',\n 'session_id',\n 'jwt',\n 'auth_token',\n 'authtoken',\n 'api-key',\n 'x-api-key',\n 'x-amz-signature',\n 'x-amz-credential',\n 'x-amz-security-token',\n 'x-goog-signature',\n 'x-goog-credential',\n 'access-token',\n 'x-access-token',\n 'x-auth-token',\n 'apitoken',\n 'api_token',\n];\n\nfunction safeDecode(value: string): string {\n try {\n return decodeURIComponent(value.replace(/\\+/g, ' '));\n } catch {\n return value;\n }\n}\n\n/** Replace the values of sensitive query parameters, leaving the rest verbatim. */\nexport function redactUrlQuery(url: string, redactParams: ReadonlySet<string>): string {\n const q = url.indexOf('?');\n if (q === -1) return url;\n const hashAt = url.indexOf('#', q);\n const query = url.slice(q + 1, hashAt === -1 ? undefined : hashAt);\n const hash = hashAt === -1 ? '' : url.slice(hashAt);\n const cleaned = query\n .split('&')\n .map((pair) => {\n const eq = pair.indexOf('=');\n const rawKey = eq === -1 ? pair : pair.slice(0, eq);\n const decoded = safeDecode(rawKey);\n const bracket = decoded.indexOf('[');\n const name = (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();\n return redactParams.has(name) && eq !== -1 ? `${rawKey}=[REDACTED]` : pair;\n })\n .join('&');\n return `${url.slice(0, q)}?${cleaned}${hash}`;\n}\n\n/** Lower-cased lookup set of the default names plus any extras. */\nexport function buildNameSet(\n defaults: readonly string[],\n extra: readonly string[] | undefined\n): Set<string> {\n return new Set([...defaults, ...(extra ?? [])].map((n) => n.toLowerCase()));\n}\n\nconst REDACTED = '[REDACTED]';\nconst MIN_SCRUB_LENGTH = 3;\n\nfunction queryNameOf(rawKey: string): string {\n const decoded = safeDecode(rawKey);\n const bracket = decoded.indexOf('[');\n return (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();\n}\n\n/**\n * Raw and decoded values of sensitive query parameters present in this URL.\n * Values shorter than 3 characters or already `[REDACTED]` are ignored.\n */\nexport function sensitiveQueryValues(url: string, names: ReadonlySet<string>): string[] {\n const q = url.indexOf('?');\n if (q === -1) return [];\n const hashAt = url.indexOf('#', q);\n const query = url.slice(q + 1, hashAt === -1 ? undefined : hashAt);\n const found = new Set<string>();\n for (const pair of query.split('&')) {\n const eq = pair.indexOf('=');\n if (eq === -1 || !names.has(queryNameOf(pair.slice(0, eq)))) continue;\n const raw = pair.slice(eq + 1);\n for (const value of [raw, safeDecode(raw)]) {\n if (value.length >= MIN_SCRUB_LENGTH && value !== REDACTED) found.add(value);\n }\n }\n return [...found];\n}\n\n/** Replace every literal occurrence of `values` in `text` with `[REDACTED]`. */\nexport function scrubValues(text: string, values: readonly string[]): string {\n if (values.length === 0 || text.length === 0) return text;\n let out = text;\n for (const value of [...values].sort((a, b) => b.length - a.length)) {\n if (value.length > 0 && out.includes(value)) out = out.split(value).join(REDACTED);\n }\n return out;\n}\n\nfunction isUrlTerminator(ch: string): boolean {\n return (\n ch === ' ' ||\n ch === '\\n' ||\n ch === '\\r' ||\n ch === '\\t' ||\n ch === '\"' ||\n ch === \"'\" ||\n ch === '`'\n );\n}\n\n/**\n * Redact sensitive query values in every `?query` found inside free text (an\n * error message, a stack line). Scans for `?` and cuts at whitespace or a quote.\n */\nexport function redactUrlsInText(text: string, names: ReadonlySet<string>): string {\n let q = text.indexOf('?');\n if (q === -1) return text;\n let out = '';\n let cursor = 0;\n while (q !== -1) {\n let end = q + 1;\n while (end < text.length && !isUrlTerminator(text.charAt(end))) end++;\n out += text.slice(cursor, q) + redactUrlQuery(text.slice(q, end), names);\n cursor = end;\n q = text.indexOf('?', end);\n }\n return out + text.slice(cursor);\n}\n\n/** Shape-preserving URL redaction plus literal scrubbing of this request's secret values. */\nexport function sanitizeText(\n text: string,\n names: ReadonlySet<string>,\n values: readonly string[]\n): string {\n return scrubValues(redactUrlsInText(text, names), values);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sensitive-names-CNgHaqv9.js","names":[],"sources":["../src/utils/own-property.ts","../src/utils/sensitive-names.ts"],"sourcesContent":["/**\n * True for the one key that must never be assigned with `target[key] = value`.\n *\n * Assigning `constructor` or `prototype` on a fresh plain object only creates an\n * own property, which is harmless. Only `__proto__` invokes the prototype setter.\n */\nexport function isProtoKey(key: string): boolean {\n return key === '__proto__';\n}\n\n/**\n * Copy `value` onto `target[key]` as an own, enumerable data property.\n *\n * Normal keys use a plain assignment, which is the fast path. `__proto__` goes\n * through `Object.defineProperty` so that a key taken from logged or parsed data\n * becomes an own property instead of replacing the object's prototype.\n */\nexport function assignOwn(target: object, key: string, value: unknown): void {\n if (key === '__proto__') {\n Object.defineProperty(target, key, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n } else {\n (target as Record<string, unknown>)[key] = value;\n }\n}\n","/**\n * The one list of sensitive names. Log redaction (redact.utils), the HTTP logger\n * middleware and transport secret handling (transports/secret-config) all read it,\n * so the lists cannot drift. No imports: safe from any module, including browser code.\n */\n\n/** Field names redacted by name, wherever they appear in logged data. */\nexport const SENSITIVE_KEY_NAMES: readonly string[] = [\n 'password',\n 'passwd',\n 'secret',\n 'token',\n 'apiKey',\n 'api_key',\n 'accessToken',\n 'access_token',\n 'refreshToken',\n 'refresh_token',\n 'authorization',\n 'credentials',\n 'privateKey',\n 'private_key',\n 'clientSecret',\n 'client_secret',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'session',\n 'proxy-authorization',\n 'x-auth-token',\n 'x-access-token',\n 'aws_secret_access_key',\n 'awsSecretAccessKey',\n 'secretAccessKey',\n];\n\n/**\n * Extra names that only mean \"secret\" inside transport configuration, where\n * short words like `key` or `signature` are credentials, not log fields.\n */\nexport const TRANSPORT_CONFIG_SECRET_NAMES: readonly string[] = [\n 'writeKey',\n 'apiSecret',\n 'connectionString',\n 'sessionToken',\n 'passphrase',\n 'signature',\n 'pwd',\n 'pfx',\n 'cert',\n 'key',\n 'auth',\n];\n\n/** Request headers whose values are never logged. */\nexport const DEFAULT_REDACT_HEADERS: readonly string[] = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'proxy-authorization',\n 'x-auth-token',\n 'x-access-token',\n 'x-csrf-token',\n 'x-amz-security-token',\n];\n\n/** Query-string parameters whose values are never logged. */\nexport const DEFAULT_REDACT_QUERY_PARAMS: readonly string[] = [\n 'token',\n 'access_token',\n 'refresh_token',\n 'code',\n 'password',\n 'secret',\n 'api_key',\n 'apikey',\n 'key',\n 'signature',\n 'sig',\n 'auth',\n 'accesstoken',\n 'refreshtoken',\n 'id_token',\n 'idtoken',\n 'client_secret',\n 'clientsecret',\n 'session',\n 'sessionid',\n 'session_id',\n 'jwt',\n 'auth_token',\n 'authtoken',\n 'api-key',\n 'x-api-key',\n 'x-amz-signature',\n 'x-amz-credential',\n 'x-amz-security-token',\n 'x-goog-signature',\n 'x-goog-credential',\n 'access-token',\n 'x-access-token',\n 'x-auth-token',\n 'apitoken',\n 'api_token',\n];\n\nfunction safeDecode(value: string): string {\n try {\n return decodeURIComponent(value.replace(/\\+/g, ' '));\n } catch {\n return value;\n }\n}\n\n/** Replace the values of sensitive query parameters, leaving the rest verbatim. */\nexport function redactUrlQuery(url: string, redactParams: ReadonlySet<string>): string {\n const q = url.indexOf('?');\n if (q === -1) return url;\n const hashAt = url.indexOf('#', q);\n const query = url.slice(q + 1, hashAt === -1 ? undefined : hashAt);\n const hash = hashAt === -1 ? '' : url.slice(hashAt);\n const cleaned = query\n .split('&')\n .map((pair) => {\n const eq = pair.indexOf('=');\n const rawKey = eq === -1 ? pair : pair.slice(0, eq);\n const decoded = safeDecode(rawKey);\n const bracket = decoded.indexOf('[');\n const name = (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();\n return redactParams.has(name) && eq !== -1 ? `${rawKey}=[REDACTED]` : pair;\n })\n .join('&');\n return `${url.slice(0, q)}?${cleaned}${hash}`;\n}\n\n/** Lower-cased lookup set of the default names plus any extras. */\nexport function buildNameSet(\n defaults: readonly string[],\n extra: readonly string[] | undefined\n): Set<string> {\n return new Set([...defaults, ...(extra ?? [])].map((n) => n.toLowerCase()));\n}\n\nconst REDACTED = '[REDACTED]';\nconst MIN_SCRUB_LENGTH = 3;\n\nfunction queryNameOf(rawKey: string): string {\n const decoded = safeDecode(rawKey);\n const bracket = decoded.indexOf('[');\n return (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();\n}\n\n/**\n * Raw and decoded values of sensitive query parameters present in this URL.\n * Values shorter than 3 characters or already `[REDACTED]` are ignored.\n */\nexport function sensitiveQueryValues(url: string, names: ReadonlySet<string>): string[] {\n const q = url.indexOf('?');\n if (q === -1) return [];\n const hashAt = url.indexOf('#', q);\n const query = url.slice(q + 1, hashAt === -1 ? undefined : hashAt);\n const found = new Set<string>();\n for (const pair of query.split('&')) {\n const eq = pair.indexOf('=');\n if (eq === -1 || !names.has(queryNameOf(pair.slice(0, eq)))) continue;\n const raw = pair.slice(eq + 1);\n for (const value of [raw, safeDecode(raw)]) {\n if (value.length >= MIN_SCRUB_LENGTH && value !== REDACTED) found.add(value);\n }\n }\n return [...found];\n}\n\n/** Replace every literal occurrence of `values` in `text` with `[REDACTED]`. */\nexport function scrubValues(text: string, values: readonly string[]): string {\n if (values.length === 0 || text.length === 0) return text;\n let out = text;\n for (const value of [...values].sort((a, b) => b.length - a.length)) {\n if (value.length > 0 && out.includes(value)) out = out.split(value).join(REDACTED);\n }\n return out;\n}\n\nfunction isUrlTerminator(ch: string): boolean {\n return (\n ch === ' ' ||\n ch === '\\n' ||\n ch === '\\r' ||\n ch === '\\t' ||\n ch === '\"' ||\n ch === \"'\" ||\n ch === '`'\n );\n}\n\n/**\n * Redact sensitive query values in every `?query` found inside free text (an\n * error message, a stack line). Scans for `?` and cuts at whitespace or a quote.\n */\nexport function redactUrlsInText(text: string, names: ReadonlySet<string>): string {\n let q = text.indexOf('?');\n if (q === -1) return text;\n let out = '';\n let cursor = 0;\n while (q !== -1) {\n let end = q + 1;\n while (end < text.length && !isUrlTerminator(text.charAt(end))) end++;\n out += text.slice(cursor, q) + redactUrlQuery(text.slice(q, end), names);\n cursor = end;\n q = text.indexOf('?', end);\n }\n return out + text.slice(cursor);\n}\n\n/** Shape-preserving URL redaction plus literal scrubbing of this request's secret values. */\nexport function sanitizeText(\n text: string,\n names: ReadonlySet<string>,\n values: readonly string[]\n): string {\n return scrubValues(redactUrlsInText(text, names), values);\n}\n"],"mappings":"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"}
|
|
@@ -212,6 +212,6 @@ function redactUrlsInText(text, names) {
|
|
|
212
212
|
function sanitizeText(text, names, values) {
|
|
213
213
|
return scrubValues(redactUrlsInText(text, names), values);
|
|
214
214
|
}
|
|
215
|
-
export { buildNameSet as a,
|
|
215
|
+
export { buildNameSet as a, sanitizeText as c, isProtoKey as d, TRANSPORT_CONFIG_SECRET_NAMES as i, sensitiveQueryValues as l, DEFAULT_REDACT_QUERY_PARAMS as n, redactUrlQuery as o, SENSITIVE_KEY_NAMES as r, redactUrlsInText as s, DEFAULT_REDACT_HEADERS as t, assignOwn as u };
|
|
216
216
|
|
|
217
|
-
//# sourceMappingURL=sensitive-names-
|
|
217
|
+
//# sourceMappingURL=sensitive-names-wv27qBQC.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sensitive-names-o__1PUUR.mjs","names":[],"sources":["../src/utils/own-property.ts","../src/utils/sensitive-names.ts"],"sourcesContent":["/**\n * True for the one key that must never be assigned with `target[key] = value`.\n *\n * Assigning `constructor` or `prototype` on a fresh plain object only creates an\n * own property, which is harmless. Only `__proto__` invokes the prototype setter.\n */\nexport function isProtoKey(key: string): boolean {\n return key === '__proto__';\n}\n\n/**\n * Copy `value` onto `target[key]` as an own, enumerable data property.\n *\n * Normal keys use a plain assignment, which is the fast path. `__proto__` goes\n * through `Object.defineProperty` so that a key taken from logged or parsed data\n * becomes an own property instead of replacing the object's prototype.\n */\nexport function assignOwn(target: object, key: string, value: unknown): void {\n if (key === '__proto__') {\n Object.defineProperty(target, key, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n } else {\n (target as Record<string, unknown>)[key] = value;\n }\n}\n","/**\n * The one list of sensitive names. Log redaction (redact.utils), the HTTP logger\n * middleware and transport secret handling (transports/secret-config) all read it,\n * so the lists cannot drift. No imports: safe from any module, including browser code.\n */\n\n/** Field names redacted by name, wherever they appear in logged data. */\nexport const SENSITIVE_KEY_NAMES: readonly string[] = [\n 'password',\n 'passwd',\n 'secret',\n 'token',\n 'apiKey',\n 'api_key',\n 'accessToken',\n 'access_token',\n 'refreshToken',\n 'refresh_token',\n 'authorization',\n 'credentials',\n 'privateKey',\n 'private_key',\n 'clientSecret',\n 'client_secret',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'session',\n 'proxy-authorization',\n 'x-auth-token',\n 'x-access-token',\n 'aws_secret_access_key',\n 'awsSecretAccessKey',\n 'secretAccessKey',\n];\n\n/**\n * Extra names that only mean \"secret\" inside transport configuration, where\n * short words like `key` or `signature` are credentials, not log fields.\n */\nexport const TRANSPORT_CONFIG_SECRET_NAMES: readonly string[] = [\n 'writeKey',\n 'apiSecret',\n 'connectionString',\n 'sessionToken',\n 'passphrase',\n 'signature',\n 'pwd',\n 'pfx',\n 'cert',\n 'key',\n 'auth',\n];\n\n/** Request headers whose values are never logged. */\nexport const DEFAULT_REDACT_HEADERS: readonly string[] = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'proxy-authorization',\n 'x-auth-token',\n 'x-access-token',\n 'x-csrf-token',\n 'x-amz-security-token',\n];\n\n/** Query-string parameters whose values are never logged. */\nexport const DEFAULT_REDACT_QUERY_PARAMS: readonly string[] = [\n 'token',\n 'access_token',\n 'refresh_token',\n 'code',\n 'password',\n 'secret',\n 'api_key',\n 'apikey',\n 'key',\n 'signature',\n 'sig',\n 'auth',\n 'accesstoken',\n 'refreshtoken',\n 'id_token',\n 'idtoken',\n 'client_secret',\n 'clientsecret',\n 'session',\n 'sessionid',\n 'session_id',\n 'jwt',\n 'auth_token',\n 'authtoken',\n 'api-key',\n 'x-api-key',\n 'x-amz-signature',\n 'x-amz-credential',\n 'x-amz-security-token',\n 'x-goog-signature',\n 'x-goog-credential',\n 'access-token',\n 'x-access-token',\n 'x-auth-token',\n 'apitoken',\n 'api_token',\n];\n\nfunction safeDecode(value: string): string {\n try {\n return decodeURIComponent(value.replace(/\\+/g, ' '));\n } catch {\n return value;\n }\n}\n\n/** Replace the values of sensitive query parameters, leaving the rest verbatim. */\nexport function redactUrlQuery(url: string, redactParams: ReadonlySet<string>): string {\n const q = url.indexOf('?');\n if (q === -1) return url;\n const hashAt = url.indexOf('#', q);\n const query = url.slice(q + 1, hashAt === -1 ? undefined : hashAt);\n const hash = hashAt === -1 ? '' : url.slice(hashAt);\n const cleaned = query\n .split('&')\n .map((pair) => {\n const eq = pair.indexOf('=');\n const rawKey = eq === -1 ? pair : pair.slice(0, eq);\n const decoded = safeDecode(rawKey);\n const bracket = decoded.indexOf('[');\n const name = (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();\n return redactParams.has(name) && eq !== -1 ? `${rawKey}=[REDACTED]` : pair;\n })\n .join('&');\n return `${url.slice(0, q)}?${cleaned}${hash}`;\n}\n\n/** Lower-cased lookup set of the default names plus any extras. */\nexport function buildNameSet(\n defaults: readonly string[],\n extra: readonly string[] | undefined\n): Set<string> {\n return new Set([...defaults, ...(extra ?? [])].map((n) => n.toLowerCase()));\n}\n\nconst REDACTED = '[REDACTED]';\nconst MIN_SCRUB_LENGTH = 3;\n\nfunction queryNameOf(rawKey: string): string {\n const decoded = safeDecode(rawKey);\n const bracket = decoded.indexOf('[');\n return (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();\n}\n\n/**\n * Raw and decoded values of sensitive query parameters present in this URL.\n * Values shorter than 3 characters or already `[REDACTED]` are ignored.\n */\nexport function sensitiveQueryValues(url: string, names: ReadonlySet<string>): string[] {\n const q = url.indexOf('?');\n if (q === -1) return [];\n const hashAt = url.indexOf('#', q);\n const query = url.slice(q + 1, hashAt === -1 ? undefined : hashAt);\n const found = new Set<string>();\n for (const pair of query.split('&')) {\n const eq = pair.indexOf('=');\n if (eq === -1 || !names.has(queryNameOf(pair.slice(0, eq)))) continue;\n const raw = pair.slice(eq + 1);\n for (const value of [raw, safeDecode(raw)]) {\n if (value.length >= MIN_SCRUB_LENGTH && value !== REDACTED) found.add(value);\n }\n }\n return [...found];\n}\n\n/** Replace every literal occurrence of `values` in `text` with `[REDACTED]`. */\nexport function scrubValues(text: string, values: readonly string[]): string {\n if (values.length === 0 || text.length === 0) return text;\n let out = text;\n for (const value of [...values].sort((a, b) => b.length - a.length)) {\n if (value.length > 0 && out.includes(value)) out = out.split(value).join(REDACTED);\n }\n return out;\n}\n\nfunction isUrlTerminator(ch: string): boolean {\n return (\n ch === ' ' ||\n ch === '\\n' ||\n ch === '\\r' ||\n ch === '\\t' ||\n ch === '\"' ||\n ch === \"'\" ||\n ch === '`'\n );\n}\n\n/**\n * Redact sensitive query values in every `?query` found inside free text (an\n * error message, a stack line). Scans for `?` and cuts at whitespace or a quote.\n */\nexport function redactUrlsInText(text: string, names: ReadonlySet<string>): string {\n let q = text.indexOf('?');\n if (q === -1) return text;\n let out = '';\n let cursor = 0;\n while (q !== -1) {\n let end = q + 1;\n while (end < text.length && !isUrlTerminator(text.charAt(end))) end++;\n out += text.slice(cursor, q) + redactUrlQuery(text.slice(q, end), names);\n cursor = end;\n q = text.indexOf('?', end);\n }\n return out + text.slice(cursor);\n}\n\n/** Shape-preserving URL redaction plus literal scrubbing of this request's secret values. */\nexport function sanitizeText(\n text: string,\n names: ReadonlySet<string>,\n values: readonly string[]\n): string {\n return scrubValues(redactUrlsInText(text, names), values);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sensitive-names-wv27qBQC.mjs","names":[],"sources":["../src/utils/own-property.ts","../src/utils/sensitive-names.ts"],"sourcesContent":["/**\n * True for the one key that must never be assigned with `target[key] = value`.\n *\n * Assigning `constructor` or `prototype` on a fresh plain object only creates an\n * own property, which is harmless. Only `__proto__` invokes the prototype setter.\n */\nexport function isProtoKey(key: string): boolean {\n return key === '__proto__';\n}\n\n/**\n * Copy `value` onto `target[key]` as an own, enumerable data property.\n *\n * Normal keys use a plain assignment, which is the fast path. `__proto__` goes\n * through `Object.defineProperty` so that a key taken from logged or parsed data\n * becomes an own property instead of replacing the object's prototype.\n */\nexport function assignOwn(target: object, key: string, value: unknown): void {\n if (key === '__proto__') {\n Object.defineProperty(target, key, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n } else {\n (target as Record<string, unknown>)[key] = value;\n }\n}\n","/**\n * The one list of sensitive names. Log redaction (redact.utils), the HTTP logger\n * middleware and transport secret handling (transports/secret-config) all read it,\n * so the lists cannot drift. No imports: safe from any module, including browser code.\n */\n\n/** Field names redacted by name, wherever they appear in logged data. */\nexport const SENSITIVE_KEY_NAMES: readonly string[] = [\n 'password',\n 'passwd',\n 'secret',\n 'token',\n 'apiKey',\n 'api_key',\n 'accessToken',\n 'access_token',\n 'refreshToken',\n 'refresh_token',\n 'authorization',\n 'credentials',\n 'privateKey',\n 'private_key',\n 'clientSecret',\n 'client_secret',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'session',\n 'proxy-authorization',\n 'x-auth-token',\n 'x-access-token',\n 'aws_secret_access_key',\n 'awsSecretAccessKey',\n 'secretAccessKey',\n];\n\n/**\n * Extra names that only mean \"secret\" inside transport configuration, where\n * short words like `key` or `signature` are credentials, not log fields.\n */\nexport const TRANSPORT_CONFIG_SECRET_NAMES: readonly string[] = [\n 'writeKey',\n 'apiSecret',\n 'connectionString',\n 'sessionToken',\n 'passphrase',\n 'signature',\n 'pwd',\n 'pfx',\n 'cert',\n 'key',\n 'auth',\n];\n\n/** Request headers whose values are never logged. */\nexport const DEFAULT_REDACT_HEADERS: readonly string[] = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'proxy-authorization',\n 'x-auth-token',\n 'x-access-token',\n 'x-csrf-token',\n 'x-amz-security-token',\n];\n\n/** Query-string parameters whose values are never logged. */\nexport const DEFAULT_REDACT_QUERY_PARAMS: readonly string[] = [\n 'token',\n 'access_token',\n 'refresh_token',\n 'code',\n 'password',\n 'secret',\n 'api_key',\n 'apikey',\n 'key',\n 'signature',\n 'sig',\n 'auth',\n 'accesstoken',\n 'refreshtoken',\n 'id_token',\n 'idtoken',\n 'client_secret',\n 'clientsecret',\n 'session',\n 'sessionid',\n 'session_id',\n 'jwt',\n 'auth_token',\n 'authtoken',\n 'api-key',\n 'x-api-key',\n 'x-amz-signature',\n 'x-amz-credential',\n 'x-amz-security-token',\n 'x-goog-signature',\n 'x-goog-credential',\n 'access-token',\n 'x-access-token',\n 'x-auth-token',\n 'apitoken',\n 'api_token',\n];\n\nfunction safeDecode(value: string): string {\n try {\n return decodeURIComponent(value.replace(/\\+/g, ' '));\n } catch {\n return value;\n }\n}\n\n/** Replace the values of sensitive query parameters, leaving the rest verbatim. */\nexport function redactUrlQuery(url: string, redactParams: ReadonlySet<string>): string {\n const q = url.indexOf('?');\n if (q === -1) return url;\n const hashAt = url.indexOf('#', q);\n const query = url.slice(q + 1, hashAt === -1 ? undefined : hashAt);\n const hash = hashAt === -1 ? '' : url.slice(hashAt);\n const cleaned = query\n .split('&')\n .map((pair) => {\n const eq = pair.indexOf('=');\n const rawKey = eq === -1 ? pair : pair.slice(0, eq);\n const decoded = safeDecode(rawKey);\n const bracket = decoded.indexOf('[');\n const name = (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();\n return redactParams.has(name) && eq !== -1 ? `${rawKey}=[REDACTED]` : pair;\n })\n .join('&');\n return `${url.slice(0, q)}?${cleaned}${hash}`;\n}\n\n/** Lower-cased lookup set of the default names plus any extras. */\nexport function buildNameSet(\n defaults: readonly string[],\n extra: readonly string[] | undefined\n): Set<string> {\n return new Set([...defaults, ...(extra ?? [])].map((n) => n.toLowerCase()));\n}\n\nconst REDACTED = '[REDACTED]';\nconst MIN_SCRUB_LENGTH = 3;\n\nfunction queryNameOf(rawKey: string): string {\n const decoded = safeDecode(rawKey);\n const bracket = decoded.indexOf('[');\n return (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();\n}\n\n/**\n * Raw and decoded values of sensitive query parameters present in this URL.\n * Values shorter than 3 characters or already `[REDACTED]` are ignored.\n */\nexport function sensitiveQueryValues(url: string, names: ReadonlySet<string>): string[] {\n const q = url.indexOf('?');\n if (q === -1) return [];\n const hashAt = url.indexOf('#', q);\n const query = url.slice(q + 1, hashAt === -1 ? undefined : hashAt);\n const found = new Set<string>();\n for (const pair of query.split('&')) {\n const eq = pair.indexOf('=');\n if (eq === -1 || !names.has(queryNameOf(pair.slice(0, eq)))) continue;\n const raw = pair.slice(eq + 1);\n for (const value of [raw, safeDecode(raw)]) {\n if (value.length >= MIN_SCRUB_LENGTH && value !== REDACTED) found.add(value);\n }\n }\n return [...found];\n}\n\n/** Replace every literal occurrence of `values` in `text` with `[REDACTED]`. */\nexport function scrubValues(text: string, values: readonly string[]): string {\n if (values.length === 0 || text.length === 0) return text;\n let out = text;\n for (const value of [...values].sort((a, b) => b.length - a.length)) {\n if (value.length > 0 && out.includes(value)) out = out.split(value).join(REDACTED);\n }\n return out;\n}\n\nfunction isUrlTerminator(ch: string): boolean {\n return (\n ch === ' ' ||\n ch === '\\n' ||\n ch === '\\r' ||\n ch === '\\t' ||\n ch === '\"' ||\n ch === \"'\" ||\n ch === '`'\n );\n}\n\n/**\n * Redact sensitive query values in every `?query` found inside free text (an\n * error message, a stack line). Scans for `?` and cuts at whitespace or a quote.\n */\nexport function redactUrlsInText(text: string, names: ReadonlySet<string>): string {\n let q = text.indexOf('?');\n if (q === -1) return text;\n let out = '';\n let cursor = 0;\n while (q !== -1) {\n let end = q + 1;\n while (end < text.length && !isUrlTerminator(text.charAt(end))) end++;\n out += text.slice(cursor, q) + redactUrlQuery(text.slice(q, end), names);\n cursor = end;\n q = text.indexOf('?', end);\n }\n return out + text.slice(cursor);\n}\n\n/** Shape-preserving URL redaction plus literal scrubbing of this request's secret values. */\nexport function sanitizeText(\n text: string,\n names: ReadonlySet<string>,\n values: readonly string[]\n): string {\n return scrubValues(redactUrlsInText(text, names), values);\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const require_secret_config = require("./secret-config-
|
|
1
|
+
const require_secret_config = require("./secret-config-6hdODxBb.js");
|
|
2
2
|
const require_internal_log = require("./internal-log-DwqTjhpb.js");
|
|
3
|
-
const require_safe_stringify = require("./safe-stringify-
|
|
4
|
-
const require_otel = require("./otel-
|
|
5
|
-
const require_batch_buffer = require("./batch-buffer-
|
|
3
|
+
const require_safe_stringify = require("./safe-stringify-CJisLyZ3.js");
|
|
4
|
+
const require_otel = require("./otel-DH6-G6DR.js");
|
|
5
|
+
const require_batch_buffer = require("./batch-buffer-BTQdAXwC.js");
|
|
6
6
|
// src/transports/otlp.transport.ts
|
|
7
7
|
const HEX_32 = /^[0-9a-f]{32}$/;
|
|
8
8
|
const HEX_16 = /^[0-9a-f]{16}$/;
|
|
@@ -128,9 +128,17 @@ function toAttributes(rec) {
|
|
|
128
128
|
}));
|
|
129
129
|
}
|
|
130
130
|
var OtlpLogTransport = class {
|
|
131
|
+
name = "otlp";
|
|
132
|
+
level;
|
|
133
|
+
url;
|
|
134
|
+
headers;
|
|
135
|
+
batchSize;
|
|
136
|
+
flushIntervalMs;
|
|
137
|
+
requestTimeoutMs;
|
|
138
|
+
resourceAttrs;
|
|
139
|
+
buffer;
|
|
140
|
+
flushTimer = null;
|
|
131
141
|
constructor(config) {
|
|
132
|
-
this.name = "otlp";
|
|
133
|
-
this.flushTimer = null;
|
|
134
142
|
this.url = config.url;
|
|
135
143
|
this.headers = config.headers ?? {};
|
|
136
144
|
this.batchSize = config.batchSize ?? 100;
|
|
@@ -329,17 +337,25 @@ async function cancelBody(res) {
|
|
|
329
337
|
* "… and N more" line and an internal warning reports the count.
|
|
330
338
|
*/
|
|
331
339
|
var WebhookQueue = class {
|
|
340
|
+
opts;
|
|
341
|
+
items = [];
|
|
342
|
+
cap;
|
|
343
|
+
host;
|
|
344
|
+
httpLabel;
|
|
345
|
+
minIntervalMs;
|
|
346
|
+
maxRetries;
|
|
347
|
+
timeoutMs;
|
|
348
|
+
timer = null;
|
|
349
|
+
inFlight;
|
|
350
|
+
nextAllowed = 0;
|
|
351
|
+
failures = 0;
|
|
352
|
+
closed = false;
|
|
353
|
+
shutdown = new AbortController();
|
|
354
|
+
overflowPending = 0;
|
|
355
|
+
lastOverflowWarn = 0;
|
|
356
|
+
fetchWarned = false;
|
|
332
357
|
constructor(opts) {
|
|
333
358
|
this.opts = opts;
|
|
334
|
-
this.items = [];
|
|
335
|
-
this.timer = null;
|
|
336
|
-
this.nextAllowed = 0;
|
|
337
|
-
this.failures = 0;
|
|
338
|
-
this.closed = false;
|
|
339
|
-
this.shutdown = new AbortController();
|
|
340
|
-
this.overflowPending = 0;
|
|
341
|
-
this.lastOverflowWarn = 0;
|
|
342
|
-
this.fetchWarned = false;
|
|
343
359
|
this.host = opts.url.hostname;
|
|
344
360
|
this.httpLabel = `${opts.label} (${this.host})`;
|
|
345
361
|
this.minIntervalMs = nonNegative(opts.minIntervalMs, 1e3);
|
|
@@ -605,8 +621,14 @@ function clampCustomEmbed(embed) {
|
|
|
605
621
|
return out;
|
|
606
622
|
}
|
|
607
623
|
var DiscordTransport = class {
|
|
624
|
+
name = "discord";
|
|
625
|
+
level;
|
|
626
|
+
filter;
|
|
627
|
+
queue;
|
|
628
|
+
username;
|
|
629
|
+
avatarUrl;
|
|
630
|
+
formatter;
|
|
608
631
|
constructor(config) {
|
|
609
|
-
this.name = "discord";
|
|
610
632
|
const url = parseWebhookUrl(config.webhookUrl, {
|
|
611
633
|
label: "DiscordTransport",
|
|
612
634
|
hostMatches: (h) => [
|
|
@@ -751,8 +773,14 @@ const LEVEL_EMOJI = {
|
|
|
751
773
|
info: ":information_source:"
|
|
752
774
|
};
|
|
753
775
|
var SlackTransport = class {
|
|
776
|
+
name = "slack";
|
|
777
|
+
level;
|
|
778
|
+
filter;
|
|
779
|
+
queue;
|
|
780
|
+
username;
|
|
781
|
+
iconEmoji;
|
|
782
|
+
formatter;
|
|
754
783
|
constructor(config) {
|
|
755
|
-
this.name = "slack";
|
|
756
784
|
const url = parseWebhookUrl(config.webhookUrl, {
|
|
757
785
|
label: "SlackTransport",
|
|
758
786
|
hostMatches: (h) => h === "hooks.slack.com" || h === "hooks.slack-gov.com",
|
|
@@ -895,4 +923,4 @@ Object.defineProperty(exports, "toOtelSeverity", {
|
|
|
895
923
|
}
|
|
896
924
|
});
|
|
897
925
|
|
|
898
|
-
//# sourceMappingURL=slack.transport-
|
|
926
|
+
//# sourceMappingURL=slack.transport-CEYBVzUj.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.transport-zLQ9MGxS.js","names":["_getOtelBridgeFieldNames","stringifyForLog","BatchBuffer","collectSecrets","HttpError","BufferCap","internalError","isPermanentHttpError","HttpError","stringifyForLog","stringifyForLog"],"sources":["../src/transports/otlp.transport.ts","../src/transports/webhook-queue.ts","../src/transports/discord.transport.ts","../src/transports/slack.transport.ts"],"sourcesContent":["/**\n * OTLP Logs transport — emit logixia logs as OpenTelemetry LogRecords.\n *\n * logixia already READS the active OTel span (the bridge injects traceId/spanId\n * into payloads). This transport closes the loop by EMITTING logs OUT in the\n * OTLP/HTTP JSON format to a collector, making logixia an OTel-Logs-native\n * source — logs land in any OTLP backend (Grafana Loki, OpenObserve, Better\n * Stack, Axiom, Datadog, SigNoz…) already correlated with traces.\n *\n * Dependency-free: builds the OTLP/HTTP JSON payload directly (no\n * @opentelemetry/* packages required — that JS API is still alpha), conforming\n * to the OTel logs data model: SeverityNumber (1–24), resource attributes\n * (service.name/version, deployment.environment), and TraceId/SpanId on each\n * record for correlation.\n *\n * @example\n * ```ts\n * transports: {\n * custom: [ new OtlpLogTransport({\n * url: 'http://localhost:4318/v1/logs',\n * serviceName: 'api',\n * headers: { 'x-api-key': process.env.OTLP_KEY! },\n * }) ],\n * }\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { _getOtelBridgeFieldNames } from '../utils/otel';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n HttpError,\n} from './batch-buffer';\nimport { collectSecrets, protectSecrets, scrubError } from './secret-config';\n\nexport interface OtlpLogTransportConfig {\n /** OTLP/HTTP logs endpoint, e.g. `http://localhost:4318/v1/logs`. */\n url: string;\n /** Extra HTTP headers (auth, tenant, etc.). */\n headers?: Record<string, string>;\n /** `service.name` resource attribute. Default: 'logixia'. */\n serviceName?: string;\n /** `service.version` resource attribute. */\n serviceVersion?: string;\n /** `deployment.environment` resource attribute. */\n environment?: string;\n /** Extra resource attributes merged into the OTLP resource. */\n resourceAttributes?: Record<string, string | number | boolean>;\n /** Entries per batch / per POST. Default: 100. */\n batchSize?: number;\n /** Auto-flush interval (ms). Default: 5000. */\n flushIntervalMs?: number;\n /** Abort a request that has not finished after this many ms. Default: 10000. */\n requestTimeoutMs?: number;\n /** Max buffered entries; the oldest are dropped past this. Default: 10000. */\n maxBufferSize?: number;\n level?: string;\n}\n\nconst HEX_32 = /^[0-9a-f]{32}$/;\nconst HEX_16 = /^[0-9a-f]{16}$/;\n\n/** OTLP JSON wants a 16-char lowercase hex span id; anything else (or all zeros) returns undefined. */\nexport function toOtlpSpanId(id: string): string | undefined {\n const hex = id.toLowerCase();\n return HEX_16.test(hex) && hex !== '0'.repeat(16) ? hex : undefined;\n}\n\n/**\n * OTLP JSON wants a 32-char lowercase hex trace id. Dashed UUIDs are\n * normalised; anything else (or all zeros) returns undefined.\n */\nexport function toOtlpTraceId(id: string): string | undefined {\n const hex = id.replace(/-/g, '').toLowerCase();\n return HEX_32.test(hex) && hex !== '0'.repeat(32) ? hex : undefined;\n}\n\n/**\n * Map a logixia level name to an OTel SeverityNumber (1–24) and text.\n * Per the OTel logs SDK spec: TRACE=1, DEBUG=5, INFO=9, WARN=13, ERROR=17,\n * FATAL=21. Custom/unknown levels fall back to INFO (9).\n */\nexport function toOtelSeverity(level: string): { number: number; text: string } {\n switch (level.toLowerCase()) {\n case 'trace':\n return { number: 1, text: 'TRACE' };\n case 'verbose':\n return { number: 5, text: 'DEBUG' }; // verbose maps to DEBUG range\n case 'debug':\n return { number: 5, text: 'DEBUG' };\n case 'info':\n return { number: 9, text: 'INFO' };\n case 'warn':\n case 'warning':\n return { number: 13, text: 'WARN' };\n case 'error':\n return { number: 17, text: 'ERROR' };\n case 'fatal':\n return { number: 21, text: 'FATAL' };\n default:\n return { number: 9, text: 'INFO' };\n }\n}\n\ninterface NativeIds {\n traceId?: string;\n spanId?: string;\n /** The entry's own trace id when it could not become the native `traceId`. */\n traceAttr?: string;\n}\n\nfunction findHexId(\n attrs: Record<string, unknown>,\n names: readonly string[],\n convert: (id: string) => string | undefined\n): { key: string; hex: string } | undefined {\n for (const key of names) {\n const value = attrs[key];\n if (typeof value !== 'string') continue;\n const hex = convert(value);\n if (hex) return { key, hex };\n }\n return undefined;\n}\n\n/**\n * Pick the native OTLP `traceId`/`spanId` and remove the ids they came from out of\n * `attrs`. A valid OTel pair in the payload (the bridge's trace and span id) wins\n * and is used for both fields, so they always belong to the same trace. Otherwise a\n * valid `entry.traceId` is used alone. A `spanId` is never emitted without its trace.\n */\nfunction resolveNativeIds(entry: TransportLogEntry, attrs: Record<string, unknown>): NativeIds {\n const bridge = _getOtelBridgeFieldNames();\n const traceNames = [...(bridge ? [bridge.traceIdField] : []), 'traceId', 'trace_id'];\n const spanNames = [...(bridge ? [bridge.spanIdField] : []), 'spanId', 'span_id'];\n const payloadTrace = findHexId(attrs, traceNames, toOtlpTraceId);\n const payloadSpan = findHexId(attrs, spanNames, toOtlpSpanId);\n const entryHex = entry.traceId ? toOtlpTraceId(entry.traceId) : undefined;\n\n const ids: NativeIds = {};\n if (payloadTrace && payloadSpan) {\n ids.traceId = payloadTrace.hex;\n ids.spanId = payloadSpan.hex;\n delete attrs[payloadSpan.key];\n } else if (entryHex) {\n ids.traceId = entryHex;\n } else if (payloadTrace) {\n ids.traceId = payloadTrace.hex;\n }\n\n if (ids.traceId) {\n for (const key of traceNames) {\n const value = attrs[key];\n if (\n typeof value === 'string' &&\n (value === entry.traceId || toOtlpTraceId(value) === ids.traceId)\n ) {\n delete attrs[key];\n }\n }\n }\n if (entry.traceId && !(entryHex && entryHex === ids.traceId)) ids.traceAttr = entry.traceId;\n return ids;\n}\n\n/** Coerce a JS value into an OTLP AnyValue. */\nfunction toAnyValue(value: unknown): Record<string, unknown> {\n if (typeof value === 'string') return { stringValue: value };\n if (typeof value === 'boolean') return { boolValue: value };\n if (typeof value === 'number') {\n return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };\n }\n if (typeof value === 'bigint') return { stringValue: value.toString() };\n if (value === null || value === undefined) return { stringValue: '' };\n // Objects/arrays → JSON string (kvlistValue would be richer but stringValue is\n // universally accepted and avoids deep recursion / circular issues here).\n try {\n return { stringValue: stringifyForLog(value) };\n } catch {\n return { stringValue: String(value) };\n }\n}\n\n/** Build the OTLP KeyValue attribute list from a flat record. */\nfunction toAttributes(rec: Record<string, unknown>): Array<{ key: string; value: unknown }> {\n return Object.entries(rec)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => ({ key, value: toAnyValue(value) }));\n}\n\nexport class OtlpLogTransport implements IAsyncTransport {\n public readonly name = 'otlp';\n public readonly level: string | undefined;\n\n private readonly url: string;\n private readonly headers: Record<string, string>;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n private readonly requestTimeoutMs: number;\n private readonly resourceAttrs: Array<{ key: string; value: unknown }>;\n\n private readonly buffer: BatchBuffer<TransportLogEntry>;\n private flushTimer: NodeJS.Timeout | null = null;\n\n constructor(config: OtlpLogTransportConfig) {\n this.url = config.url;\n this.headers = config.headers ?? {};\n this.batchSize = config.batchSize ?? 100;\n this.flushIntervalMs = config.flushIntervalMs ?? 5000;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.level = config.level;\n this.buffer = new BatchBuffer<TransportLogEntry>({\n name: 'OtlpLogTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (entries) => this.send(entries),\n secrets: () => this.secretValues(),\n });\n\n const resource: Record<string, unknown> = {\n 'service.name': config.serviceName ?? 'logixia',\n ...(config.serviceVersion ? { 'service.version': config.serviceVersion } : {}),\n ...(config.environment ? { 'deployment.environment': config.environment } : {}),\n ...(config.resourceAttributes ?? {}),\n };\n this.resourceAttrs = toAttributes(resource);\n\n protectSecrets(this, ['headers', 'url']);\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushIntervalMs);\n if (this.flushTimer.unref) this.flushTimer.unref();\n }\n\n write(entry: TransportLogEntry): void {\n this.buffer.push(entry);\n this.buffer.flushIfFull();\n }\n\n /** Convert one entry into an OTLP LogRecord. */\n private toLogRecord(entry: TransportLogEntry): Record<string, unknown> {\n const sev = toOtelSeverity(entry.level);\n const tsNanos = String(entry.timestamp.getTime() * 1_000_000);\n\n const attrs: Record<string, unknown> = { ...(entry.data ?? {}) };\n const ids = resolveNativeIds(entry, attrs);\n if (entry.context !== undefined) attrs['context'] = entry.context;\n if (entry.appName !== undefined) attrs['app.name'] = entry.appName;\n if (entry.environment !== undefined) attrs['deployment.environment'] = entry.environment;\n\n const record: Record<string, unknown> = {\n timeUnixNano: tsNanos,\n observedTimeUnixNano: tsNanos,\n severityNumber: sev.number,\n severityText: sev.text,\n body: { stringValue: entry.message },\n attributes: toAttributes(attrs),\n };\n\n if (ids.traceId) record['traceId'] = ids.traceId;\n if (ids.spanId) record['spanId'] = ids.spanId;\n if (ids.traceAttr !== undefined) {\n (record['attributes'] as unknown[]).push({\n key: 'trace_id',\n value: { stringValue: ids.traceAttr },\n });\n }\n\n return record;\n }\n\n private buildPayload(entries: TransportLogEntry[]): string {\n return stringifyForLog({\n resourceLogs: [\n {\n resource: { attributes: this.resourceAttrs },\n scopeLogs: [\n {\n scope: { name: 'logixia' },\n logRecords: entries.map((e) => this.toLogRecord(e)),\n },\n ],\n },\n ],\n });\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n private secretValues(): string[] {\n return collectSecrets({ headers: this.headers, url: this.url });\n }\n\n private async send(entries: TransportLogEntry[]): Promise<void> {\n if (typeof fetch !== 'function') {\n internalWarn('OtlpLogTransport: global fetch unavailable — cannot export logs');\n return;\n }\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);\n try {\n const res = await fetch(this.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...this.headers },\n body: this.buildPayload(entries),\n signal: controller.signal,\n });\n if (!res.ok) {\n throw new HttpError(res.status, '', 'OTLP export');\n }\n } catch (err) {\n scrubError(err, this.secretValues());\n if (controller.signal.aborted) {\n throw new Error(`OTLP export timed out after ${this.requestTimeoutMs}ms`, { cause: err });\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n }\n\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.buffer.close();\n }\n}\n","/**\n * Shared core for the chat-webhook transports (Slack, Discord).\n *\n * - entries inside `minIntervalMs` are batched into one message; entries past\n * `maxPerMessage` are dropped and reported to the payload builder as overflow\n * - the queue is bounded (BufferCap); the oldest entries go first\n * - HTTP 429 waits for Retry-After (bounded, cancelled by close()) and retries a\n * bounded number of times\n * - an entry that cannot be rendered is dropped alone; an unserializable batch or a\n * missing global fetch is dropped, never retried\n * - errors only ever name the webhook host, never the URL (the URL is a secret)\n */\n\nimport type { TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { HttpError, isPermanentHttpError } from './batch-buffer';\nimport { BufferCap } from './buffer-cap';\n\nconst MAX_BACKOFF_MS = 30_000;\nconst DEFAULT_QUEUE_CAP = 1000;\nconst CLOSE_TIMEOUT_MS = 3000;\nconst OVERFLOW_WARN_MS = 5000;\nexport const DEFAULT_WEBHOOK_TIMEOUT_MS = 10_000;\nconst ZWSP = '';\n\nconst LEVEL_RANK: Record<string, number> = {\n fatal: 0,\n error: 1,\n warn: 2,\n warning: 2,\n info: 3,\n debug: 4,\n verbose: 5,\n trace: 6,\n};\n\nexport function resolveMinLevel(level: string | undefined, label: string): string {\n const resolved = (level ?? 'error').toLowerCase();\n if (!(resolved in LEVEL_RANK)) {\n throw new TypeError(`${label}: unknown level \"${String(level).slice(0, 40)}\"`);\n }\n return resolved;\n}\n\n/** Unknown (custom) entry levels pass, like the TransportManager does. */\nexport function meetsLevel(entryLevel: string, minLevel: string): boolean {\n const entryRank = LEVEL_RANK[entryLevel.toLowerCase()];\n return entryRank === undefined || entryRank <= (LEVEL_RANK[minLevel] ?? 1);\n}\n\nexport interface WebhookUrlRules {\n label: string;\n hostMatches: (host: string) => boolean;\n pathMatches: (path: string) => boolean;\n expected: string;\n allowCustomUrl: boolean | undefined;\n}\n\n/** Validates the secret URL. Error messages never contain it. */\nexport function parseWebhookUrl(raw: unknown, rules: WebhookUrlRules): URL {\n const { label, allowCustomUrl, hostMatches, pathMatches, expected } = rules;\n if (typeof raw !== 'string' || raw.length === 0) {\n throw new TypeError(`${label}: webhookUrl is required`);\n }\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new TypeError(`${label}: webhookUrl is not a valid URL`);\n }\n if (url.protocol !== 'https:') {\n throw new TypeError(`${label}: webhookUrl must use https:`);\n }\n if (url.username || url.password) {\n throw new TypeError(`${label}: webhookUrl must not contain credentials`);\n }\n if (!allowCustomUrl) {\n const host = url.hostname.toLowerCase();\n if (!hostMatches(host) || !pathMatches(url.pathname) || url.port) {\n throw new TypeError(\n `${label}: webhookUrl must be a ${expected} webhook (set allowCustomUrl: true to use another https endpoint)`\n );\n }\n }\n return url;\n}\n\nexport interface WebhookResponseLike {\n ok: boolean;\n status: number;\n headers?: { get(name: string): string | null } | undefined;\n body?: { cancel(): Promise<void> } | null | undefined;\n json?: () => Promise<unknown>;\n}\n\ninterface PostResult {\n ok: boolean;\n status: number;\n retryAfterMs: number | undefined;\n}\n\nexport interface WebhookQueueOptions<T> {\n label: string;\n url: URL;\n minIntervalMs: number | undefined;\n maxPerMessage: number;\n maxQueueSize: number | undefined;\n maxRetries: number | undefined;\n requestTimeoutMs: number | undefined;\n /** Renders one entry. A throw (or unserializable result) drops only that entry. */\n render: (entry: TransportLogEntry) => T;\n /**\n * Builds the JSON request body from the rendered entries. `overflow` counts\n * entries beyond `maxPerMessage` that were dropped; the body must say so.\n */\n assemble: (rendered: T[], overflow: number) => unknown;\n /** Reads the wait (ms) from a 429 response. Defaults to the Retry-After header. */\n retryAfterMs?: (res: WebhookResponseLike) => Promise<number | undefined>;\n}\n\nexport function retryAfterHeaderMs(res: WebhookResponseLike): number | undefined {\n const raw = res.headers?.get('retry-after');\n if (raw === null || raw === undefined || raw === '') return undefined;\n const seconds = Number(raw);\n return Number.isFinite(seconds) && seconds >= 0 ? seconds * 1000 : undefined;\n}\n\nfunction nonNegative(value: number | undefined, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n}\n\nfunction failureCode(err: unknown): string {\n const cause = (err as { cause?: { code?: unknown } } | null)?.cause;\n const code = cause?.code ?? (err as { code?: unknown } | null)?.code;\n if (typeof code === 'string') return code.slice(0, 40);\n return err instanceof Error ? err.name : 'error';\n}\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise<void>((resolve) => {\n if (signal.aborted) return resolve();\n const done = (): void => {\n clearTimeout(timer);\n signal.removeEventListener('abort', done);\n resolve();\n };\n const timer = setTimeout(done, ms);\n timer.unref?.();\n signal.addEventListener('abort', done, { once: true });\n });\n}\n\nasync function cancelBody(res: WebhookResponseLike): Promise<void> {\n try {\n await res.body?.cancel();\n } catch {\n // body already consumed or locked\n }\n}\n\n/**\n * Throttled queue for chat webhooks. It is not built on BatchBuffer because the\n * semantics differ: sends are spaced by `minIntervalMs` (leading edge, then one\n * message per window), a window sends one summarized message and drops the\n * overflow, 429 waits are cancellable by close(), and flush() surfaces send\n * errors. HttpError / isPermanentHttpError are shared with BatchBuffer.\n *\n * Entries past `maxPerMessage` in one window are not sent: the message carries a\n * \"… and N more\" line and an internal warning reports the count.\n */\nexport class WebhookQueue<T> {\n private items: TransportLogEntry[] = [];\n private readonly cap: BufferCap;\n private readonly host: string;\n private readonly httpLabel: string;\n private readonly minIntervalMs: number;\n private readonly maxRetries: number;\n private readonly timeoutMs: number;\n private timer: NodeJS.Timeout | null = null;\n private inFlight: Promise<void> | undefined;\n private nextAllowed = 0;\n private failures = 0;\n private closed = false;\n private readonly shutdown = new AbortController();\n private overflowPending = 0;\n private lastOverflowWarn = 0;\n private fetchWarned = false;\n\n constructor(private readonly opts: WebhookQueueOptions<T>) {\n this.host = opts.url.hostname;\n this.httpLabel = `${opts.label} (${this.host})`;\n this.minIntervalMs = nonNegative(opts.minIntervalMs, 1000);\n this.maxRetries = Math.floor(nonNegative(opts.maxRetries, 1));\n this.timeoutMs = nonNegative(opts.requestTimeoutMs, DEFAULT_WEBHOOK_TIMEOUT_MS);\n this.cap = new BufferCap(opts.label, opts.maxQueueSize, opts.maxPerMessage, DEFAULT_QUEUE_CAP);\n }\n\n get length(): number {\n return this.items.length;\n }\n\n push(entry: TransportLogEntry): void {\n if (this.closed) return;\n this.items.push(entry);\n this.cap.enforce(this.items);\n this.schedule();\n }\n\n flush(): Promise<void> {\n this.inFlight ??= this.drain().finally(() => {\n this.inFlight = undefined;\n });\n return this.inFlight;\n }\n\n /** Cancels rate-limit waits, then makes one best-effort send with a short timeout. */\n async close(): Promise<void> {\n this.closed = true;\n this.clearTimer();\n this.shutdown.abort();\n try {\n await this.inFlight;\n } catch {\n // already reported by the timer path\n }\n try {\n await this.flush();\n } catch (err) {\n internalError(`${this.opts.label} flush on close failed`, err);\n }\n this.reportOverflow(true);\n if (this.items.length > 0) {\n internalError(`${this.opts.label} closing with ${this.items.length} undelivered entries`);\n this.items = [];\n }\n }\n\n private clearTimer(): void {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n }\n\n private schedule(): void {\n if (this.timer || this.closed || this.items.length === 0) return;\n const delay = Math.max(0, this.nextAllowed - Date.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n this.flush().catch((err) => internalError(`${this.opts.label} send failed`, err));\n }, delay);\n this.timer.unref?.();\n }\n\n private reportOverflow(force = false): void {\n const now = Date.now();\n if (this.overflowPending === 0 || (!force && now - this.lastOverflowWarn < OVERFLOW_WARN_MS)) {\n return;\n }\n const n = this.overflowPending;\n internalWarn(\n `${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`\n );\n this.overflowPending = 0;\n this.lastOverflowWarn = now;\n }\n\n private render(batch: TransportLogEntry[]): T[] {\n const rendered: T[] = [];\n let bad = 0;\n for (const entry of batch) {\n try {\n const value = this.opts.render(entry);\n JSON.stringify(value);\n rendered.push(value);\n } catch (err) {\n bad += 1;\n if (bad === 1) {\n internalError(`${this.opts.label}: dropping an entry that cannot be rendered`, err);\n }\n }\n }\n if (bad > 1) {\n internalError(`${this.opts.label}: dropped ${bad} entries that cannot be rendered`);\n }\n return rendered;\n }\n\n private async drain(): Promise<void> {\n if (this.items.length === 0) return;\n if (typeof fetch !== 'function') {\n if (!this.fetchWarned) {\n this.fetchWarned = true;\n internalWarn(\n `${this.opts.label}: global fetch is unavailable (Node 18+ required); dropping entries`\n );\n }\n this.items = [];\n return;\n }\n const all = this.items;\n this.items = [];\n const batch = all.slice(0, this.opts.maxPerMessage);\n const overflow = all.length - batch.length;\n if (overflow > 0) {\n this.overflowPending += overflow;\n this.reportOverflow();\n }\n this.nextAllowed = Date.now() + this.minIntervalMs;\n try {\n const rendered = this.render(batch);\n if (rendered.length === 0 && overflow === 0) return;\n let body: string;\n try {\n body = JSON.stringify(this.opts.assemble(rendered, overflow));\n } catch (err) {\n internalError(`${this.opts.label}: dropping a batch that cannot be serialized`, err);\n return;\n }\n try {\n await this.sendWithRetry(body);\n this.failures = 0;\n } catch (err) {\n if (!isPermanentHttpError(err)) {\n this.items.unshift(...batch);\n this.cap.enforce(this.items);\n this.failures += 1;\n const backoff = Math.min(\n MAX_BACKOFF_MS,\n Math.max(1000, this.minIntervalMs) * 2 ** this.failures\n );\n this.nextAllowed = Math.max(this.nextAllowed, Date.now() + backoff);\n }\n throw err;\n }\n } finally {\n this.schedule();\n }\n }\n\n private async sendWithRetry(body: string): Promise<void> {\n for (let attempt = 0; ; attempt += 1) {\n const res = await this.post(body);\n if (res.ok) return;\n if (res.status !== 429) throw new HttpError(res.status, '', this.httpLabel);\n const waitMs = Math.min(\n MAX_BACKOFF_MS,\n Math.max(0, res.retryAfterMs ?? Math.max(1000, this.minIntervalMs))\n );\n this.nextAllowed = Math.max(this.nextAllowed, Date.now() + waitMs);\n if (attempt >= this.maxRetries || this.closed) {\n throw new HttpError(429, '', this.httpLabel);\n }\n await sleep(waitMs, this.shutdown.signal);\n if (this.shutdown.signal.aborted) {\n throw new Error(`${this.opts.label}: rate-limit wait cancelled by close()`);\n }\n }\n }\n\n private async readRetryAfter(res: WebhookResponseLike): Promise<number | undefined> {\n try {\n return this.opts.retryAfterMs ? await this.opts.retryAfterMs(res) : retryAfterHeaderMs(res);\n } catch {\n return undefined;\n }\n }\n\n private async post(body: string): Promise<PostResult> {\n const controller = new AbortController();\n const timeoutMs = this.closed ? Math.min(this.timeoutMs, CLOSE_TIMEOUT_MS) : this.timeoutMs;\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = (await fetch(this.opts.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n redirect: 'error',\n signal: controller.signal,\n })) as WebhookResponseLike;\n let retryAfterMs: number | undefined;\n try {\n if (res.status === 429) retryAfterMs = await this.readRetryAfter(res);\n } finally {\n await cancelBody(res);\n }\n return { ok: res.ok, status: res.status, retryAfterMs };\n } catch (err) {\n // fetch errors may echo the request URL; keep only the host and a short code (no cause)\n // eslint-disable-next-line preserve-caught-error -- the cause can contain the secret URL\n throw new Error(\n `${this.opts.label}: request to ${this.host} failed (${controller.signal.aborted ? 'timeout' : failureCode(err)})`\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/** Cuts `text` to `max` chars without splitting a surrogate pair or a `&`-style entity. */\nexport function truncateText(text: string, max: number, ellipsis = '…'): string {\n if (text.length <= max) return text;\n let cut = text.slice(0, Math.max(0, max - ellipsis.length));\n cut = cut.replace(/&[a-z]{0,3}$/, '');\n if (/[\\uD800-\\uDBFF]$/.test(cut)) cut = cut.slice(0, -1);\n return cut + ellipsis;\n}\n\n/** Breaks code-fence and broadcast-mention sequences with a zero-width space. */\nexport function defuseText(text: string): string {\n return text\n .replace(/`{3,}/g, (m) => m.split('').join(ZWSP))\n .replace(/@(everyone|here|channel)/gi, `@${ZWSP}$1`);\n}\n\n/** Collapses control characters and newlines for single-line fields. */\nexport function singleLine(text: string): string {\n // eslint-disable-next-line no-control-regex\n return text.replace(/[\\u0000-\\u001f\\u007f]+/g, ' ').trim();\n}\n\n/** ISO string, or undefined for a missing or invalid timestamp. */\nexport function safeIso(date: unknown): string | undefined {\n return date instanceof Date && !Number.isNaN(date.getTime()) ? date.toISOString() : undefined;\n}\n","/**\n * Discord webhook transport.\n *\n * Sends error-level (by default) entries to a Discord channel as embeds.\n * Entries inside `minIntervalMs` are batched into one request (max 10 embeds;\n * the overflow is dropped and counted in the message `content`). Every request\n * carries `allowed_mentions: { parse: [] }` so logged data can never ping\n * @everyone, roles or users, and logged text has its markdown escaped.\n *\n * Limits follow https://discord.com/developers/docs/resources/message#embed-object-embed-limits\n *\n * The webhook URL is a secret: it is never logged and never appears in errors.\n *\n * @example\n * transports: { custom: [ new DiscordTransport({ webhookUrl: process.env.DISCORD_WEBHOOK_URL! }) ] }\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n DEFAULT_WEBHOOK_TIMEOUT_MS,\n defuseText,\n meetsLevel,\n parseWebhookUrl,\n resolveMinLevel,\n retryAfterHeaderMs,\n safeIso,\n singleLine,\n truncateText,\n WebhookQueue,\n type WebhookResponseLike,\n} from './webhook-queue';\n\nexport const DISCORD_TITLE_MAX = 256;\nexport const DISCORD_DESCRIPTION_MAX = 4096;\nexport const DISCORD_FIELD_VALUE_MAX = 1024;\nexport const DISCORD_EMBEDS_MAX = 10;\nexport const DISCORD_FIELDS_MAX = 25;\nexport const DISCORD_EMBED_TOTAL_MAX = 6000;\nexport const DISCORD_CONTENT_MAX = 2000;\nconst FIELD_VALUE_CAP = 64;\nconst DISCORD_FOOTER_MAX = 2048;\nconst FENCE_OVERHEAD = '```json\\n\\n```'.length;\nconst MIN_USEFUL_BODY = 20;\n\nexport interface DiscordEmbedField {\n name: string;\n value: string;\n inline?: boolean;\n}\n\nexport interface DiscordEmbed {\n title?: string;\n description?: string;\n color?: number;\n timestamp?: string;\n fields?: DiscordEmbedField[];\n [key: string]: unknown;\n}\n\nexport interface DiscordTransportConfig {\n /** Webhook URL (secret). Must be https on discord.com / discordapp.com unless `allowCustomUrl`. */\n webhookUrl: string;\n /** Minimum level to send. Default: 'error'. */\n level?: string;\n username?: string;\n /** https URL of the avatar shown for the webhook. */\n avatarUrl?: string;\n /** Entries within this window are batched into one request. Default: 1000. */\n minIntervalMs?: number;\n /**\n * Overrides the embed built for each entry. Limits are still enforced, but\n * escaping the content is the formatter's responsibility.\n */\n formatter?: (entry: TransportLogEntry) => DiscordEmbed;\n filter?: (entry: TransportLogEntry) => boolean;\n /** Accept an https webhook URL on any host (e.g. a proxy or Discord-compatible service). */\n allowCustomUrl?: boolean;\n /** Retries after an HTTP 429 (waits retry_after, max 30s). Default: 1. */\n maxRetries?: number;\n /** Max queued entries; the oldest are dropped past this. Default: 1000. */\n maxBufferSize?: number;\n requestTimeoutMs?: number;\n}\n\nexport const DISCORD_LEVEL_COLORS: Record<string, number> = {\n fatal: 0x992d22,\n error: 0xed4245,\n warn: 0xfee75c,\n warning: 0xfee75c,\n info: 0x3498db,\n debug: 0x95a5a6,\n verbose: 0x95a5a6,\n trace: 0x95a5a6,\n};\nconst DEFAULT_COLOR = 0x95a5a6;\n\n/** Escapes Discord markdown and defuses @everyone / @here and code fences. */\nexport function escapeDiscord(text: string): string {\n return defuseText(text.replace(/[\\\\*_~`|>#[\\]()]/g, '\\\\$&'));\n}\n\nfunction textLength(value: unknown, key: string): number {\n const text = (value as Record<string, unknown> | undefined)?.[key];\n return typeof text === 'string' ? text.length : 0;\n}\n\n/** Counts every text field Discord counts toward the 6000-character limit. */\nfunction embedChars(embed: DiscordEmbed): number {\n return (\n (embed.title?.length ?? 0) +\n (embed.description?.length ?? 0) +\n (embed.fields ?? []).reduce((n, f) => n + f.name.length + f.value.length, 0) +\n textLength(embed['footer'], 'text') +\n textLength(embed['author'], 'name')\n );\n}\n\n/** Shrinks one embed to `limit` chars: description first, then fields, footer/author, title. */\nfunction shrinkToFit(embed: DiscordEmbed, limit: number): void {\n const excess = () => embedChars(embed) - limit;\n if (excess() > 0 && embed.description) {\n const keep = embed.description.length - excess();\n if (keep > 0) embed.description = truncateText(embed.description, keep);\n else delete embed.description;\n }\n while (excess() > 0 && embed.fields?.length) embed.fields.pop();\n if (excess() > 0) {\n delete embed['footer'];\n delete embed['author'];\n }\n if (excess() > 0 && embed.title) {\n embed.title = truncateText(embed.title, Math.max(1, embed.title.length - excess()));\n }\n}\n\nfunction codeBlock(json: string, max: number): string {\n return `\\`\\`\\`json\\n${truncateText(\n json.replace(/`{3,}/g, (m) => m.split('').join('')),\n max\n )}\\n\\`\\`\\``;\n}\n\nfunction clampCustomEmbed(embed: DiscordEmbed): DiscordEmbed {\n const out: DiscordEmbed = { ...embed };\n if (typeof out.title === 'string') out.title = truncateText(out.title, DISCORD_TITLE_MAX);\n if (typeof out.description === 'string') {\n out.description = truncateText(out.description, DISCORD_DESCRIPTION_MAX);\n }\n if (Array.isArray(out.fields)) {\n out.fields = out.fields.slice(0, DISCORD_FIELDS_MAX).map((f) => ({\n ...f,\n name: truncateText(String(f.name), DISCORD_TITLE_MAX),\n value: truncateText(String(f.value), DISCORD_FIELD_VALUE_MAX),\n }));\n }\n const footer = out['footer'] as { text?: unknown } | undefined;\n if (footer && typeof footer.text === 'string') {\n out['footer'] = { ...footer, text: truncateText(footer.text, DISCORD_FOOTER_MAX) };\n }\n const author = out['author'] as { name?: unknown } | undefined;\n if (author && typeof author.name === 'string') {\n out['author'] = { ...author, name: truncateText(author.name, DISCORD_TITLE_MAX) };\n }\n return out;\n}\n\ninterface EmbedDraft {\n embed: DiscordEmbed;\n json: string | undefined;\n}\n\nexport class DiscordTransport implements IAsyncTransport {\n public readonly name = 'discord';\n public readonly level: string;\n public filter?: (entry: TransportLogEntry) => boolean;\n\n private readonly queue!: WebhookQueue<EmbedDraft>;\n private readonly username: string | undefined;\n private readonly avatarUrl: string | undefined;\n private readonly formatter: ((entry: TransportLogEntry) => DiscordEmbed) | undefined;\n\n constructor(config: DiscordTransportConfig) {\n const url = parseWebhookUrl(config.webhookUrl, {\n label: 'DiscordTransport',\n hostMatches: (h) =>\n ['discord.com', 'discordapp.com', 'ptb.discord.com', 'canary.discord.com'].includes(h),\n pathMatches: (p) => /^\\/api(\\/v\\d+)?\\/webhooks\\//.test(p),\n expected: 'discord.com',\n allowCustomUrl: config.allowCustomUrl,\n });\n if (config.avatarUrl !== undefined && !/^https:\\/\\//i.test(config.avatarUrl)) {\n throw new TypeError('DiscordTransport: avatarUrl must use https:');\n }\n this.level = resolveMinLevel(config.level, 'DiscordTransport');\n if (config.filter) this.filter = config.filter;\n this.username = config.username;\n this.avatarUrl = config.avatarUrl;\n this.formatter = config.formatter;\n const queue = new WebhookQueue<EmbedDraft>({\n label: 'DiscordTransport',\n url,\n minIntervalMs: config.minIntervalMs,\n maxPerMessage: DISCORD_EMBEDS_MAX,\n maxQueueSize: config.maxBufferSize,\n maxRetries: config.maxRetries,\n requestTimeoutMs: config.requestTimeoutMs ?? DEFAULT_WEBHOOK_TIMEOUT_MS,\n render: (entry) => this.draftFor(entry),\n assemble: (drafts, overflow) => this.buildPayload(drafts, overflow),\n retryAfterMs: readRetryAfter,\n });\n // non-enumerable so util.inspect / spreads of the transport never reach the secret URL\n Object.defineProperty(this, 'queue', { value: queue, enumerable: false });\n }\n\n write(entry: TransportLogEntry): void {\n if (!meetsLevel(entry.level, this.level)) return;\n // `filter` is applied by the TransportManager; it is not run again here\n this.queue.push(entry);\n }\n\n flush(): Promise<void> {\n return this.queue.flush();\n }\n\n close(): Promise<void> {\n return this.queue.close();\n }\n\n /** Never serialise the webhook URL (JSON.stringify, util.inspect). */\n toJSON(): Record<string, unknown> {\n return { name: this.name, level: this.level };\n }\n\n /** `showHidden` would otherwise print the non-enumerable queue (and its URL). */\n [Symbol.for('nodejs.util.inspect.custom')](): Record<string, unknown> {\n return this.toJSON();\n }\n\n private buildPayload(drafts: EmbedDraft[], overflow: number): Record<string, unknown> {\n const kept = this.fitToBudget(drafts);\n const dropped = overflow + (drafts.length - kept.length);\n\n const payload: Record<string, unknown> = {\n embeds: kept.map((d) => d.embed),\n allowed_mentions: { parse: [] },\n };\n if (dropped > 0) {\n payload['content'] = truncateText(\n `… and ${dropped} more entr${dropped === 1 ? 'y' : 'ies'} not shown`,\n DISCORD_CONTENT_MAX\n );\n }\n if (this.username) payload['username'] = truncateText(this.username, 80);\n if (this.avatarUrl) payload['avatar_url'] = this.avatarUrl;\n return payload;\n }\n\n /** Enforces the 6000-char total: drops trailing embeds if needed, then shares the rest among descriptions. */\n private fitToBudget(drafts: EmbedDraft[]): EmbedDraft[] {\n const kept = [...drafts];\n const base = () => kept.reduce((n, d) => n + embedChars(d.embed), 0);\n while (kept.length > 1 && base() > DISCORD_EMBED_TOTAL_MAX) kept.pop();\n const only = kept[0];\n if (only && base() > DISCORD_EMBED_TOTAL_MAX) shrinkToFit(only.embed, DISCORD_EMBED_TOTAL_MAX);\n const withData = kept.filter((d) => d.json !== undefined);\n if (withData.length === 0) return kept;\n const share = Math.floor((DISCORD_EMBED_TOTAL_MAX - base()) / withData.length);\n const body = Math.min(DISCORD_DESCRIPTION_MAX, share) - FENCE_OVERHEAD;\n for (const d of withData) {\n if (body >= MIN_USEFUL_BODY) d.embed.description = codeBlock(d.json!, body);\n }\n return kept;\n }\n\n private draftFor(entry: TransportLogEntry): EmbedDraft {\n if (this.formatter) {\n try {\n return { embed: clampCustomEmbed(this.formatter(entry)), json: undefined };\n } catch {\n internalWarn('DiscordTransport: formatter threw; using the default embed');\n }\n }\n return defaultDraft(entry);\n }\n}\n\nfunction defaultDraft(entry: TransportLogEntry): EmbedDraft {\n const title = `[${singleLine(entry.level).toUpperCase()}] ${escapeDiscord(singleLine(entry.message))}`;\n const candidates: Array<[string, string | undefined]> = [\n ['appName', entry.appName],\n ['environment', entry.environment],\n ['context', entry.context],\n ['traceId', entry.traceId],\n ];\n const fields: DiscordEmbedField[] = candidates\n .map(([name, value]) => [name, typeof value === 'string' ? singleLine(value) : ''] as const)\n .filter(([, value]) => value !== '')\n .map(([name, value]) => ({\n name,\n value: truncateText(escapeDiscord(value), FIELD_VALUE_CAP),\n inline: true,\n }));\n const embed: DiscordEmbed = {\n title: truncateText(title, DISCORD_TITLE_MAX),\n color: DISCORD_LEVEL_COLORS[entry.level.toLowerCase()] ?? DEFAULT_COLOR,\n fields,\n };\n const timestamp = safeIso(entry.timestamp);\n if (timestamp) embed.timestamp = timestamp;\n const json =\n entry.data && Object.keys(entry.data).length > 0 ? stringifyForLog(entry.data) : undefined;\n return { embed, json };\n}\n\nasync function readRetryAfter(res: WebhookResponseLike): Promise<number | undefined> {\n try {\n const body = (await res.json?.()) as { retry_after?: unknown } | undefined;\n const seconds = Number(body?.retry_after);\n if (body?.retry_after !== undefined && Number.isFinite(seconds) && seconds >= 0) {\n return seconds * 1000;\n }\n } catch {\n // fall through to the header\n }\n return retryAfterHeaderMs(res);\n}\n","/**\n * Slack incoming-webhook transport.\n *\n * Sends error-level (by default) entries to a Slack channel as Block Kit\n * messages. Entries inside `minIntervalMs` are batched into one message so a\n * log storm cannot flood the channel. All logged text is escaped for Slack\n * mrkdwn (`&`, `<`, `>`), which also disarms `<!channel>`, `<!here>` and\n * `<@U123>` mentions coming from logged data.\n *\n * The webhook URL is a secret: it is never logged and never appears in errors.\n *\n * @example\n * transports: { custom: [ new SlackTransport({ webhookUrl: process.env.SLACK_WEBHOOK_URL! }) ] }\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n DEFAULT_WEBHOOK_TIMEOUT_MS,\n defuseText,\n meetsLevel,\n parseWebhookUrl,\n resolveMinLevel,\n safeIso,\n singleLine,\n truncateText,\n WebhookQueue,\n} from './webhook-queue';\n\nexport const SLACK_HEADER_MAX = 150;\nexport const SLACK_DATA_MAX = 2500;\nconst SLACK_FIELD_MAX = 300;\nconst SLACK_TEXT_MAX = 1000;\nconst SLACK_MAX_BLOCKS = 50;\nconst SLACK_MAX_PER_MESSAGE = 10;\n\n/** A Slack message body as returned by a custom `formatter`. */\nexport interface SlackMessage {\n text?: string;\n blocks?: unknown[];\n [key: string]: unknown;\n}\n\nexport interface SlackTransportConfig {\n /** Incoming-webhook URL (secret). Must be https on hooks.slack.com unless `allowCustomUrl`. */\n webhookUrl: string;\n /** Minimum level to send. Default: 'error'. */\n level?: string;\n username?: string;\n iconEmoji?: string;\n /** Entries within this window are batched into one message. Default: 1000. */\n minIntervalMs?: number;\n /**\n * Overrides the message built for each entry. The results of one window are\n * merged: texts are joined and blocks concatenated (capped at 50 blocks).\n * Escaping the content is the formatter's responsibility.\n */\n formatter?: (entry: TransportLogEntry) => SlackMessage;\n filter?: (entry: TransportLogEntry) => boolean;\n /** Accept an https webhook URL on any host (e.g. a proxy or Slack-compatible service). */\n allowCustomUrl?: boolean;\n /** Retries after an HTTP 429 (waits Retry-After, max 30s). Default: 1. */\n maxRetries?: number;\n /** Max queued entries; the oldest are dropped past this. Default: 1000. */\n maxBufferSize?: number;\n requestTimeoutMs?: number;\n}\n\n/** Escapes the three characters Slack treats specially and defuses broadcast mentions. */\nexport function escapeSlack(text: string): string {\n return defuseText(text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'));\n}\n\nconst LEVEL_EMOJI: Record<string, string> = {\n fatal: ':rotating_light:',\n error: ':red_circle:',\n warn: ':warning:',\n warning: ':warning:',\n info: ':information_source:',\n};\n\nexport class SlackTransport implements IAsyncTransport {\n public readonly name = 'slack';\n public readonly level: string;\n public filter?: (entry: TransportLogEntry) => boolean;\n\n private readonly queue!: WebhookQueue<SlackMessage>;\n private readonly username: string | undefined;\n private readonly iconEmoji: string | undefined;\n private readonly formatter: ((entry: TransportLogEntry) => SlackMessage) | undefined;\n\n constructor(config: SlackTransportConfig) {\n const url = parseWebhookUrl(config.webhookUrl, {\n label: 'SlackTransport',\n hostMatches: (h) => h === 'hooks.slack.com' || h === 'hooks.slack-gov.com',\n pathMatches: (p) => p.startsWith('/services/') || p.startsWith('/triggers/'),\n expected: 'hooks.slack.com',\n allowCustomUrl: config.allowCustomUrl,\n });\n this.level = resolveMinLevel(config.level, 'SlackTransport');\n if (config.filter) this.filter = config.filter;\n this.username = config.username;\n this.iconEmoji = config.iconEmoji;\n this.formatter = config.formatter;\n const queue = new WebhookQueue<SlackMessage>({\n label: 'SlackTransport',\n url,\n minIntervalMs: config.minIntervalMs,\n maxPerMessage: SLACK_MAX_PER_MESSAGE,\n maxQueueSize: config.maxBufferSize,\n maxRetries: config.maxRetries,\n requestTimeoutMs: config.requestTimeoutMs ?? DEFAULT_WEBHOOK_TIMEOUT_MS,\n render: (entry) => this.messageFor(entry),\n assemble: (messages, overflow) => this.buildPayload(messages, overflow),\n });\n // non-enumerable so util.inspect / spreads of the transport never reach the secret URL\n Object.defineProperty(this, 'queue', { value: queue, enumerable: false });\n }\n\n write(entry: TransportLogEntry): void {\n if (!meetsLevel(entry.level, this.level)) return;\n // `filter` is applied by the TransportManager; it is not run again here\n this.queue.push(entry);\n }\n\n flush(): Promise<void> {\n return this.queue.flush();\n }\n\n close(): Promise<void> {\n return this.queue.close();\n }\n\n /** Never serialise the webhook URL (JSON.stringify, util.inspect). */\n toJSON(): Record<string, unknown> {\n return { name: this.name, level: this.level };\n }\n\n /** `showHidden` would otherwise print the non-enumerable queue (and its URL). */\n [Symbol.for('nodejs.util.inspect.custom')](): Record<string, unknown> {\n return this.toJSON();\n }\n\n private buildPayload(messages: SlackMessage[], overflow: number): Record<string, unknown> {\n const blocks = messages.flatMap((m) => m.blocks ?? []);\n const plural = overflow === 1 ? 'y' : 'ies';\n const counter = overflow > 0 ? `… and ${overflow} more entr${plural} not shown` : '';\n if (counter) {\n blocks.push({ type: 'context', elements: [{ type: 'mrkdwn', text: counter }] });\n }\n const text = [...messages.map((m) => m.text ?? ''), counter].filter(Boolean).join('\\n');\n const payload: Record<string, unknown> = {\n text: truncateText(text, SLACK_TEXT_MAX),\n blocks: blocks.slice(0, SLACK_MAX_BLOCKS),\n };\n if (this.username) payload['username'] = truncateText(this.username, 80);\n if (this.iconEmoji) payload['icon_emoji'] = this.iconEmoji;\n return payload;\n }\n\n private messageFor(entry: TransportLogEntry): SlackMessage {\n if (this.formatter) {\n try {\n return this.formatter(entry);\n } catch {\n internalWarn('SlackTransport: formatter threw; using the default message');\n }\n }\n return defaultMessage(entry);\n }\n}\n\nfunction defaultMessage(entry: TransportLogEntry): SlackMessage {\n const level = singleLine(entry.level).toUpperCase();\n const emoji = LEVEL_EMOJI[entry.level.toLowerCase()] ?? ':white_circle:';\n const headline = `[${level}] ${singleLine(entry.message)}`;\n const blocks: unknown[] = [\n {\n type: 'header',\n text: {\n type: 'plain_text',\n text: truncateText(escapeSlack(headline), SLACK_HEADER_MAX),\n emoji: false,\n },\n },\n ];\n\n const context = [\n ['app', entry.appName],\n ['env', entry.environment],\n ['context', entry.context],\n ['trace', entry.traceId],\n ['time', safeIso(entry.timestamp)],\n ]\n .map(([label, value]) => [label, typeof value === 'string' ? singleLine(value) : ''] as const)\n .filter(([, value]) => value !== '')\n .map(([label, value]) => ({\n type: 'mrkdwn',\n text: `*${label}:* ${truncateText(escapeSlack(value), SLACK_FIELD_MAX)}`,\n }));\n if (context.length > 0) blocks.push({ type: 'context', elements: context });\n\n if (entry.data && Object.keys(entry.data).length > 0) {\n const json = truncateText(escapeSlack(stringifyForLog(entry.data)), SLACK_DATA_MAX);\n blocks.push({ type: 'section', text: { type: 'mrkdwn', text: `\\`\\`\\`${json}\\`\\`\\`` } });\n }\n\n return {\n text: `${emoji} ${escapeSlack(truncateText(headline, SLACK_HEADER_MAX))}`,\n blocks,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"slack.transport-CEYBVzUj.js","names":["_getOtelBridgeFieldNames","stringifyForLog","BatchBuffer","collectSecrets","HttpError","BufferCap","internalError","isPermanentHttpError","HttpError","stringifyForLog","stringifyForLog"],"sources":["../src/transports/otlp.transport.ts","../src/transports/webhook-queue.ts","../src/transports/discord.transport.ts","../src/transports/slack.transport.ts"],"sourcesContent":["/**\n * OTLP Logs transport — emit logixia logs as OpenTelemetry LogRecords.\n *\n * logixia already READS the active OTel span (the bridge injects traceId/spanId\n * into payloads). This transport closes the loop by EMITTING logs OUT in the\n * OTLP/HTTP JSON format to a collector, making logixia an OTel-Logs-native\n * source — logs land in any OTLP backend (Grafana Loki, OpenObserve, Better\n * Stack, Axiom, Datadog, SigNoz…) already correlated with traces.\n *\n * Dependency-free: builds the OTLP/HTTP JSON payload directly (no\n * @opentelemetry/* packages required — that JS API is still alpha), conforming\n * to the OTel logs data model: SeverityNumber (1–24), resource attributes\n * (service.name/version, deployment.environment), and TraceId/SpanId on each\n * record for correlation.\n *\n * @example\n * ```ts\n * transports: {\n * custom: [ new OtlpLogTransport({\n * url: 'http://localhost:4318/v1/logs',\n * serviceName: 'api',\n * headers: { 'x-api-key': process.env.OTLP_KEY! },\n * }) ],\n * }\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { _getOtelBridgeFieldNames } from '../utils/otel';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n HttpError,\n} from './batch-buffer';\nimport { collectSecrets, protectSecrets, scrubError } from './secret-config';\n\nexport interface OtlpLogTransportConfig {\n /** OTLP/HTTP logs endpoint, e.g. `http://localhost:4318/v1/logs`. */\n url: string;\n /** Extra HTTP headers (auth, tenant, etc.). */\n headers?: Record<string, string>;\n /** `service.name` resource attribute. Default: 'logixia'. */\n serviceName?: string;\n /** `service.version` resource attribute. */\n serviceVersion?: string;\n /** `deployment.environment` resource attribute. */\n environment?: string;\n /** Extra resource attributes merged into the OTLP resource. */\n resourceAttributes?: Record<string, string | number | boolean>;\n /** Entries per batch / per POST. Default: 100. */\n batchSize?: number;\n /** Auto-flush interval (ms). Default: 5000. */\n flushIntervalMs?: number;\n /** Abort a request that has not finished after this many ms. Default: 10000. */\n requestTimeoutMs?: number;\n /** Max buffered entries; the oldest are dropped past this. Default: 10000. */\n maxBufferSize?: number;\n level?: string;\n}\n\nconst HEX_32 = /^[0-9a-f]{32}$/;\nconst HEX_16 = /^[0-9a-f]{16}$/;\n\n/** OTLP JSON wants a 16-char lowercase hex span id; anything else (or all zeros) returns undefined. */\nexport function toOtlpSpanId(id: string): string | undefined {\n const hex = id.toLowerCase();\n return HEX_16.test(hex) && hex !== '0'.repeat(16) ? hex : undefined;\n}\n\n/**\n * OTLP JSON wants a 32-char lowercase hex trace id. Dashed UUIDs are\n * normalised; anything else (or all zeros) returns undefined.\n */\nexport function toOtlpTraceId(id: string): string | undefined {\n const hex = id.replace(/-/g, '').toLowerCase();\n return HEX_32.test(hex) && hex !== '0'.repeat(32) ? hex : undefined;\n}\n\n/**\n * Map a logixia level name to an OTel SeverityNumber (1–24) and text.\n * Per the OTel logs SDK spec: TRACE=1, DEBUG=5, INFO=9, WARN=13, ERROR=17,\n * FATAL=21. Custom/unknown levels fall back to INFO (9).\n */\nexport function toOtelSeverity(level: string): { number: number; text: string } {\n switch (level.toLowerCase()) {\n case 'trace':\n return { number: 1, text: 'TRACE' };\n case 'verbose':\n return { number: 5, text: 'DEBUG' }; // verbose maps to DEBUG range\n case 'debug':\n return { number: 5, text: 'DEBUG' };\n case 'info':\n return { number: 9, text: 'INFO' };\n case 'warn':\n case 'warning':\n return { number: 13, text: 'WARN' };\n case 'error':\n return { number: 17, text: 'ERROR' };\n case 'fatal':\n return { number: 21, text: 'FATAL' };\n default:\n return { number: 9, text: 'INFO' };\n }\n}\n\ninterface NativeIds {\n traceId?: string;\n spanId?: string;\n /** The entry's own trace id when it could not become the native `traceId`. */\n traceAttr?: string;\n}\n\nfunction findHexId(\n attrs: Record<string, unknown>,\n names: readonly string[],\n convert: (id: string) => string | undefined\n): { key: string; hex: string } | undefined {\n for (const key of names) {\n const value = attrs[key];\n if (typeof value !== 'string') continue;\n const hex = convert(value);\n if (hex) return { key, hex };\n }\n return undefined;\n}\n\n/**\n * Pick the native OTLP `traceId`/`spanId` and remove the ids they came from out of\n * `attrs`. A valid OTel pair in the payload (the bridge's trace and span id) wins\n * and is used for both fields, so they always belong to the same trace. Otherwise a\n * valid `entry.traceId` is used alone. A `spanId` is never emitted without its trace.\n */\nfunction resolveNativeIds(entry: TransportLogEntry, attrs: Record<string, unknown>): NativeIds {\n const bridge = _getOtelBridgeFieldNames();\n const traceNames = [...(bridge ? [bridge.traceIdField] : []), 'traceId', 'trace_id'];\n const spanNames = [...(bridge ? [bridge.spanIdField] : []), 'spanId', 'span_id'];\n const payloadTrace = findHexId(attrs, traceNames, toOtlpTraceId);\n const payloadSpan = findHexId(attrs, spanNames, toOtlpSpanId);\n const entryHex = entry.traceId ? toOtlpTraceId(entry.traceId) : undefined;\n\n const ids: NativeIds = {};\n if (payloadTrace && payloadSpan) {\n ids.traceId = payloadTrace.hex;\n ids.spanId = payloadSpan.hex;\n delete attrs[payloadSpan.key];\n } else if (entryHex) {\n ids.traceId = entryHex;\n } else if (payloadTrace) {\n ids.traceId = payloadTrace.hex;\n }\n\n if (ids.traceId) {\n for (const key of traceNames) {\n const value = attrs[key];\n if (\n typeof value === 'string' &&\n (value === entry.traceId || toOtlpTraceId(value) === ids.traceId)\n ) {\n delete attrs[key];\n }\n }\n }\n if (entry.traceId && !(entryHex && entryHex === ids.traceId)) ids.traceAttr = entry.traceId;\n return ids;\n}\n\n/** Coerce a JS value into an OTLP AnyValue. */\nfunction toAnyValue(value: unknown): Record<string, unknown> {\n if (typeof value === 'string') return { stringValue: value };\n if (typeof value === 'boolean') return { boolValue: value };\n if (typeof value === 'number') {\n return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };\n }\n if (typeof value === 'bigint') return { stringValue: value.toString() };\n if (value === null || value === undefined) return { stringValue: '' };\n // Objects/arrays → JSON string (kvlistValue would be richer but stringValue is\n // universally accepted and avoids deep recursion / circular issues here).\n try {\n return { stringValue: stringifyForLog(value) };\n } catch {\n return { stringValue: String(value) };\n }\n}\n\n/** Build the OTLP KeyValue attribute list from a flat record. */\nfunction toAttributes(rec: Record<string, unknown>): Array<{ key: string; value: unknown }> {\n return Object.entries(rec)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => ({ key, value: toAnyValue(value) }));\n}\n\nexport class OtlpLogTransport implements IAsyncTransport {\n public readonly name = 'otlp';\n public readonly level: string | undefined;\n\n private readonly url: string;\n private readonly headers: Record<string, string>;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n private readonly requestTimeoutMs: number;\n private readonly resourceAttrs: Array<{ key: string; value: unknown }>;\n\n private readonly buffer: BatchBuffer<TransportLogEntry>;\n private flushTimer: NodeJS.Timeout | null = null;\n\n constructor(config: OtlpLogTransportConfig) {\n this.url = config.url;\n this.headers = config.headers ?? {};\n this.batchSize = config.batchSize ?? 100;\n this.flushIntervalMs = config.flushIntervalMs ?? 5000;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.level = config.level;\n this.buffer = new BatchBuffer<TransportLogEntry>({\n name: 'OtlpLogTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (entries) => this.send(entries),\n secrets: () => this.secretValues(),\n });\n\n const resource: Record<string, unknown> = {\n 'service.name': config.serviceName ?? 'logixia',\n ...(config.serviceVersion ? { 'service.version': config.serviceVersion } : {}),\n ...(config.environment ? { 'deployment.environment': config.environment } : {}),\n ...(config.resourceAttributes ?? {}),\n };\n this.resourceAttrs = toAttributes(resource);\n\n protectSecrets(this, ['headers', 'url']);\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushIntervalMs);\n if (this.flushTimer.unref) this.flushTimer.unref();\n }\n\n write(entry: TransportLogEntry): void {\n this.buffer.push(entry);\n this.buffer.flushIfFull();\n }\n\n /** Convert one entry into an OTLP LogRecord. */\n private toLogRecord(entry: TransportLogEntry): Record<string, unknown> {\n const sev = toOtelSeverity(entry.level);\n const tsNanos = String(entry.timestamp.getTime() * 1_000_000);\n\n const attrs: Record<string, unknown> = { ...(entry.data ?? {}) };\n const ids = resolveNativeIds(entry, attrs);\n if (entry.context !== undefined) attrs['context'] = entry.context;\n if (entry.appName !== undefined) attrs['app.name'] = entry.appName;\n if (entry.environment !== undefined) attrs['deployment.environment'] = entry.environment;\n\n const record: Record<string, unknown> = {\n timeUnixNano: tsNanos,\n observedTimeUnixNano: tsNanos,\n severityNumber: sev.number,\n severityText: sev.text,\n body: { stringValue: entry.message },\n attributes: toAttributes(attrs),\n };\n\n if (ids.traceId) record['traceId'] = ids.traceId;\n if (ids.spanId) record['spanId'] = ids.spanId;\n if (ids.traceAttr !== undefined) {\n (record['attributes'] as unknown[]).push({\n key: 'trace_id',\n value: { stringValue: ids.traceAttr },\n });\n }\n\n return record;\n }\n\n private buildPayload(entries: TransportLogEntry[]): string {\n return stringifyForLog({\n resourceLogs: [\n {\n resource: { attributes: this.resourceAttrs },\n scopeLogs: [\n {\n scope: { name: 'logixia' },\n logRecords: entries.map((e) => this.toLogRecord(e)),\n },\n ],\n },\n ],\n });\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n private secretValues(): string[] {\n return collectSecrets({ headers: this.headers, url: this.url });\n }\n\n private async send(entries: TransportLogEntry[]): Promise<void> {\n if (typeof fetch !== 'function') {\n internalWarn('OtlpLogTransport: global fetch unavailable — cannot export logs');\n return;\n }\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);\n try {\n const res = await fetch(this.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...this.headers },\n body: this.buildPayload(entries),\n signal: controller.signal,\n });\n if (!res.ok) {\n throw new HttpError(res.status, '', 'OTLP export');\n }\n } catch (err) {\n scrubError(err, this.secretValues());\n if (controller.signal.aborted) {\n throw new Error(`OTLP export timed out after ${this.requestTimeoutMs}ms`, { cause: err });\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n }\n\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.buffer.close();\n }\n}\n","/**\n * Shared core for the chat-webhook transports (Slack, Discord).\n *\n * - entries inside `minIntervalMs` are batched into one message; entries past\n * `maxPerMessage` are dropped and reported to the payload builder as overflow\n * - the queue is bounded (BufferCap); the oldest entries go first\n * - HTTP 429 waits for Retry-After (bounded, cancelled by close()) and retries a\n * bounded number of times\n * - an entry that cannot be rendered is dropped alone; an unserializable batch or a\n * missing global fetch is dropped, never retried\n * - errors only ever name the webhook host, never the URL (the URL is a secret)\n */\n\nimport type { TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { HttpError, isPermanentHttpError } from './batch-buffer';\nimport { BufferCap } from './buffer-cap';\n\nconst MAX_BACKOFF_MS = 30_000;\nconst DEFAULT_QUEUE_CAP = 1000;\nconst CLOSE_TIMEOUT_MS = 3000;\nconst OVERFLOW_WARN_MS = 5000;\nexport const DEFAULT_WEBHOOK_TIMEOUT_MS = 10_000;\nconst ZWSP = '';\n\nconst LEVEL_RANK: Record<string, number> = {\n fatal: 0,\n error: 1,\n warn: 2,\n warning: 2,\n info: 3,\n debug: 4,\n verbose: 5,\n trace: 6,\n};\n\nexport function resolveMinLevel(level: string | undefined, label: string): string {\n const resolved = (level ?? 'error').toLowerCase();\n if (!(resolved in LEVEL_RANK)) {\n throw new TypeError(`${label}: unknown level \"${String(level).slice(0, 40)}\"`);\n }\n return resolved;\n}\n\n/** Unknown (custom) entry levels pass, like the TransportManager does. */\nexport function meetsLevel(entryLevel: string, minLevel: string): boolean {\n const entryRank = LEVEL_RANK[entryLevel.toLowerCase()];\n return entryRank === undefined || entryRank <= (LEVEL_RANK[minLevel] ?? 1);\n}\n\nexport interface WebhookUrlRules {\n label: string;\n hostMatches: (host: string) => boolean;\n pathMatches: (path: string) => boolean;\n expected: string;\n allowCustomUrl: boolean | undefined;\n}\n\n/** Validates the secret URL. Error messages never contain it. */\nexport function parseWebhookUrl(raw: unknown, rules: WebhookUrlRules): URL {\n const { label, allowCustomUrl, hostMatches, pathMatches, expected } = rules;\n if (typeof raw !== 'string' || raw.length === 0) {\n throw new TypeError(`${label}: webhookUrl is required`);\n }\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new TypeError(`${label}: webhookUrl is not a valid URL`);\n }\n if (url.protocol !== 'https:') {\n throw new TypeError(`${label}: webhookUrl must use https:`);\n }\n if (url.username || url.password) {\n throw new TypeError(`${label}: webhookUrl must not contain credentials`);\n }\n if (!allowCustomUrl) {\n const host = url.hostname.toLowerCase();\n if (!hostMatches(host) || !pathMatches(url.pathname) || url.port) {\n throw new TypeError(\n `${label}: webhookUrl must be a ${expected} webhook (set allowCustomUrl: true to use another https endpoint)`\n );\n }\n }\n return url;\n}\n\nexport interface WebhookResponseLike {\n ok: boolean;\n status: number;\n headers?: { get(name: string): string | null } | undefined;\n body?: { cancel(): Promise<void> } | null | undefined;\n json?: () => Promise<unknown>;\n}\n\ninterface PostResult {\n ok: boolean;\n status: number;\n retryAfterMs: number | undefined;\n}\n\nexport interface WebhookQueueOptions<T> {\n label: string;\n url: URL;\n minIntervalMs: number | undefined;\n maxPerMessage: number;\n maxQueueSize: number | undefined;\n maxRetries: number | undefined;\n requestTimeoutMs: number | undefined;\n /** Renders one entry. A throw (or unserializable result) drops only that entry. */\n render: (entry: TransportLogEntry) => T;\n /**\n * Builds the JSON request body from the rendered entries. `overflow` counts\n * entries beyond `maxPerMessage` that were dropped; the body must say so.\n */\n assemble: (rendered: T[], overflow: number) => unknown;\n /** Reads the wait (ms) from a 429 response. Defaults to the Retry-After header. */\n retryAfterMs?: (res: WebhookResponseLike) => Promise<number | undefined>;\n}\n\nexport function retryAfterHeaderMs(res: WebhookResponseLike): number | undefined {\n const raw = res.headers?.get('retry-after');\n if (raw === null || raw === undefined || raw === '') return undefined;\n const seconds = Number(raw);\n return Number.isFinite(seconds) && seconds >= 0 ? seconds * 1000 : undefined;\n}\n\nfunction nonNegative(value: number | undefined, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n}\n\nfunction failureCode(err: unknown): string {\n const cause = (err as { cause?: { code?: unknown } } | null)?.cause;\n const code = cause?.code ?? (err as { code?: unknown } | null)?.code;\n if (typeof code === 'string') return code.slice(0, 40);\n return err instanceof Error ? err.name : 'error';\n}\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise<void>((resolve) => {\n if (signal.aborted) return resolve();\n const done = (): void => {\n clearTimeout(timer);\n signal.removeEventListener('abort', done);\n resolve();\n };\n const timer = setTimeout(done, ms);\n timer.unref?.();\n signal.addEventListener('abort', done, { once: true });\n });\n}\n\nasync function cancelBody(res: WebhookResponseLike): Promise<void> {\n try {\n await res.body?.cancel();\n } catch {\n // body already consumed or locked\n }\n}\n\n/**\n * Throttled queue for chat webhooks. It is not built on BatchBuffer because the\n * semantics differ: sends are spaced by `minIntervalMs` (leading edge, then one\n * message per window), a window sends one summarized message and drops the\n * overflow, 429 waits are cancellable by close(), and flush() surfaces send\n * errors. HttpError / isPermanentHttpError are shared with BatchBuffer.\n *\n * Entries past `maxPerMessage` in one window are not sent: the message carries a\n * \"… and N more\" line and an internal warning reports the count.\n */\nexport class WebhookQueue<T> {\n private items: TransportLogEntry[] = [];\n private readonly cap: BufferCap;\n private readonly host: string;\n private readonly httpLabel: string;\n private readonly minIntervalMs: number;\n private readonly maxRetries: number;\n private readonly timeoutMs: number;\n private timer: NodeJS.Timeout | null = null;\n private inFlight: Promise<void> | undefined;\n private nextAllowed = 0;\n private failures = 0;\n private closed = false;\n private readonly shutdown = new AbortController();\n private overflowPending = 0;\n private lastOverflowWarn = 0;\n private fetchWarned = false;\n\n constructor(private readonly opts: WebhookQueueOptions<T>) {\n this.host = opts.url.hostname;\n this.httpLabel = `${opts.label} (${this.host})`;\n this.minIntervalMs = nonNegative(opts.minIntervalMs, 1000);\n this.maxRetries = Math.floor(nonNegative(opts.maxRetries, 1));\n this.timeoutMs = nonNegative(opts.requestTimeoutMs, DEFAULT_WEBHOOK_TIMEOUT_MS);\n this.cap = new BufferCap(opts.label, opts.maxQueueSize, opts.maxPerMessage, DEFAULT_QUEUE_CAP);\n }\n\n get length(): number {\n return this.items.length;\n }\n\n push(entry: TransportLogEntry): void {\n if (this.closed) return;\n this.items.push(entry);\n this.cap.enforce(this.items);\n this.schedule();\n }\n\n flush(): Promise<void> {\n this.inFlight ??= this.drain().finally(() => {\n this.inFlight = undefined;\n });\n return this.inFlight;\n }\n\n /** Cancels rate-limit waits, then makes one best-effort send with a short timeout. */\n async close(): Promise<void> {\n this.closed = true;\n this.clearTimer();\n this.shutdown.abort();\n try {\n await this.inFlight;\n } catch {\n // already reported by the timer path\n }\n try {\n await this.flush();\n } catch (err) {\n internalError(`${this.opts.label} flush on close failed`, err);\n }\n this.reportOverflow(true);\n if (this.items.length > 0) {\n internalError(`${this.opts.label} closing with ${this.items.length} undelivered entries`);\n this.items = [];\n }\n }\n\n private clearTimer(): void {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n }\n\n private schedule(): void {\n if (this.timer || this.closed || this.items.length === 0) return;\n const delay = Math.max(0, this.nextAllowed - Date.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n this.flush().catch((err) => internalError(`${this.opts.label} send failed`, err));\n }, delay);\n this.timer.unref?.();\n }\n\n private reportOverflow(force = false): void {\n const now = Date.now();\n if (this.overflowPending === 0 || (!force && now - this.lastOverflowWarn < OVERFLOW_WARN_MS)) {\n return;\n }\n const n = this.overflowPending;\n internalWarn(\n `${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`\n );\n this.overflowPending = 0;\n this.lastOverflowWarn = now;\n }\n\n private render(batch: TransportLogEntry[]): T[] {\n const rendered: T[] = [];\n let bad = 0;\n for (const entry of batch) {\n try {\n const value = this.opts.render(entry);\n JSON.stringify(value);\n rendered.push(value);\n } catch (err) {\n bad += 1;\n if (bad === 1) {\n internalError(`${this.opts.label}: dropping an entry that cannot be rendered`, err);\n }\n }\n }\n if (bad > 1) {\n internalError(`${this.opts.label}: dropped ${bad} entries that cannot be rendered`);\n }\n return rendered;\n }\n\n private async drain(): Promise<void> {\n if (this.items.length === 0) return;\n if (typeof fetch !== 'function') {\n if (!this.fetchWarned) {\n this.fetchWarned = true;\n internalWarn(\n `${this.opts.label}: global fetch is unavailable (Node 18+ required); dropping entries`\n );\n }\n this.items = [];\n return;\n }\n const all = this.items;\n this.items = [];\n const batch = all.slice(0, this.opts.maxPerMessage);\n const overflow = all.length - batch.length;\n if (overflow > 0) {\n this.overflowPending += overflow;\n this.reportOverflow();\n }\n this.nextAllowed = Date.now() + this.minIntervalMs;\n try {\n const rendered = this.render(batch);\n if (rendered.length === 0 && overflow === 0) return;\n let body: string;\n try {\n body = JSON.stringify(this.opts.assemble(rendered, overflow));\n } catch (err) {\n internalError(`${this.opts.label}: dropping a batch that cannot be serialized`, err);\n return;\n }\n try {\n await this.sendWithRetry(body);\n this.failures = 0;\n } catch (err) {\n if (!isPermanentHttpError(err)) {\n this.items.unshift(...batch);\n this.cap.enforce(this.items);\n this.failures += 1;\n const backoff = Math.min(\n MAX_BACKOFF_MS,\n Math.max(1000, this.minIntervalMs) * 2 ** this.failures\n );\n this.nextAllowed = Math.max(this.nextAllowed, Date.now() + backoff);\n }\n throw err;\n }\n } finally {\n this.schedule();\n }\n }\n\n private async sendWithRetry(body: string): Promise<void> {\n for (let attempt = 0; ; attempt += 1) {\n const res = await this.post(body);\n if (res.ok) return;\n if (res.status !== 429) throw new HttpError(res.status, '', this.httpLabel);\n const waitMs = Math.min(\n MAX_BACKOFF_MS,\n Math.max(0, res.retryAfterMs ?? Math.max(1000, this.minIntervalMs))\n );\n this.nextAllowed = Math.max(this.nextAllowed, Date.now() + waitMs);\n if (attempt >= this.maxRetries || this.closed) {\n throw new HttpError(429, '', this.httpLabel);\n }\n await sleep(waitMs, this.shutdown.signal);\n if (this.shutdown.signal.aborted) {\n throw new Error(`${this.opts.label}: rate-limit wait cancelled by close()`);\n }\n }\n }\n\n private async readRetryAfter(res: WebhookResponseLike): Promise<number | undefined> {\n try {\n return this.opts.retryAfterMs ? await this.opts.retryAfterMs(res) : retryAfterHeaderMs(res);\n } catch {\n return undefined;\n }\n }\n\n private async post(body: string): Promise<PostResult> {\n const controller = new AbortController();\n const timeoutMs = this.closed ? Math.min(this.timeoutMs, CLOSE_TIMEOUT_MS) : this.timeoutMs;\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = (await fetch(this.opts.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n redirect: 'error',\n signal: controller.signal,\n })) as WebhookResponseLike;\n let retryAfterMs: number | undefined;\n try {\n if (res.status === 429) retryAfterMs = await this.readRetryAfter(res);\n } finally {\n await cancelBody(res);\n }\n return { ok: res.ok, status: res.status, retryAfterMs };\n } catch (err) {\n // fetch errors may echo the request URL; keep only the host and a short code (no cause)\n // eslint-disable-next-line preserve-caught-error -- the cause can contain the secret URL\n throw new Error(\n `${this.opts.label}: request to ${this.host} failed (${controller.signal.aborted ? 'timeout' : failureCode(err)})`\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/** Cuts `text` to `max` chars without splitting a surrogate pair or a `&`-style entity. */\nexport function truncateText(text: string, max: number, ellipsis = '…'): string {\n if (text.length <= max) return text;\n let cut = text.slice(0, Math.max(0, max - ellipsis.length));\n cut = cut.replace(/&[a-z]{0,3}$/, '');\n if (/[\\uD800-\\uDBFF]$/.test(cut)) cut = cut.slice(0, -1);\n return cut + ellipsis;\n}\n\n/** Breaks code-fence and broadcast-mention sequences with a zero-width space. */\nexport function defuseText(text: string): string {\n return text\n .replace(/`{3,}/g, (m) => m.split('').join(ZWSP))\n .replace(/@(everyone|here|channel)/gi, `@${ZWSP}$1`);\n}\n\n/** Collapses control characters and newlines for single-line fields. */\nexport function singleLine(text: string): string {\n // eslint-disable-next-line no-control-regex\n return text.replace(/[\\u0000-\\u001f\\u007f]+/g, ' ').trim();\n}\n\n/** ISO string, or undefined for a missing or invalid timestamp. */\nexport function safeIso(date: unknown): string | undefined {\n return date instanceof Date && !Number.isNaN(date.getTime()) ? date.toISOString() : undefined;\n}\n","/**\n * Discord webhook transport.\n *\n * Sends error-level (by default) entries to a Discord channel as embeds.\n * Entries inside `minIntervalMs` are batched into one request (max 10 embeds;\n * the overflow is dropped and counted in the message `content`). Every request\n * carries `allowed_mentions: { parse: [] }` so logged data can never ping\n * @everyone, roles or users, and logged text has its markdown escaped.\n *\n * Limits follow https://discord.com/developers/docs/resources/message#embed-object-embed-limits\n *\n * The webhook URL is a secret: it is never logged and never appears in errors.\n *\n * @example\n * transports: { custom: [ new DiscordTransport({ webhookUrl: process.env.DISCORD_WEBHOOK_URL! }) ] }\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n DEFAULT_WEBHOOK_TIMEOUT_MS,\n defuseText,\n meetsLevel,\n parseWebhookUrl,\n resolveMinLevel,\n retryAfterHeaderMs,\n safeIso,\n singleLine,\n truncateText,\n WebhookQueue,\n type WebhookResponseLike,\n} from './webhook-queue';\n\nexport const DISCORD_TITLE_MAX = 256;\nexport const DISCORD_DESCRIPTION_MAX = 4096;\nexport const DISCORD_FIELD_VALUE_MAX = 1024;\nexport const DISCORD_EMBEDS_MAX = 10;\nexport const DISCORD_FIELDS_MAX = 25;\nexport const DISCORD_EMBED_TOTAL_MAX = 6000;\nexport const DISCORD_CONTENT_MAX = 2000;\nconst FIELD_VALUE_CAP = 64;\nconst DISCORD_FOOTER_MAX = 2048;\nconst FENCE_OVERHEAD = '```json\\n\\n```'.length;\nconst MIN_USEFUL_BODY = 20;\n\nexport interface DiscordEmbedField {\n name: string;\n value: string;\n inline?: boolean;\n}\n\nexport interface DiscordEmbed {\n title?: string;\n description?: string;\n color?: number;\n timestamp?: string;\n fields?: DiscordEmbedField[];\n [key: string]: unknown;\n}\n\nexport interface DiscordTransportConfig {\n /** Webhook URL (secret). Must be https on discord.com / discordapp.com unless `allowCustomUrl`. */\n webhookUrl: string;\n /** Minimum level to send. Default: 'error'. */\n level?: string;\n username?: string;\n /** https URL of the avatar shown for the webhook. */\n avatarUrl?: string;\n /** Entries within this window are batched into one request. Default: 1000. */\n minIntervalMs?: number;\n /**\n * Overrides the embed built for each entry. Limits are still enforced, but\n * escaping the content is the formatter's responsibility.\n */\n formatter?: (entry: TransportLogEntry) => DiscordEmbed;\n filter?: (entry: TransportLogEntry) => boolean;\n /** Accept an https webhook URL on any host (e.g. a proxy or Discord-compatible service). */\n allowCustomUrl?: boolean;\n /** Retries after an HTTP 429 (waits retry_after, max 30s). Default: 1. */\n maxRetries?: number;\n /** Max queued entries; the oldest are dropped past this. Default: 1000. */\n maxBufferSize?: number;\n requestTimeoutMs?: number;\n}\n\nexport const DISCORD_LEVEL_COLORS: Record<string, number> = {\n fatal: 0x992d22,\n error: 0xed4245,\n warn: 0xfee75c,\n warning: 0xfee75c,\n info: 0x3498db,\n debug: 0x95a5a6,\n verbose: 0x95a5a6,\n trace: 0x95a5a6,\n};\nconst DEFAULT_COLOR = 0x95a5a6;\n\n/** Escapes Discord markdown and defuses @everyone / @here and code fences. */\nexport function escapeDiscord(text: string): string {\n return defuseText(text.replace(/[\\\\*_~`|>#[\\]()]/g, '\\\\$&'));\n}\n\nfunction textLength(value: unknown, key: string): number {\n const text = (value as Record<string, unknown> | undefined)?.[key];\n return typeof text === 'string' ? text.length : 0;\n}\n\n/** Counts every text field Discord counts toward the 6000-character limit. */\nfunction embedChars(embed: DiscordEmbed): number {\n return (\n (embed.title?.length ?? 0) +\n (embed.description?.length ?? 0) +\n (embed.fields ?? []).reduce((n, f) => n + f.name.length + f.value.length, 0) +\n textLength(embed['footer'], 'text') +\n textLength(embed['author'], 'name')\n );\n}\n\n/** Shrinks one embed to `limit` chars: description first, then fields, footer/author, title. */\nfunction shrinkToFit(embed: DiscordEmbed, limit: number): void {\n const excess = () => embedChars(embed) - limit;\n if (excess() > 0 && embed.description) {\n const keep = embed.description.length - excess();\n if (keep > 0) embed.description = truncateText(embed.description, keep);\n else delete embed.description;\n }\n while (excess() > 0 && embed.fields?.length) embed.fields.pop();\n if (excess() > 0) {\n delete embed['footer'];\n delete embed['author'];\n }\n if (excess() > 0 && embed.title) {\n embed.title = truncateText(embed.title, Math.max(1, embed.title.length - excess()));\n }\n}\n\nfunction codeBlock(json: string, max: number): string {\n return `\\`\\`\\`json\\n${truncateText(\n json.replace(/`{3,}/g, (m) => m.split('').join('')),\n max\n )}\\n\\`\\`\\``;\n}\n\nfunction clampCustomEmbed(embed: DiscordEmbed): DiscordEmbed {\n const out: DiscordEmbed = { ...embed };\n if (typeof out.title === 'string') out.title = truncateText(out.title, DISCORD_TITLE_MAX);\n if (typeof out.description === 'string') {\n out.description = truncateText(out.description, DISCORD_DESCRIPTION_MAX);\n }\n if (Array.isArray(out.fields)) {\n out.fields = out.fields.slice(0, DISCORD_FIELDS_MAX).map((f) => ({\n ...f,\n name: truncateText(String(f.name), DISCORD_TITLE_MAX),\n value: truncateText(String(f.value), DISCORD_FIELD_VALUE_MAX),\n }));\n }\n const footer = out['footer'] as { text?: unknown } | undefined;\n if (footer && typeof footer.text === 'string') {\n out['footer'] = { ...footer, text: truncateText(footer.text, DISCORD_FOOTER_MAX) };\n }\n const author = out['author'] as { name?: unknown } | undefined;\n if (author && typeof author.name === 'string') {\n out['author'] = { ...author, name: truncateText(author.name, DISCORD_TITLE_MAX) };\n }\n return out;\n}\n\ninterface EmbedDraft {\n embed: DiscordEmbed;\n json: string | undefined;\n}\n\nexport class DiscordTransport implements IAsyncTransport {\n public readonly name = 'discord';\n public readonly level: string;\n public filter?: (entry: TransportLogEntry) => boolean;\n\n private readonly queue!: WebhookQueue<EmbedDraft>;\n private readonly username: string | undefined;\n private readonly avatarUrl: string | undefined;\n private readonly formatter: ((entry: TransportLogEntry) => DiscordEmbed) | undefined;\n\n constructor(config: DiscordTransportConfig) {\n const url = parseWebhookUrl(config.webhookUrl, {\n label: 'DiscordTransport',\n hostMatches: (h) =>\n ['discord.com', 'discordapp.com', 'ptb.discord.com', 'canary.discord.com'].includes(h),\n pathMatches: (p) => /^\\/api(\\/v\\d+)?\\/webhooks\\//.test(p),\n expected: 'discord.com',\n allowCustomUrl: config.allowCustomUrl,\n });\n if (config.avatarUrl !== undefined && !/^https:\\/\\//i.test(config.avatarUrl)) {\n throw new TypeError('DiscordTransport: avatarUrl must use https:');\n }\n this.level = resolveMinLevel(config.level, 'DiscordTransport');\n if (config.filter) this.filter = config.filter;\n this.username = config.username;\n this.avatarUrl = config.avatarUrl;\n this.formatter = config.formatter;\n const queue = new WebhookQueue<EmbedDraft>({\n label: 'DiscordTransport',\n url,\n minIntervalMs: config.minIntervalMs,\n maxPerMessage: DISCORD_EMBEDS_MAX,\n maxQueueSize: config.maxBufferSize,\n maxRetries: config.maxRetries,\n requestTimeoutMs: config.requestTimeoutMs ?? DEFAULT_WEBHOOK_TIMEOUT_MS,\n render: (entry) => this.draftFor(entry),\n assemble: (drafts, overflow) => this.buildPayload(drafts, overflow),\n retryAfterMs: readRetryAfter,\n });\n // non-enumerable so util.inspect / spreads of the transport never reach the secret URL\n Object.defineProperty(this, 'queue', { value: queue, enumerable: false });\n }\n\n write(entry: TransportLogEntry): void {\n if (!meetsLevel(entry.level, this.level)) return;\n // `filter` is applied by the TransportManager; it is not run again here\n this.queue.push(entry);\n }\n\n flush(): Promise<void> {\n return this.queue.flush();\n }\n\n close(): Promise<void> {\n return this.queue.close();\n }\n\n /** Never serialise the webhook URL (JSON.stringify, util.inspect). */\n toJSON(): Record<string, unknown> {\n return { name: this.name, level: this.level };\n }\n\n /** `showHidden` would otherwise print the non-enumerable queue (and its URL). */\n [Symbol.for('nodejs.util.inspect.custom')](): Record<string, unknown> {\n return this.toJSON();\n }\n\n private buildPayload(drafts: EmbedDraft[], overflow: number): Record<string, unknown> {\n const kept = this.fitToBudget(drafts);\n const dropped = overflow + (drafts.length - kept.length);\n\n const payload: Record<string, unknown> = {\n embeds: kept.map((d) => d.embed),\n allowed_mentions: { parse: [] },\n };\n if (dropped > 0) {\n payload['content'] = truncateText(\n `… and ${dropped} more entr${dropped === 1 ? 'y' : 'ies'} not shown`,\n DISCORD_CONTENT_MAX\n );\n }\n if (this.username) payload['username'] = truncateText(this.username, 80);\n if (this.avatarUrl) payload['avatar_url'] = this.avatarUrl;\n return payload;\n }\n\n /** Enforces the 6000-char total: drops trailing embeds if needed, then shares the rest among descriptions. */\n private fitToBudget(drafts: EmbedDraft[]): EmbedDraft[] {\n const kept = [...drafts];\n const base = () => kept.reduce((n, d) => n + embedChars(d.embed), 0);\n while (kept.length > 1 && base() > DISCORD_EMBED_TOTAL_MAX) kept.pop();\n const only = kept[0];\n if (only && base() > DISCORD_EMBED_TOTAL_MAX) shrinkToFit(only.embed, DISCORD_EMBED_TOTAL_MAX);\n const withData = kept.filter((d) => d.json !== undefined);\n if (withData.length === 0) return kept;\n const share = Math.floor((DISCORD_EMBED_TOTAL_MAX - base()) / withData.length);\n const body = Math.min(DISCORD_DESCRIPTION_MAX, share) - FENCE_OVERHEAD;\n for (const d of withData) {\n if (body >= MIN_USEFUL_BODY) d.embed.description = codeBlock(d.json!, body);\n }\n return kept;\n }\n\n private draftFor(entry: TransportLogEntry): EmbedDraft {\n if (this.formatter) {\n try {\n return { embed: clampCustomEmbed(this.formatter(entry)), json: undefined };\n } catch {\n internalWarn('DiscordTransport: formatter threw; using the default embed');\n }\n }\n return defaultDraft(entry);\n }\n}\n\nfunction defaultDraft(entry: TransportLogEntry): EmbedDraft {\n const title = `[${singleLine(entry.level).toUpperCase()}] ${escapeDiscord(singleLine(entry.message))}`;\n const candidates: Array<[string, string | undefined]> = [\n ['appName', entry.appName],\n ['environment', entry.environment],\n ['context', entry.context],\n ['traceId', entry.traceId],\n ];\n const fields: DiscordEmbedField[] = candidates\n .map(([name, value]) => [name, typeof value === 'string' ? singleLine(value) : ''] as const)\n .filter(([, value]) => value !== '')\n .map(([name, value]) => ({\n name,\n value: truncateText(escapeDiscord(value), FIELD_VALUE_CAP),\n inline: true,\n }));\n const embed: DiscordEmbed = {\n title: truncateText(title, DISCORD_TITLE_MAX),\n color: DISCORD_LEVEL_COLORS[entry.level.toLowerCase()] ?? DEFAULT_COLOR,\n fields,\n };\n const timestamp = safeIso(entry.timestamp);\n if (timestamp) embed.timestamp = timestamp;\n const json =\n entry.data && Object.keys(entry.data).length > 0 ? stringifyForLog(entry.data) : undefined;\n return { embed, json };\n}\n\nasync function readRetryAfter(res: WebhookResponseLike): Promise<number | undefined> {\n try {\n const body = (await res.json?.()) as { retry_after?: unknown } | undefined;\n const seconds = Number(body?.retry_after);\n if (body?.retry_after !== undefined && Number.isFinite(seconds) && seconds >= 0) {\n return seconds * 1000;\n }\n } catch {\n // fall through to the header\n }\n return retryAfterHeaderMs(res);\n}\n","/**\n * Slack incoming-webhook transport.\n *\n * Sends error-level (by default) entries to a Slack channel as Block Kit\n * messages. Entries inside `minIntervalMs` are batched into one message so a\n * log storm cannot flood the channel. All logged text is escaped for Slack\n * mrkdwn (`&`, `<`, `>`), which also disarms `<!channel>`, `<!here>` and\n * `<@U123>` mentions coming from logged data.\n *\n * The webhook URL is a secret: it is never logged and never appears in errors.\n *\n * @example\n * transports: { custom: [ new SlackTransport({ webhookUrl: process.env.SLACK_WEBHOOK_URL! }) ] }\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n DEFAULT_WEBHOOK_TIMEOUT_MS,\n defuseText,\n meetsLevel,\n parseWebhookUrl,\n resolveMinLevel,\n safeIso,\n singleLine,\n truncateText,\n WebhookQueue,\n} from './webhook-queue';\n\nexport const SLACK_HEADER_MAX = 150;\nexport const SLACK_DATA_MAX = 2500;\nconst SLACK_FIELD_MAX = 300;\nconst SLACK_TEXT_MAX = 1000;\nconst SLACK_MAX_BLOCKS = 50;\nconst SLACK_MAX_PER_MESSAGE = 10;\n\n/** A Slack message body as returned by a custom `formatter`. */\nexport interface SlackMessage {\n text?: string;\n blocks?: unknown[];\n [key: string]: unknown;\n}\n\nexport interface SlackTransportConfig {\n /** Incoming-webhook URL (secret). Must be https on hooks.slack.com unless `allowCustomUrl`. */\n webhookUrl: string;\n /** Minimum level to send. Default: 'error'. */\n level?: string;\n username?: string;\n iconEmoji?: string;\n /** Entries within this window are batched into one message. Default: 1000. */\n minIntervalMs?: number;\n /**\n * Overrides the message built for each entry. The results of one window are\n * merged: texts are joined and blocks concatenated (capped at 50 blocks).\n * Escaping the content is the formatter's responsibility.\n */\n formatter?: (entry: TransportLogEntry) => SlackMessage;\n filter?: (entry: TransportLogEntry) => boolean;\n /** Accept an https webhook URL on any host (e.g. a proxy or Slack-compatible service). */\n allowCustomUrl?: boolean;\n /** Retries after an HTTP 429 (waits Retry-After, max 30s). Default: 1. */\n maxRetries?: number;\n /** Max queued entries; the oldest are dropped past this. Default: 1000. */\n maxBufferSize?: number;\n requestTimeoutMs?: number;\n}\n\n/** Escapes the three characters Slack treats specially and defuses broadcast mentions. */\nexport function escapeSlack(text: string): string {\n return defuseText(text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'));\n}\n\nconst LEVEL_EMOJI: Record<string, string> = {\n fatal: ':rotating_light:',\n error: ':red_circle:',\n warn: ':warning:',\n warning: ':warning:',\n info: ':information_source:',\n};\n\nexport class SlackTransport implements IAsyncTransport {\n public readonly name = 'slack';\n public readonly level: string;\n public filter?: (entry: TransportLogEntry) => boolean;\n\n private readonly queue!: WebhookQueue<SlackMessage>;\n private readonly username: string | undefined;\n private readonly iconEmoji: string | undefined;\n private readonly formatter: ((entry: TransportLogEntry) => SlackMessage) | undefined;\n\n constructor(config: SlackTransportConfig) {\n const url = parseWebhookUrl(config.webhookUrl, {\n label: 'SlackTransport',\n hostMatches: (h) => h === 'hooks.slack.com' || h === 'hooks.slack-gov.com',\n pathMatches: (p) => p.startsWith('/services/') || p.startsWith('/triggers/'),\n expected: 'hooks.slack.com',\n allowCustomUrl: config.allowCustomUrl,\n });\n this.level = resolveMinLevel(config.level, 'SlackTransport');\n if (config.filter) this.filter = config.filter;\n this.username = config.username;\n this.iconEmoji = config.iconEmoji;\n this.formatter = config.formatter;\n const queue = new WebhookQueue<SlackMessage>({\n label: 'SlackTransport',\n url,\n minIntervalMs: config.minIntervalMs,\n maxPerMessage: SLACK_MAX_PER_MESSAGE,\n maxQueueSize: config.maxBufferSize,\n maxRetries: config.maxRetries,\n requestTimeoutMs: config.requestTimeoutMs ?? DEFAULT_WEBHOOK_TIMEOUT_MS,\n render: (entry) => this.messageFor(entry),\n assemble: (messages, overflow) => this.buildPayload(messages, overflow),\n });\n // non-enumerable so util.inspect / spreads of the transport never reach the secret URL\n Object.defineProperty(this, 'queue', { value: queue, enumerable: false });\n }\n\n write(entry: TransportLogEntry): void {\n if (!meetsLevel(entry.level, this.level)) return;\n // `filter` is applied by the TransportManager; it is not run again here\n this.queue.push(entry);\n }\n\n flush(): Promise<void> {\n return this.queue.flush();\n }\n\n close(): Promise<void> {\n return this.queue.close();\n }\n\n /** Never serialise the webhook URL (JSON.stringify, util.inspect). */\n toJSON(): Record<string, unknown> {\n return { name: this.name, level: this.level };\n }\n\n /** `showHidden` would otherwise print the non-enumerable queue (and its URL). */\n [Symbol.for('nodejs.util.inspect.custom')](): Record<string, unknown> {\n return this.toJSON();\n }\n\n private buildPayload(messages: SlackMessage[], overflow: number): Record<string, unknown> {\n const blocks = messages.flatMap((m) => m.blocks ?? []);\n const plural = overflow === 1 ? 'y' : 'ies';\n const counter = overflow > 0 ? `… and ${overflow} more entr${plural} not shown` : '';\n if (counter) {\n blocks.push({ type: 'context', elements: [{ type: 'mrkdwn', text: counter }] });\n }\n const text = [...messages.map((m) => m.text ?? ''), counter].filter(Boolean).join('\\n');\n const payload: Record<string, unknown> = {\n text: truncateText(text, SLACK_TEXT_MAX),\n blocks: blocks.slice(0, SLACK_MAX_BLOCKS),\n };\n if (this.username) payload['username'] = truncateText(this.username, 80);\n if (this.iconEmoji) payload['icon_emoji'] = this.iconEmoji;\n return payload;\n }\n\n private messageFor(entry: TransportLogEntry): SlackMessage {\n if (this.formatter) {\n try {\n return this.formatter(entry);\n } catch {\n internalWarn('SlackTransport: formatter threw; using the default message');\n }\n }\n return defaultMessage(entry);\n }\n}\n\nfunction defaultMessage(entry: TransportLogEntry): SlackMessage {\n const level = singleLine(entry.level).toUpperCase();\n const emoji = LEVEL_EMOJI[entry.level.toLowerCase()] ?? ':white_circle:';\n const headline = `[${level}] ${singleLine(entry.message)}`;\n const blocks: unknown[] = [\n {\n type: 'header',\n text: {\n type: 'plain_text',\n text: truncateText(escapeSlack(headline), SLACK_HEADER_MAX),\n emoji: false,\n },\n },\n ];\n\n const context = [\n ['app', entry.appName],\n ['env', entry.environment],\n ['context', entry.context],\n ['trace', entry.traceId],\n ['time', safeIso(entry.timestamp)],\n ]\n .map(([label, value]) => [label, typeof value === 'string' ? singleLine(value) : ''] as const)\n .filter(([, value]) => value !== '')\n .map(([label, value]) => ({\n type: 'mrkdwn',\n text: `*${label}:* ${truncateText(escapeSlack(value), SLACK_FIELD_MAX)}`,\n }));\n if (context.length > 0) blocks.push({ type: 'context', elements: context });\n\n if (entry.data && Object.keys(entry.data).length > 0) {\n const json = truncateText(escapeSlack(stringifyForLog(entry.data)), SLACK_DATA_MAX);\n blocks.push({ type: 'section', text: { type: 'mrkdwn', text: `\\`\\`\\`${json}\\`\\`\\`` } });\n }\n\n return {\n text: `${emoji} ${escapeSlack(truncateText(headline, SLACK_HEADER_MAX))}`,\n blocks,\n };\n}\n"],"mappings":"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"}
|