logixia 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +2 -2
  2. package/dist/{async-context-Dgl3ZLyb.js → async-context-BqjwNzgS.js} +2 -2
  3. package/dist/{async-context-Dgl3ZLyb.js.map → async-context-BqjwNzgS.js.map} +1 -1
  4. package/dist/{async-context-fBqC5lNc.mjs → async-context-DJdxbbCU.mjs} +2 -2
  5. package/dist/{async-context-fBqC5lNc.mjs.map → async-context-DJdxbbCU.mjs.map} +1 -1
  6. package/dist/{azure-monitor.transport-C5VjvREZ.d.mts → azure-monitor.transport-BZDaQjy8.d.mts} +2 -2
  7. package/dist/{azure-monitor.transport-C5VjvREZ.d.mts.map → azure-monitor.transport-BZDaQjy8.d.mts.map} +1 -1
  8. package/dist/{azure-monitor.transport-DGiacfvT.mjs → azure-monitor.transport-B_dqSV_o.mjs} +3 -3
  9. package/dist/{azure-monitor.transport-DGiacfvT.mjs.map → azure-monitor.transport-B_dqSV_o.mjs.map} +1 -1
  10. package/dist/{azure-monitor.transport-DX3fuK2t.js → azure-monitor.transport-Bl3xVvpN.js} +3 -3
  11. package/dist/{azure-monitor.transport-DX3fuK2t.js.map → azure-monitor.transport-Bl3xVvpN.js.map} +1 -1
  12. package/dist/{azure-monitor.transport-CyHDjHS-.d.ts → azure-monitor.transport-MDigkKsS.d.ts} +2 -2
  13. package/dist/{azure-monitor.transport-CyHDjHS-.d.ts.map → azure-monitor.transport-MDigkKsS.d.ts.map} +1 -1
  14. package/dist/azure.d.mts +1 -1
  15. package/dist/azure.d.ts +1 -1
  16. package/dist/azure.js +1 -1
  17. package/dist/azure.mjs +1 -1
  18. package/dist/{batch-buffer-CM-lK_Qy.mjs → batch-buffer-BnmVCCwI.mjs} +2 -2
  19. package/dist/{batch-buffer-CM-lK_Qy.mjs.map → batch-buffer-BnmVCCwI.mjs.map} +1 -1
  20. package/dist/{batch-buffer-BLmVvJAZ.js → batch-buffer-C-ETEX3Y.js} +2 -2
  21. package/dist/{batch-buffer-BLmVvJAZ.js.map → batch-buffer-C-ETEX3Y.js.map} +1 -1
  22. package/dist/browser.js +15 -11
  23. package/dist/browser.js.map +1 -1
  24. package/dist/browser.mjs +15 -11
  25. package/dist/browser.mjs.map +1 -1
  26. package/dist/{builder-jMlVYaQa.js → builder-CC48eDJ3.js} +58 -250
  27. package/dist/builder-CC48eDJ3.js.map +1 -0
  28. package/dist/{builder-CA_dokb4.mjs → builder-CoisL7S7.mjs} +55 -223
  29. package/dist/builder-CoisL7S7.mjs.map +1 -0
  30. package/dist/cloudwatch.d.mts +1 -1
  31. package/dist/cloudwatch.d.ts +1 -1
  32. package/dist/cloudwatch.js +1 -1
  33. package/dist/cloudwatch.mjs +1 -1
  34. package/dist/{cloudwatch.transport-Cr0fEpko.mjs → cloudwatch.transport-Brh5fmRN.mjs} +3 -3
  35. package/dist/{cloudwatch.transport-Cr0fEpko.mjs.map → cloudwatch.transport-Brh5fmRN.mjs.map} +1 -1
  36. package/dist/{cloudwatch.transport-_hS7Mcc5.d.mts → cloudwatch.transport-CqXAGHPH.d.mts} +2 -2
  37. package/dist/{cloudwatch.transport-_hS7Mcc5.d.mts.map → cloudwatch.transport-CqXAGHPH.d.mts.map} +1 -1
  38. package/dist/{cloudwatch.transport-Lst1Bbmr.js → cloudwatch.transport-DO9e__Z-.js} +3 -3
  39. package/dist/{cloudwatch.transport-Lst1Bbmr.js.map → cloudwatch.transport-DO9e__Z-.js.map} +1 -1
  40. package/dist/{cloudwatch.transport-DdSjUWvu.d.ts → cloudwatch.transport-pY96iAfO.d.ts} +2 -2
  41. package/dist/{cloudwatch.transport-DdSjUWvu.d.ts.map → cloudwatch.transport-pY96iAfO.d.ts.map} +1 -1
  42. package/dist/correlation.js +2 -2
  43. package/dist/correlation.mjs +2 -2
  44. package/dist/error.utils-B8_CwESF.mjs +189 -0
  45. package/dist/error.utils-B8_CwESF.mjs.map +1 -0
  46. package/dist/error.utils-fZkRExam.js +212 -0
  47. package/dist/error.utils-fZkRExam.js.map +1 -0
  48. package/dist/fastify.d.mts +89 -0
  49. package/dist/fastify.d.mts.map +1 -0
  50. package/dist/fastify.d.ts +89 -0
  51. package/dist/fastify.d.ts.map +1 -0
  52. package/dist/fastify.js +265 -0
  53. package/dist/fastify.js.map +1 -0
  54. package/dist/fastify.mjs +263 -0
  55. package/dist/fastify.mjs.map +1 -0
  56. package/dist/gcp.d.mts +1 -1
  57. package/dist/gcp.d.ts +1 -1
  58. package/dist/gcp.js +1 -1
  59. package/dist/gcp.mjs +1 -1
  60. package/dist/{gcp.transport-C8VJiEM7.d.mts → gcp.transport-BFlvl8uy.d.mts} +2 -2
  61. package/dist/{gcp.transport-C8VJiEM7.d.mts.map → gcp.transport-BFlvl8uy.d.mts.map} +1 -1
  62. package/dist/{gcp.transport-Cw3Qke85.mjs → gcp.transport-BY1Z3cS0.mjs} +3 -3
  63. package/dist/{gcp.transport-Cw3Qke85.mjs.map → gcp.transport-BY1Z3cS0.mjs.map} +1 -1
  64. package/dist/{gcp.transport-Ct0BtKFo.d.ts → gcp.transport-Bo60F-_3.d.ts} +2 -2
  65. package/dist/{gcp.transport-Ct0BtKFo.d.ts.map → gcp.transport-Bo60F-_3.d.ts.map} +1 -1
  66. package/dist/{gcp.transport-5-ZIjN8r.js → gcp.transport-V9R4dwdT.js} +3 -3
  67. package/dist/{gcp.transport-5-ZIjN8r.js.map → gcp.transport-V9R4dwdT.js.map} +1 -1
  68. package/dist/hono.d.mts +66 -0
  69. package/dist/hono.d.mts.map +1 -0
  70. package/dist/hono.d.ts +66 -0
  71. package/dist/hono.d.ts.map +1 -0
  72. package/dist/hono.js +512 -0
  73. package/dist/hono.js.map +1 -0
  74. package/dist/hono.mjs +511 -0
  75. package/dist/hono.mjs.map +1 -0
  76. package/dist/{index-B3ajtXJd.d.mts → index-0X_XpQ_f.d.mts} +2 -2
  77. package/dist/{index-B3ajtXJd.d.mts.map → index-0X_XpQ_f.d.mts.map} +1 -1
  78. package/dist/{index-oqD3WNxf.d.ts → index-BnbqeNl6.d.ts} +2 -2
  79. package/dist/{index-oqD3WNxf.d.ts.map → index-BnbqeNl6.d.ts.map} +1 -1
  80. package/dist/index.d.mts +5 -5
  81. package/dist/index.d.ts +5 -5
  82. package/dist/index.js +12 -11
  83. package/dist/index.js.map +1 -1
  84. package/dist/index.mjs +6 -5
  85. package/dist/index.mjs.map +1 -1
  86. package/dist/{logitron-logger-fMOkXr8H.d.mts → logixia-logger-DcvASx5B.d.mts} +2 -2
  87. package/dist/logixia-logger-DcvASx5B.d.mts.map +1 -0
  88. package/dist/{logitron-logger-Csn3e6Qu.d.ts → logixia-logger-cLi5x5I2.d.ts} +2 -2
  89. package/dist/logixia-logger-cLi5x5I2.d.ts.map +1 -0
  90. package/dist/middleware.d.mts +1 -1
  91. package/dist/middleware.d.ts +1 -1
  92. package/dist/middleware.js +1 -1
  93. package/dist/middleware.mjs +1 -1
  94. package/dist/nest.d.mts +2 -2
  95. package/dist/nest.d.mts.map +1 -1
  96. package/dist/nest.d.ts +2 -2
  97. package/dist/nest.d.ts.map +1 -1
  98. package/dist/nest.js +7 -6
  99. package/dist/nest.js.map +1 -1
  100. package/dist/nest.mjs +6 -5
  101. package/dist/nest.mjs.map +1 -1
  102. package/dist/{otel-8ypFGG19.js → otel-D4SdNYhR.js} +5 -7
  103. package/dist/otel-D4SdNYhR.js.map +1 -0
  104. package/dist/{otel-b-s2QbY4.mjs → otel-LeATAHbW.mjs} +5 -7
  105. package/dist/otel-LeATAHbW.mjs.map +1 -0
  106. package/dist/{request-trace-Dekcnenb.mjs → request-trace-B-S5-LVk.mjs} +10 -5
  107. package/dist/request-trace-B-S5-LVk.mjs.map +1 -0
  108. package/dist/{request-trace-C7kXMjx5.js → request-trace-BqU7ARjg.js} +15 -4
  109. package/dist/request-trace-BqU7ARjg.js.map +1 -0
  110. package/dist/{search-BlKBLm5D.d.ts → search-B9fKizkh.d.ts} +2 -2
  111. package/dist/{search-BlKBLm5D.d.ts.map → search-B9fKizkh.d.ts.map} +1 -1
  112. package/dist/{search-B-hJe2N-.d.mts → search-CdTwNSQm.d.mts} +2 -2
  113. package/dist/{search-B-hJe2N-.d.mts.map → search-CdTwNSQm.d.mts.map} +1 -1
  114. package/dist/search.d.mts +1 -1
  115. package/dist/search.d.ts +1 -1
  116. package/dist/{secret-config-GKmb15Me.mjs → secret-config-Cng7XPLx.mjs} +16 -12
  117. package/dist/secret-config-Cng7XPLx.mjs.map +1 -0
  118. package/dist/{secret-config--cVTOWwp.js → secret-config-OqmIPmpV.js} +16 -12
  119. package/dist/secret-config-OqmIPmpV.js.map +1 -0
  120. package/dist/{slack.transport-Cpx5fKga.js → slack.transport-B-IsNaB8.js} +4 -4
  121. package/dist/{slack.transport-Cpx5fKga.js.map → slack.transport-B-IsNaB8.js.map} +1 -1
  122. package/dist/{slack.transport-Qwj9WkrJ.mjs → slack.transport-BXmDHthx.mjs} +4 -4
  123. package/dist/{slack.transport-Qwj9WkrJ.mjs.map → slack.transport-BXmDHthx.mjs.map} +1 -1
  124. package/dist/{slack.transport-5OPL7H3u.d.mts → slack.transport-DAGuMvm7.d.mts} +2 -2
  125. package/dist/{slack.transport-5OPL7H3u.d.mts.map → slack.transport-DAGuMvm7.d.mts.map} +1 -1
  126. package/dist/{slack.transport-BFpBD_Ri.d.ts → slack.transport-LvYsOoFZ.d.ts} +2 -2
  127. package/dist/{slack.transport-BFpBD_Ri.d.ts.map → slack.transport-LvYsOoFZ.d.ts.map} +1 -1
  128. package/dist/testing.d.mts +1 -1
  129. package/dist/testing.d.ts +1 -1
  130. package/dist/{transport.types-B1ejJJP6.d.mts → transport.types-MjMDjOQ7.d.mts} +6 -1
  131. package/dist/{transport.types-B1ejJJP6.d.mts.map → transport.types-MjMDjOQ7.d.mts.map} +1 -1
  132. package/dist/{transport.types-B1ejJJP6.d.ts → transport.types-MjMDjOQ7.d.ts} +6 -1
  133. package/dist/{transport.types-B1ejJJP6.d.ts.map → transport.types-MjMDjOQ7.d.ts.map} +1 -1
  134. package/dist/transports.d.mts +8 -10
  135. package/dist/transports.d.mts.map +1 -1
  136. package/dist/transports.d.ts +8 -10
  137. package/dist/transports.d.ts.map +1 -1
  138. package/dist/transports.js +6 -6
  139. package/dist/transports.mjs +6 -6
  140. package/dist/worker-entry.js +15 -11
  141. package/dist/worker.d.mts +1 -1
  142. package/dist/worker.d.ts +1 -1
  143. package/dist/worker.js +1 -1
  144. package/dist/worker.mjs +1 -1
  145. package/dist/{worker.transport-B7olL1Uh.d.ts → worker.transport-ByBBDx7U.d.ts} +2 -2
  146. package/dist/{worker.transport-B7olL1Uh.d.ts.map → worker.transport-ByBBDx7U.d.ts.map} +1 -1
  147. package/dist/{worker.transport-C7jNGhev.js → worker.transport-CZOutlxC.js} +2 -2
  148. package/dist/{worker.transport-C7jNGhev.js.map → worker.transport-CZOutlxC.js.map} +1 -1
  149. package/dist/{worker.transport-BHnWDXPH.mjs → worker.transport-CxFD8mvS.mjs} +2 -2
  150. package/dist/{worker.transport-BHnWDXPH.mjs.map → worker.transport-CxFD8mvS.mjs.map} +1 -1
  151. package/dist/{worker.transport-DnU0_9Bd.d.mts → worker.transport-DWyyRVJW.d.mts} +2 -2
  152. package/dist/{worker.transport-DnU0_9Bd.d.mts.map → worker.transport-DWyyRVJW.d.mts.map} +1 -1
  153. package/package.json +37 -2
  154. package/dist/builder-CA_dokb4.mjs.map +0 -1
  155. package/dist/builder-jMlVYaQa.js.map +0 -1
  156. package/dist/logitron-logger-Csn3e6Qu.d.ts.map +0 -1
  157. package/dist/logitron-logger-fMOkXr8H.d.mts.map +0 -1
  158. package/dist/otel-8ypFGG19.js.map +0 -1
  159. package/dist/otel-b-s2QbY4.mjs.map +0 -1
  160. package/dist/request-trace-C7kXMjx5.js.map +0 -1
  161. package/dist/request-trace-Dekcnenb.mjs.map +0 -1
  162. package/dist/secret-config--cVTOWwp.js.map +0 -1
  163. package/dist/secret-config-GKmb15Me.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"cloudwatch.transport-Cr0fEpko.mjs","names":["path"],"sources":["../src/transports/aws-credentials.ts","../src/transports/aws-sigv4.ts","../src/transports/cloudwatch.transport.ts"],"sourcesContent":["/**\n * AWS credential resolution without the SDK.\n *\n * Order: explicit config, environment variables, shared credentials file,\n * ECS/container credentials, EC2 instance metadata (IMDSv2).\n * Not supported: SSO, assume-role profiles, credential_process, web identity.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { internalWarn } from '../utils/internal-log';\nimport { type AwsCredentials } from './aws-sigv4';\nimport { httpRequest } from './batch-buffer';\nimport { protectSecrets } from './secret-config';\n\nconst REFRESH_MARGIN_MS = 5 * 60 * 1000;\nconst FAILURE_BACKOFF_MS = 30 * 1000;\nconst METADATA_TIMEOUT_MS = 2000;\n\nexport interface AwsCredentialConfig {\n accessKeyId?: string | undefined;\n secretAccessKey?: string | undefined;\n sessionToken?: string | undefined;\n}\n\n/** Parse the sections of an AWS ini file into `{ section: { key: value } }`. */\nexport function parseIni(text: string): Record<string, Record<string, string>> {\n const out: Record<string, Record<string, string>> = {};\n let section: Record<string, string> | undefined;\n for (const raw of text.split(/\\r?\\n/)) {\n const line = raw.trim();\n if (!line || line.startsWith('#') || line.startsWith(';')) continue;\n const header = /^\\[(.+)\\]$/.exec(line);\n if (header) {\n section = out[header[1]!.trim()] = out[header[1]!.trim()] ?? {};\n continue;\n }\n const eq = line.indexOf('=');\n if (eq > 0 && section) section[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();\n }\n return out;\n}\n\nfunction fromEnv(): AwsCredentials | undefined {\n const accessKeyId = process.env['AWS_ACCESS_KEY_ID'];\n const secretAccessKey = process.env['AWS_SECRET_ACCESS_KEY'];\n if (!accessKeyId || !secretAccessKey) return undefined;\n return { accessKeyId, secretAccessKey, sessionToken: process.env['AWS_SESSION_TOKEN'] };\n}\n\nfunction fromSharedFile(): AwsCredentials | undefined {\n const file =\n process.env['AWS_SHARED_CREDENTIALS_FILE'] ?? path.join(os.homedir(), '.aws', 'credentials');\n const profile = process.env['AWS_PROFILE'] ?? 'default';\n let text: string;\n try {\n text = fs.readFileSync(file, 'utf8');\n } catch {\n return undefined;\n }\n const section = parseIni(text)[profile];\n const accessKeyId = section?.['aws_access_key_id'];\n const secretAccessKey = section?.['aws_secret_access_key'];\n if (!accessKeyId || !secretAccessKey) return undefined;\n return { accessKeyId, secretAccessKey, sessionToken: section?.['aws_session_token'] };\n}\n\ninterface CredentialDocument {\n AccessKeyId?: string;\n SecretAccessKey?: string;\n Token?: string;\n Expiration?: string;\n}\n\nfunction fromDocument(raw: string): AwsCredentials | undefined {\n const doc = JSON.parse(raw) as CredentialDocument;\n if (!doc.AccessKeyId || !doc.SecretAccessKey) return undefined;\n const expiresAt = doc.Expiration ? Date.parse(doc.Expiration) : Number.NaN;\n return {\n accessKeyId: doc.AccessKeyId,\n secretAccessKey: doc.SecretAccessKey,\n sessionToken: doc.Token,\n expiresAt: Number.isNaN(expiresAt) ? undefined : expiresAt,\n };\n}\n\nconst CONTAINER_HOSTS = new Set([\n // eslint-disable-next-line sonarjs/no-hardcoded-ip -- fixed ECS credentials endpoint\n '169.254.170.2',\n // eslint-disable-next-line sonarjs/no-hardcoded-ip -- fixed EKS pod identity endpoint\n '169.254.170.23',\n '[fd00:ec2::23]',\n 'localhost',\n '[::1]',\n]);\n\n/** Same rule as the AWS SDKs: never send the container token to an arbitrary http host. */\nfunction isAllowedContainerHost(url: URL): boolean {\n if (url.protocol === 'https:') return true;\n if (url.protocol !== 'http:') return false;\n return CONTAINER_HOSTS.has(url.hostname) || /^127(?:\\.\\d{1,3}){3}$/.test(url.hostname);\n}\n\nasync function fromContainer(): Promise<AwsCredentials | undefined> {\n const relative = process.env['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'];\n const full = process.env['AWS_CONTAINER_CREDENTIALS_FULL_URI'];\n if (!relative && !full) return undefined;\n const url = new URL(relative ? `http://169.254.170.2${relative}` : (full as string));\n if (!relative && !isAllowedContainerHost(url)) {\n internalWarn(\n `ignoring AWS_CONTAINER_CREDENTIALS_FULL_URI: ${url.host} is not https or a loopback / ECS / EKS address`\n );\n return undefined;\n }\n const headers: Record<string, string> = {};\n const tokenFile = process.env['AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE'];\n const token = tokenFile\n ? fs.readFileSync(tokenFile, 'utf8').trim()\n : process.env['AWS_CONTAINER_AUTHORIZATION_TOKEN'];\n if (token) headers['Authorization'] = token;\n const res = await httpRequest({\n url,\n method: 'GET',\n headers,\n timeoutMs: METADATA_TIMEOUT_MS,\n label: 'AWS container credentials',\n });\n return fromDocument(res.body);\n}\n\nasync function fromInstanceMetadata(): Promise<AwsCredentials | undefined> {\n if (process.env['AWS_EC2_METADATA_DISABLED'] === 'true') return undefined;\n const base = process.env['AWS_EC2_METADATA_SERVICE_ENDPOINT'] ?? 'http://169.254.169.254';\n const get = (p: string, method: string, headers: Record<string, string>) =>\n httpRequest({\n url: new URL(p, base),\n method,\n headers,\n timeoutMs: METADATA_TIMEOUT_MS,\n label: 'EC2 instance metadata',\n });\n const session = await get('/latest/api/token', 'PUT', {\n 'X-aws-ec2-metadata-token-ttl-seconds': '21600',\n });\n const auth = { 'X-aws-ec2-metadata-token': session.body.trim() };\n const roles = await get('/latest/meta-data/iam/security-credentials/', 'GET', auth);\n const role = roles.body.split('\\n')[0]?.trim();\n if (!role) return undefined;\n const creds = await get(\n `/latest/meta-data/iam/security-credentials/${encodeURIComponent(role)}`,\n 'GET',\n auth\n );\n return fromDocument(creds.body);\n}\n\nexport class AwsCredentialProvider {\n private cached: AwsCredentials | undefined;\n private failedAt = 0;\n private readonly explicit: AwsCredentials | undefined;\n\n constructor(config: AwsCredentialConfig) {\n protectSecrets(this, ['cached', 'explicit']);\n if (config.accessKeyId && config.secretAccessKey) {\n this.explicit = {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n sessionToken: config.sessionToken,\n };\n }\n }\n\n /** Throws when no source yields credentials, so the caller keeps its buffer. */\n async get(): Promise<AwsCredentials> {\n if (this.explicit) return this.explicit;\n const env = fromEnv();\n if (env) return env;\n if (this.cached && !this.isExpiring(this.cached)) return this.cached;\n if (Date.now() - this.failedAt < FAILURE_BACKOFF_MS) {\n throw new Error('no AWS credentials found (retrying shortly)');\n }\n\n let found = fromSharedFile();\n for (const source of [fromContainer, fromInstanceMetadata]) {\n if (found) break;\n try {\n found = await source();\n } catch {\n /* source not available here; try the next */\n }\n }\n if (!found) {\n this.failedAt = Date.now();\n throw new Error(\n 'no AWS credentials found: set AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, a shared credentials file, container credentials, or run on EC2 with an instance role'\n );\n }\n this.cached = found;\n return found;\n }\n\n /** Secret values this provider may hold, for scrubbing error text. */\n secretValues(): (string | undefined)[] {\n const env = process.env;\n return [\n this.explicit?.secretAccessKey,\n this.explicit?.sessionToken,\n this.cached?.secretAccessKey,\n this.cached?.sessionToken,\n env['AWS_SECRET_ACCESS_KEY'],\n env['AWS_SESSION_TOKEN'],\n ];\n }\n\n invalidate(): void {\n this.cached = undefined;\n this.failedAt = 0;\n }\n\n private isExpiring(c: AwsCredentials): boolean {\n return c.expiresAt !== undefined && c.expiresAt - Date.now() < REFRESH_MARGIN_MS;\n }\n}\n","/**\n * AWS Signature Version 4 request signing, dependency-free.\n */\n\nimport * as crypto from 'node:crypto';\n\nexport interface AwsCredentials {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string | undefined;\n /** Epoch ms after which the credentials are no longer valid. */\n expiresAt?: number | undefined;\n}\n\nexport interface SignV4Options {\n method: string;\n path: string;\n query?: string;\n /** Must include `host`. Names are lowercased before signing. */\n headers: Record<string, string>;\n body: string;\n region: string;\n service: string;\n credentials: AwsCredentials;\n date?: Date;\n}\n\nconst sha256Hex = (data: string): string => crypto.createHash('sha256').update(data).digest('hex');\n\nconst hmac = (key: Buffer | string, msg: string): Buffer =>\n crypto.createHmac('sha256', key).update(msg).digest();\n\n/** Returns the request headers (lowercase names) with `x-amz-date` and `authorization` added. */\nexport function signV4(opts: SignV4Options): Record<string, string> {\n const {\n method,\n path,\n query = '',\n body,\n region,\n service,\n credentials,\n headers: input,\n date,\n } = opts;\n const amzDate = (date ?? new Date()).toISOString().replace(/[:-]|\\.\\d{3}/g, '');\n const dateStamp = amzDate.slice(0, 8);\n\n const headers: Record<string, string> = {};\n for (const [k, v] of Object.entries(input)) headers[k.toLowerCase()] = v;\n headers['x-amz-date'] = amzDate;\n if (credentials.sessionToken) headers['x-amz-security-token'] = credentials.sessionToken;\n\n const names = Object.keys(headers).sort();\n const canonicalHeaders = names\n .map((n) => `${n}:${headers[n]!.trim().replace(/\\s+/g, ' ')}\\n`)\n .join('');\n const signedHeaders = names.join(';');\n\n const canonicalRequest = [\n method,\n path,\n query,\n canonicalHeaders,\n signedHeaders,\n sha256Hex(body),\n ].join('\\n');\n const scope = `${dateStamp}/${region}/${service}/aws4_request`;\n const stringToSign = ['AWS4-HMAC-SHA256', amzDate, scope, sha256Hex(canonicalRequest)].join('\\n');\n\n const signingKey = hmac(\n hmac(hmac(hmac(`AWS4${credentials.secretAccessKey}`, dateStamp), region), service),\n 'aws4_request'\n );\n const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');\n\n headers['authorization'] =\n `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${scope}, ` +\n `SignedHeaders=${signedHeaders}, Signature=${signature}`;\n return headers;\n}\n","/**\n * logixia — AWS CloudWatch Logs transport\n *\n * Sends log entries to AWS CloudWatch Logs via the PutLogEvents API.\n * Supports EMF (Embedded Metric Format) for automatic metric extraction.\n *\n * Auth, in order: explicit config, environment variables, the shared\n * credentials file (AWS_PROFILE, AWS_SHARED_CREDENTIALS_FILE), ECS container\n * credentials, then EC2 instance metadata (IMDSv2). SSO, assume-role profiles\n * and credential_process are not supported. No SDK dependency — raw HTTPS with\n * AWS Signature Version 4.\n *\n * The log group and stream are created on first use if they do not exist.\n *\n * @example\n * ```ts\n * import { createLogger } from 'logixia';\n * import { CloudWatchTransport } from 'logixia/cloudwatch';\n *\n * const logger = createLogger({\n * transports: {\n * custom: [\n * new CloudWatchTransport({\n * region: 'us-east-1',\n * logGroupName: '/myapp/production',\n * logStreamName: `api-${process.env.HOSTNAME}`,\n * batchSize: 100,\n * flushIntervalMs: 5000,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { AwsCredentialProvider } from './aws-credentials';\nimport { signV4 } from './aws-sigv4';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n HttpError,\n httpRequest,\n isPermanentHttpError,\n} from './batch-buffer';\nimport { mergeCoreFields } from './entry-fields';\nimport { protectSecrets, urlSecrets } from './secret-config';\n\nexport interface CloudWatchTransportConfig {\n /** AWS region, e.g. `'us-east-1'`. Falls back to `AWS_REGION` env var. */\n region?: string;\n /** CloudWatch Logs group name. Created automatically if it does not exist. */\n logGroupName: string;\n /** CloudWatch Logs stream name. Defaults to hostname + process PID. */\n logStreamName?: string;\n /** Override the Logs endpoint, e.g. for LocalStack. Default: `https://logs.<region>.amazonaws.com`. */\n endpoint?: string;\n /** AWS access key ID. Falls back to the credential chain described above. */\n accessKeyId?: string;\n /** AWS secret access key. Used together with `accessKeyId`. */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials. Used together with `accessKeyId`. */\n sessionToken?: string;\n /**\n * Number of log events to batch before flushing.\n * CloudWatch allows max 10,000 events per PutLogEvents call; larger values are capped.\n * @default 100\n */\n batchSize?: number;\n /**\n * Max ms between flushes even if batchSize is not reached.\n * @default 5000\n */\n flushIntervalMs?: number;\n /**\n * Enable EMF (Embedded Metric Format) — wraps log entries as CloudWatch\n * metric namespaces so numeric fields become CloudWatch Metrics automatically.\n * @default false\n */\n emf?: boolean;\n /** CloudWatch Metrics namespace used by EMF. @default 'Logixia' */\n emfNamespace?: string;\n /**\n * Payload fields published as EMF metrics. When set, only these numeric\n * fields become metrics; when unset every numeric payload field does (up to\n * the EMF limit of 100 per entry).\n */\n emfMetrics?: string[];\n /** Abort a request that has not finished after this many ms. @default 10000 */\n requestTimeoutMs?: number;\n /** Max buffered events; the oldest are dropped past this. @default 10000 */\n maxBufferSize?: number;\n /** Minimum log level to forward. */\n level?: string;\n}\n\nconst MAX_EVENTS_PER_PUT = 10_000;\nconst EMF_MAX_METRICS = 100;\n\n/** Error types worth retrying even though CloudWatch reports them as HTTP 400. */\nconst RETRYABLE_TYPES = new Set([\n 'ThrottlingException',\n 'ThrottledException',\n 'LimitExceededException',\n 'ServiceUnavailableException',\n 'ExpiredTokenException',\n 'ResourceNotFoundException',\n]);\n\nfunction awsErrorType(err: unknown): string | undefined {\n if (!(err instanceof HttpError)) return undefined;\n try {\n const type = (JSON.parse(err.body) as { __type?: string }).__type;\n return type?.split('#').pop();\n } catch {\n return undefined;\n }\n}\n\ninterface CwLogEvent {\n timestamp: number;\n message: string;\n}\n\n/**\n * AWS CloudWatch Logs transport with EMF support and batched PutLogEvents calls.\n */\nexport class CloudWatchTransport implements IAsyncTransport {\n public readonly name = 'cloudwatch';\n public readonly batchSize: number;\n public readonly flushInterval: number;\n public readonly level: string | undefined;\n\n private readonly buffer: BatchBuffer<CwLogEvent>;\n private readonly requestTimeoutMs: number;\n private flushTimer: NodeJS.Timeout | null = null;\n private readonly credentials: AwsCredentialProvider;\n private readonly endpoint: URL;\n private streamReady = false;\n\n private readonly region: string;\n private readonly logGroupName: string;\n private readonly logStreamName: string;\n private readonly emf: boolean;\n private readonly emfNamespace: string;\n private readonly emfMetrics: ReadonlySet<string> | undefined;\n\n constructor(config: CloudWatchTransportConfig) {\n this.region =\n config.region ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n 'us-east-1';\n this.logGroupName = config.logGroupName;\n this.logStreamName =\n config.logStreamName ?? `${process.env['HOSTNAME'] ?? 'node'}-${process.pid}`;\n this.credentials = new AwsCredentialProvider(config);\n this.endpoint = new URL(config.endpoint ?? `https://logs.${this.region}.amazonaws.com`);\n this.batchSize = Math.min(config.batchSize ?? 100, MAX_EVENTS_PER_PUT);\n this.emfNamespace = config.emfNamespace ?? 'Logixia';\n this.flushInterval = config.flushIntervalMs ?? 5000;\n this.emf = config.emf ?? false;\n this.emfMetrics = config.emfMetrics ? new Set(config.emfMetrics) : undefined;\n this.level = config.level;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.buffer = new BatchBuffer<CwLogEvent>({\n name: 'CloudWatchTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (events) => this.putLogEvents(events),\n secrets: () => [...this.credentials.secretValues(), ...urlSecrets(this.endpoint)],\n shouldRetry: (err) =>\n !isPermanentHttpError(err) || RETRYABLE_TYPES.has(awsErrorType(err) ?? ''),\n });\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushInterval);\n if (this.flushTimer.unref) this.flushTimer.unref();\n protectSecrets(this, ['credentials', 'endpoint']);\n }\n\n write(entry: TransportLogEntry): void {\n const timestamp = entry.timestamp ? new Date(entry.timestamp).getTime() : Date.now();\n const message = this.emf ? this.toEmf(entry) : stringifyForLog(entry);\n this.buffer.push({ timestamp, message });\n this.buffer.flushIfFull();\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n /**\n * Flush remaining events and stop the interval timer on shutdown. Without this\n * the manager's close() skips the transport (close was undefined) and any\n * buffered events were lost on deploy. Waits for any in-flight send first.\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 private toEmf(entry: TransportLogEntry): string {\n const { level, message, data: payload, timestamp } = entry;\n const fields = mergeCoreFields({ _aws: undefined, level, message }, payload);\n const metricNames: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (k === '_aws' || k === 'level' || k === 'message' || typeof v !== 'number') continue;\n if (this.emfMetrics && !this.emfMetrics.has(k)) continue;\n if (metricNames.length < EMF_MAX_METRICS) metricNames.push(k);\n }\n fields['_aws'] = {\n Timestamp: timestamp ? new Date(timestamp).getTime() : Date.now(),\n CloudWatchMetrics:\n metricNames.length > 0\n ? [\n {\n Namespace: this.emfNamespace,\n Dimensions: [['level']],\n Metrics: metricNames.map((n) => ({ Name: n, Unit: 'None' })),\n },\n ]\n : [],\n };\n return stringifyForLog(fields);\n }\n\n private async putLogEvents(events: CwLogEvent[]): Promise<void> {\n await this.ensureStream();\n const logEvents = [...events].sort((a, b) => a.timestamp - b.timestamp);\n try {\n await this.call('PutLogEvents', {\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n logEvents,\n });\n } catch (err) {\n const type = awsErrorType(err);\n if (type === 'DataAlreadyAcceptedException') return;\n if (type === 'ResourceNotFoundException') this.streamReady = false;\n if (type === 'ExpiredTokenException') this.credentials.invalidate();\n throw err;\n }\n }\n\n private async ensureStream(): Promise<void> {\n if (this.streamReady) return;\n try {\n await this.call('CreateLogGroup', { logGroupName: this.logGroupName });\n } catch (err) {\n if (awsErrorType(err) === 'ExpiredTokenException') this.credentials.invalidate();\n if (!(err instanceof HttpError)) throw err;\n if (awsErrorType(err) !== 'ResourceAlreadyExistsException') {\n internalWarn(`CloudWatchTransport: CreateLogGroup failed, continuing (${err.message})`);\n }\n }\n try {\n await this.call('CreateLogStream', {\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n });\n } catch (err) {\n if (awsErrorType(err) !== 'ResourceAlreadyExistsException') throw err;\n }\n this.streamReady = true;\n }\n\n private async call(action: string, payload: Record<string, unknown>): Promise<void> {\n const credentials = await this.credentials.get();\n const body = stringifyForLog(payload);\n const { host } = this.endpoint;\n const headers = signV4({\n method: 'POST',\n path: '/',\n headers: {\n host,\n 'content-type': 'application/x-amz-json-1.1',\n 'x-amz-target': `Logs_20140328.${action}`,\n },\n body,\n region: this.region,\n service: 'logs',\n credentials,\n });\n await httpRequest({\n url: this.endpoint,\n headers,\n body,\n timeoutMs: this.requestTimeoutMs,\n label: `CloudWatch ${action}`,\n secrets: () => [\n ...this.credentials.secretValues(),\n headers['authorization'],\n headers['x-amz-security-token'],\n ...urlSecrets(this.endpoint),\n ],\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cloudwatch.transport-Brh5fmRN.mjs","names":["path"],"sources":["../src/transports/aws-credentials.ts","../src/transports/aws-sigv4.ts","../src/transports/cloudwatch.transport.ts"],"sourcesContent":["/**\n * AWS credential resolution without the SDK.\n *\n * Order: explicit config, environment variables, shared credentials file,\n * ECS/container credentials, EC2 instance metadata (IMDSv2).\n * Not supported: SSO, assume-role profiles, credential_process, web identity.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { internalWarn } from '../utils/internal-log';\nimport { type AwsCredentials } from './aws-sigv4';\nimport { httpRequest } from './batch-buffer';\nimport { protectSecrets } from './secret-config';\n\nconst REFRESH_MARGIN_MS = 5 * 60 * 1000;\nconst FAILURE_BACKOFF_MS = 30 * 1000;\nconst METADATA_TIMEOUT_MS = 2000;\n\nexport interface AwsCredentialConfig {\n accessKeyId?: string | undefined;\n secretAccessKey?: string | undefined;\n sessionToken?: string | undefined;\n}\n\n/** Parse the sections of an AWS ini file into `{ section: { key: value } }`. */\nexport function parseIni(text: string): Record<string, Record<string, string>> {\n const out: Record<string, Record<string, string>> = {};\n let section: Record<string, string> | undefined;\n for (const raw of text.split(/\\r?\\n/)) {\n const line = raw.trim();\n if (!line || line.startsWith('#') || line.startsWith(';')) continue;\n const header = /^\\[(.+)\\]$/.exec(line);\n if (header) {\n section = out[header[1]!.trim()] = out[header[1]!.trim()] ?? {};\n continue;\n }\n const eq = line.indexOf('=');\n if (eq > 0 && section) section[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();\n }\n return out;\n}\n\nfunction fromEnv(): AwsCredentials | undefined {\n const accessKeyId = process.env['AWS_ACCESS_KEY_ID'];\n const secretAccessKey = process.env['AWS_SECRET_ACCESS_KEY'];\n if (!accessKeyId || !secretAccessKey) return undefined;\n return { accessKeyId, secretAccessKey, sessionToken: process.env['AWS_SESSION_TOKEN'] };\n}\n\nfunction fromSharedFile(): AwsCredentials | undefined {\n const file =\n process.env['AWS_SHARED_CREDENTIALS_FILE'] ?? path.join(os.homedir(), '.aws', 'credentials');\n const profile = process.env['AWS_PROFILE'] ?? 'default';\n let text: string;\n try {\n text = fs.readFileSync(file, 'utf8');\n } catch {\n return undefined;\n }\n const section = parseIni(text)[profile];\n const accessKeyId = section?.['aws_access_key_id'];\n const secretAccessKey = section?.['aws_secret_access_key'];\n if (!accessKeyId || !secretAccessKey) return undefined;\n return { accessKeyId, secretAccessKey, sessionToken: section?.['aws_session_token'] };\n}\n\ninterface CredentialDocument {\n AccessKeyId?: string;\n SecretAccessKey?: string;\n Token?: string;\n Expiration?: string;\n}\n\nfunction fromDocument(raw: string): AwsCredentials | undefined {\n const doc = JSON.parse(raw) as CredentialDocument;\n if (!doc.AccessKeyId || !doc.SecretAccessKey) return undefined;\n const expiresAt = doc.Expiration ? Date.parse(doc.Expiration) : Number.NaN;\n return {\n accessKeyId: doc.AccessKeyId,\n secretAccessKey: doc.SecretAccessKey,\n sessionToken: doc.Token,\n expiresAt: Number.isNaN(expiresAt) ? undefined : expiresAt,\n };\n}\n\nconst CONTAINER_HOSTS = new Set([\n // eslint-disable-next-line sonarjs/no-hardcoded-ip -- fixed ECS credentials endpoint\n '169.254.170.2',\n // eslint-disable-next-line sonarjs/no-hardcoded-ip -- fixed EKS pod identity endpoint\n '169.254.170.23',\n '[fd00:ec2::23]',\n 'localhost',\n '[::1]',\n]);\n\n/** Same rule as the AWS SDKs: never send the container token to an arbitrary http host. */\nfunction isAllowedContainerHost(url: URL): boolean {\n if (url.protocol === 'https:') return true;\n if (url.protocol !== 'http:') return false;\n return CONTAINER_HOSTS.has(url.hostname) || /^127(?:\\.\\d{1,3}){3}$/.test(url.hostname);\n}\n\nasync function fromContainer(): Promise<AwsCredentials | undefined> {\n const relative = process.env['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'];\n const full = process.env['AWS_CONTAINER_CREDENTIALS_FULL_URI'];\n if (!relative && !full) return undefined;\n const url = new URL(relative ? `http://169.254.170.2${relative}` : (full as string));\n if (!relative && !isAllowedContainerHost(url)) {\n internalWarn(\n `ignoring AWS_CONTAINER_CREDENTIALS_FULL_URI: ${url.host} is not https or a loopback / ECS / EKS address`\n );\n return undefined;\n }\n const headers: Record<string, string> = {};\n const tokenFile = process.env['AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE'];\n const token = tokenFile\n ? fs.readFileSync(tokenFile, 'utf8').trim()\n : process.env['AWS_CONTAINER_AUTHORIZATION_TOKEN'];\n if (token) headers['Authorization'] = token;\n const res = await httpRequest({\n url,\n method: 'GET',\n headers,\n timeoutMs: METADATA_TIMEOUT_MS,\n label: 'AWS container credentials',\n });\n return fromDocument(res.body);\n}\n\nasync function fromInstanceMetadata(): Promise<AwsCredentials | undefined> {\n if (process.env['AWS_EC2_METADATA_DISABLED'] === 'true') return undefined;\n const base = process.env['AWS_EC2_METADATA_SERVICE_ENDPOINT'] ?? 'http://169.254.169.254';\n const get = (p: string, method: string, headers: Record<string, string>) =>\n httpRequest({\n url: new URL(p, base),\n method,\n headers,\n timeoutMs: METADATA_TIMEOUT_MS,\n label: 'EC2 instance metadata',\n });\n const session = await get('/latest/api/token', 'PUT', {\n 'X-aws-ec2-metadata-token-ttl-seconds': '21600',\n });\n const auth = { 'X-aws-ec2-metadata-token': session.body.trim() };\n const roles = await get('/latest/meta-data/iam/security-credentials/', 'GET', auth);\n const role = roles.body.split('\\n')[0]?.trim();\n if (!role) return undefined;\n const creds = await get(\n `/latest/meta-data/iam/security-credentials/${encodeURIComponent(role)}`,\n 'GET',\n auth\n );\n return fromDocument(creds.body);\n}\n\nexport class AwsCredentialProvider {\n private cached: AwsCredentials | undefined;\n private failedAt = 0;\n private readonly explicit: AwsCredentials | undefined;\n\n constructor(config: AwsCredentialConfig) {\n protectSecrets(this, ['cached', 'explicit']);\n if (config.accessKeyId && config.secretAccessKey) {\n this.explicit = {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n sessionToken: config.sessionToken,\n };\n }\n }\n\n /** Throws when no source yields credentials, so the caller keeps its buffer. */\n async get(): Promise<AwsCredentials> {\n if (this.explicit) return this.explicit;\n const env = fromEnv();\n if (env) return env;\n if (this.cached && !this.isExpiring(this.cached)) return this.cached;\n if (Date.now() - this.failedAt < FAILURE_BACKOFF_MS) {\n throw new Error('no AWS credentials found (retrying shortly)');\n }\n\n let found = fromSharedFile();\n for (const source of [fromContainer, fromInstanceMetadata]) {\n if (found) break;\n try {\n found = await source();\n } catch {\n /* source not available here; try the next */\n }\n }\n if (!found) {\n this.failedAt = Date.now();\n throw new Error(\n 'no AWS credentials found: set AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, a shared credentials file, container credentials, or run on EC2 with an instance role'\n );\n }\n this.cached = found;\n return found;\n }\n\n /** Secret values this provider may hold, for scrubbing error text. */\n secretValues(): (string | undefined)[] {\n const env = process.env;\n return [\n this.explicit?.secretAccessKey,\n this.explicit?.sessionToken,\n this.cached?.secretAccessKey,\n this.cached?.sessionToken,\n env['AWS_SECRET_ACCESS_KEY'],\n env['AWS_SESSION_TOKEN'],\n ];\n }\n\n invalidate(): void {\n this.cached = undefined;\n this.failedAt = 0;\n }\n\n private isExpiring(c: AwsCredentials): boolean {\n return c.expiresAt !== undefined && c.expiresAt - Date.now() < REFRESH_MARGIN_MS;\n }\n}\n","/**\n * AWS Signature Version 4 request signing, dependency-free.\n */\n\nimport * as crypto from 'node:crypto';\n\nexport interface AwsCredentials {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string | undefined;\n /** Epoch ms after which the credentials are no longer valid. */\n expiresAt?: number | undefined;\n}\n\nexport interface SignV4Options {\n method: string;\n path: string;\n query?: string;\n /** Must include `host`. Names are lowercased before signing. */\n headers: Record<string, string>;\n body: string;\n region: string;\n service: string;\n credentials: AwsCredentials;\n date?: Date;\n}\n\nconst sha256Hex = (data: string): string => crypto.createHash('sha256').update(data).digest('hex');\n\nconst hmac = (key: Buffer | string, msg: string): Buffer =>\n crypto.createHmac('sha256', key).update(msg).digest();\n\n/** Returns the request headers (lowercase names) with `x-amz-date` and `authorization` added. */\nexport function signV4(opts: SignV4Options): Record<string, string> {\n const {\n method,\n path,\n query = '',\n body,\n region,\n service,\n credentials,\n headers: input,\n date,\n } = opts;\n const amzDate = (date ?? new Date()).toISOString().replace(/[:-]|\\.\\d{3}/g, '');\n const dateStamp = amzDate.slice(0, 8);\n\n const headers: Record<string, string> = {};\n for (const [k, v] of Object.entries(input)) headers[k.toLowerCase()] = v;\n headers['x-amz-date'] = amzDate;\n if (credentials.sessionToken) headers['x-amz-security-token'] = credentials.sessionToken;\n\n const names = Object.keys(headers).sort();\n const canonicalHeaders = names\n .map((n) => `${n}:${headers[n]!.trim().replace(/\\s+/g, ' ')}\\n`)\n .join('');\n const signedHeaders = names.join(';');\n\n const canonicalRequest = [\n method,\n path,\n query,\n canonicalHeaders,\n signedHeaders,\n sha256Hex(body),\n ].join('\\n');\n const scope = `${dateStamp}/${region}/${service}/aws4_request`;\n const stringToSign = ['AWS4-HMAC-SHA256', amzDate, scope, sha256Hex(canonicalRequest)].join('\\n');\n\n const signingKey = hmac(\n hmac(hmac(hmac(`AWS4${credentials.secretAccessKey}`, dateStamp), region), service),\n 'aws4_request'\n );\n const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');\n\n headers['authorization'] =\n `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${scope}, ` +\n `SignedHeaders=${signedHeaders}, Signature=${signature}`;\n return headers;\n}\n","/**\n * logixia — AWS CloudWatch Logs transport\n *\n * Sends log entries to AWS CloudWatch Logs via the PutLogEvents API.\n * Supports EMF (Embedded Metric Format) for automatic metric extraction.\n *\n * Auth, in order: explicit config, environment variables, the shared\n * credentials file (AWS_PROFILE, AWS_SHARED_CREDENTIALS_FILE), ECS container\n * credentials, then EC2 instance metadata (IMDSv2). SSO, assume-role profiles\n * and credential_process are not supported. No SDK dependency — raw HTTPS with\n * AWS Signature Version 4.\n *\n * The log group and stream are created on first use if they do not exist.\n *\n * @example\n * ```ts\n * import { createLogger } from 'logixia';\n * import { CloudWatchTransport } from 'logixia/cloudwatch';\n *\n * const logger = createLogger({\n * transports: {\n * custom: [\n * new CloudWatchTransport({\n * region: 'us-east-1',\n * logGroupName: '/myapp/production',\n * logStreamName: `api-${process.env.HOSTNAME}`,\n * batchSize: 100,\n * flushIntervalMs: 5000,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { AwsCredentialProvider } from './aws-credentials';\nimport { signV4 } from './aws-sigv4';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n HttpError,\n httpRequest,\n isPermanentHttpError,\n} from './batch-buffer';\nimport { mergeCoreFields } from './entry-fields';\nimport { protectSecrets, urlSecrets } from './secret-config';\n\nexport interface CloudWatchTransportConfig {\n /** AWS region, e.g. `'us-east-1'`. Falls back to `AWS_REGION` env var. */\n region?: string;\n /** CloudWatch Logs group name. Created automatically if it does not exist. */\n logGroupName: string;\n /** CloudWatch Logs stream name. Defaults to hostname + process PID. */\n logStreamName?: string;\n /** Override the Logs endpoint, e.g. for LocalStack. Default: `https://logs.<region>.amazonaws.com`. */\n endpoint?: string;\n /** AWS access key ID. Falls back to the credential chain described above. */\n accessKeyId?: string;\n /** AWS secret access key. Used together with `accessKeyId`. */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials. Used together with `accessKeyId`. */\n sessionToken?: string;\n /**\n * Number of log events to batch before flushing.\n * CloudWatch allows max 10,000 events per PutLogEvents call; larger values are capped.\n * @default 100\n */\n batchSize?: number;\n /**\n * Max ms between flushes even if batchSize is not reached.\n * @default 5000\n */\n flushIntervalMs?: number;\n /**\n * Enable EMF (Embedded Metric Format) — wraps log entries as CloudWatch\n * metric namespaces so numeric fields become CloudWatch Metrics automatically.\n * @default false\n */\n emf?: boolean;\n /** CloudWatch Metrics namespace used by EMF. @default 'Logixia' */\n emfNamespace?: string;\n /**\n * Payload fields published as EMF metrics. When set, only these numeric\n * fields become metrics; when unset every numeric payload field does (up to\n * the EMF limit of 100 per entry).\n */\n emfMetrics?: string[];\n /** Abort a request that has not finished after this many ms. @default 10000 */\n requestTimeoutMs?: number;\n /** Max buffered events; the oldest are dropped past this. @default 10000 */\n maxBufferSize?: number;\n /** Minimum log level to forward. */\n level?: string;\n}\n\nconst MAX_EVENTS_PER_PUT = 10_000;\nconst EMF_MAX_METRICS = 100;\n\n/** Error types worth retrying even though CloudWatch reports them as HTTP 400. */\nconst RETRYABLE_TYPES = new Set([\n 'ThrottlingException',\n 'ThrottledException',\n 'LimitExceededException',\n 'ServiceUnavailableException',\n 'ExpiredTokenException',\n 'ResourceNotFoundException',\n]);\n\nfunction awsErrorType(err: unknown): string | undefined {\n if (!(err instanceof HttpError)) return undefined;\n try {\n const type = (JSON.parse(err.body) as { __type?: string }).__type;\n return type?.split('#').pop();\n } catch {\n return undefined;\n }\n}\n\ninterface CwLogEvent {\n timestamp: number;\n message: string;\n}\n\n/**\n * AWS CloudWatch Logs transport with EMF support and batched PutLogEvents calls.\n */\nexport class CloudWatchTransport implements IAsyncTransport {\n public readonly name = 'cloudwatch';\n public readonly batchSize: number;\n public readonly flushInterval: number;\n public readonly level: string | undefined;\n\n private readonly buffer: BatchBuffer<CwLogEvent>;\n private readonly requestTimeoutMs: number;\n private flushTimer: NodeJS.Timeout | null = null;\n private readonly credentials: AwsCredentialProvider;\n private readonly endpoint: URL;\n private streamReady = false;\n\n private readonly region: string;\n private readonly logGroupName: string;\n private readonly logStreamName: string;\n private readonly emf: boolean;\n private readonly emfNamespace: string;\n private readonly emfMetrics: ReadonlySet<string> | undefined;\n\n constructor(config: CloudWatchTransportConfig) {\n this.region =\n config.region ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n 'us-east-1';\n this.logGroupName = config.logGroupName;\n this.logStreamName =\n config.logStreamName ?? `${process.env['HOSTNAME'] ?? 'node'}-${process.pid}`;\n this.credentials = new AwsCredentialProvider(config);\n this.endpoint = new URL(config.endpoint ?? `https://logs.${this.region}.amazonaws.com`);\n this.batchSize = Math.min(config.batchSize ?? 100, MAX_EVENTS_PER_PUT);\n this.emfNamespace = config.emfNamespace ?? 'Logixia';\n this.flushInterval = config.flushIntervalMs ?? 5000;\n this.emf = config.emf ?? false;\n this.emfMetrics = config.emfMetrics ? new Set(config.emfMetrics) : undefined;\n this.level = config.level;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.buffer = new BatchBuffer<CwLogEvent>({\n name: 'CloudWatchTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (events) => this.putLogEvents(events),\n secrets: () => [...this.credentials.secretValues(), ...urlSecrets(this.endpoint)],\n shouldRetry: (err) =>\n !isPermanentHttpError(err) || RETRYABLE_TYPES.has(awsErrorType(err) ?? ''),\n });\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushInterval);\n if (this.flushTimer.unref) this.flushTimer.unref();\n protectSecrets(this, ['credentials', 'endpoint']);\n }\n\n write(entry: TransportLogEntry): void {\n const timestamp = entry.timestamp ? new Date(entry.timestamp).getTime() : Date.now();\n const message = this.emf ? this.toEmf(entry) : stringifyForLog(entry);\n this.buffer.push({ timestamp, message });\n this.buffer.flushIfFull();\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n /**\n * Flush remaining events and stop the interval timer on shutdown. Without this\n * the manager's close() skips the transport (close was undefined) and any\n * buffered events were lost on deploy. Waits for any in-flight send first.\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 private toEmf(entry: TransportLogEntry): string {\n const { level, message, data: payload, timestamp } = entry;\n const fields = mergeCoreFields({ _aws: undefined, level, message }, payload);\n const metricNames: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (k === '_aws' || k === 'level' || k === 'message' || typeof v !== 'number') continue;\n if (this.emfMetrics && !this.emfMetrics.has(k)) continue;\n if (metricNames.length < EMF_MAX_METRICS) metricNames.push(k);\n }\n fields['_aws'] = {\n Timestamp: timestamp ? new Date(timestamp).getTime() : Date.now(),\n CloudWatchMetrics:\n metricNames.length > 0\n ? [\n {\n Namespace: this.emfNamespace,\n Dimensions: [['level']],\n Metrics: metricNames.map((n) => ({ Name: n, Unit: 'None' })),\n },\n ]\n : [],\n };\n return stringifyForLog(fields);\n }\n\n private async putLogEvents(events: CwLogEvent[]): Promise<void> {\n await this.ensureStream();\n const logEvents = [...events].sort((a, b) => a.timestamp - b.timestamp);\n try {\n await this.call('PutLogEvents', {\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n logEvents,\n });\n } catch (err) {\n const type = awsErrorType(err);\n if (type === 'DataAlreadyAcceptedException') return;\n if (type === 'ResourceNotFoundException') this.streamReady = false;\n if (type === 'ExpiredTokenException') this.credentials.invalidate();\n throw err;\n }\n }\n\n private async ensureStream(): Promise<void> {\n if (this.streamReady) return;\n try {\n await this.call('CreateLogGroup', { logGroupName: this.logGroupName });\n } catch (err) {\n if (awsErrorType(err) === 'ExpiredTokenException') this.credentials.invalidate();\n if (!(err instanceof HttpError)) throw err;\n if (awsErrorType(err) !== 'ResourceAlreadyExistsException') {\n internalWarn(`CloudWatchTransport: CreateLogGroup failed, continuing (${err.message})`);\n }\n }\n try {\n await this.call('CreateLogStream', {\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n });\n } catch (err) {\n if (awsErrorType(err) !== 'ResourceAlreadyExistsException') throw err;\n }\n this.streamReady = true;\n }\n\n private async call(action: string, payload: Record<string, unknown>): Promise<void> {\n const credentials = await this.credentials.get();\n const body = stringifyForLog(payload);\n const { host } = this.endpoint;\n const headers = signV4({\n method: 'POST',\n path: '/',\n headers: {\n host,\n 'content-type': 'application/x-amz-json-1.1',\n 'x-amz-target': `Logs_20140328.${action}`,\n },\n body,\n region: this.region,\n service: 'logs',\n credentials,\n });\n await httpRequest({\n url: this.endpoint,\n headers,\n body,\n timeoutMs: this.requestTimeoutMs,\n label: `CloudWatch ${action}`,\n secrets: () => [\n ...this.credentials.secretValues(),\n headers['authorization'],\n headers['x-amz-security-token'],\n ...urlSecrets(this.endpoint),\n ],\n });\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { _ as TransportLogEntry, l as IAsyncTransport } from "./transport.types-B1ejJJP6.mjs";
1
+ import { _ as TransportLogEntry, l as IAsyncTransport } from "./transport.types-MjMDjOQ7.mjs";
2
2
  interface CloudWatchTransportConfig {
3
3
  /** AWS region, e.g. `'us-east-1'`. Falls back to `AWS_REGION` env var. */
4
4
  region?: string;
@@ -81,4 +81,4 @@ declare class CloudWatchTransport implements IAsyncTransport {
81
81
  private call;
82
82
  }
83
83
  export { CloudWatchTransportConfig as n, CloudWatchTransport as t };
84
- //# sourceMappingURL=cloudwatch.transport-_hS7Mcc5.d.mts.map
84
+ //# sourceMappingURL=cloudwatch.transport-CqXAGHPH.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudwatch.transport-_hS7Mcc5.d.mts","names":[],"sources":["../src/transports/cloudwatch.transport.ts"],"mappings":";UAmDiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;EAMA;;;;;EAKA;;;;;;EAMA;;EAEA;;;;;;EAMA;;EAEA;;EAEA;;EAEA;;;;;cAkCW,+BAA+B;WAC1B;WACA;WACA;WACA;mBAEC;mBACA;UACT;mBACS;mBACA;UACT;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;EAEL,YAAA,QAAQ;EAmCpB,MAAM,OAAO;EAOb,SAAS;;;;;;EASH,SAAS;UAQP;UAyBM;UAkBA;UAsBA"}
1
+ {"version":3,"file":"cloudwatch.transport-CqXAGHPH.d.mts","names":[],"sources":["../src/transports/cloudwatch.transport.ts"],"mappings":";UAmDiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;EAMA;;;;;EAKA;;;;;;EAMA;;EAEA;;;;;;EAMA;;EAEA;;EAEA;;EAEA;;;;;cAkCW,+BAA+B;WAC1B;WACA;WACA;WACA;mBAEC;mBACA;UACT;mBACS;mBACA;UACT;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;EAEL,YAAA,QAAQ;EAmCpB,MAAM,OAAO;EAOb,SAAS;;;;;;EASH,SAAS;UAQP;UAyBM;UAkBA;UAsBA"}
@@ -1,8 +1,8 @@
1
- const require_secret_config = require("./secret-config--cVTOWwp.js");
1
+ const require_secret_config = require("./secret-config-OqmIPmpV.js");
2
2
  const require_internal_log = require("./internal-log-DwqTjhpb.js");
3
3
  const require_safe_stringify = require("./safe-stringify-J3sh-7YM.js");
4
4
  const require_entry_fields = require("./entry-fields-BSLkzw18.js");
5
- const require_batch_buffer = require("./batch-buffer-BLmVvJAZ.js");
5
+ const require_batch_buffer = require("./batch-buffer-C-ETEX3Y.js");
6
6
  let node_crypto = require("node:crypto");
7
7
  node_crypto = require_secret_config.__toESM(node_crypto);
8
8
  let node_os = require("node:os");
@@ -414,4 +414,4 @@ Object.defineProperty(exports, "CloudWatchTransport", {
414
414
  }
415
415
  });
416
416
 
417
- //# sourceMappingURL=cloudwatch.transport-Lst1Bbmr.js.map
417
+ //# sourceMappingURL=cloudwatch.transport-DO9e__Z-.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudwatch.transport-Lst1Bbmr.js","names":["path","os","fs","httpRequest","crypto","HttpError","BatchBuffer","urlSecrets","isPermanentHttpError","stringifyForLog","mergeCoreFields","httpRequest"],"sources":["../src/transports/aws-credentials.ts","../src/transports/aws-sigv4.ts","../src/transports/cloudwatch.transport.ts"],"sourcesContent":["/**\n * AWS credential resolution without the SDK.\n *\n * Order: explicit config, environment variables, shared credentials file,\n * ECS/container credentials, EC2 instance metadata (IMDSv2).\n * Not supported: SSO, assume-role profiles, credential_process, web identity.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { internalWarn } from '../utils/internal-log';\nimport { type AwsCredentials } from './aws-sigv4';\nimport { httpRequest } from './batch-buffer';\nimport { protectSecrets } from './secret-config';\n\nconst REFRESH_MARGIN_MS = 5 * 60 * 1000;\nconst FAILURE_BACKOFF_MS = 30 * 1000;\nconst METADATA_TIMEOUT_MS = 2000;\n\nexport interface AwsCredentialConfig {\n accessKeyId?: string | undefined;\n secretAccessKey?: string | undefined;\n sessionToken?: string | undefined;\n}\n\n/** Parse the sections of an AWS ini file into `{ section: { key: value } }`. */\nexport function parseIni(text: string): Record<string, Record<string, string>> {\n const out: Record<string, Record<string, string>> = {};\n let section: Record<string, string> | undefined;\n for (const raw of text.split(/\\r?\\n/)) {\n const line = raw.trim();\n if (!line || line.startsWith('#') || line.startsWith(';')) continue;\n const header = /^\\[(.+)\\]$/.exec(line);\n if (header) {\n section = out[header[1]!.trim()] = out[header[1]!.trim()] ?? {};\n continue;\n }\n const eq = line.indexOf('=');\n if (eq > 0 && section) section[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();\n }\n return out;\n}\n\nfunction fromEnv(): AwsCredentials | undefined {\n const accessKeyId = process.env['AWS_ACCESS_KEY_ID'];\n const secretAccessKey = process.env['AWS_SECRET_ACCESS_KEY'];\n if (!accessKeyId || !secretAccessKey) return undefined;\n return { accessKeyId, secretAccessKey, sessionToken: process.env['AWS_SESSION_TOKEN'] };\n}\n\nfunction fromSharedFile(): AwsCredentials | undefined {\n const file =\n process.env['AWS_SHARED_CREDENTIALS_FILE'] ?? path.join(os.homedir(), '.aws', 'credentials');\n const profile = process.env['AWS_PROFILE'] ?? 'default';\n let text: string;\n try {\n text = fs.readFileSync(file, 'utf8');\n } catch {\n return undefined;\n }\n const section = parseIni(text)[profile];\n const accessKeyId = section?.['aws_access_key_id'];\n const secretAccessKey = section?.['aws_secret_access_key'];\n if (!accessKeyId || !secretAccessKey) return undefined;\n return { accessKeyId, secretAccessKey, sessionToken: section?.['aws_session_token'] };\n}\n\ninterface CredentialDocument {\n AccessKeyId?: string;\n SecretAccessKey?: string;\n Token?: string;\n Expiration?: string;\n}\n\nfunction fromDocument(raw: string): AwsCredentials | undefined {\n const doc = JSON.parse(raw) as CredentialDocument;\n if (!doc.AccessKeyId || !doc.SecretAccessKey) return undefined;\n const expiresAt = doc.Expiration ? Date.parse(doc.Expiration) : Number.NaN;\n return {\n accessKeyId: doc.AccessKeyId,\n secretAccessKey: doc.SecretAccessKey,\n sessionToken: doc.Token,\n expiresAt: Number.isNaN(expiresAt) ? undefined : expiresAt,\n };\n}\n\nconst CONTAINER_HOSTS = new Set([\n // eslint-disable-next-line sonarjs/no-hardcoded-ip -- fixed ECS credentials endpoint\n '169.254.170.2',\n // eslint-disable-next-line sonarjs/no-hardcoded-ip -- fixed EKS pod identity endpoint\n '169.254.170.23',\n '[fd00:ec2::23]',\n 'localhost',\n '[::1]',\n]);\n\n/** Same rule as the AWS SDKs: never send the container token to an arbitrary http host. */\nfunction isAllowedContainerHost(url: URL): boolean {\n if (url.protocol === 'https:') return true;\n if (url.protocol !== 'http:') return false;\n return CONTAINER_HOSTS.has(url.hostname) || /^127(?:\\.\\d{1,3}){3}$/.test(url.hostname);\n}\n\nasync function fromContainer(): Promise<AwsCredentials | undefined> {\n const relative = process.env['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'];\n const full = process.env['AWS_CONTAINER_CREDENTIALS_FULL_URI'];\n if (!relative && !full) return undefined;\n const url = new URL(relative ? `http://169.254.170.2${relative}` : (full as string));\n if (!relative && !isAllowedContainerHost(url)) {\n internalWarn(\n `ignoring AWS_CONTAINER_CREDENTIALS_FULL_URI: ${url.host} is not https or a loopback / ECS / EKS address`\n );\n return undefined;\n }\n const headers: Record<string, string> = {};\n const tokenFile = process.env['AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE'];\n const token = tokenFile\n ? fs.readFileSync(tokenFile, 'utf8').trim()\n : process.env['AWS_CONTAINER_AUTHORIZATION_TOKEN'];\n if (token) headers['Authorization'] = token;\n const res = await httpRequest({\n url,\n method: 'GET',\n headers,\n timeoutMs: METADATA_TIMEOUT_MS,\n label: 'AWS container credentials',\n });\n return fromDocument(res.body);\n}\n\nasync function fromInstanceMetadata(): Promise<AwsCredentials | undefined> {\n if (process.env['AWS_EC2_METADATA_DISABLED'] === 'true') return undefined;\n const base = process.env['AWS_EC2_METADATA_SERVICE_ENDPOINT'] ?? 'http://169.254.169.254';\n const get = (p: string, method: string, headers: Record<string, string>) =>\n httpRequest({\n url: new URL(p, base),\n method,\n headers,\n timeoutMs: METADATA_TIMEOUT_MS,\n label: 'EC2 instance metadata',\n });\n const session = await get('/latest/api/token', 'PUT', {\n 'X-aws-ec2-metadata-token-ttl-seconds': '21600',\n });\n const auth = { 'X-aws-ec2-metadata-token': session.body.trim() };\n const roles = await get('/latest/meta-data/iam/security-credentials/', 'GET', auth);\n const role = roles.body.split('\\n')[0]?.trim();\n if (!role) return undefined;\n const creds = await get(\n `/latest/meta-data/iam/security-credentials/${encodeURIComponent(role)}`,\n 'GET',\n auth\n );\n return fromDocument(creds.body);\n}\n\nexport class AwsCredentialProvider {\n private cached: AwsCredentials | undefined;\n private failedAt = 0;\n private readonly explicit: AwsCredentials | undefined;\n\n constructor(config: AwsCredentialConfig) {\n protectSecrets(this, ['cached', 'explicit']);\n if (config.accessKeyId && config.secretAccessKey) {\n this.explicit = {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n sessionToken: config.sessionToken,\n };\n }\n }\n\n /** Throws when no source yields credentials, so the caller keeps its buffer. */\n async get(): Promise<AwsCredentials> {\n if (this.explicit) return this.explicit;\n const env = fromEnv();\n if (env) return env;\n if (this.cached && !this.isExpiring(this.cached)) return this.cached;\n if (Date.now() - this.failedAt < FAILURE_BACKOFF_MS) {\n throw new Error('no AWS credentials found (retrying shortly)');\n }\n\n let found = fromSharedFile();\n for (const source of [fromContainer, fromInstanceMetadata]) {\n if (found) break;\n try {\n found = await source();\n } catch {\n /* source not available here; try the next */\n }\n }\n if (!found) {\n this.failedAt = Date.now();\n throw new Error(\n 'no AWS credentials found: set AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, a shared credentials file, container credentials, or run on EC2 with an instance role'\n );\n }\n this.cached = found;\n return found;\n }\n\n /** Secret values this provider may hold, for scrubbing error text. */\n secretValues(): (string | undefined)[] {\n const env = process.env;\n return [\n this.explicit?.secretAccessKey,\n this.explicit?.sessionToken,\n this.cached?.secretAccessKey,\n this.cached?.sessionToken,\n env['AWS_SECRET_ACCESS_KEY'],\n env['AWS_SESSION_TOKEN'],\n ];\n }\n\n invalidate(): void {\n this.cached = undefined;\n this.failedAt = 0;\n }\n\n private isExpiring(c: AwsCredentials): boolean {\n return c.expiresAt !== undefined && c.expiresAt - Date.now() < REFRESH_MARGIN_MS;\n }\n}\n","/**\n * AWS Signature Version 4 request signing, dependency-free.\n */\n\nimport * as crypto from 'node:crypto';\n\nexport interface AwsCredentials {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string | undefined;\n /** Epoch ms after which the credentials are no longer valid. */\n expiresAt?: number | undefined;\n}\n\nexport interface SignV4Options {\n method: string;\n path: string;\n query?: string;\n /** Must include `host`. Names are lowercased before signing. */\n headers: Record<string, string>;\n body: string;\n region: string;\n service: string;\n credentials: AwsCredentials;\n date?: Date;\n}\n\nconst sha256Hex = (data: string): string => crypto.createHash('sha256').update(data).digest('hex');\n\nconst hmac = (key: Buffer | string, msg: string): Buffer =>\n crypto.createHmac('sha256', key).update(msg).digest();\n\n/** Returns the request headers (lowercase names) with `x-amz-date` and `authorization` added. */\nexport function signV4(opts: SignV4Options): Record<string, string> {\n const {\n method,\n path,\n query = '',\n body,\n region,\n service,\n credentials,\n headers: input,\n date,\n } = opts;\n const amzDate = (date ?? new Date()).toISOString().replace(/[:-]|\\.\\d{3}/g, '');\n const dateStamp = amzDate.slice(0, 8);\n\n const headers: Record<string, string> = {};\n for (const [k, v] of Object.entries(input)) headers[k.toLowerCase()] = v;\n headers['x-amz-date'] = amzDate;\n if (credentials.sessionToken) headers['x-amz-security-token'] = credentials.sessionToken;\n\n const names = Object.keys(headers).sort();\n const canonicalHeaders = names\n .map((n) => `${n}:${headers[n]!.trim().replace(/\\s+/g, ' ')}\\n`)\n .join('');\n const signedHeaders = names.join(';');\n\n const canonicalRequest = [\n method,\n path,\n query,\n canonicalHeaders,\n signedHeaders,\n sha256Hex(body),\n ].join('\\n');\n const scope = `${dateStamp}/${region}/${service}/aws4_request`;\n const stringToSign = ['AWS4-HMAC-SHA256', amzDate, scope, sha256Hex(canonicalRequest)].join('\\n');\n\n const signingKey = hmac(\n hmac(hmac(hmac(`AWS4${credentials.secretAccessKey}`, dateStamp), region), service),\n 'aws4_request'\n );\n const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');\n\n headers['authorization'] =\n `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${scope}, ` +\n `SignedHeaders=${signedHeaders}, Signature=${signature}`;\n return headers;\n}\n","/**\n * logixia — AWS CloudWatch Logs transport\n *\n * Sends log entries to AWS CloudWatch Logs via the PutLogEvents API.\n * Supports EMF (Embedded Metric Format) for automatic metric extraction.\n *\n * Auth, in order: explicit config, environment variables, the shared\n * credentials file (AWS_PROFILE, AWS_SHARED_CREDENTIALS_FILE), ECS container\n * credentials, then EC2 instance metadata (IMDSv2). SSO, assume-role profiles\n * and credential_process are not supported. No SDK dependency — raw HTTPS with\n * AWS Signature Version 4.\n *\n * The log group and stream are created on first use if they do not exist.\n *\n * @example\n * ```ts\n * import { createLogger } from 'logixia';\n * import { CloudWatchTransport } from 'logixia/cloudwatch';\n *\n * const logger = createLogger({\n * transports: {\n * custom: [\n * new CloudWatchTransport({\n * region: 'us-east-1',\n * logGroupName: '/myapp/production',\n * logStreamName: `api-${process.env.HOSTNAME}`,\n * batchSize: 100,\n * flushIntervalMs: 5000,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { AwsCredentialProvider } from './aws-credentials';\nimport { signV4 } from './aws-sigv4';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n HttpError,\n httpRequest,\n isPermanentHttpError,\n} from './batch-buffer';\nimport { mergeCoreFields } from './entry-fields';\nimport { protectSecrets, urlSecrets } from './secret-config';\n\nexport interface CloudWatchTransportConfig {\n /** AWS region, e.g. `'us-east-1'`. Falls back to `AWS_REGION` env var. */\n region?: string;\n /** CloudWatch Logs group name. Created automatically if it does not exist. */\n logGroupName: string;\n /** CloudWatch Logs stream name. Defaults to hostname + process PID. */\n logStreamName?: string;\n /** Override the Logs endpoint, e.g. for LocalStack. Default: `https://logs.<region>.amazonaws.com`. */\n endpoint?: string;\n /** AWS access key ID. Falls back to the credential chain described above. */\n accessKeyId?: string;\n /** AWS secret access key. Used together with `accessKeyId`. */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials. Used together with `accessKeyId`. */\n sessionToken?: string;\n /**\n * Number of log events to batch before flushing.\n * CloudWatch allows max 10,000 events per PutLogEvents call; larger values are capped.\n * @default 100\n */\n batchSize?: number;\n /**\n * Max ms between flushes even if batchSize is not reached.\n * @default 5000\n */\n flushIntervalMs?: number;\n /**\n * Enable EMF (Embedded Metric Format) — wraps log entries as CloudWatch\n * metric namespaces so numeric fields become CloudWatch Metrics automatically.\n * @default false\n */\n emf?: boolean;\n /** CloudWatch Metrics namespace used by EMF. @default 'Logixia' */\n emfNamespace?: string;\n /**\n * Payload fields published as EMF metrics. When set, only these numeric\n * fields become metrics; when unset every numeric payload field does (up to\n * the EMF limit of 100 per entry).\n */\n emfMetrics?: string[];\n /** Abort a request that has not finished after this many ms. @default 10000 */\n requestTimeoutMs?: number;\n /** Max buffered events; the oldest are dropped past this. @default 10000 */\n maxBufferSize?: number;\n /** Minimum log level to forward. */\n level?: string;\n}\n\nconst MAX_EVENTS_PER_PUT = 10_000;\nconst EMF_MAX_METRICS = 100;\n\n/** Error types worth retrying even though CloudWatch reports them as HTTP 400. */\nconst RETRYABLE_TYPES = new Set([\n 'ThrottlingException',\n 'ThrottledException',\n 'LimitExceededException',\n 'ServiceUnavailableException',\n 'ExpiredTokenException',\n 'ResourceNotFoundException',\n]);\n\nfunction awsErrorType(err: unknown): string | undefined {\n if (!(err instanceof HttpError)) return undefined;\n try {\n const type = (JSON.parse(err.body) as { __type?: string }).__type;\n return type?.split('#').pop();\n } catch {\n return undefined;\n }\n}\n\ninterface CwLogEvent {\n timestamp: number;\n message: string;\n}\n\n/**\n * AWS CloudWatch Logs transport with EMF support and batched PutLogEvents calls.\n */\nexport class CloudWatchTransport implements IAsyncTransport {\n public readonly name = 'cloudwatch';\n public readonly batchSize: number;\n public readonly flushInterval: number;\n public readonly level: string | undefined;\n\n private readonly buffer: BatchBuffer<CwLogEvent>;\n private readonly requestTimeoutMs: number;\n private flushTimer: NodeJS.Timeout | null = null;\n private readonly credentials: AwsCredentialProvider;\n private readonly endpoint: URL;\n private streamReady = false;\n\n private readonly region: string;\n private readonly logGroupName: string;\n private readonly logStreamName: string;\n private readonly emf: boolean;\n private readonly emfNamespace: string;\n private readonly emfMetrics: ReadonlySet<string> | undefined;\n\n constructor(config: CloudWatchTransportConfig) {\n this.region =\n config.region ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n 'us-east-1';\n this.logGroupName = config.logGroupName;\n this.logStreamName =\n config.logStreamName ?? `${process.env['HOSTNAME'] ?? 'node'}-${process.pid}`;\n this.credentials = new AwsCredentialProvider(config);\n this.endpoint = new URL(config.endpoint ?? `https://logs.${this.region}.amazonaws.com`);\n this.batchSize = Math.min(config.batchSize ?? 100, MAX_EVENTS_PER_PUT);\n this.emfNamespace = config.emfNamespace ?? 'Logixia';\n this.flushInterval = config.flushIntervalMs ?? 5000;\n this.emf = config.emf ?? false;\n this.emfMetrics = config.emfMetrics ? new Set(config.emfMetrics) : undefined;\n this.level = config.level;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.buffer = new BatchBuffer<CwLogEvent>({\n name: 'CloudWatchTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (events) => this.putLogEvents(events),\n secrets: () => [...this.credentials.secretValues(), ...urlSecrets(this.endpoint)],\n shouldRetry: (err) =>\n !isPermanentHttpError(err) || RETRYABLE_TYPES.has(awsErrorType(err) ?? ''),\n });\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushInterval);\n if (this.flushTimer.unref) this.flushTimer.unref();\n protectSecrets(this, ['credentials', 'endpoint']);\n }\n\n write(entry: TransportLogEntry): void {\n const timestamp = entry.timestamp ? new Date(entry.timestamp).getTime() : Date.now();\n const message = this.emf ? this.toEmf(entry) : stringifyForLog(entry);\n this.buffer.push({ timestamp, message });\n this.buffer.flushIfFull();\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n /**\n * Flush remaining events and stop the interval timer on shutdown. Without this\n * the manager's close() skips the transport (close was undefined) and any\n * buffered events were lost on deploy. Waits for any in-flight send first.\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 private toEmf(entry: TransportLogEntry): string {\n const { level, message, data: payload, timestamp } = entry;\n const fields = mergeCoreFields({ _aws: undefined, level, message }, payload);\n const metricNames: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (k === '_aws' || k === 'level' || k === 'message' || typeof v !== 'number') continue;\n if (this.emfMetrics && !this.emfMetrics.has(k)) continue;\n if (metricNames.length < EMF_MAX_METRICS) metricNames.push(k);\n }\n fields['_aws'] = {\n Timestamp: timestamp ? new Date(timestamp).getTime() : Date.now(),\n CloudWatchMetrics:\n metricNames.length > 0\n ? [\n {\n Namespace: this.emfNamespace,\n Dimensions: [['level']],\n Metrics: metricNames.map((n) => ({ Name: n, Unit: 'None' })),\n },\n ]\n : [],\n };\n return stringifyForLog(fields);\n }\n\n private async putLogEvents(events: CwLogEvent[]): Promise<void> {\n await this.ensureStream();\n const logEvents = [...events].sort((a, b) => a.timestamp - b.timestamp);\n try {\n await this.call('PutLogEvents', {\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n logEvents,\n });\n } catch (err) {\n const type = awsErrorType(err);\n if (type === 'DataAlreadyAcceptedException') return;\n if (type === 'ResourceNotFoundException') this.streamReady = false;\n if (type === 'ExpiredTokenException') this.credentials.invalidate();\n throw err;\n }\n }\n\n private async ensureStream(): Promise<void> {\n if (this.streamReady) return;\n try {\n await this.call('CreateLogGroup', { logGroupName: this.logGroupName });\n } catch (err) {\n if (awsErrorType(err) === 'ExpiredTokenException') this.credentials.invalidate();\n if (!(err instanceof HttpError)) throw err;\n if (awsErrorType(err) !== 'ResourceAlreadyExistsException') {\n internalWarn(`CloudWatchTransport: CreateLogGroup failed, continuing (${err.message})`);\n }\n }\n try {\n await this.call('CreateLogStream', {\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n });\n } catch (err) {\n if (awsErrorType(err) !== 'ResourceAlreadyExistsException') throw err;\n }\n this.streamReady = true;\n }\n\n private async call(action: string, payload: Record<string, unknown>): Promise<void> {\n const credentials = await this.credentials.get();\n const body = stringifyForLog(payload);\n const { host } = this.endpoint;\n const headers = signV4({\n method: 'POST',\n path: '/',\n headers: {\n host,\n 'content-type': 'application/x-amz-json-1.1',\n 'x-amz-target': `Logs_20140328.${action}`,\n },\n body,\n region: this.region,\n service: 'logs',\n credentials,\n });\n await httpRequest({\n url: this.endpoint,\n headers,\n body,\n timeoutMs: this.requestTimeoutMs,\n label: `CloudWatch ${action}`,\n secrets: () => [\n ...this.credentials.secretValues(),\n headers['authorization'],\n headers['x-amz-security-token'],\n ...urlSecrets(this.endpoint),\n ],\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cloudwatch.transport-DO9e__Z-.js","names":["path","os","fs","httpRequest","crypto","HttpError","BatchBuffer","urlSecrets","isPermanentHttpError","stringifyForLog","mergeCoreFields","httpRequest"],"sources":["../src/transports/aws-credentials.ts","../src/transports/aws-sigv4.ts","../src/transports/cloudwatch.transport.ts"],"sourcesContent":["/**\n * AWS credential resolution without the SDK.\n *\n * Order: explicit config, environment variables, shared credentials file,\n * ECS/container credentials, EC2 instance metadata (IMDSv2).\n * Not supported: SSO, assume-role profiles, credential_process, web identity.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { internalWarn } from '../utils/internal-log';\nimport { type AwsCredentials } from './aws-sigv4';\nimport { httpRequest } from './batch-buffer';\nimport { protectSecrets } from './secret-config';\n\nconst REFRESH_MARGIN_MS = 5 * 60 * 1000;\nconst FAILURE_BACKOFF_MS = 30 * 1000;\nconst METADATA_TIMEOUT_MS = 2000;\n\nexport interface AwsCredentialConfig {\n accessKeyId?: string | undefined;\n secretAccessKey?: string | undefined;\n sessionToken?: string | undefined;\n}\n\n/** Parse the sections of an AWS ini file into `{ section: { key: value } }`. */\nexport function parseIni(text: string): Record<string, Record<string, string>> {\n const out: Record<string, Record<string, string>> = {};\n let section: Record<string, string> | undefined;\n for (const raw of text.split(/\\r?\\n/)) {\n const line = raw.trim();\n if (!line || line.startsWith('#') || line.startsWith(';')) continue;\n const header = /^\\[(.+)\\]$/.exec(line);\n if (header) {\n section = out[header[1]!.trim()] = out[header[1]!.trim()] ?? {};\n continue;\n }\n const eq = line.indexOf('=');\n if (eq > 0 && section) section[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();\n }\n return out;\n}\n\nfunction fromEnv(): AwsCredentials | undefined {\n const accessKeyId = process.env['AWS_ACCESS_KEY_ID'];\n const secretAccessKey = process.env['AWS_SECRET_ACCESS_KEY'];\n if (!accessKeyId || !secretAccessKey) return undefined;\n return { accessKeyId, secretAccessKey, sessionToken: process.env['AWS_SESSION_TOKEN'] };\n}\n\nfunction fromSharedFile(): AwsCredentials | undefined {\n const file =\n process.env['AWS_SHARED_CREDENTIALS_FILE'] ?? path.join(os.homedir(), '.aws', 'credentials');\n const profile = process.env['AWS_PROFILE'] ?? 'default';\n let text: string;\n try {\n text = fs.readFileSync(file, 'utf8');\n } catch {\n return undefined;\n }\n const section = parseIni(text)[profile];\n const accessKeyId = section?.['aws_access_key_id'];\n const secretAccessKey = section?.['aws_secret_access_key'];\n if (!accessKeyId || !secretAccessKey) return undefined;\n return { accessKeyId, secretAccessKey, sessionToken: section?.['aws_session_token'] };\n}\n\ninterface CredentialDocument {\n AccessKeyId?: string;\n SecretAccessKey?: string;\n Token?: string;\n Expiration?: string;\n}\n\nfunction fromDocument(raw: string): AwsCredentials | undefined {\n const doc = JSON.parse(raw) as CredentialDocument;\n if (!doc.AccessKeyId || !doc.SecretAccessKey) return undefined;\n const expiresAt = doc.Expiration ? Date.parse(doc.Expiration) : Number.NaN;\n return {\n accessKeyId: doc.AccessKeyId,\n secretAccessKey: doc.SecretAccessKey,\n sessionToken: doc.Token,\n expiresAt: Number.isNaN(expiresAt) ? undefined : expiresAt,\n };\n}\n\nconst CONTAINER_HOSTS = new Set([\n // eslint-disable-next-line sonarjs/no-hardcoded-ip -- fixed ECS credentials endpoint\n '169.254.170.2',\n // eslint-disable-next-line sonarjs/no-hardcoded-ip -- fixed EKS pod identity endpoint\n '169.254.170.23',\n '[fd00:ec2::23]',\n 'localhost',\n '[::1]',\n]);\n\n/** Same rule as the AWS SDKs: never send the container token to an arbitrary http host. */\nfunction isAllowedContainerHost(url: URL): boolean {\n if (url.protocol === 'https:') return true;\n if (url.protocol !== 'http:') return false;\n return CONTAINER_HOSTS.has(url.hostname) || /^127(?:\\.\\d{1,3}){3}$/.test(url.hostname);\n}\n\nasync function fromContainer(): Promise<AwsCredentials | undefined> {\n const relative = process.env['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'];\n const full = process.env['AWS_CONTAINER_CREDENTIALS_FULL_URI'];\n if (!relative && !full) return undefined;\n const url = new URL(relative ? `http://169.254.170.2${relative}` : (full as string));\n if (!relative && !isAllowedContainerHost(url)) {\n internalWarn(\n `ignoring AWS_CONTAINER_CREDENTIALS_FULL_URI: ${url.host} is not https or a loopback / ECS / EKS address`\n );\n return undefined;\n }\n const headers: Record<string, string> = {};\n const tokenFile = process.env['AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE'];\n const token = tokenFile\n ? fs.readFileSync(tokenFile, 'utf8').trim()\n : process.env['AWS_CONTAINER_AUTHORIZATION_TOKEN'];\n if (token) headers['Authorization'] = token;\n const res = await httpRequest({\n url,\n method: 'GET',\n headers,\n timeoutMs: METADATA_TIMEOUT_MS,\n label: 'AWS container credentials',\n });\n return fromDocument(res.body);\n}\n\nasync function fromInstanceMetadata(): Promise<AwsCredentials | undefined> {\n if (process.env['AWS_EC2_METADATA_DISABLED'] === 'true') return undefined;\n const base = process.env['AWS_EC2_METADATA_SERVICE_ENDPOINT'] ?? 'http://169.254.169.254';\n const get = (p: string, method: string, headers: Record<string, string>) =>\n httpRequest({\n url: new URL(p, base),\n method,\n headers,\n timeoutMs: METADATA_TIMEOUT_MS,\n label: 'EC2 instance metadata',\n });\n const session = await get('/latest/api/token', 'PUT', {\n 'X-aws-ec2-metadata-token-ttl-seconds': '21600',\n });\n const auth = { 'X-aws-ec2-metadata-token': session.body.trim() };\n const roles = await get('/latest/meta-data/iam/security-credentials/', 'GET', auth);\n const role = roles.body.split('\\n')[0]?.trim();\n if (!role) return undefined;\n const creds = await get(\n `/latest/meta-data/iam/security-credentials/${encodeURIComponent(role)}`,\n 'GET',\n auth\n );\n return fromDocument(creds.body);\n}\n\nexport class AwsCredentialProvider {\n private cached: AwsCredentials | undefined;\n private failedAt = 0;\n private readonly explicit: AwsCredentials | undefined;\n\n constructor(config: AwsCredentialConfig) {\n protectSecrets(this, ['cached', 'explicit']);\n if (config.accessKeyId && config.secretAccessKey) {\n this.explicit = {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n sessionToken: config.sessionToken,\n };\n }\n }\n\n /** Throws when no source yields credentials, so the caller keeps its buffer. */\n async get(): Promise<AwsCredentials> {\n if (this.explicit) return this.explicit;\n const env = fromEnv();\n if (env) return env;\n if (this.cached && !this.isExpiring(this.cached)) return this.cached;\n if (Date.now() - this.failedAt < FAILURE_BACKOFF_MS) {\n throw new Error('no AWS credentials found (retrying shortly)');\n }\n\n let found = fromSharedFile();\n for (const source of [fromContainer, fromInstanceMetadata]) {\n if (found) break;\n try {\n found = await source();\n } catch {\n /* source not available here; try the next */\n }\n }\n if (!found) {\n this.failedAt = Date.now();\n throw new Error(\n 'no AWS credentials found: set AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, a shared credentials file, container credentials, or run on EC2 with an instance role'\n );\n }\n this.cached = found;\n return found;\n }\n\n /** Secret values this provider may hold, for scrubbing error text. */\n secretValues(): (string | undefined)[] {\n const env = process.env;\n return [\n this.explicit?.secretAccessKey,\n this.explicit?.sessionToken,\n this.cached?.secretAccessKey,\n this.cached?.sessionToken,\n env['AWS_SECRET_ACCESS_KEY'],\n env['AWS_SESSION_TOKEN'],\n ];\n }\n\n invalidate(): void {\n this.cached = undefined;\n this.failedAt = 0;\n }\n\n private isExpiring(c: AwsCredentials): boolean {\n return c.expiresAt !== undefined && c.expiresAt - Date.now() < REFRESH_MARGIN_MS;\n }\n}\n","/**\n * AWS Signature Version 4 request signing, dependency-free.\n */\n\nimport * as crypto from 'node:crypto';\n\nexport interface AwsCredentials {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string | undefined;\n /** Epoch ms after which the credentials are no longer valid. */\n expiresAt?: number | undefined;\n}\n\nexport interface SignV4Options {\n method: string;\n path: string;\n query?: string;\n /** Must include `host`. Names are lowercased before signing. */\n headers: Record<string, string>;\n body: string;\n region: string;\n service: string;\n credentials: AwsCredentials;\n date?: Date;\n}\n\nconst sha256Hex = (data: string): string => crypto.createHash('sha256').update(data).digest('hex');\n\nconst hmac = (key: Buffer | string, msg: string): Buffer =>\n crypto.createHmac('sha256', key).update(msg).digest();\n\n/** Returns the request headers (lowercase names) with `x-amz-date` and `authorization` added. */\nexport function signV4(opts: SignV4Options): Record<string, string> {\n const {\n method,\n path,\n query = '',\n body,\n region,\n service,\n credentials,\n headers: input,\n date,\n } = opts;\n const amzDate = (date ?? new Date()).toISOString().replace(/[:-]|\\.\\d{3}/g, '');\n const dateStamp = amzDate.slice(0, 8);\n\n const headers: Record<string, string> = {};\n for (const [k, v] of Object.entries(input)) headers[k.toLowerCase()] = v;\n headers['x-amz-date'] = amzDate;\n if (credentials.sessionToken) headers['x-amz-security-token'] = credentials.sessionToken;\n\n const names = Object.keys(headers).sort();\n const canonicalHeaders = names\n .map((n) => `${n}:${headers[n]!.trim().replace(/\\s+/g, ' ')}\\n`)\n .join('');\n const signedHeaders = names.join(';');\n\n const canonicalRequest = [\n method,\n path,\n query,\n canonicalHeaders,\n signedHeaders,\n sha256Hex(body),\n ].join('\\n');\n const scope = `${dateStamp}/${region}/${service}/aws4_request`;\n const stringToSign = ['AWS4-HMAC-SHA256', amzDate, scope, sha256Hex(canonicalRequest)].join('\\n');\n\n const signingKey = hmac(\n hmac(hmac(hmac(`AWS4${credentials.secretAccessKey}`, dateStamp), region), service),\n 'aws4_request'\n );\n const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');\n\n headers['authorization'] =\n `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${scope}, ` +\n `SignedHeaders=${signedHeaders}, Signature=${signature}`;\n return headers;\n}\n","/**\n * logixia — AWS CloudWatch Logs transport\n *\n * Sends log entries to AWS CloudWatch Logs via the PutLogEvents API.\n * Supports EMF (Embedded Metric Format) for automatic metric extraction.\n *\n * Auth, in order: explicit config, environment variables, the shared\n * credentials file (AWS_PROFILE, AWS_SHARED_CREDENTIALS_FILE), ECS container\n * credentials, then EC2 instance metadata (IMDSv2). SSO, assume-role profiles\n * and credential_process are not supported. No SDK dependency — raw HTTPS with\n * AWS Signature Version 4.\n *\n * The log group and stream are created on first use if they do not exist.\n *\n * @example\n * ```ts\n * import { createLogger } from 'logixia';\n * import { CloudWatchTransport } from 'logixia/cloudwatch';\n *\n * const logger = createLogger({\n * transports: {\n * custom: [\n * new CloudWatchTransport({\n * region: 'us-east-1',\n * logGroupName: '/myapp/production',\n * logStreamName: `api-${process.env.HOSTNAME}`,\n * batchSize: 100,\n * flushIntervalMs: 5000,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { AwsCredentialProvider } from './aws-credentials';\nimport { signV4 } from './aws-sigv4';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n HttpError,\n httpRequest,\n isPermanentHttpError,\n} from './batch-buffer';\nimport { mergeCoreFields } from './entry-fields';\nimport { protectSecrets, urlSecrets } from './secret-config';\n\nexport interface CloudWatchTransportConfig {\n /** AWS region, e.g. `'us-east-1'`. Falls back to `AWS_REGION` env var. */\n region?: string;\n /** CloudWatch Logs group name. Created automatically if it does not exist. */\n logGroupName: string;\n /** CloudWatch Logs stream name. Defaults to hostname + process PID. */\n logStreamName?: string;\n /** Override the Logs endpoint, e.g. for LocalStack. Default: `https://logs.<region>.amazonaws.com`. */\n endpoint?: string;\n /** AWS access key ID. Falls back to the credential chain described above. */\n accessKeyId?: string;\n /** AWS secret access key. Used together with `accessKeyId`. */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials. Used together with `accessKeyId`. */\n sessionToken?: string;\n /**\n * Number of log events to batch before flushing.\n * CloudWatch allows max 10,000 events per PutLogEvents call; larger values are capped.\n * @default 100\n */\n batchSize?: number;\n /**\n * Max ms between flushes even if batchSize is not reached.\n * @default 5000\n */\n flushIntervalMs?: number;\n /**\n * Enable EMF (Embedded Metric Format) — wraps log entries as CloudWatch\n * metric namespaces so numeric fields become CloudWatch Metrics automatically.\n * @default false\n */\n emf?: boolean;\n /** CloudWatch Metrics namespace used by EMF. @default 'Logixia' */\n emfNamespace?: string;\n /**\n * Payload fields published as EMF metrics. When set, only these numeric\n * fields become metrics; when unset every numeric payload field does (up to\n * the EMF limit of 100 per entry).\n */\n emfMetrics?: string[];\n /** Abort a request that has not finished after this many ms. @default 10000 */\n requestTimeoutMs?: number;\n /** Max buffered events; the oldest are dropped past this. @default 10000 */\n maxBufferSize?: number;\n /** Minimum log level to forward. */\n level?: string;\n}\n\nconst MAX_EVENTS_PER_PUT = 10_000;\nconst EMF_MAX_METRICS = 100;\n\n/** Error types worth retrying even though CloudWatch reports them as HTTP 400. */\nconst RETRYABLE_TYPES = new Set([\n 'ThrottlingException',\n 'ThrottledException',\n 'LimitExceededException',\n 'ServiceUnavailableException',\n 'ExpiredTokenException',\n 'ResourceNotFoundException',\n]);\n\nfunction awsErrorType(err: unknown): string | undefined {\n if (!(err instanceof HttpError)) return undefined;\n try {\n const type = (JSON.parse(err.body) as { __type?: string }).__type;\n return type?.split('#').pop();\n } catch {\n return undefined;\n }\n}\n\ninterface CwLogEvent {\n timestamp: number;\n message: string;\n}\n\n/**\n * AWS CloudWatch Logs transport with EMF support and batched PutLogEvents calls.\n */\nexport class CloudWatchTransport implements IAsyncTransport {\n public readonly name = 'cloudwatch';\n public readonly batchSize: number;\n public readonly flushInterval: number;\n public readonly level: string | undefined;\n\n private readonly buffer: BatchBuffer<CwLogEvent>;\n private readonly requestTimeoutMs: number;\n private flushTimer: NodeJS.Timeout | null = null;\n private readonly credentials: AwsCredentialProvider;\n private readonly endpoint: URL;\n private streamReady = false;\n\n private readonly region: string;\n private readonly logGroupName: string;\n private readonly logStreamName: string;\n private readonly emf: boolean;\n private readonly emfNamespace: string;\n private readonly emfMetrics: ReadonlySet<string> | undefined;\n\n constructor(config: CloudWatchTransportConfig) {\n this.region =\n config.region ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n 'us-east-1';\n this.logGroupName = config.logGroupName;\n this.logStreamName =\n config.logStreamName ?? `${process.env['HOSTNAME'] ?? 'node'}-${process.pid}`;\n this.credentials = new AwsCredentialProvider(config);\n this.endpoint = new URL(config.endpoint ?? `https://logs.${this.region}.amazonaws.com`);\n this.batchSize = Math.min(config.batchSize ?? 100, MAX_EVENTS_PER_PUT);\n this.emfNamespace = config.emfNamespace ?? 'Logixia';\n this.flushInterval = config.flushIntervalMs ?? 5000;\n this.emf = config.emf ?? false;\n this.emfMetrics = config.emfMetrics ? new Set(config.emfMetrics) : undefined;\n this.level = config.level;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.buffer = new BatchBuffer<CwLogEvent>({\n name: 'CloudWatchTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (events) => this.putLogEvents(events),\n secrets: () => [...this.credentials.secretValues(), ...urlSecrets(this.endpoint)],\n shouldRetry: (err) =>\n !isPermanentHttpError(err) || RETRYABLE_TYPES.has(awsErrorType(err) ?? ''),\n });\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushInterval);\n if (this.flushTimer.unref) this.flushTimer.unref();\n protectSecrets(this, ['credentials', 'endpoint']);\n }\n\n write(entry: TransportLogEntry): void {\n const timestamp = entry.timestamp ? new Date(entry.timestamp).getTime() : Date.now();\n const message = this.emf ? this.toEmf(entry) : stringifyForLog(entry);\n this.buffer.push({ timestamp, message });\n this.buffer.flushIfFull();\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n /**\n * Flush remaining events and stop the interval timer on shutdown. Without this\n * the manager's close() skips the transport (close was undefined) and any\n * buffered events were lost on deploy. Waits for any in-flight send first.\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 private toEmf(entry: TransportLogEntry): string {\n const { level, message, data: payload, timestamp } = entry;\n const fields = mergeCoreFields({ _aws: undefined, level, message }, payload);\n const metricNames: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (k === '_aws' || k === 'level' || k === 'message' || typeof v !== 'number') continue;\n if (this.emfMetrics && !this.emfMetrics.has(k)) continue;\n if (metricNames.length < EMF_MAX_METRICS) metricNames.push(k);\n }\n fields['_aws'] = {\n Timestamp: timestamp ? new Date(timestamp).getTime() : Date.now(),\n CloudWatchMetrics:\n metricNames.length > 0\n ? [\n {\n Namespace: this.emfNamespace,\n Dimensions: [['level']],\n Metrics: metricNames.map((n) => ({ Name: n, Unit: 'None' })),\n },\n ]\n : [],\n };\n return stringifyForLog(fields);\n }\n\n private async putLogEvents(events: CwLogEvent[]): Promise<void> {\n await this.ensureStream();\n const logEvents = [...events].sort((a, b) => a.timestamp - b.timestamp);\n try {\n await this.call('PutLogEvents', {\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n logEvents,\n });\n } catch (err) {\n const type = awsErrorType(err);\n if (type === 'DataAlreadyAcceptedException') return;\n if (type === 'ResourceNotFoundException') this.streamReady = false;\n if (type === 'ExpiredTokenException') this.credentials.invalidate();\n throw err;\n }\n }\n\n private async ensureStream(): Promise<void> {\n if (this.streamReady) return;\n try {\n await this.call('CreateLogGroup', { logGroupName: this.logGroupName });\n } catch (err) {\n if (awsErrorType(err) === 'ExpiredTokenException') this.credentials.invalidate();\n if (!(err instanceof HttpError)) throw err;\n if (awsErrorType(err) !== 'ResourceAlreadyExistsException') {\n internalWarn(`CloudWatchTransport: CreateLogGroup failed, continuing (${err.message})`);\n }\n }\n try {\n await this.call('CreateLogStream', {\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n });\n } catch (err) {\n if (awsErrorType(err) !== 'ResourceAlreadyExistsException') throw err;\n }\n this.streamReady = true;\n }\n\n private async call(action: string, payload: Record<string, unknown>): Promise<void> {\n const credentials = await this.credentials.get();\n const body = stringifyForLog(payload);\n const { host } = this.endpoint;\n const headers = signV4({\n method: 'POST',\n path: '/',\n headers: {\n host,\n 'content-type': 'application/x-amz-json-1.1',\n 'x-amz-target': `Logs_20140328.${action}`,\n },\n body,\n region: this.region,\n service: 'logs',\n credentials,\n });\n await httpRequest({\n url: this.endpoint,\n headers,\n body,\n timeoutMs: this.requestTimeoutMs,\n label: `CloudWatch ${action}`,\n secrets: () => [\n ...this.credentials.secretValues(),\n headers['authorization'],\n headers['x-amz-security-token'],\n ...urlSecrets(this.endpoint),\n ],\n });\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { _ as TransportLogEntry, l as IAsyncTransport } from "./transport.types-B1ejJJP6.js";
1
+ import { _ as TransportLogEntry, l as IAsyncTransport } from "./transport.types-MjMDjOQ7.js";
2
2
  interface CloudWatchTransportConfig {
3
3
  /** AWS region, e.g. `'us-east-1'`. Falls back to `AWS_REGION` env var. */
4
4
  region?: string;
@@ -81,4 +81,4 @@ declare class CloudWatchTransport implements IAsyncTransport {
81
81
  private call;
82
82
  }
83
83
  export { CloudWatchTransportConfig as n, CloudWatchTransport as t };
84
- //# sourceMappingURL=cloudwatch.transport-DdSjUWvu.d.ts.map
84
+ //# sourceMappingURL=cloudwatch.transport-pY96iAfO.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudwatch.transport-DdSjUWvu.d.ts","names":[],"sources":["../src/transports/cloudwatch.transport.ts"],"mappings":";UAmDiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;EAMA;;;;;EAKA;;;;;;EAMA;;EAEA;;;;;;EAMA;;EAEA;;EAEA;;EAEA;;;;;cAkCW,+BAA+B;WAC1B;WACA;WACA;WACA;mBAEC;mBACA;UACT;mBACS;mBACA;UACT;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;EAEL,YAAA,QAAQ;EAmCpB,MAAM,OAAO;EAOb,SAAS;;;;;;EASH,SAAS;UAQP;UAyBM;UAkBA;UAsBA"}
1
+ {"version":3,"file":"cloudwatch.transport-pY96iAfO.d.ts","names":[],"sources":["../src/transports/cloudwatch.transport.ts"],"mappings":";UAmDiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;EAMA;;;;;EAKA;;;;;;EAMA;;EAEA;;;;;;EAMA;;EAEA;;EAEA;;EAEA;;;;;cAkCW,+BAA+B;WAC1B;WACA;WACA;WACA;mBAEC;mBACA;UACT;mBACS;mBACA;UACT;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;EAEL,YAAA,QAAQ;EAmCpB,MAAM,OAAO;EAOb,SAAS;;;;;;EASH,SAAS;UAQP;UAyBM;UAkBA;UAsBA"}
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_request_trace = require("./request-trace-C7kXMjx5.js");
3
- const require_async_context = require("./async-context-Dgl3ZLyb.js");
2
+ const require_request_trace = require("./request-trace-BqU7ARjg.js");
3
+ const require_async_context = require("./async-context-BqjwNzgS.js");
4
4
  const require_sensitive_names = require("./sensitive-names-NiKIRhp5.js");
5
5
  let node_crypto = require("node:crypto");
6
6
  // src/correlation.ts
@@ -1,5 +1,5 @@
1
- import { p as sanitizeIncomingId } from "./request-trace-Dekcnenb.mjs";
2
- import { t as LogixiaContext } from "./async-context-fBqC5lNc.mjs";
1
+ import { m as sanitizeIncomingId } from "./request-trace-B-S5-LVk.mjs";
2
+ import { t as LogixiaContext } from "./async-context-DJdxbbCU.mjs";
3
3
  import { a as buildNameSet, n as DEFAULT_REDACT_QUERY_PARAMS, o as redactUrlQuery } from "./sensitive-names-BiBM6R1X.mjs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  // src/correlation.ts
@@ -0,0 +1,189 @@
1
+ import { t as assignOwn } from "./own-property-CjFw9qwZ.mjs";
2
+ // src/utils/error.utils.ts
3
+ const EXTRA_FIELDS = [
4
+ "code",
5
+ "statusCode",
6
+ "status",
7
+ "errno",
8
+ "syscall",
9
+ "path",
10
+ "address",
11
+ "port",
12
+ "type"
13
+ ];
14
+ const SKIPPED_FIELDS = /* @__PURE__ */ new Set([
15
+ "name",
16
+ "message",
17
+ "stack",
18
+ "cause",
19
+ "errors",
20
+ ...EXTRA_FIELDS
21
+ ]);
22
+ const NO_OPTIONS = Object.freeze({});
23
+ const NO_FIELDS = [];
24
+ /**
25
+ * Serialize an Error into a plain JSON-safe object, including:
26
+ * - `name`, `message`, `stack`
27
+ * - `cause` (recursively serialized, full ES2022 chain)
28
+ * - `errors` (for AggregateError)
29
+ * - standard Node.js error fields (`code`, `statusCode`, `errno`, …)
30
+ * - any other enumerable own properties
31
+ */
32
+ function serializeError(error, options = NO_OPTIONS) {
33
+ const { includeStack = true, maxDepth = 5, excludeFields = NO_FIELDS } = options;
34
+ return _serializeError(error, includeStack, maxDepth, excludeFields, 0, /* @__PURE__ */ new WeakSet());
35
+ }
36
+ function _serializeError(error, includeStack, maxDepth, excludeFields, depth, seen) {
37
+ if (depth >= maxDepth) return {
38
+ name: error.name,
39
+ message: error.message,
40
+ _truncated: true
41
+ };
42
+ if (seen.has(error)) return {
43
+ name: error.name,
44
+ message: error.message,
45
+ _circular: true
46
+ };
47
+ seen.add(error);
48
+ const serialized = {
49
+ name: error.name,
50
+ message: error.message
51
+ };
52
+ if (includeStack && error.stack) serialized.stack = error.stack;
53
+ const errorWithCause = error;
54
+ if (errorWithCause.cause !== void 0) {
55
+ if (errorWithCause.cause instanceof Error) serialized.cause = _serializeError(errorWithCause.cause, includeStack, maxDepth, excludeFields, depth + 1, seen);
56
+ else serialized.cause = serializeValue(errorWithCause.cause, maxDepth - depth - 1, seen);
57
+ }
58
+ const aggregateError = error;
59
+ if (Array.isArray(aggregateError.errors)) serialized.errors = aggregateError.errors.map((e) => e instanceof Error ? _serializeError(e, includeStack, maxDepth, excludeFields, depth + 1, seen) : serializeValue(e, maxDepth - depth - 1, seen));
60
+ const errorRecord = error;
61
+ for (const field of EXTRA_FIELDS) if (!excludeFields.includes(field) && errorRecord[field] !== void 0) serialized[field] = errorRecord[field];
62
+ const skip = excludeFields.length === 0 ? SKIPPED_FIELDS : /* @__PURE__ */ new Set([...SKIPPED_FIELDS, ...excludeFields]);
63
+ for (const key of Object.getOwnPropertyNames(error)) {
64
+ if (skip.has(key)) continue;
65
+ try {
66
+ assignOwn(serialized, key, serializeValue(errorRecord[key], maxDepth - depth - 1, seen));
67
+ } catch {
68
+ assignOwn(serialized, key, "[Unserializable]");
69
+ }
70
+ }
71
+ return serialized;
72
+ }
73
+ /**
74
+ * Recursively serialize an arbitrary value to a JSON-safe representation.
75
+ *
76
+ * The `seen` guard is threaded through (not recreated) so cross-referencing
77
+ * errors nested inside plain objects/arrays are caught by the same circular
78
+ * check that protects the top-level error tree, rather than only relying on the
79
+ * depth limit.
80
+ */
81
+ function serializeValue(value, remainingDepth, seen) {
82
+ if (remainingDepth <= 0) return "[Max Depth]";
83
+ if (value === null || value === void 0) return value;
84
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
85
+ if (value instanceof Date) return value.toISOString();
86
+ if (value instanceof Error) {
87
+ if (seen.has(value)) return {
88
+ name: value.name,
89
+ message: value.message,
90
+ _circular: true
91
+ };
92
+ return _serializeError(value, true, remainingDepth, [], 0, seen);
93
+ }
94
+ if (Array.isArray(value)) {
95
+ if (seen.has(value)) return "[Circular]";
96
+ seen.add(value);
97
+ return value.map((item) => serializeValue(item, remainingDepth - 1, seen));
98
+ }
99
+ if (typeof value === "object") {
100
+ if (seen.has(value)) return "[Circular]";
101
+ seen.add(value);
102
+ const out = {};
103
+ for (const [k, v] of Object.entries(value)) try {
104
+ assignOwn(out, k, serializeValue(v, remainingDepth - 1, seen));
105
+ } catch {
106
+ assignOwn(out, k, "[Unserializable]");
107
+ }
108
+ return out;
109
+ }
110
+ return String(value);
111
+ }
112
+ /**
113
+ * Type guard: returns true if the value looks like an Error object.
114
+ */
115
+ function isError(value) {
116
+ return value instanceof Error || Boolean(value) && typeof value === "object" && "name" in value && "message" in value;
117
+ }
118
+ const MAX_ERROR_SEARCH_DEPTH = 3;
119
+ const MAX_ERROR_SEARCH_NODES = 2e3;
120
+ /**
121
+ * Replaces real Error instances inside a log payload with their serialized form,
122
+ * because JSON output drops an Error's non-enumerable `message` and `stack`. Runs on
123
+ * every log call, so its work is capped and it copies nothing until it finds one.
124
+ * Duck-typed objects are data, and an Error with its own toJSON has already chosen
125
+ * what to show. The caller's objects are never mutated.
126
+ */
127
+ function serializeNestedErrors(data) {
128
+ const outerBudget = errorSearchBudget;
129
+ errorSearchBudget = MAX_ERROR_SEARCH_NODES;
130
+ try {
131
+ return walkForErrors(data, MAX_ERROR_SEARCH_DEPTH, void 0, void 0);
132
+ } finally {
133
+ errorSearchBudget = outerBudget;
134
+ }
135
+ }
136
+ let errorSearchBudget = 0;
137
+ const hasOwn = Object.prototype.hasOwnProperty;
138
+ function walkForErrors(value, depth, parent, grandparent) {
139
+ if (value instanceof Error) return typeof value.toJSON === "function" ? value : serializeError(value);
140
+ if (depth === 0 || --errorSearchBudget < 0 || value === parent || value === grandparent) return value;
141
+ let copy;
142
+ if (Array.isArray(value)) for (let i = 0; i < value.length; i++) {
143
+ const item = value[i];
144
+ if (item === null || typeof item !== "object") continue;
145
+ const next = walkForErrors(item, depth - 1, value, parent);
146
+ if (next !== item) {
147
+ copy ??= value.slice();
148
+ copy[i] = next;
149
+ }
150
+ }
151
+ else {
152
+ const proto = Object.getPrototypeOf(value);
153
+ if (proto !== Object.prototype && proto !== null) return value;
154
+ const record = value;
155
+ for (const key in record) {
156
+ if (!hasOwn.call(record, key)) continue;
157
+ let item;
158
+ try {
159
+ item = record[key];
160
+ } catch {
161
+ continue;
162
+ }
163
+ if (item === null || typeof item !== "object") continue;
164
+ const next = walkForErrors(item, depth - 1, record, parent);
165
+ if (next !== item) {
166
+ copy ??= { ...record };
167
+ assignOwn(copy, key, next);
168
+ }
169
+ }
170
+ }
171
+ return copy ?? value;
172
+ }
173
+ /**
174
+ * Coerce any thrown value into a proper Error instance.
175
+ */
176
+ function normalizeError(error) {
177
+ if (isError(error)) return error;
178
+ if (typeof error === "string") return new Error(error);
179
+ if (typeof error === "object" && error !== null) {
180
+ const e = error;
181
+ const err = new Error(typeof e["message"] === "string" ? e["message"] : "Unknown error");
182
+ Object.assign(err, error);
183
+ return err;
184
+ }
185
+ return new Error(String(error));
186
+ }
187
+ export { serializeNestedErrors as i, normalizeError as n, serializeError as r, isError as t };
188
+
189
+ //# sourceMappingURL=error.utils-B8_CwESF.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.utils-B8_CwESF.mjs","names":[],"sources":["../src/utils/error.utils.ts"],"sourcesContent":["/**\n * Error serialization utilities for Logixia\n *\n * v1.1 improvements:\n * - Full ES2022 `cause` chain serialization (recursive)\n * - AggregateError.errors array serialization\n * - Standard extra fields: code, statusCode, status, errno, syscall, path\n * - Circular-reference safe via a WeakSet seen guard\n */\n\nimport type { ErrorSerializationOptions } from '../types';\nimport { assignOwn } from './own-property';\n\n// Well-known extra fields to always capture when present on an error\nconst EXTRA_FIELDS = [\n 'code',\n 'statusCode',\n 'status',\n 'errno',\n 'syscall',\n 'path',\n 'address',\n 'port',\n 'type',\n] as const;\n\n// Fields the own-property pass skips, built once instead of per error.\nconst SKIPPED_FIELDS = new Set<string>([\n 'name',\n 'message',\n 'stack',\n 'cause',\n 'errors',\n ...EXTRA_FIELDS,\n]);\nconst NO_OPTIONS: ErrorSerializationOptions = Object.freeze({});\nconst NO_FIELDS: string[] = [];\n\n/**\n * Serialize an Error into a plain JSON-safe object, including:\n * - `name`, `message`, `stack`\n * - `cause` (recursively serialized, full ES2022 chain)\n * - `errors` (for AggregateError)\n * - standard Node.js error fields (`code`, `statusCode`, `errno`, …)\n * - any other enumerable own properties\n */\nexport function serializeError(\n error: Error,\n options: ErrorSerializationOptions = NO_OPTIONS\n): Record<string, unknown> {\n const { includeStack = true, maxDepth = 5, excludeFields = NO_FIELDS } = options;\n const seen = new WeakSet<object>();\n return _serializeError(error, includeStack, maxDepth, excludeFields, 0, seen);\n}\n\nfunction _serializeError(\n error: Error,\n includeStack: boolean,\n maxDepth: number,\n excludeFields: string[],\n depth: number,\n seen: WeakSet<object>\n): Record<string, unknown> {\n if (depth >= maxDepth) {\n return { name: error.name, message: error.message, _truncated: true };\n }\n\n if (seen.has(error)) {\n return { name: error.name, message: error.message, _circular: true };\n }\n seen.add(error);\n\n const serialized: Record<string, unknown> = {\n name: error.name,\n message: error.message,\n };\n\n if (includeStack && error.stack) {\n serialized.stack = error.stack;\n }\n\n // ── ES2022 cause chain ──────────────────────────────────────────────────────\n const errorWithCause = error as Error & { cause?: unknown };\n if (errorWithCause.cause !== undefined) {\n if (errorWithCause.cause instanceof Error) {\n serialized.cause = _serializeError(\n errorWithCause.cause,\n includeStack,\n maxDepth,\n excludeFields,\n depth + 1,\n seen\n );\n } else {\n serialized.cause = serializeValue(errorWithCause.cause, maxDepth - depth - 1, seen);\n }\n }\n\n // ── AggregateError.errors ───────────────────────────────────────────────────\n const aggregateError = error as Error & { errors?: unknown[] };\n if (Array.isArray(aggregateError.errors)) {\n serialized.errors = aggregateError.errors.map((e) =>\n e instanceof Error\n ? _serializeError(e, includeStack, maxDepth, excludeFields, depth + 1, seen)\n : serializeValue(e, maxDepth - depth - 1, seen)\n );\n }\n\n // ── Standard Node.js / HTTP error fields ───────────────────────────────────\n const errorRecord = error as unknown as Record<string, unknown>;\n for (const field of EXTRA_FIELDS) {\n if (!excludeFields.includes(field) && errorRecord[field] !== undefined) {\n serialized[field] = errorRecord[field];\n }\n }\n\n // ── All other own enumerable properties ────────────────────────────────────\n const skip =\n excludeFields.length === 0 ? SKIPPED_FIELDS : new Set([...SKIPPED_FIELDS, ...excludeFields]);\n\n for (const key of Object.getOwnPropertyNames(error)) {\n if (skip.has(key)) continue;\n try {\n assignOwn(serialized, key, serializeValue(errorRecord[key], maxDepth - depth - 1, seen));\n } catch {\n assignOwn(serialized, key, '[Unserializable]');\n }\n }\n\n return serialized;\n}\n\n/**\n * Recursively serialize an arbitrary value to a JSON-safe representation.\n *\n * The `seen` guard is threaded through (not recreated) so cross-referencing\n * errors nested inside plain objects/arrays are caught by the same circular\n * check that protects the top-level error tree, rather than only relying on the\n * depth limit.\n */\nfunction serializeValue(value: unknown, remainingDepth: number, seen: WeakSet<object>): unknown {\n if (remainingDepth <= 0) return '[Max Depth]';\n if (value === null || value === undefined) return value;\n\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n\n if (value instanceof Date) return value.toISOString();\n\n if (value instanceof Error) {\n if (seen.has(value)) {\n return { name: value.name, message: value.message, _circular: true };\n }\n return _serializeError(value, true, remainingDepth, [], 0, seen);\n }\n\n if (Array.isArray(value)) {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n return value.map((item) => serializeValue(item, remainingDepth - 1, seen));\n }\n\n if (typeof value === 'object') {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n try {\n assignOwn(out, k, serializeValue(v, remainingDepth - 1, seen));\n } catch {\n assignOwn(out, k, '[Unserializable]');\n }\n }\n return out;\n }\n\n return String(value);\n}\n\n/**\n * Type guard: returns true if the value looks like an Error object.\n */\nexport function isError(value: unknown): value is Error {\n return (\n value instanceof Error ||\n (Boolean(value) &&\n typeof value === 'object' &&\n 'name' in (value as object) &&\n 'message' in (value as object))\n );\n}\n\nconst MAX_ERROR_SEARCH_DEPTH = 3;\nconst MAX_ERROR_SEARCH_NODES = 2000;\n\n/**\n * Replaces real Error instances inside a log payload with their serialized form,\n * because JSON output drops an Error's non-enumerable `message` and `stack`. Runs on\n * every log call, so its work is capped and it copies nothing until it finds one.\n * Duck-typed objects are data, and an Error with its own toJSON has already chosen\n * what to show. The caller's objects are never mutated.\n */\nexport function serializeNestedErrors(data: Record<string, unknown>): Record<string, unknown> {\n // Saved and restored, because a getter read during the walk can log and re-enter.\n const outerBudget = errorSearchBudget;\n errorSearchBudget = MAX_ERROR_SEARCH_NODES;\n try {\n return walkForErrors(data, MAX_ERROR_SEARCH_DEPTH, undefined, undefined) as Record<\n string,\n unknown\n >;\n } finally {\n errorSearchBudget = outerBudget;\n }\n}\n\nlet errorSearchBudget = 0;\nconst hasOwn = Object.prototype.hasOwnProperty;\n\n// Allocates nothing until it finds an Error. With the depth cap, a node has at most\n// two ancestors that can still recurse, so they are passed as arguments for the cycle check.\nfunction walkForErrors(\n value: object,\n depth: number,\n parent?: object,\n grandparent?: object\n): unknown {\n if (value instanceof Error) {\n return typeof (value as { toJSON?: unknown }).toJSON === 'function'\n ? value\n : serializeError(value);\n }\n if (depth === 0 || --errorSearchBudget < 0 || value === parent || value === grandparent) {\n return value;\n }\n\n let copy: unknown;\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n const item: unknown = value[i];\n if (item === null || typeof item !== 'object') continue;\n const next = walkForErrors(item, depth - 1, value, parent);\n if (next !== item) {\n copy ??= value.slice();\n (copy as unknown[])[i] = next;\n }\n }\n } else {\n const proto: unknown = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n const record = value as Record<string, unknown>;\n // for...in avoids the key array that Object.keys allocates on every call.\n for (const key in record) {\n if (!hasOwn.call(record, key)) continue;\n let item: unknown;\n try {\n item = record[key];\n } catch {\n continue;\n }\n if (item === null || typeof item !== 'object') continue;\n const next = walkForErrors(item, depth - 1, record, parent);\n if (next !== item) {\n copy ??= { ...record };\n assignOwn(copy as Record<string, unknown>, key, next);\n }\n }\n }\n return copy ?? value;\n}\n\n/**\n * Coerce any thrown value into a proper Error instance.\n */\nexport function normalizeError(error: unknown): Error {\n if (isError(error)) return error;\n\n if (typeof error === 'string') return new Error(error);\n\n if (typeof error === 'object' && error !== null) {\n const e = error as Record<string, unknown>;\n const err = new Error(typeof e['message'] === 'string' ? e['message'] : 'Unknown error');\n Object.assign(err, error);\n return err;\n }\n\n return new Error(String(error));\n}\n"],"mappings":"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"}