@arkyn/server 3.0.8 → 3.0.10

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 (33) hide show
  1. package/dist/http/api/_logRequest.d.ts.map +1 -1
  2. package/dist/http/badResponses/_badResponse.d.ts +7 -0
  3. package/dist/http/badResponses/_badResponse.d.ts.map +1 -1
  4. package/dist/http/badResponses/unprocessableEntity.d.ts.map +1 -1
  5. package/dist/index.js +239 -165
  6. package/dist/index.js.map +1 -1
  7. package/dist/modules/http/api/_logRequest.js +34 -31
  8. package/dist/modules/http/api/_logRequest.js.map +1 -1
  9. package/dist/modules/http/badResponses/_badResponse.js +3 -1
  10. package/dist/modules/http/badResponses/_badResponse.js.map +1 -1
  11. package/dist/modules/http/badResponses/unprocessableEntity.js +15 -5
  12. package/dist/modules/http/badResponses/unprocessableEntity.js.map +1 -1
  13. package/dist/modules/services/apiService.js +35 -23
  14. package/dist/modules/services/apiService.js.map +1 -1
  15. package/dist/modules/services/logService.js +29 -11
  16. package/dist/modules/services/logService.js.map +1 -1
  17. package/dist/modules/utilities/decodeRequestBody.js +13 -8
  18. package/dist/modules/utilities/decodeRequestBody.js.map +1 -1
  19. package/dist/modules/utilities/sensitiveDataKeys.js +31 -0
  20. package/dist/modules/utilities/sensitiveDataKeys.js.map +1 -0
  21. package/dist/modules/validations/validateEmail.js +21 -16
  22. package/dist/modules/validations/validateEmail.js.map +1 -1
  23. package/dist/services/apiService.d.ts +12 -0
  24. package/dist/services/apiService.d.ts.map +1 -1
  25. package/dist/services/logService.d.ts +15 -2
  26. package/dist/services/logService.d.ts.map +1 -1
  27. package/dist/utilities/decodeRequestBody.d.ts +7 -2
  28. package/dist/utilities/decodeRequestBody.d.ts.map +1 -1
  29. package/dist/utilities/sensitiveDataKeys.d.ts +22 -0
  30. package/dist/utilities/sensitiveDataKeys.d.ts.map +1 -0
  31. package/dist/validations/validateEmail.d.ts +10 -1
  32. package/dist/validations/validateEmail.d.ts.map +1 -1
  33. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"validateEmail.js","names":[],"sources":["../../../src/validations/validateEmail.ts"],"sourcesContent":["import dns from \"node:dns\";\n\nfunction isValidBasicFormat(email: string): boolean {\n\tconst emailRegex =\n\t\t/^[a-zA-Z0-9.!$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n\n\treturn emailRegex.test(email);\n}\n\nfunction isValidLocalPart(localPart: string): boolean {\n\tif (localPart.length === 0 || localPart.length > 64) return false;\n\tif (localPart.startsWith(\".\") || localPart.endsWith(\".\")) return false;\n\tif (localPart.includes(\"..\")) return false;\n\n\tconst validLocalRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+$/;\n\tif (!validLocalRegex.test(localPart)) return false;\n\n\treturn true;\n}\n\nfunction isValidDomainLabel(label: string): boolean {\n\tif (label.length === 0 || label.length > 63) return false;\n\tif (label.startsWith(\"-\") || label.endsWith(\"-\")) return false;\n\tif (!/^[a-zA-Z0-9-]+$/.test(label)) return false;\n\treturn true;\n}\n\nfunction isValidDomainPart(domainPart: string): boolean {\n\tif (domainPart.length === 0 || domainPart.length > 253) return false;\n\n\tif (\n\t\tdomainPart.startsWith(\".\") ||\n\t\tdomainPart.endsWith(\".\") ||\n\t\tdomainPart.startsWith(\"-\") ||\n\t\tdomainPart.endsWith(\"-\")\n\t) {\n\t\treturn false;\n\t}\n\n\tconst labels = domainPart.split(\".\");\n\tif (labels.length < 2) return false;\n\n\tfor (const label of labels) if (!isValidDomainLabel(label)) return false;\n\n\tconst tld = labels[labels.length - 1];\n\tif (tld.length < 2 || !/^[a-zA-Z]+$/.test(tld)) return false;\n\n\treturn true;\n}\n\nfunction isValidAdvancedSyntax(email: string): boolean {\n\tconst parts = email.split(\"@\");\n\tif (parts.length !== 2) return false;\n\n\tconst [localPart, domainPart] = parts;\n\n\tif (!isValidLocalPart(localPart)) return false;\n\tif (!isValidDomainPart(domainPart)) return false;\n\treturn true;\n}\n\nfunction extractDomain(email: string): string | null {\n\tconst parts = email.split(\"@\");\n\treturn parts.length === 2 ? parts[1].toLowerCase() : null;\n}\n\nconst DNS_RECORD_TYPES = [\"MX\", \"A\", \"AAAA\"] as const;\n\nasync function tryResolveDnsRecord(\n\tdomain: string,\n\trecordType: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait dns?.promises?.resolve(domain, recordType);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function isValidDns(domain: string): Promise<boolean> {\n\tfor (const recordType of DNS_RECORD_TYPES) {\n\t\tconst hasRecord = await tryResolveDnsRecord(domain, recordType);\n\t\tif (hasRecord) return true;\n\t}\n\treturn false;\n}\n\n/**\n * Validates if an email address is valid in all possible ways, including DNS validation.\n *\n * This function performs comprehensive email validation by:\n * - Checking basic email format and syntax\n * - Validating advanced RFC 5322 compliance rules\n * - Verifying that the domain has valid MX or A records in DNS\n *\n * @param rawEmail - The email address to validate.\n * @returns `true` if the email passes format checks and its domain resolves in DNS, otherwise `false`.\n *\n * @example\n * ```typescript\n * await validateEmail(\"user@gmail.com\"); // true\n * await validateEmail(\"user@gmil.com\"); // false (invalid domain)\n * await validateEmail(\"invalid-email\"); // false (invalid format)\n * ```\n */\n\nasync function validateEmail(rawEmail: string): Promise<boolean> {\n\tif (!rawEmail || typeof rawEmail !== \"string\") return false;\n\tconst email = rawEmail.trim();\n\n\tif (!isValidBasicFormat(email)) return false;\n\tif (!isValidAdvancedSyntax(email)) return false;\n\n\tconst domain = extractDomain(email);\n\tif (!domain) return false;\n\n\treturn await isValidDns(domain);\n}\n\nexport { validateEmail };\n"],"mappings":";;AAEA,SAAS,EAAmB,GAAwB;CAInD,OAAO,sIAAW,KAAK,CAAK;AAC7B;AAEA,SAAS,EAAiB,GAA4B;CAQrD,OAFA,EALI,EAAU,WAAW,KAAK,EAAU,SAAS,MAC7C,EAAU,WAAW,GAAG,KAAK,EAAU,SAAS,GAAG,KACnD,EAAU,SAAS,IAAI,KAGvB,CAAC,qCAAgB,KAAK,CAAS;AAGpC;AAEA,SAAS,EAAmB,GAAwB;CAInD,OADA,EAFI,EAAM,WAAW,KAAK,EAAM,SAAS,MACrC,EAAM,WAAW,GAAG,KAAK,EAAM,SAAS,GAAG,KAC3C,CAAC,kBAAkB,KAAK,CAAK;AAElC;AAEA,SAAS,EAAkB,GAA6B;CAGvD,IAFI,EAAW,WAAW,KAAK,EAAW,SAAS,OAGlD,EAAW,WAAW,GAAG,KACzB,EAAW,SAAS,GAAG,KACvB,EAAW,WAAW,GAAG,KACzB,EAAW,SAAS,GAAG,GAEvB,OAAO;CAGR,IAAM,IAAS,EAAW,MAAM,GAAG;CACnC,IAAI,EAAO,SAAS,GAAG,OAAO;CAE9B,KAAK,IAAM,KAAS,GAAQ,IAAI,CAAC,EAAmB,CAAK,GAAG,OAAO;CAEnE,IAAM,IAAM,EAAO,EAAO,SAAS;CAGnC,OAFA,EAAI,EAAI,SAAS,KAAK,CAAC,cAAc,KAAK,CAAG;AAG9C;AAEA,SAAS,EAAsB,GAAwB;CACtD,IAAM,IAAQ,EAAM,MAAM,GAAG;CAC7B,IAAI,EAAM,WAAW,GAAG,OAAO;CAE/B,IAAM,CAAC,GAAW,KAAc;CAIhC,OADA,EADI,CAAC,EAAiB,CAAS,KAC3B,CAAC,EAAkB,CAAU;AAElC;AAEA,SAAS,EAAc,GAA8B;CACpD,IAAM,IAAQ,EAAM,MAAM,GAAG;CAC7B,OAAO,EAAM,WAAW,IAAI,EAAM,EAAE,CAAC,YAAY,IAAI;AACtD;AAEA,IAAM,IAAmB;CAAC;CAAM;CAAK;AAAM;AAE3C,eAAe,EACd,GACA,GACmB;CACnB,IAAI;EAEH,OADA,MAAM,GAAK,UAAU,QAAQ,GAAQ,CAAU,GACxC;CACR,QAAQ;EACP,OAAO;CACR;AACD;AAEA,eAAe,EAAW,GAAkC;CAC3D,KAAK,IAAM,KAAc,GAExB,IAAI,MADoB,EAAoB,GAAQ,CAAU,GAC/C,OAAO;CAEvB,OAAO;AACR;AAqBA,eAAe,EAAc,GAAoC;CAChE,IAAI,CAAC,KAAY,OAAO,KAAa,UAAU,OAAO;CACtD,IAAM,IAAQ,EAAS,KAAK;CAG5B,IADI,CAAC,EAAmB,CAAK,KACzB,CAAC,EAAsB,CAAK,GAAG,OAAO;CAE1C,IAAM,IAAS,EAAc,CAAK;CAGlC,OAFK,IAEE,MAAM,EAAW,CAAM,IAFV;AAGrB"}
1
+ {"version":3,"file":"validateEmail.js","names":[],"sources":["../../../src/validations/validateEmail.ts"],"sourcesContent":["import dns from \"node:dns\";\n\nfunction isValidBasicFormat(email: string): boolean {\n\tconst emailRegex =\n\t\t/^[a-zA-Z0-9.!$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n\n\treturn emailRegex.test(email);\n}\n\nfunction isValidLocalPart(localPart: string): boolean {\n\tif (localPart.length === 0 || localPart.length > 64) return false;\n\tif (localPart.startsWith(\".\") || localPart.endsWith(\".\")) return false;\n\tif (localPart.includes(\"..\")) return false;\n\n\tconst validLocalRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+$/;\n\tif (!validLocalRegex.test(localPart)) return false;\n\n\treturn true;\n}\n\nfunction isValidDomainLabel(label: string): boolean {\n\tif (label.length === 0 || label.length > 63) return false;\n\tif (label.startsWith(\"-\") || label.endsWith(\"-\")) return false;\n\tif (!/^[a-zA-Z0-9-]+$/.test(label)) return false;\n\treturn true;\n}\n\nfunction isValidDomainPart(domainPart: string): boolean {\n\tif (domainPart.length === 0 || domainPart.length > 253) return false;\n\n\tif (\n\t\tdomainPart.startsWith(\".\") ||\n\t\tdomainPart.endsWith(\".\") ||\n\t\tdomainPart.startsWith(\"-\") ||\n\t\tdomainPart.endsWith(\"-\")\n\t) {\n\t\treturn false;\n\t}\n\n\tconst labels = domainPart.split(\".\");\n\tif (labels.length < 2) return false;\n\n\tfor (const label of labels) if (!isValidDomainLabel(label)) return false;\n\n\tconst tld = labels[labels.length - 1];\n\tif (tld.length < 2 || !/^[a-zA-Z]+$/.test(tld)) return false;\n\n\treturn true;\n}\n\nfunction isValidAdvancedSyntax(email: string): boolean {\n\tconst parts = email.split(\"@\");\n\tif (parts.length !== 2) return false;\n\n\tconst [localPart, domainPart] = parts;\n\n\tif (!isValidLocalPart(localPart)) return false;\n\tif (!isValidDomainPart(domainPart)) return false;\n\treturn true;\n}\n\nfunction extractDomain(email: string): string | null {\n\tconst parts = email.split(\"@\");\n\treturn parts.length === 2 ? parts[1].toLowerCase() : null;\n}\n\nconst DNS_RECORD_TYPES = [\"MX\", \"A\", \"AAAA\"] as const;\n\n/** Default time to wait for a single DNS lookup before treating it as unresolved. */\nconst DEFAULT_DNS_TIMEOUT_MS = 5000;\n\n/**\n * Resolves a single DNS record type for `domain`, racing it against `timeoutMs` so a slow\n * or unresponsive DNS server can never leave the caller waiting indefinitely. A timeout is\n * treated the same as \"no record found\" (`false`), not as a validation error.\n */\nasync function tryResolveDnsRecord(\n\tdomain: string,\n\trecordType: string,\n\ttimeoutMs: number,\n): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tlet settled = false;\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tresolve(false);\n\t\t}, timeoutMs);\n\t\ttimer.unref?.();\n\n\t\tdns.promises\n\t\t\t.resolve(domain, recordType)\n\t\t\t.then(() => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(true);\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(false);\n\t\t\t});\n\t});\n}\n\nasync function isValidDns(domain: string, timeoutMs: number): Promise<boolean> {\n\tfor (const recordType of DNS_RECORD_TYPES) {\n\t\tconst hasRecord = await tryResolveDnsRecord(domain, recordType, timeoutMs);\n\t\tif (hasRecord) return true;\n\t}\n\treturn false;\n}\n\ntype ValidateEmailOptions = {\n\t/** Max time (ms) to wait for each DNS lookup before treating it as unresolved. @default 5000 */\n\tdnsTimeoutMs?: number;\n};\n\n/**\n * Validates if an email address is valid in all possible ways, including DNS validation.\n *\n * This function performs comprehensive email validation by:\n * - Checking basic email format and syntax\n * - Validating advanced RFC 5322 compliance rules\n * - Verifying that the domain has valid MX or A records in DNS\n *\n * DNS lookups are bounded by `options.dnsTimeoutMs` (default 5000ms) so a slow or\n * unresponsive DNS server can never leave the returned promise pending indefinitely.\n *\n * @param rawEmail - The email address to validate.\n * @param options.dnsTimeoutMs - Max time (ms) to wait for each DNS lookup. Default: 5000.\n * @returns `true` if the email passes format checks and its domain resolves in DNS, otherwise `false`.\n *\n * @example\n * ```typescript\n * await validateEmail(\"user@gmail.com\"); // true\n * await validateEmail(\"user@gmil.com\"); // false (invalid domain)\n * await validateEmail(\"invalid-email\"); // false (invalid format)\n * await validateEmail(\"user@slow-dns.com\", { dnsTimeoutMs: 2000 }); // bounded wait\n * ```\n */\n\nasync function validateEmail(\n\trawEmail: string,\n\toptions?: ValidateEmailOptions,\n): Promise<boolean> {\n\tif (!rawEmail || typeof rawEmail !== \"string\") return false;\n\tconst email = rawEmail.trim();\n\n\tif (!isValidBasicFormat(email)) return false;\n\tif (!isValidAdvancedSyntax(email)) return false;\n\n\tconst domain = extractDomain(email);\n\tif (!domain) return false;\n\n\tconst timeoutMs = options?.dnsTimeoutMs ?? DEFAULT_DNS_TIMEOUT_MS;\n\treturn await isValidDns(domain, timeoutMs);\n}\n\nexport { validateEmail };\n"],"mappings":";;AAEA,SAAS,EAAmB,GAAwB;CAInD,OAAO,sIAAW,KAAK,CAAK;AAC7B;AAEA,SAAS,EAAiB,GAA4B;CAQrD,OAFA,EALI,EAAU,WAAW,KAAK,EAAU,SAAS,MAC7C,EAAU,WAAW,GAAG,KAAK,EAAU,SAAS,GAAG,KACnD,EAAU,SAAS,IAAI,KAGvB,CAAC,qCAAgB,KAAK,CAAS;AAGpC;AAEA,SAAS,EAAmB,GAAwB;CAInD,OADA,EAFI,EAAM,WAAW,KAAK,EAAM,SAAS,MACrC,EAAM,WAAW,GAAG,KAAK,EAAM,SAAS,GAAG,KAC3C,CAAC,kBAAkB,KAAK,CAAK;AAElC;AAEA,SAAS,EAAkB,GAA6B;CAGvD,IAFI,EAAW,WAAW,KAAK,EAAW,SAAS,OAGlD,EAAW,WAAW,GAAG,KACzB,EAAW,SAAS,GAAG,KACvB,EAAW,WAAW,GAAG,KACzB,EAAW,SAAS,GAAG,GAEvB,OAAO;CAGR,IAAM,IAAS,EAAW,MAAM,GAAG;CACnC,IAAI,EAAO,SAAS,GAAG,OAAO;CAE9B,KAAK,IAAM,KAAS,GAAQ,IAAI,CAAC,EAAmB,CAAK,GAAG,OAAO;CAEnE,IAAM,IAAM,EAAO,EAAO,SAAS;CAGnC,OAFA,EAAI,EAAI,SAAS,KAAK,CAAC,cAAc,KAAK,CAAG;AAG9C;AAEA,SAAS,EAAsB,GAAwB;CACtD,IAAM,IAAQ,EAAM,MAAM,GAAG;CAC7B,IAAI,EAAM,WAAW,GAAG,OAAO;CAE/B,IAAM,CAAC,GAAW,KAAc;CAIhC,OADA,EADI,CAAC,EAAiB,CAAS,KAC3B,CAAC,EAAkB,CAAU;AAElC;AAEA,SAAS,EAAc,GAA8B;CACpD,IAAM,IAAQ,EAAM,MAAM,GAAG;CAC7B,OAAO,EAAM,WAAW,IAAI,EAAM,EAAE,CAAC,YAAY,IAAI;AACtD;AAEA,IAAM,IAAmB;CAAC;CAAM;CAAK;AAAM,GAGrC,IAAyB;AAO/B,eAAe,EACd,GACA,GACA,GACmB;CACnB,OAAO,IAAI,SAAS,MAAY;EAC/B,IAAI,IAAU,IAER,IAAQ,iBAAiB;GAC1B,MACJ,IAAU,IACV,EAAQ,EAAK;EACd,GAAG,CAAS;EAGZ,AAFA,EAAM,QAAQ,GAEd,EAAI,SACF,QAAQ,GAAQ,CAAU,CAAC,CAC3B,WAAW;GACP,MACJ,IAAU,IACV,aAAa,CAAK,GAClB,EAAQ,EAAI;EACb,CAAC,CAAC,CACD,YAAY;GACR,MACJ,IAAU,IACV,aAAa,CAAK,GAClB,EAAQ,EAAK;EACd,CAAC;CACH,CAAC;AACF;AAEA,eAAe,EAAW,GAAgB,GAAqC;CAC9E,KAAK,IAAM,KAAc,GAExB,IAAI,MADoB,EAAoB,GAAQ,GAAY,CAAS,GAC1D,OAAO;CAEvB,OAAO;AACR;AA+BA,eAAe,EACd,GACA,GACmB;CACnB,IAAI,CAAC,KAAY,OAAO,KAAa,UAAU,OAAO;CACtD,IAAM,IAAQ,EAAS,KAAK;CAG5B,IADI,CAAC,EAAmB,CAAK,KACzB,CAAC,EAAsB,CAAK,GAAG,OAAO;CAE1C,IAAM,IAAS,EAAc,CAAK;CAIlC,OAHK,IAGE,MAAM,EAAW,GADN,GAAS,gBAAgB,CACF,IAHrB;AAIrB"}
@@ -41,6 +41,18 @@ declare class ApiService {
41
41
  private baseToken?;
42
42
  private enableDebug?;
43
43
  constructor(props: ApiServiceConstructorProps);
44
+ /**
45
+ * Converts a `HeadersInit` value into a plain object so it can be safely
46
+ * JSON-serialized and redacted, regardless of whether it was provided as a
47
+ * `Headers` instance, a plain object, or an array of tuples.
48
+ */
49
+ private static toPlainHeaders;
50
+ /**
51
+ * Serializes a value to JSON and masks sensitive fields (e.g. `Authorization`
52
+ * headers, `password`/`token`/`secret` body fields) before it is written to the
53
+ * debug output. See `SENSITIVE_DATA_KEYS`.
54
+ */
55
+ private static toRedactedJson;
44
56
  private onDebug;
45
57
  private generateHeaders;
46
58
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"apiService.d.ts","sourceRoot":"","sources":["../../src/services/apiService.ts"],"names":[],"mappings":"AAOA,KAAK,0BAA0B,GAAG;IACjC,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,KAAK,8BAA8B,GAAG;IACrC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC,CAAC;AAEF,KAAK,2BAA2B,GAAG;IAElC,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC,CAAC;AAUF;;;;;;;;;;;;;;;GAeG;AACH,cAAM,UAAU;IACf,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAC,CAAU;gBAElB,KAAK,EAAE,0BAA0B;IAO7C,OAAO,CAAC,OAAO;IAiBf,OAAO,CAAC,eAAe;IAcvB;;;;;OAKG;IAEG,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,8BAA8B;IAkBjE;;;;;OAKG;IAEG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,2BAA2B;IAqB/D;;;;;OAKG;IAEG,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,2BAA2B;IAqB9D;;;;;OAKG;IAEG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,2BAA2B;IAqBhE;;;;;OAKG;IAEG,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,2BAA2B;CAoBjE;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"apiService.d.ts","sourceRoot":"","sources":["../../src/services/apiService.ts"],"names":[],"mappings":"AASA,KAAK,0BAA0B,GAAG;IACjC,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,KAAK,8BAA8B,GAAG;IACrC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC,CAAC;AAEF,KAAK,2BAA2B,GAAG;IAElC,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC,CAAC;AAUF;;;;;;;;;;;;;;;GAeG;AACH,cAAM,UAAU;IACf,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAC,CAAU;gBAElB,KAAK,EAAE,0BAA0B;IAO7C;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAM7B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAI7B,OAAO,CAAC,OAAO;IAoBf,OAAO,CAAC,eAAe;IAcvB;;;;;OAKG;IAEG,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,8BAA8B;IAkBjE;;;;;OAKG;IAEG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,2BAA2B;IAqB/D;;;;;OAKG;IAEG,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,2BAA2B;IAqB9D;;;;;OAKG;IAEG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,2BAA2B;IAqBhE;;;;;OAKG;IAEG,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,2BAA2B;CAoBjE;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -6,12 +6,25 @@
6
6
  */
7
7
  declare class LogService {
8
8
  private static config?;
9
+ /**
10
+ * Checks whether a log ingestion URL is safe to send telemetry to: `https://`
11
+ * is always accepted, and plain `http://` is only accepted for local development
12
+ * (`localhost` / `127.0.0.1`). Every other `http://` destination is rejected so
13
+ * request/response data is never transmitted in plaintext over the network.
14
+ */
15
+ private static isSecureEndpoint;
9
16
  /**
10
17
  * Sets the log service configuration once. Subsequent calls are ignored.
11
18
  *
19
+ * There is no hardcoded fallback endpoint: if `logBaseApiUrl` is omitted, invalid,
20
+ * or points at an insecure (`http://`) non-local destination, remote log delivery
21
+ * is disabled (`getConfig().apiUrl` is `null`) instead of silently sending data to
22
+ * a default/insecure host — `logRequest` skips the network call in that case.
23
+ *
12
24
  * @param config.trafficSourceId - Traffic source identifier.
13
25
  * @param config.userToken - User token for authentication.
14
- * @param config.logBaseApiUrl - Override the default log ingestion base URL.
26
+ * @param config.logBaseApiUrl - Log ingestion base URL. Must be `https://`, or
27
+ * `http://localhost`/`http://127.0.0.1` for local development.
15
28
  */
16
29
  static setConfig(config: {
17
30
  trafficSourceId: string;
@@ -22,7 +35,7 @@ declare class LogService {
22
35
  static getConfig(): {
23
36
  trafficSourceId: string;
24
37
  userToken: string;
25
- apiUrl: string;
38
+ apiUrl: string | null;
26
39
  } | undefined;
27
40
  /**
28
41
  * Resets the stored configuration, allowing a new initialization.
@@ -1 +1 @@
1
- {"version":3,"file":"logService.d.ts","sourceRoot":"","sources":["../../src/services/logService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAM,UAAU;IACf,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAIpB;IAEF;;;;;;OAMG;IACH,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE;QACxB,eAAe,EAAE,MAAM,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;KACvB,GAAG,IAAI;IAUR,2FAA2F;IAC3F,MAAM,CAAC,SAAS,IACb;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAC9D,SAAS;IAIZ;;OAEG;IACH,MAAM,CAAC,WAAW;CAGlB;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"logService.d.ts","sourceRoot":"","sources":["../../src/services/logService.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,cAAM,UAAU;IACf,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAIpB;IAEF;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAK/B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE;QACxB,eAAe,EAAE,MAAM,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;KACvB,GAAG,IAAI;IAmCR,2FAA2F;IAC3F,MAAM,CAAC,SAAS,IACb;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GACrE,SAAS;IAIZ;;OAEG;IACH,MAAM,CAAC,WAAW;CAGlB;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -1,8 +1,13 @@
1
+ type DecodeRequestBodyOptions = {
2
+ /** Max accepted body size in bytes. Larger bodies are rejected with `BadRequest`. @default 5242880 (5 MB) */
3
+ maxBodySizeBytes?: number;
4
+ };
1
5
  /**
2
6
  * Decodes a request body into a plain object, trying JSON first then URL-encoded form data.
3
- * Throws `BadRequest` if neither format can be parsed.
7
+ * Throws `BadRequest` if neither format can be parsed, or if the body exceeds `maxBodySizeBytes`.
4
8
  *
5
9
  * @param request - The incoming request whose body will be decoded.
10
+ * @param options.maxBodySizeBytes - Max accepted body size in bytes. Default: 5 MB.
6
11
  * @returns The decoded body as a plain object.
7
12
  *
8
13
  * @example
@@ -13,6 +18,6 @@
13
18
  * }
14
19
  * ```
15
20
  */
16
- declare function decodeRequestBody(request: Request): Promise<any>;
21
+ declare function decodeRequestBody(request: Request, options?: DecodeRequestBodyOptions): Promise<any>;
17
22
  export { decodeRequestBody };
18
23
  //# sourceMappingURL=decodeRequestBody.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"decodeRequestBody.d.ts","sourceRoot":"","sources":["../../src/utilities/decodeRequestBody.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAGH,iBAAe,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAuB/D;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"decodeRequestBody.d.ts","sourceRoot":"","sources":["../../src/utilities/decodeRequestBody.ts"],"names":[],"mappings":"AAKA,KAAK,wBAAwB,GAAG;IAC/B,6GAA6G;IAC7G,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AAEH,iBAAe,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,wBAAwB,GAEhC,OAAO,CAAC,GAAG,CAAC,CAoCd;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Keys treated as sensitive wherever request/response data is written to a debug
3
+ * log or sent to a telemetry sink (see `ApiService`'s debug output and the
4
+ * `LogService` remote logging pipeline).
5
+ *
6
+ * Passed as the `sensitiveKeys` argument to `parseSensitiveData` (from `@arkyn/shared`)
7
+ * so values are masked with `"****"` before they reach any console output or network
8
+ * request.
9
+ *
10
+ * Both `lowerCase` and `Capitalized`/`Header-Case` variants are listed explicitly for
11
+ * header-shaped keys (`Authorization`, `Cookie`, `Set-Cookie`, ...). This is
12
+ * intentionally redundant with `parseSensitiveData`'s own case-insensitive matching:
13
+ * it keeps this list correct regardless of which build of `parseSensitiveData` is
14
+ * resolved at runtime, since a `Headers` instance lowercases names on iteration while
15
+ * plain header objects commonly use `Authorization`/`Header-Case`.
16
+ *
17
+ * Extends the default `parseSensitiveData` key list (`password`, `confirmPassword`,
18
+ * `creditCard`) with header/token-shaped keys that commonly carry credentials.
19
+ */
20
+ declare const SENSITIVE_DATA_KEYS: readonly ["password", "Password", "confirmPassword", "ConfirmPassword", "creditCard", "CreditCard", "authorization", "Authorization", "cookie", "Cookie", "set-cookie", "Set-Cookie", "setCookie", "SetCookie", "token", "Token", "refreshToken", "RefreshToken", "accessToken", "AccessToken", "apiKey", "ApiKey", "secret", "Secret"];
21
+ export { SENSITIVE_DATA_KEYS };
22
+ //# sourceMappingURL=sensitiveDataKeys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensitiveDataKeys.d.ts","sourceRoot":"","sources":["../../src/utilities/sensitiveDataKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,QAAA,MAAM,mBAAmB,yUAyBf,CAAC;AAEX,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
@@ -1,3 +1,7 @@
1
+ type ValidateEmailOptions = {
2
+ /** Max time (ms) to wait for each DNS lookup before treating it as unresolved. @default 5000 */
3
+ dnsTimeoutMs?: number;
4
+ };
1
5
  /**
2
6
  * Validates if an email address is valid in all possible ways, including DNS validation.
3
7
  *
@@ -6,7 +10,11 @@
6
10
  * - Validating advanced RFC 5322 compliance rules
7
11
  * - Verifying that the domain has valid MX or A records in DNS
8
12
  *
13
+ * DNS lookups are bounded by `options.dnsTimeoutMs` (default 5000ms) so a slow or
14
+ * unresponsive DNS server can never leave the returned promise pending indefinitely.
15
+ *
9
16
  * @param rawEmail - The email address to validate.
17
+ * @param options.dnsTimeoutMs - Max time (ms) to wait for each DNS lookup. Default: 5000.
10
18
  * @returns `true` if the email passes format checks and its domain resolves in DNS, otherwise `false`.
11
19
  *
12
20
  * @example
@@ -14,8 +22,9 @@
14
22
  * await validateEmail("user@gmail.com"); // true
15
23
  * await validateEmail("user@gmil.com"); // false (invalid domain)
16
24
  * await validateEmail("invalid-email"); // false (invalid format)
25
+ * await validateEmail("user@slow-dns.com", { dnsTimeoutMs: 2000 }); // bounded wait
17
26
  * ```
18
27
  */
19
- declare function validateEmail(rawEmail: string): Promise<boolean>;
28
+ declare function validateEmail(rawEmail: string, options?: ValidateEmailOptions): Promise<boolean>;
20
29
  export { validateEmail };
21
30
  //# sourceMappingURL=validateEmail.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validateEmail.d.ts","sourceRoot":"","sources":["../../src/validations/validateEmail.ts"],"names":[],"mappings":"AAwFA;;;;;;;;;;;;;;;;;GAiBG;AAEH,iBAAe,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAW/D;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"validateEmail.d.ts","sourceRoot":"","sources":["../../src/validations/validateEmail.ts"],"names":[],"mappings":"AAoHA,KAAK,oBAAoB,GAAG;IAC3B,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,iBAAe,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAYlB;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkyn/server",
3
- "version": "3.0.8",
3
+ "version": "3.0.10",
4
4
  "author": "Arkyn | Lucas Gonçalves",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -40,19 +40,19 @@
40
40
  ],
41
41
  "sideEffects": false,
42
42
  "dependencies": {
43
- "@arkyn/shared": ">=3.0.2",
44
- "@arkyn/templates": ">=3.0.2"
43
+ "@arkyn/shared": "^3.0.2",
44
+ "@arkyn/templates": "^3.0.2"
45
45
  },
46
46
  "scripts": {
47
- "audit": "bun audit --workspace packages/server",
48
- "build": "bunx vite build && bunx tsc --project tsconfig.json --emitDeclarationOnly && bun ./generate-exports.ts && rm -f dist/modules/index.js dist/modules/style.css",
47
+ "audit": "bun audit",
48
+ "build": "bunx vite build && bunx tsc --project tsconfig.json --emitDeclarationOnly && bun ../../scripts/generate-exports.ts && rm -f dist/modules/index.js dist/modules/style.css",
49
49
  "prebuild": "rm -rf dist",
50
- "publish:beta": "npm publish --tag beta",
51
- "publish:latest": "npm publish --tag latest",
52
- "release:beta": "bun ./generate-version.ts beta",
53
- "release:patch": "bun ./generate-version.ts patch",
54
- "release:minor": "bun ./generate-version.ts minor",
55
- "release:major": "bun ./generate-version.ts major",
50
+ "publish:beta": "bash ../../scripts/publish-idempotent.sh beta",
51
+ "publish:latest": "bash ../../scripts/publish-idempotent.sh latest",
52
+ "release:beta": "bun ../../scripts/generate-version.ts beta",
53
+ "release:patch": "bun ../../scripts/generate-version.ts patch",
54
+ "release:minor": "bun ../../scripts/generate-version.ts minor",
55
+ "release:major": "bun ../../scripts/generate-version.ts major",
56
56
  "test": "bunx vitest --config vitest.config.ts --run",
57
57
  "test:watch": "bunx vitest --config vitest.config.ts --watch",
58
58
  "typecheck": "bunx tsc --project tsconfig.json --noEmit"