@blamejs/core 0.15.11 → 0.15.13

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 (305) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/index.js +2 -0
  3. package/lib/a2a-tasks.js +7 -23
  4. package/lib/acme.js +13 -16
  5. package/lib/agent-event-bus.js +5 -4
  6. package/lib/agent-idempotency.js +2 -5
  7. package/lib/agent-orchestrator.js +2 -5
  8. package/lib/agent-saga.js +3 -5
  9. package/lib/agent-tenant.js +2 -5
  10. package/lib/ai-adverse-decision.js +2 -15
  11. package/lib/ai-capability.js +1 -6
  12. package/lib/ai-dp.js +1 -5
  13. package/lib/ai-input.js +2 -2
  14. package/lib/ai-pref.js +3 -8
  15. package/lib/ai-quota.js +3 -14
  16. package/lib/api-key.js +37 -28
  17. package/lib/archive-adapters.js +2 -4
  18. package/lib/archive-entry-policy.js +32 -0
  19. package/lib/archive-read.js +5 -17
  20. package/lib/archive-tar-read.js +5 -16
  21. package/lib/archive.js +2 -10
  22. package/lib/arg-parser.js +7 -6
  23. package/lib/asyncapi-traits.js +2 -6
  24. package/lib/atomic-file.js +108 -31
  25. package/lib/audit-chain.js +133 -53
  26. package/lib/audit-daily-review.js +24 -14
  27. package/lib/audit-emit.js +82 -0
  28. package/lib/audit-sign.js +257 -0
  29. package/lib/audit.js +84 -0
  30. package/lib/auth/access-lock.js +5 -27
  31. package/lib/auth/dpop.js +23 -35
  32. package/lib/auth/fido-mds3.js +9 -15
  33. package/lib/auth/jwt-external.js +26 -8
  34. package/lib/auth/jwt.js +13 -15
  35. package/lib/auth/lockout.js +6 -25
  36. package/lib/auth/oauth.js +67 -45
  37. package/lib/auth/oid4vci.js +55 -32
  38. package/lib/auth/oid4vp.js +3 -2
  39. package/lib/auth/openid-federation.js +6 -6
  40. package/lib/auth/passkey.js +3 -3
  41. package/lib/auth/password.js +7 -1
  42. package/lib/auth/saml.js +37 -27
  43. package/lib/auth/sd-jwt-vc-holder.js +2 -14
  44. package/lib/auth/sd-jwt-vc-issuer.js +2 -14
  45. package/lib/auth/sd-jwt-vc.js +1 -1
  46. package/lib/auth/status-list.js +7 -7
  47. package/lib/auth/step-up.js +6 -12
  48. package/lib/auth-bot-challenge.js +6 -37
  49. package/lib/backup/bundle.js +11 -18
  50. package/lib/backup/index.js +14 -47
  51. package/lib/bounded-map.js +112 -1
  52. package/lib/breach-deadline.js +1 -11
  53. package/lib/cache.js +7 -18
  54. package/lib/cbor.js +2 -1
  55. package/lib/cdn-cache-control.js +8 -9
  56. package/lib/cert.js +3 -10
  57. package/lib/chain-writer.js +162 -47
  58. package/lib/cli.js +5 -1
  59. package/lib/client-hints.js +10 -10
  60. package/lib/cloud-events.js +40 -31
  61. package/lib/cluster-storage.js +2 -38
  62. package/lib/cluster.js +4 -2
  63. package/lib/cms-codec.js +2 -1
  64. package/lib/codepoint-class.js +67 -1
  65. package/lib/compliance-ai-act-logging.js +1 -6
  66. package/lib/compliance-ai-act-transparency.js +2 -4
  67. package/lib/compliance-eaa.js +1 -11
  68. package/lib/compliance-sanctions-fetcher.js +21 -28
  69. package/lib/compliance-sanctions.js +2 -14
  70. package/lib/compliance.js +2 -9
  71. package/lib/config-drift.js +2 -12
  72. package/lib/content-digest.js +10 -8
  73. package/lib/cookies.js +5 -11
  74. package/lib/cose.js +19 -11
  75. package/lib/cra-report.js +1 -11
  76. package/lib/crypto-field.js +5 -10
  77. package/lib/crypto.js +120 -3
  78. package/lib/csp.js +235 -3
  79. package/lib/daemon.js +42 -41
  80. package/lib/data-act.js +19 -9
  81. package/lib/db-query.js +6 -40
  82. package/lib/db.js +34 -12
  83. package/lib/dbsc.js +5 -6
  84. package/lib/ddl-change-control.js +18 -14
  85. package/lib/deprecate.js +4 -5
  86. package/lib/did.js +6 -2
  87. package/lib/dsr.js +8 -12
  88. package/lib/external-db-migrate.js +21 -22
  89. package/lib/external-db.js +14 -26
  90. package/lib/fda-21cfr11.js +12 -8
  91. package/lib/fdx.js +22 -18
  92. package/lib/file-upload.js +80 -66
  93. package/lib/flag-evaluation-context.js +5 -8
  94. package/lib/framework-error.js +12 -0
  95. package/lib/framework-schema.js +19 -0
  96. package/lib/fsm.js +7 -12
  97. package/lib/gate-contract.js +869 -38
  98. package/lib/gdpr-ropa.js +4 -13
  99. package/lib/graphql-federation.js +1 -1
  100. package/lib/guard-agent-registry.js +2 -1
  101. package/lib/guard-all.js +1 -0
  102. package/lib/guard-archive.js +9 -30
  103. package/lib/guard-auth.js +23 -80
  104. package/lib/guard-cidr.js +20 -96
  105. package/lib/guard-csv.js +135 -196
  106. package/lib/guard-domain.js +23 -106
  107. package/lib/guard-dsn.js +16 -13
  108. package/lib/guard-email.js +46 -53
  109. package/lib/guard-envelope.js +1 -1
  110. package/lib/guard-event-bus-topic.js +2 -1
  111. package/lib/guard-filename.js +18 -62
  112. package/lib/guard-graphql.js +28 -75
  113. package/lib/guard-html-wcag.js +15 -2
  114. package/lib/guard-html.js +65 -117
  115. package/lib/guard-idempotency-key.js +2 -1
  116. package/lib/guard-image.js +280 -77
  117. package/lib/guard-imap-command.js +8 -9
  118. package/lib/guard-json.js +87 -103
  119. package/lib/guard-jsonpath.js +20 -88
  120. package/lib/guard-jwt.js +32 -114
  121. package/lib/guard-list-id.js +2 -7
  122. package/lib/guard-list-unsubscribe.js +2 -7
  123. package/lib/guard-mail-compose.js +5 -6
  124. package/lib/guard-mail-move.js +2 -1
  125. package/lib/guard-mail-query.js +5 -3
  126. package/lib/guard-mail-sieve.js +6 -7
  127. package/lib/guard-managesieve-command.js +5 -4
  128. package/lib/guard-markdown.js +76 -110
  129. package/lib/guard-message-id.js +5 -6
  130. package/lib/guard-mime.js +20 -99
  131. package/lib/guard-oauth.js +19 -73
  132. package/lib/guard-pdf.js +65 -72
  133. package/lib/guard-pop3-command.js +12 -13
  134. package/lib/guard-posture-chain.js +2 -1
  135. package/lib/guard-regex.js +24 -99
  136. package/lib/guard-saga-config.js +2 -1
  137. package/lib/guard-shell.js +22 -87
  138. package/lib/guard-smtp-command.js +8 -11
  139. package/lib/guard-sql.js +15 -13
  140. package/lib/guard-stream-args.js +2 -1
  141. package/lib/guard-svg.js +95 -140
  142. package/lib/guard-template.js +23 -80
  143. package/lib/guard-tenant-id.js +2 -1
  144. package/lib/guard-text.js +592 -0
  145. package/lib/guard-time.js +27 -95
  146. package/lib/guard-uuid.js +21 -93
  147. package/lib/guard-xml.js +76 -106
  148. package/lib/guard-yaml.js +24 -60
  149. package/lib/http-client-cache.js +8 -13
  150. package/lib/http-client-cookie-jar.js +2 -4
  151. package/lib/http-client.js +8 -21
  152. package/lib/http-message-signature.js +26 -8
  153. package/lib/i18n-messageformat.js +5 -7
  154. package/lib/i18n.js +83 -26
  155. package/lib/inbox.js +8 -8
  156. package/lib/incident-report.js +3 -21
  157. package/lib/ip-utils.js +49 -6
  158. package/lib/jobs.js +3 -2
  159. package/lib/keychain.js +6 -18
  160. package/lib/legal-hold.js +6 -15
  161. package/lib/log-stream-cloudwatch.js +44 -112
  162. package/lib/log-stream-otlp-grpc.js +28 -14
  163. package/lib/log-stream-otlp.js +16 -80
  164. package/lib/log-stream-syslog.js +6 -0
  165. package/lib/log-stream-webhook.js +20 -92
  166. package/lib/log.js +24 -2
  167. package/lib/mail-arc-sign.js +8 -3
  168. package/lib/mail-arf.js +3 -2
  169. package/lib/mail-auth.js +40 -66
  170. package/lib/mail-bimi.js +19 -39
  171. package/lib/mail-crypto-pgp.js +3 -2
  172. package/lib/mail-dav.js +8 -35
  173. package/lib/mail-deploy.js +6 -9
  174. package/lib/mail-dkim.js +19 -38
  175. package/lib/mail-greylist.js +15 -26
  176. package/lib/mail-helo.js +2 -3
  177. package/lib/mail-journal.js +2 -1
  178. package/lib/mail-mdn.js +4 -3
  179. package/lib/mail-rbl.js +5 -8
  180. package/lib/mail-scan.js +2 -2
  181. package/lib/mail-send-deliver.js +33 -20
  182. package/lib/mail-server-imap.js +32 -87
  183. package/lib/mail-server-jmap.js +35 -51
  184. package/lib/mail-server-managesieve.js +29 -83
  185. package/lib/mail-server-mx.js +33 -74
  186. package/lib/mail-server-net.js +177 -0
  187. package/lib/mail-server-pop3.js +30 -83
  188. package/lib/mail-server-rate-limit.js +30 -6
  189. package/lib/mail-server-submission.js +34 -73
  190. package/lib/mail-server-tls.js +89 -0
  191. package/lib/mail-spam-score.js +7 -8
  192. package/lib/mail-store.js +3 -2
  193. package/lib/mail.js +11 -11
  194. package/lib/markup-escape.js +31 -0
  195. package/lib/markup-tokenizer.js +54 -0
  196. package/lib/mcp-tool-registry.js +26 -23
  197. package/lib/mcp.js +1 -1
  198. package/lib/mdoc.js +11 -12
  199. package/lib/metrics.js +32 -30
  200. package/lib/middleware/age-gate.js +3 -23
  201. package/lib/middleware/api-encrypt.js +11 -22
  202. package/lib/middleware/assetlinks.js +2 -7
  203. package/lib/middleware/bearer-auth.js +2 -1
  204. package/lib/middleware/body-parser.js +79 -54
  205. package/lib/middleware/bot-disclose.js +7 -10
  206. package/lib/middleware/bot-guard.js +2 -10
  207. package/lib/middleware/csrf-protect.js +13 -2
  208. package/lib/middleware/daily-byte-quota.js +6 -26
  209. package/lib/middleware/deny-response.js +19 -1
  210. package/lib/middleware/fetch-metadata.js +7 -0
  211. package/lib/middleware/idempotency-key.js +4 -20
  212. package/lib/middleware/rate-limit.js +20 -28
  213. package/lib/middleware/scim-server.js +3 -2
  214. package/lib/middleware/security-headers.js +9 -1
  215. package/lib/middleware/security-txt.js +2 -6
  216. package/lib/middleware/tus-upload.js +12 -27
  217. package/lib/middleware/web-app-manifest.js +2 -7
  218. package/lib/migrations.js +4 -13
  219. package/lib/mime-parse.js +34 -17
  220. package/lib/module-loader.js +44 -0
  221. package/lib/money.js +105 -0
  222. package/lib/mtls-ca.js +116 -36
  223. package/lib/network-byte-quota.js +4 -18
  224. package/lib/network-dane.js +8 -6
  225. package/lib/network-dns-resolver.js +97 -6
  226. package/lib/network-dns.js +22 -26
  227. package/lib/network-dnssec.js +16 -16
  228. package/lib/network-heartbeat.js +6 -5
  229. package/lib/network-proxy.js +3 -7
  230. package/lib/network-smtp-policy.js +29 -41
  231. package/lib/network-tls.js +74 -55
  232. package/lib/network.js +2 -6
  233. package/lib/nis2-report.js +1 -11
  234. package/lib/nonce-store.js +81 -3
  235. package/lib/notify.js +7 -12
  236. package/lib/numeric-bounds.js +22 -8
  237. package/lib/object-store/azure-blob-bucket-ops.js +2 -6
  238. package/lib/object-store/azure-blob.js +5 -45
  239. package/lib/object-store/gcs.js +8 -71
  240. package/lib/object-store/http-put.js +1 -5
  241. package/lib/object-store/http-request.js +128 -0
  242. package/lib/object-store/sigv4-bucket-ops.js +2 -1
  243. package/lib/object-store/sigv4.js +9 -77
  244. package/lib/observability-otlp-exporter.js +9 -25
  245. package/lib/observability.js +95 -0
  246. package/lib/openapi-paths-builder.js +7 -3
  247. package/lib/otel-export.js +7 -10
  248. package/lib/outbox.js +43 -37
  249. package/lib/pagination.js +11 -15
  250. package/lib/parsers/safe-env.js +5 -4
  251. package/lib/parsers/safe-ini.js +10 -4
  252. package/lib/parsers/safe-toml.js +11 -9
  253. package/lib/parsers/safe-xml.js +2 -2
  254. package/lib/parsers/safe-yaml.js +7 -6
  255. package/lib/pick.js +63 -5
  256. package/lib/pipl-cn.js +69 -59
  257. package/lib/privacy-pass.js +8 -6
  258. package/lib/problem-details.js +2 -5
  259. package/lib/pubsub.js +4 -8
  260. package/lib/redact.js +2 -1
  261. package/lib/render.js +4 -2
  262. package/lib/request-helpers.js +133 -6
  263. package/lib/restore.js +5 -22
  264. package/lib/retention.js +3 -5
  265. package/lib/safe-async.js +457 -0
  266. package/lib/safe-buffer.js +137 -6
  267. package/lib/safe-decompress.js +2 -1
  268. package/lib/safe-dns.js +2 -1
  269. package/lib/safe-ical.js +12 -26
  270. package/lib/safe-json.js +7 -8
  271. package/lib/safe-jsonpath.js +6 -5
  272. package/lib/safe-mime.js +25 -29
  273. package/lib/safe-redirect.js +2 -5
  274. package/lib/safe-schema.js +7 -6
  275. package/lib/safe-sql.js +126 -0
  276. package/lib/safe-vcard.js +12 -26
  277. package/lib/sandbox-worker.js +9 -2
  278. package/lib/sandbox.js +3 -2
  279. package/lib/scheduler.js +5 -12
  280. package/lib/sec-cyber.js +82 -37
  281. package/lib/seeders.js +9 -21
  282. package/lib/self-update.js +92 -69
  283. package/lib/session-device-binding.js +112 -66
  284. package/lib/session.js +194 -6
  285. package/lib/sql.js +225 -78
  286. package/lib/sse.js +6 -10
  287. package/lib/static.js +136 -98
  288. package/lib/storage.js +4 -2
  289. package/lib/structured-fields.js +313 -17
  290. package/lib/tenant-quota.js +2 -20
  291. package/lib/tsa.js +11 -15
  292. package/lib/validate-opts.js +154 -8
  293. package/lib/vault/seal-pem-file.js +30 -48
  294. package/lib/vault-aad.js +3 -2
  295. package/lib/vc.js +2 -7
  296. package/lib/vex.js +35 -13
  297. package/lib/web-push-vapid.js +23 -20
  298. package/lib/webhook-dispatcher.js +706 -0
  299. package/lib/webhook.js +43 -18
  300. package/lib/websocket-channels.js +9 -7
  301. package/lib/websocket.js +7 -3
  302. package/lib/worm.js +2 -3
  303. package/lib/ws-client.js +8 -10
  304. package/package.json +1 -1
  305. package/sbom.cdx.json +6 -6
@@ -93,14 +93,6 @@ function _encodeBlobKey(key) {
93
93
 
94
94
  var DEFAULT_API_VERSION = "2024-08-04";
95
95
 
96
- // Service SAS expiry bounds. Azure doesn't enforce a hard max, but
97
- // matching the SigV4 / V4 7-day cap keeps semantics uniform across
98
- // backends and reflects best practice (long-lived SAS tokens are
99
- // effectively credentials).
100
- var SAS_DEFAULT_EXPIRES_SECONDS = C.TIME.minutes(15) / C.TIME.seconds(1);
101
- var SAS_MAX_EXPIRES_SECONDS = C.TIME.days(7) / C.TIME.seconds(1);
102
- var SAS_MIN_EXPIRES_SECONDS = 1;
103
-
104
96
  var _err = ObjectStoreError.factory;
105
97
 
106
98
  var _httpRequest = sharedRequest;
@@ -312,33 +304,13 @@ function create(config) {
312
304
  function getResponse(key, opts) {
313
305
  opts = opts || {};
314
306
  var url = _blobUrl(key);
315
- var extraHeaders = {};
316
- if (opts.range) {
317
- extraHeaders["x-ms-range"] = "bytes=" + opts.range.start + "-" + opts.range.end;
318
- }
319
- if (opts.ifNoneMatch) extraHeaders["If-None-Match"] = opts.ifNoneMatch;
320
- if (opts.ifMatch) extraHeaders["If-Match"] = opts.ifMatch;
321
- if (opts.ifModifiedSince) extraHeaders["If-Modified-Since"] = opts.ifModifiedSince;
322
- if (opts.ifUnmodifiedSince) extraHeaders["If-Unmodified-Since"] = opts.ifUnmodifiedSince;
307
+ var extraHeaders = sharedRequest.applyConditionalGetHeaders({}, opts, "x-ms-range");
323
308
  var headers = _signed("GET", url, extraHeaders);
324
309
  return _httpRequest("GET", url, headers, null, reqOpts).then(function (res) {
325
- return {
326
- statusCode: res.statusCode,
327
- body: res.body,
328
- etag: res.headers && res.headers.etag,
329
- lastModified: res.headers && res.headers["last-modified"]
330
- ? Date.parse(res.headers["last-modified"]) : null,
331
- contentRange: res.headers && res.headers["content-range"] || null,
332
- size: res.headers && res.headers["content-length"]
333
- ? parseInt(res.headers["content-length"], 10) : null,
334
- contentType: res.headers && res.headers["content-type"] || null,
335
- };
310
+ return sharedRequest.mapGetResponse(res);
336
311
  }, function (err) {
337
312
  if (err && err.statusCode === requestHelpers.HTTP_STATUS.NOT_MODIFIED) {
338
- return {
339
- statusCode: requestHelpers.HTTP_STATUS.NOT_MODIFIED,
340
- body: null, etag: null, lastModified: null,
341
- };
313
+ return sharedRequest.notModifiedGetResult();
342
314
  }
343
315
  throw err;
344
316
  });
@@ -348,11 +320,7 @@ function create(config) {
348
320
  var url = _blobUrl(key);
349
321
  var headers = _signed("HEAD", url, {});
350
322
  return _httpRequest("HEAD", url, headers, null, reqOpts).then(function (res) {
351
- return {
352
- size: res.headers["content-length"] ? parseInt(res.headers["content-length"], 10) : null,
353
- etag: res.headers.etag,
354
- lastModified: res.headers["last-modified"] ? Date.parse(res.headers["last-modified"]) : null,
355
- };
323
+ return sharedRequest.mapHeadResponse(res);
356
324
  });
357
325
  }
358
326
 
@@ -415,15 +383,7 @@ function create(config) {
415
383
  // "Create a service SAS". We sign with the account key (HMAC-SHA256
416
384
  // over the canonical string) and emit the token as URL query params.
417
385
  function _buildSasToken(permissions, opts) {
418
- var expiresIn = opts.expiresIn != null ? opts.expiresIn : SAS_DEFAULT_EXPIRES_SECONDS;
419
- if (typeof expiresIn !== "number" ||
420
- expiresIn < SAS_MIN_EXPIRES_SECONDS ||
421
- expiresIn > SAS_MAX_EXPIRES_SECONDS) {
422
- throw _err("INVALID_EXPIRES",
423
- "presigned URL: expiresIn must be a number of seconds between " +
424
- SAS_MIN_EXPIRES_SECONDS + " and " + SAS_MAX_EXPIRES_SECONDS +
425
- " (7 days)", true);
426
- }
386
+ var expiresIn = sharedRequest.resolvePresignExpires(opts, "presigned URL", "");
427
387
  var nowDate = opts.date || new Date();
428
388
  var expiry = new Date(nowDate.getTime() + C.TIME.seconds(expiresIn));
429
389
  // Azure accepts ISO 8601 with second precision; strip ms.
@@ -28,7 +28,6 @@ var { Readable } = require("node:stream");
28
28
  var bCrypto = require("../crypto");
29
29
  var safeJson = require("../safe-json");
30
30
  var C = require("../constants");
31
- var numericBounds = require("../numeric-bounds");
32
31
  var requestHelpers = require("../request-helpers");
33
32
  var { ObjectStoreError } = require("../framework-error");
34
33
  var safeUrl = require("../safe-url");
@@ -60,11 +59,6 @@ var GCS_V4_ALGORITHM = "GOOG4-RSA-SHA256";
60
59
  var GCS_V4_SERVICE = "storage";
61
60
  var GCS_V4_REGION = "auto";
62
61
 
63
- // Same expiry bounds as SigV4 — V4 presigning shares the spec's 7-day cap.
64
- var PRESIGN_DEFAULT_EXPIRES_SECONDS = C.TIME.minutes(15) / C.TIME.seconds(1);
65
- var PRESIGN_MAX_EXPIRES_SECONDS = C.TIME.days(7) / C.TIME.seconds(1);
66
- var PRESIGN_MIN_EXPIRES_SECONDS = 1;
67
-
68
62
  var SERVICE_ACCOUNT_SCHEMA = {
69
63
  type: "object",
70
64
  required: ["client_email", "private_key"],
@@ -238,32 +232,13 @@ function create(config) {
238
232
  var token = await _ensureToken();
239
233
  var url = _objectUrl(key, { alt: "media" });
240
234
  var headers = authHeader.bearer(token);
241
- if (opts.range) {
242
- headers["Range"] = "bytes=" + opts.range.start + "-" + opts.range.end;
243
- }
244
- if (opts.ifNoneMatch) headers["If-None-Match"] = opts.ifNoneMatch;
245
- if (opts.ifMatch) headers["If-Match"] = opts.ifMatch;
246
- if (opts.ifModifiedSince) headers["If-Modified-Since"] = opts.ifModifiedSince;
247
- if (opts.ifUnmodifiedSince) headers["If-Unmodified-Since"] = opts.ifUnmodifiedSince;
235
+ sharedRequest.applyConditionalGetHeaders(headers, opts, "Range");
248
236
  try {
249
237
  var res = await _httpRequest("GET", url, headers, null, reqOpts);
250
- return {
251
- statusCode: res.statusCode,
252
- body: res.body,
253
- etag: res.headers && res.headers.etag,
254
- lastModified: res.headers && res.headers["last-modified"]
255
- ? Date.parse(res.headers["last-modified"]) : null,
256
- contentRange: res.headers && res.headers["content-range"] || null,
257
- size: res.headers && res.headers["content-length"]
258
- ? parseInt(res.headers["content-length"], 10) : null,
259
- contentType: res.headers && res.headers["content-type"] || null,
260
- };
238
+ return sharedRequest.mapGetResponse(res);
261
239
  } catch (err) {
262
240
  if (err && err.statusCode === requestHelpers.HTTP_STATUS.NOT_MODIFIED) {
263
- return {
264
- statusCode: requestHelpers.HTTP_STATUS.NOT_MODIFIED,
265
- body: null, etag: null, lastModified: null,
266
- };
241
+ return sharedRequest.notModifiedGetResult();
267
242
  }
268
243
  throw err;
269
244
  }
@@ -326,21 +301,8 @@ function create(config) {
326
301
 
327
302
  function _v4Presign(method, opts) {
328
303
  opts = opts || {};
329
- if (!opts.key || typeof opts.key !== "string") {
330
- throw _err("INVALID_KEY", "presigned URL: key is required", true);
331
- }
332
- if (opts.key.indexOf("\0") !== -1) {
333
- throw _err("INVALID_KEY", "null byte in key", true);
334
- }
335
- var expiresIn = opts.expiresIn != null ? opts.expiresIn : PRESIGN_DEFAULT_EXPIRES_SECONDS;
336
- if (typeof expiresIn !== "number" ||
337
- expiresIn < PRESIGN_MIN_EXPIRES_SECONDS ||
338
- expiresIn > PRESIGN_MAX_EXPIRES_SECONDS) {
339
- throw _err("INVALID_EXPIRES",
340
- "presigned URL: expiresIn must be a number of seconds between " +
341
- PRESIGN_MIN_EXPIRES_SECONDS + " and " + PRESIGN_MAX_EXPIRES_SECONDS +
342
- " (7 days, V4 hard cap)", true);
343
- }
304
+ sharedRequest.requirePresignKey(opts, "presigned URL");
305
+ var expiresIn = sharedRequest.resolvePresignExpires(opts, "presigned URL", "V4");
344
306
 
345
307
  // V4 presigned URLs target the XML API path-style endpoint
346
308
  // (storage.googleapis.com/{bucket}/{key}), not the JSON API used
@@ -418,34 +380,9 @@ function create(config) {
418
380
  // declared range get a 403 from the bucket itself.
419
381
  function presignedUploadPolicy(opts) {
420
382
  opts = opts || {};
421
- if (!opts.key || typeof opts.key !== "string") {
422
- throw _err("INVALID_KEY", "presignedUploadPolicy: key is required", true);
423
- }
424
- if (opts.key.indexOf("\0") !== -1) {
425
- throw _err("INVALID_KEY", "null byte in key", true);
426
- }
427
- if (typeof opts.maxBytes !== "number" || !Number.isFinite(opts.maxBytes) ||
428
- opts.maxBytes <= 0) {
429
- throw _err("INVALID_MAX_BYTES",
430
- "presignedUploadPolicy: maxBytes (positive number of bytes) is required — " +
431
- "POST-form policy enforces body size via the content-length-range condition; " +
432
- "use presignedUploadUrl if size enforcement is not needed", true);
433
- }
434
- if (opts.minBytes !== undefined && !numericBounds.isNonNegativeFiniteInt(opts.minBytes)) {
435
- throw _err("INVALID_MIN_BYTES",
436
- "presignedUploadPolicy: minBytes must be a non-negative finite integer; got " +
437
- numericBounds.shape(opts.minBytes), true);
438
- }
439
- var minBytes = opts.minBytes !== undefined ? opts.minBytes : 0;
440
- var expiresIn = opts.expiresIn != null ? opts.expiresIn : PRESIGN_DEFAULT_EXPIRES_SECONDS;
441
- if (typeof expiresIn !== "number" ||
442
- expiresIn < PRESIGN_MIN_EXPIRES_SECONDS ||
443
- expiresIn > PRESIGN_MAX_EXPIRES_SECONDS) {
444
- throw _err("INVALID_EXPIRES",
445
- "presignedUploadPolicy: expiresIn must be a number of seconds between " +
446
- PRESIGN_MIN_EXPIRES_SECONDS + " and " + PRESIGN_MAX_EXPIRES_SECONDS +
447
- " (7 days, V4 hard cap)", true);
448
- }
383
+ sharedRequest.requirePresignKey(opts, "presignedUploadPolicy");
384
+ var minBytes = sharedRequest.resolvePresignUploadMinBytes(opts);
385
+ var expiresIn = sharedRequest.resolvePresignExpires(opts, "presignedUploadPolicy", "V4");
449
386
 
450
387
  var date = opts.date || new Date();
451
388
  var amzDate = sigv4.formatAmzDate(date);
@@ -102,11 +102,7 @@ function create(config) {
102
102
  function head(key) {
103
103
  var url = _keyToUrl(baseUrl, key);
104
104
  return _request("HEAD", url, null, headers, reqOpts).then(function (res) {
105
- return {
106
- size: res.headers["content-length"] ? parseInt(res.headers["content-length"], 10) : null,
107
- etag: res.headers.etag,
108
- lastModified: res.headers["last-modified"] ? Date.parse(res.headers["last-modified"]) : null,
109
- };
105
+ return sharedRequest.mapHeadResponse(res);
110
106
  });
111
107
  }
112
108
 
@@ -17,8 +17,17 @@
17
17
  */
18
18
 
19
19
  var httpClient = require("../http-client");
20
+ var C = require("../constants");
21
+ var numericBounds = require("../numeric-bounds");
22
+ var requestHelpers = require("../request-helpers");
20
23
  var { ObjectStoreError } = require("../framework-error");
21
24
 
25
+ // Same code-first error factory the object-store backends use; the bare
26
+ // cloud-provider-style codes (INVALID_KEY / INVALID_EXPIRES / ...) are the
27
+ // existing object-store convention, swept to namespace/kebab-case for the
28
+ // whole family in the v1.0 error-code pass.
29
+ var _err = ObjectStoreError.factory;
30
+
22
31
  function request(method, url, headers, body, opts) {
23
32
  opts = opts || {};
24
33
  var req = {
@@ -35,4 +44,123 @@ function request(method, url, headers, body, opts) {
35
44
  return httpClient.request(req);
36
45
  }
37
46
 
47
+ // ---- Shared presign opts validation (gcs / sigv4) ----
48
+ // Every presigning entry point (presigned GET/PUT URLs and the POST-form
49
+ // upload policy) opened with the same key + expiry bounds checks, varying
50
+ // only in the message prefix and the signature-version label in the
51
+ // hard-cap text. Centralized so the bounds are enforced identically and a
52
+ // future change (a new cap, a different default) is one edit.
53
+ var PRESIGN_DEFAULT_EXPIRES_SECONDS = C.TIME.minutes(15) / C.TIME.seconds(1); // 15 minutes
54
+ var PRESIGN_MAX_EXPIRES_SECONDS = C.TIME.days(7) / C.TIME.seconds(1); // 7 days
55
+ var PRESIGN_MIN_EXPIRES_SECONDS = 1;
56
+
57
+ function requirePresignKey(opts, msgPrefix) {
58
+ if (!opts.key || typeof opts.key !== "string") {
59
+ throw _err("INVALID_KEY", msgPrefix + ": key is required", true);
60
+ }
61
+ if (opts.key.indexOf("\0") !== -1) {
62
+ throw _err("INVALID_KEY", "null byte in key", true);
63
+ }
64
+ }
65
+
66
+ // hardCapLabel names the signing version in the cap text ("V4" / "SigV4"
67
+ // → "(7 days, V4 hard cap)"); an empty / omitted label yields the plain
68
+ // "(7 days)" tail used by the Azure SAS path, whose cap is the same 7 days
69
+ // but is not a SigV4 family limit.
70
+ function resolvePresignExpires(opts, msgPrefix, hardCapLabel) {
71
+ var expiresIn = opts.expiresIn != null ? opts.expiresIn : PRESIGN_DEFAULT_EXPIRES_SECONDS;
72
+ if (typeof expiresIn !== "number" ||
73
+ expiresIn < PRESIGN_MIN_EXPIRES_SECONDS ||
74
+ expiresIn > PRESIGN_MAX_EXPIRES_SECONDS) {
75
+ var capTail = hardCapLabel ? " (7 days, " + hardCapLabel + " hard cap)" : " (7 days)";
76
+ throw _err("INVALID_EXPIRES",
77
+ msgPrefix + ": expiresIn must be a number of seconds between " +
78
+ PRESIGN_MIN_EXPIRES_SECONDS + " and " + PRESIGN_MAX_EXPIRES_SECONDS +
79
+ capTail, true);
80
+ }
81
+ return expiresIn;
82
+ }
83
+
84
+ // POST-form upload policy size bounds — returns the resolved minBytes
85
+ // (defaulting to 0). maxBytes is validated here but consumed by the
86
+ // caller (it becomes the content-length-range upper bound).
87
+ function resolvePresignUploadMinBytes(opts) {
88
+ if (typeof opts.maxBytes !== "number" || !Number.isFinite(opts.maxBytes) ||
89
+ opts.maxBytes <= 0) {
90
+ throw _err("INVALID_MAX_BYTES",
91
+ "presignedUploadPolicy: maxBytes (positive number of bytes) is required — " +
92
+ "POST-form policy enforces body size via the content-length-range condition; " +
93
+ "use presignedUploadUrl if size enforcement is not needed", true);
94
+ }
95
+ if (opts.minBytes !== undefined && !numericBounds.isNonNegativeFiniteInt(opts.minBytes)) {
96
+ throw _err("INVALID_MIN_BYTES",
97
+ "presignedUploadPolicy: minBytes must be a non-negative finite integer; got " +
98
+ numericBounds.shape(opts.minBytes), true);
99
+ }
100
+ return opts.minBytes !== undefined ? opts.minBytes : 0;
101
+ }
102
+
103
+ // ---- Shared conditional-GET request + response mapping (gcs / sigv4 / azure) ----
104
+ // Every backend's getResponse() set the same RFC 7232/7233 conditional
105
+ // headers (Range + If-Match family), mapped the HTTP response into the same
106
+ // { statusCode, body, etag, lastModified, contentRange, size, contentType }
107
+ // shape, and turned a 304 into the same short-circuit result. Only the Range
108
+ // header NAME differs (S3/GCS "Range" vs Azure "x-ms-range"), so it is a
109
+ // parameter; the rest is identical.
110
+ function applyConditionalGetHeaders(target, opts, rangeHeaderName) {
111
+ if (opts.range) {
112
+ target[rangeHeaderName] = "bytes=" + opts.range.start + "-" + opts.range.end;
113
+ }
114
+ if (opts.ifNoneMatch) target["If-None-Match"] = opts.ifNoneMatch;
115
+ if (opts.ifMatch) target["If-Match"] = opts.ifMatch;
116
+ if (opts.ifModifiedSince) target["If-Modified-Since"] = opts.ifModifiedSince;
117
+ if (opts.ifUnmodifiedSince) target["If-Unmodified-Since"] = opts.ifUnmodifiedSince;
118
+ return target;
119
+ }
120
+
121
+ function mapGetResponse(res) {
122
+ return {
123
+ statusCode: res.statusCode,
124
+ body: res.body,
125
+ etag: res.headers && res.headers.etag,
126
+ lastModified: res.headers && res.headers["last-modified"]
127
+ ? Date.parse(res.headers["last-modified"]) : null,
128
+ contentRange: res.headers && res.headers["content-range"] || null,
129
+ size: res.headers && res.headers["content-length"]
130
+ ? parseInt(res.headers["content-length"], 10) : null,
131
+ contentType: res.headers && res.headers["content-type"] || null,
132
+ };
133
+ }
134
+
135
+ // HEAD response projection — { size, etag, lastModified } from the response
136
+ // headers. Shared by the header-driven backends (azure / sigv4 / http-put);
137
+ // gcs.head is NOT a member (it reads a JSON metadata body, not headers).
138
+ function mapHeadResponse(res) {
139
+ return {
140
+ size: res.headers["content-length"] ? parseInt(res.headers["content-length"], 10) : null,
141
+ etag: res.headers.etag,
142
+ lastModified: res.headers["last-modified"] ? Date.parse(res.headers["last-modified"]) : null,
143
+ };
144
+ }
145
+
146
+ // The structured result a conditional GET returns when the server answers
147
+ // 304 Not Modified (httpClient surfaces it as a non-2xx error each backend
148
+ // catches and maps here).
149
+ function notModifiedGetResult() {
150
+ return {
151
+ statusCode: requestHelpers.HTTP_STATUS.NOT_MODIFIED,
152
+ body: null, etag: null, lastModified: null,
153
+ };
154
+ }
155
+
38
156
  module.exports = request;
157
+ module.exports.applyConditionalGetHeaders = applyConditionalGetHeaders;
158
+ module.exports.mapGetResponse = mapGetResponse;
159
+ module.exports.mapHeadResponse = mapHeadResponse;
160
+ module.exports.notModifiedGetResult = notModifiedGetResult;
161
+ module.exports.PRESIGN_DEFAULT_EXPIRES_SECONDS = PRESIGN_DEFAULT_EXPIRES_SECONDS;
162
+ module.exports.PRESIGN_MAX_EXPIRES_SECONDS = PRESIGN_MAX_EXPIRES_SECONDS;
163
+ module.exports.PRESIGN_MIN_EXPIRES_SECONDS = PRESIGN_MIN_EXPIRES_SECONDS;
164
+ module.exports.requirePresignKey = requirePresignKey;
165
+ module.exports.resolvePresignExpires = resolvePresignExpires;
166
+ module.exports.resolvePresignUploadMinBytes = resolvePresignUploadMinBytes;
@@ -50,6 +50,7 @@
50
50
  * BUCKET_ALREADY_OWNED, BUCKET_NOT_EMPTY, etc.).
51
51
  */
52
52
  var nodeCrypto = require("node:crypto");
53
+ var safeBuffer = require("../safe-buffer");
53
54
  var C = require("../constants");
54
55
  var requestHelpers = require("../request-helpers");
55
56
  var sigv4 = require("./sigv4");
@@ -403,7 +404,7 @@ function _validateObjectKey(key) {
403
404
  if (typeof key !== "string" || key.length === 0) {
404
405
  throw _err("INVALID_KEY", "object key must be a non-empty string", true);
405
406
  }
406
- if (key.length > C.BYTES.kib(1)) {
407
+ if (safeBuffer.byteLengthOf(key) > C.BYTES.kib(1)) {
407
408
  throw _err("INVALID_KEY", "object key exceeds 1024 bytes (S3 limit)", true);
408
409
  }
409
410
  }
@@ -29,7 +29,6 @@ var { Readable } = require("node:stream");
29
29
  var safeXml = require("../parsers/safe-xml");
30
30
  var sharedRequest = require("./http-request");
31
31
  var C = require("../constants");
32
- var numericBounds = require("../numeric-bounds");
33
32
  var requestHelpers = require("../request-helpers");
34
33
  var { ObjectStoreError } = require("../framework-error");
35
34
  var safeUrl = require("../safe-url");
@@ -66,12 +65,6 @@ function _arrayify(value) {
66
65
  var SERVICE = "s3";
67
66
  var ALGORITHM = "AWS4-HMAC-SHA256";
68
67
 
69
- // Presigned-URL expiry bounds. The SigV4 spec caps query-string-signed
70
- // URLs at 7 days; below 1 second is effectively unsignable.
71
- var PRESIGN_DEFAULT_EXPIRES_SECONDS = C.TIME.minutes(15) / C.TIME.seconds(1); // 15 minutes
72
- var PRESIGN_MAX_EXPIRES_SECONDS = C.TIME.days(7) / C.TIME.seconds(1); // 7 days (SigV4 hard cap)
73
- var PRESIGN_MIN_EXPIRES_SECONDS = 1;
74
-
75
68
  var _err = ObjectStoreError.factory;
76
69
 
77
70
  // ---- SigV4 primitives ----
@@ -658,35 +651,16 @@ function create(config) {
658
651
  // param is in the SigV4 canonical request.
659
652
  if (opts.versionId) url.searchParams.set("versionId", opts.versionId);
660
653
  var headers = _makeSigned("GET", url, sha256Hex(Buffer.alloc(0)));
661
- if (opts.range) {
662
- headers["Range"] = "bytes=" + opts.range.start + "-" + opts.range.end;
663
- }
664
- if (opts.ifNoneMatch) headers["If-None-Match"] = opts.ifNoneMatch;
665
- if (opts.ifMatch) headers["If-Match"] = opts.ifMatch;
666
- if (opts.ifModifiedSince) headers["If-Modified-Since"] = opts.ifModifiedSince;
667
- if (opts.ifUnmodifiedSince) headers["If-Unmodified-Since"] = opts.ifUnmodifiedSince;
654
+ sharedRequest.applyConditionalGetHeaders(headers, opts, "Range");
668
655
  var localReqOpts = Object.assign({}, reqOpts, { _resolveOnRedirect: false });
669
656
  return _request("GET", url, headers, null, localReqOpts).then(function (res) {
670
- return {
671
- statusCode: res.statusCode,
672
- body: res.body,
673
- etag: res.headers && res.headers.etag,
674
- lastModified: res.headers && res.headers["last-modified"]
675
- ? Date.parse(res.headers["last-modified"]) : null,
676
- contentRange: res.headers && res.headers["content-range"] || null,
677
- size: res.headers && res.headers["content-length"]
678
- ? parseInt(res.headers["content-length"], 10) : null,
679
- contentType: res.headers && res.headers["content-type"] || null,
680
- };
657
+ return sharedRequest.mapGetResponse(res);
681
658
  }, function (err) {
682
659
  // 304 surfaces as a "non-2xx error" via httpClient; propagate it
683
660
  // as a structured 304 result instead so operator routes get
684
661
  // the conditional-GET short-circuit they expect.
685
662
  if (err && err.statusCode === requestHelpers.HTTP_STATUS.NOT_MODIFIED) {
686
- return {
687
- statusCode: requestHelpers.HTTP_STATUS.NOT_MODIFIED,
688
- body: null, etag: null, lastModified: null,
689
- };
663
+ return sharedRequest.notModifiedGetResult();
690
664
  }
691
665
  throw err;
692
666
  });
@@ -698,11 +672,7 @@ function create(config) {
698
672
  if (opts.versionId) url.searchParams.set("versionId", opts.versionId);
699
673
  var headers = _makeSigned("HEAD", url, sha256Hex(Buffer.alloc(0)));
700
674
  return _request("HEAD", url, headers, null, reqOpts).then(function (res) {
701
- return {
702
- size: res.headers["content-length"] ? parseInt(res.headers["content-length"], 10) : null,
703
- etag: res.headers.etag,
704
- lastModified: res.headers["last-modified"] ? Date.parse(res.headers["last-modified"]) : null,
705
- };
675
+ return sharedRequest.mapHeadResponse(res);
706
676
  }, function (e) {
707
677
  // A missing key surfaces as the framework NOT_FOUND code — the same
708
678
  // contract local.js head() exposes and that deleteKey already maps 404
@@ -758,21 +728,8 @@ function create(config) {
758
728
 
759
729
  function _presign(method, opts) {
760
730
  opts = opts || {};
761
- if (!opts.key || typeof opts.key !== "string") {
762
- throw _err("INVALID_KEY", "presigned URL: key is required", true);
763
- }
764
- if (opts.key.indexOf("\0") !== -1) {
765
- throw _err("INVALID_KEY", "null byte in key", true);
766
- }
767
- var expiresIn = opts.expiresIn != null ? opts.expiresIn : PRESIGN_DEFAULT_EXPIRES_SECONDS;
768
- if (typeof expiresIn !== "number" ||
769
- expiresIn < PRESIGN_MIN_EXPIRES_SECONDS ||
770
- expiresIn > PRESIGN_MAX_EXPIRES_SECONDS) {
771
- throw _err("INVALID_EXPIRES",
772
- "presigned URL: expiresIn must be a number of seconds between " +
773
- PRESIGN_MIN_EXPIRES_SECONDS + " and " + PRESIGN_MAX_EXPIRES_SECONDS +
774
- " (7 days, SigV4 hard cap)", true);
775
- }
731
+ sharedRequest.requirePresignKey(opts, "presigned URL");
732
+ var expiresIn = sharedRequest.resolvePresignExpires(opts, "presigned URL", "SigV4");
776
733
 
777
734
  // Validate opts.responseHeaders shape — operators pass camelCase
778
735
  // keys; refuse unknown keys at config-time so a typo surfaces at
@@ -876,34 +833,9 @@ function create(config) {
876
833
  // Reference: AWS S3 "Browser-Based Uploads Using POST".
877
834
  function presignedUploadPolicy(opts) {
878
835
  opts = opts || {};
879
- if (!opts.key || typeof opts.key !== "string") {
880
- throw _err("INVALID_KEY", "presignedUploadPolicy: key is required", true);
881
- }
882
- if (opts.key.indexOf("\0") !== -1) {
883
- throw _err("INVALID_KEY", "null byte in key", true);
884
- }
885
- if (typeof opts.maxBytes !== "number" || !Number.isFinite(opts.maxBytes) ||
886
- opts.maxBytes <= 0) {
887
- throw _err("INVALID_MAX_BYTES",
888
- "presignedUploadPolicy: maxBytes (positive number of bytes) is required — " +
889
- "POST-form policy enforces body size via the content-length-range condition; " +
890
- "use presignedUploadUrl if size enforcement is not needed", true);
891
- }
892
- if (opts.minBytes !== undefined && !numericBounds.isNonNegativeFiniteInt(opts.minBytes)) {
893
- throw _err("INVALID_MIN_BYTES",
894
- "presignedUploadPolicy: minBytes must be a non-negative finite integer; got " +
895
- numericBounds.shape(opts.minBytes), true);
896
- }
897
- var minBytes = opts.minBytes !== undefined ? opts.minBytes : 0;
898
- var expiresIn = opts.expiresIn != null ? opts.expiresIn : PRESIGN_DEFAULT_EXPIRES_SECONDS;
899
- if (typeof expiresIn !== "number" ||
900
- expiresIn < PRESIGN_MIN_EXPIRES_SECONDS ||
901
- expiresIn > PRESIGN_MAX_EXPIRES_SECONDS) {
902
- throw _err("INVALID_EXPIRES",
903
- "presignedUploadPolicy: expiresIn must be a number of seconds between " +
904
- PRESIGN_MIN_EXPIRES_SECONDS + " and " + PRESIGN_MAX_EXPIRES_SECONDS +
905
- " (7 days, SigV4 hard cap)", true);
906
- }
836
+ sharedRequest.requirePresignKey(opts, "presignedUploadPolicy");
837
+ var minBytes = sharedRequest.resolvePresignUploadMinBytes(opts);
838
+ var expiresIn = sharedRequest.resolvePresignExpires(opts, "presignedUploadPolicy", "SigV4");
907
839
 
908
840
  var date = opts.date || new Date();
909
841
  var amzDate = _formatAmzDate(date);
@@ -37,6 +37,7 @@ var safeBuffer = require("./safe-buffer");
37
37
  var validateOpts = require("./validate-opts");
38
38
  var safeUrl = require("./safe-url");
39
39
  var pb = require("./protobuf-encoder");
40
+ var boundedMap = require("./bounded-map");
40
41
  var { defineClass } = require("./framework-error");
41
42
 
42
43
  var OtlpExporterError = defineClass("OtlpExporterError", { alwaysPermanent: true });
@@ -167,15 +168,13 @@ function _bundleSpans(spans) {
167
168
  for (var i = 0; i < spans.length; i++) {
168
169
  var s = spans[i];
169
170
  var resKey = JSON.stringify(s.resource || {});
170
- var bucket = byResource.get(resKey);
171
- if (!bucket) {
172
- bucket = {
171
+ var bucket = boundedMap.getOrInsert(byResource, resKey, function () {
172
+ return {
173
173
  resource: s.resource || {},
174
174
  scope: s.scope || { name: "blamejs", version: null },
175
175
  spans: [],
176
176
  };
177
- byResource.set(resKey, bucket);
178
- }
177
+ });
179
178
  bucket.spans.push(s);
180
179
  }
181
180
  var resourceSpans = [];
@@ -380,15 +379,13 @@ function _bundleSpansToProto(spansArray) {
380
379
  for (var i = 0; i < spansArray.length; i += 1) {
381
380
  var s = spansArray[i];
382
381
  var resKey = JSON.stringify(s.resource || {});
383
- var bucket = byResource.get(resKey);
384
- if (!bucket) {
385
- bucket = {
382
+ var bucket = boundedMap.getOrInsert(byResource, resKey, function () {
383
+ return {
386
384
  resource: s.resource || {},
387
385
  scope: s.scope || { name: "blamejs", version: null },
388
386
  spans: [],
389
387
  };
390
- byResource.set(resKey, bucket);
391
- }
388
+ });
392
389
  bucket.spans.push(s);
393
390
  }
394
391
  var resourceSpansPieces = [];
@@ -493,21 +490,8 @@ function create(opts) {
493
490
  var inFlight = false;
494
491
  var stopping = false;
495
492
 
496
- var auditOn = opts.audit !== false;
497
- function _emitMetric(verb, n, labels) {
498
- try { observability().safeEvent("otlp.exporter." + verb, n || 1, labels || {}); }
499
- catch (_e) { /* drop-silent */ }
500
- }
501
- function _emitAudit(action, outcome, metadata) {
502
- if (!auditOn) return;
503
- try {
504
- audit().safeEmit({
505
- action: "system.observability.otlp_exporter." + action,
506
- outcome: outcome,
507
- metadata: metadata || {},
508
- });
509
- } catch (_e) { /* drop-silent — audit is best-effort, never crashes the exporter */ }
510
- }
493
+ var _emitMetric = observability().namespaced("otlp.exporter");
494
+ var _emitAudit = audit().namespaced("system.observability.otlp_exporter", opts.audit);
511
495
 
512
496
  function queue_(span) {
513
497
  if (stopping) { droppedExportFailed += 1; return; }