@blamejs/core 0.9.12 → 0.9.15

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 (119) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/lib/a2a.js +11 -11
  3. package/lib/acme.js +5 -5
  4. package/lib/ai-input.js +2 -2
  5. package/lib/api-key.js +4 -4
  6. package/lib/api-snapshot.js +10 -7
  7. package/lib/app-shutdown.js +2 -2
  8. package/lib/app.js +5 -5
  9. package/lib/archive.js +8 -8
  10. package/lib/argon2-builtin.js +2 -2
  11. package/lib/atomic-file.js +53 -53
  12. package/lib/audit-sign.js +8 -8
  13. package/lib/audit-tools.js +22 -22
  14. package/lib/audit.js +29 -17
  15. package/lib/auth/dpop.js +3 -3
  16. package/lib/auth/sd-jwt-vc.js +2 -2
  17. package/lib/backup/bundle.js +17 -17
  18. package/lib/backup/index.js +36 -36
  19. package/lib/budr.js +3 -3
  20. package/lib/bundler.js +20 -20
  21. package/lib/circuit-breaker.js +24 -9
  22. package/lib/cli.js +25 -26
  23. package/lib/cluster.js +2 -2
  24. package/lib/compliance-sanctions.js +2 -2
  25. package/lib/config-drift.js +15 -15
  26. package/lib/content-credentials.js +4 -4
  27. package/lib/credential-hash.js +3 -3
  28. package/lib/crypto.js +145 -0
  29. package/lib/daemon.js +19 -19
  30. package/lib/db-file-lifecycle.js +24 -24
  31. package/lib/db-schema.js +2 -2
  32. package/lib/db.js +35 -35
  33. package/lib/dev.js +10 -10
  34. package/lib/dr-runbook.js +5 -5
  35. package/lib/dsr.js +22 -15
  36. package/lib/dual-control.js +2 -2
  37. package/lib/external-db-migrate.js +2 -2
  38. package/lib/external-db.js +2 -2
  39. package/lib/fdx.js +2 -2
  40. package/lib/file-upload.js +30 -30
  41. package/lib/flag-providers.js +4 -4
  42. package/lib/gate-contract.js +5 -5
  43. package/lib/graphql-federation.js +4 -7
  44. package/lib/honeytoken.js +6 -6
  45. package/lib/http-client-cookie-jar.js +6 -6
  46. package/lib/http-client.js +18 -18
  47. package/lib/i18n.js +5 -5
  48. package/lib/inbox.js +21 -15
  49. package/lib/keychain.js +9 -9
  50. package/lib/legal-hold.js +2 -2
  51. package/lib/local-db-thin.js +9 -9
  52. package/lib/log-stream-local.js +17 -17
  53. package/lib/log-stream-syslog.js +2 -2
  54. package/lib/log-stream.js +3 -3
  55. package/lib/mail-bounce.js +2 -2
  56. package/lib/mail-mdn.js +2 -2
  57. package/lib/mail-srs.js +2 -2
  58. package/lib/mail.js +4 -4
  59. package/lib/mcp.js +2 -2
  60. package/lib/metrics.js +249 -2
  61. package/lib/middleware/api-encrypt.js +16 -16
  62. package/lib/middleware/body-parser.js +16 -16
  63. package/lib/middleware/compression.js +3 -3
  64. package/lib/middleware/csp-nonce.js +4 -4
  65. package/lib/middleware/health.js +7 -7
  66. package/lib/middleware/idempotency-key.js +250 -0
  67. package/lib/migrations.js +3 -3
  68. package/lib/mtls-ca.js +26 -26
  69. package/lib/mtls-engine-default.js +5 -5
  70. package/lib/network-dns.js +2 -2
  71. package/lib/network-nts.js +2 -2
  72. package/lib/network-proxy.js +3 -3
  73. package/lib/network-smtp-policy.js +2 -2
  74. package/lib/network-tls.js +17 -17
  75. package/lib/network.js +13 -13
  76. package/lib/notify.js +3 -3
  77. package/lib/object-store/gcs-bucket-ops.js +2 -2
  78. package/lib/object-store/gcs.js +5 -5
  79. package/lib/object-store/index.js +6 -6
  80. package/lib/object-store/local.js +19 -19
  81. package/lib/object-store/sigv4.js +3 -3
  82. package/lib/observability-tracer.js +4 -4
  83. package/lib/otel-export.js +3 -3
  84. package/lib/pagination.js +5 -5
  85. package/lib/parsers/safe-xml.js +3 -3
  86. package/lib/pqc-agent.js +116 -26
  87. package/lib/pqc-gate.js +5 -5
  88. package/lib/pubsub-redis.js +2 -2
  89. package/lib/queue-local.js +3 -3
  90. package/lib/queue.js +2 -2
  91. package/lib/redis-client.js +4 -4
  92. package/lib/restore-bundle.js +18 -18
  93. package/lib/restore-rollback.js +34 -34
  94. package/lib/restore.js +16 -16
  95. package/lib/retry.js +50 -0
  96. package/lib/router.js +13 -13
  97. package/lib/sandbox.js +8 -8
  98. package/lib/sec-cyber.js +3 -3
  99. package/lib/security-assert.js +2 -2
  100. package/lib/seeders.js +4 -4
  101. package/lib/self-update-standalone-verifier.js +280 -0
  102. package/lib/self-update.js +32 -26
  103. package/lib/session-device-binding.js +2 -2
  104. package/lib/static.js +22 -22
  105. package/lib/template.js +19 -19
  106. package/lib/testing.js +7 -7
  107. package/lib/tls-exporter.js +5 -5
  108. package/lib/tracing.js +3 -3
  109. package/lib/vault/index.js +11 -11
  110. package/lib/vault/passphrase-ops.js +37 -37
  111. package/lib/vault/passphrase-source.js +2 -2
  112. package/lib/vault/rotate.js +70 -66
  113. package/lib/vault/seal-pem-file.js +26 -26
  114. package/lib/watcher.js +23 -23
  115. package/lib/webhook.js +10 -10
  116. package/lib/worker-pool.js +6 -6
  117. package/lib/ws-client.js +4 -4
  118. package/package.json +1 -1
  119. package/sbom.cdx.json +6 -6
@@ -35,7 +35,7 @@ var C = require("./constants");
35
35
  var { generateToken } = require("./crypto");
36
36
  var cryptoField = require("./crypto-field");
37
37
  var lazyRequire = require("./lazy-require");
38
- var nb = require("./numeric-bounds");
38
+ var numericBounds = require("./numeric-bounds");
39
39
  var safeJson = require("./safe-json");
40
40
  var scheduler = require("./scheduler");
41
41
  var { QueueError } = require("./framework-error");
@@ -388,10 +388,10 @@ function create(_config) {
388
388
  opts = opts || {};
389
389
  var limit = 100;
390
390
  if (opts.limit !== undefined) {
391
- if (!nb.isPositiveFiniteInt(opts.limit)) {
391
+ if (!numericBounds.isPositiveFiniteInt(opts.limit)) {
392
392
  throw new QueueError("queue/bad-opt",
393
393
  "queue.dlqList: limit must be a positive finite integer; got " +
394
- nb.shape(opts.limit), true);
394
+ numericBounds.shape(opts.limit), true);
395
395
  }
396
396
  limit = opts.limit;
397
397
  }
package/lib/queue.js CHANGED
@@ -43,7 +43,7 @@
43
43
  */
44
44
  var C = require("./constants");
45
45
  var clusterStorage = require("./cluster-storage");
46
- var crypto = require("./crypto");
46
+ var bCrypto = require("./crypto");
47
47
  var lazyRequire = require("./lazy-require");
48
48
  var { boot } = require("./log");
49
49
  var numericChecks = require("./numeric-checks");
@@ -877,7 +877,7 @@ function enqueueFlow(spec) {
877
877
  return Promise.reject(e);
878
878
  }
879
879
 
880
- var flowId = "flow-" + crypto.generateToken(C.BYTES.bytes(8));
880
+ var flowId = "flow-" + bCrypto.generateToken(C.BYTES.bytes(8));
881
881
 
882
882
  return observability.tap("queue.enqueueFlow",
883
883
  { queueName: spec.queueName, flowId: flowId, childCount: spec.children.length },
@@ -25,8 +25,8 @@
25
25
  * callers can branch on transport vs server-side errors.
26
26
  */
27
27
  var net = require("node:net");
28
- var tls = require("node:tls");
29
- var url = require("node:url");
28
+ var nodeTls = require("node:tls");
29
+ var nodeUrl = require("node:url");
30
30
  var C = require("./constants");
31
31
  var safeAsync = require("./safe-async");
32
32
  var validateOpts = require("./validate-opts");
@@ -319,7 +319,7 @@ function create(opts) {
319
319
  var tlsConnectOpts = { host: host, port: port };
320
320
  if (servername) tlsConnectOpts.servername = servername;
321
321
  if (caBundle) tlsConnectOpts.ca = caBundle;
322
- sock = tls.connect(tlsConnectOpts, onOk);
322
+ sock = nodeTls.connect(tlsConnectOpts, onOk);
323
323
  } else {
324
324
  sock = net.connect({ host: host, port: port }, onOk);
325
325
  }
@@ -457,7 +457,7 @@ function create(opts) {
457
457
  // Empty-username + non-empty password is the legacy single-arg AUTH form.
458
458
  function _parseRedisUrl(s) {
459
459
  var u;
460
- try { u = new url.URL(s); }
460
+ try { u = new nodeUrl.URL(s); }
461
461
  catch (e) {
462
462
  throw _err("BAD_URL", "redis url parse failed: " + ((e && e.message) || String(e)));
463
463
  }
@@ -45,8 +45,8 @@
45
45
  * Backup-bundle reader — verify the manifest signature, list bundle contents without decrypting, and cherry-pick a restore subset to a staging directory the caller atomically swaps into place.
46
46
  */
47
47
 
48
- var fs = require("fs");
49
- var path = require("path");
48
+ var nodeFs = require("fs");
49
+ var nodePath = require("path");
50
50
  var atomicFile = require("./atomic-file");
51
51
  var backupCrypto = require("./backup/crypto");
52
52
  var backupManifest = require("./backup/manifest");
@@ -65,7 +65,7 @@ function _cleanupStaging(stagingDir) {
65
65
  // Best-effort recursive remove — if cleanup fails, surface that to
66
66
  // the caller via stderr but never override the original error
67
67
  // we're already throwing.
68
- try { fs.rmSync(stagingDir, { recursive: true, force: true }); }
68
+ try { nodeFs.rmSync(stagingDir, { recursive: true, force: true }); }
69
69
  catch (_e) { /* best-effort */ }
70
70
  }
71
71
 
@@ -123,15 +123,15 @@ function _cleanupStaging(stagingDir) {
123
123
  async function extract(opts) {
124
124
  var t0 = Date.now();
125
125
  opts = opts || {};
126
- if (typeof opts.bundleDir !== "string" || !fs.existsSync(opts.bundleDir)) {
126
+ if (typeof opts.bundleDir !== "string" || !nodeFs.existsSync(opts.bundleDir)) {
127
127
  throw new RestoreBundleError("restore-bundle/no-bundle",
128
128
  "extract: opts.bundleDir is required and must exist");
129
129
  }
130
130
  validateOpts.requireNonEmptyString(opts.stagingDir, "extract: opts.stagingDir", RestoreBundleError, "restore-bundle/no-staging");
131
- if (fs.existsSync(opts.stagingDir)) {
131
+ if (nodeFs.existsSync(opts.stagingDir)) {
132
132
  throw new RestoreBundleError("restore-bundle/staging-exists",
133
133
  "extract: stagingDir already exists: " + opts.stagingDir +
134
- " (refusing to merge into existing directory — pick a fresh path)");
134
+ " (refusing to merge into existing directory — pick a fresh nodePath)");
135
135
  }
136
136
  if (!Buffer.isBuffer(opts.passphrase) && typeof opts.passphrase !== "string") {
137
137
  throw new RestoreBundleError("restore-bundle/no-passphrase",
@@ -145,14 +145,14 @@ async function extract(opts) {
145
145
 
146
146
  // 1. Read + parse + validate manifest
147
147
  _emit(progress, { phase: "read_manifest" });
148
- var manifestPath = path.join(bundleDir, "manifest.json");
149
- if (!fs.existsSync(manifestPath)) {
148
+ var manifestPath = nodePath.join(bundleDir, "manifest.json");
149
+ if (!nodeFs.existsSync(manifestPath)) {
150
150
  throw new RestoreBundleError("restore-bundle/missing-manifest",
151
151
  "extract: bundleDir has no manifest.json — bundle is incomplete or not a blamejs backup");
152
152
  }
153
153
  var manifest;
154
154
  try {
155
- manifest = backupManifest.parse(fs.readFileSync(manifestPath, "utf8"));
155
+ manifest = backupManifest.parse(nodeFs.readFileSync(manifestPath, "utf8"));
156
156
  } catch (e) {
157
157
  if (e && e.isBackupManifestError) throw e;
158
158
  throw new RestoreBundleError("restore-bundle/bad-manifest",
@@ -215,13 +215,13 @@ async function extract(opts) {
215
215
  continue;
216
216
  }
217
217
 
218
- var blobPath = path.join(bundleDir, entry.encryptedPath);
219
- if (!fs.existsSync(blobPath)) {
218
+ var blobPath = nodePath.join(bundleDir, entry.encryptedPath);
219
+ if (!nodeFs.existsSync(blobPath)) {
220
220
  throw new RestoreBundleError("restore-bundle/missing-blob",
221
221
  "extract: manifest references '" + entry.encryptedPath +
222
222
  "' but the bundle has no such file");
223
223
  }
224
- var blob = fs.readFileSync(blobPath);
224
+ var blob = nodeFs.readFileSync(blobPath);
225
225
  if (blob.length !== entry.encryptedSize) {
226
226
  throw new RestoreBundleError("restore-bundle/size-mismatch",
227
227
  "extract: blob '" + entry.encryptedPath + "' has size " + blob.length +
@@ -257,8 +257,8 @@ async function extract(opts) {
257
257
  " — bundle is corrupted or manifest tampered");
258
258
  }
259
259
 
260
- var destPath = path.join(stagingDir, entry.relativePath);
261
- atomicFile.ensureDir(path.dirname(destPath));
260
+ var destPath = nodePath.join(stagingDir, entry.relativePath);
261
+ atomicFile.ensureDir(nodePath.dirname(destPath));
262
262
  atomicFile.writeSync(destPath, plaintext, { fileMode: 0o600 });
263
263
 
264
264
  fileCount++;
@@ -321,16 +321,16 @@ async function extract(opts) {
321
321
  */
322
322
  function inspect(opts) {
323
323
  opts = opts || {};
324
- if (typeof opts.bundleDir !== "string" || !fs.existsSync(opts.bundleDir)) {
324
+ if (typeof opts.bundleDir !== "string" || !nodeFs.existsSync(opts.bundleDir)) {
325
325
  throw new RestoreBundleError("restore-bundle/no-bundle",
326
326
  "inspect: opts.bundleDir is required and must exist");
327
327
  }
328
- var manifestPath = path.join(opts.bundleDir, "manifest.json");
329
- if (!fs.existsSync(manifestPath)) {
328
+ var manifestPath = nodePath.join(opts.bundleDir, "manifest.json");
329
+ if (!nodeFs.existsSync(manifestPath)) {
330
330
  throw new RestoreBundleError("restore-bundle/missing-manifest",
331
331
  "inspect: bundleDir has no manifest.json");
332
332
  }
333
- return backupManifest.parse(fs.readFileSync(manifestPath, "utf8"));
333
+ return backupManifest.parse(nodeFs.readFileSync(manifestPath, "utf8"));
334
334
  }
335
335
 
336
336
  module.exports = {
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @intro
8
8
  * Backup-restore safety net — atomic dataDir swap with a versioned
9
- * rollback path. The primitive `b.restore` calls to put a
9
+ * rollback nodePath. The primitive `b.restore` calls to put a
10
10
  * freshly-decrypted bundle into place: filesystem rename is atomic
11
11
  * on POSIX (and on Windows when nothing has the dir open), so the
12
12
  * swap either fully completes or the previous `dataDir` is
@@ -39,14 +39,14 @@
39
39
  * corrupting state.
40
40
  *
41
41
  * @card
42
- * Backup-restore safety net — atomic dataDir swap with a versioned rollback path.
42
+ * Backup-restore safety net — atomic dataDir swap with a versioned rollback nodePath.
43
43
  */
44
44
 
45
- var fs = require("fs");
46
- var path = require("path");
45
+ var nodeFs = require("fs");
46
+ var nodePath = require("path");
47
47
  var atomicFile = require("./atomic-file");
48
48
  var C = require("./constants");
49
- var nb = require("./numeric-bounds");
49
+ var numericBounds = require("./numeric-bounds");
50
50
  var safeJson = require("./safe-json");
51
51
  var { defineClass } = require("./framework-error");
52
52
 
@@ -94,7 +94,7 @@ function _resolveRollbackRoot(opts) {
94
94
  */
95
95
  function swap(opts) {
96
96
  opts = opts || {};
97
- if (typeof opts.stagingDir !== "string" || !fs.existsSync(opts.stagingDir)) {
97
+ if (typeof opts.stagingDir !== "string" || !nodeFs.existsSync(opts.stagingDir)) {
98
98
  throw new RestoreRollbackError("restore-rollback/no-staging",
99
99
  "swap: opts.stagingDir is required and must exist");
100
100
  }
@@ -106,20 +106,20 @@ function swap(opts) {
106
106
  atomicFile.ensureDir(rollbackRoot);
107
107
 
108
108
  var swappedAt = atomicFile.pathTimestamp();
109
- var rollbackPath = path.join(rollbackRoot, swappedAt);
110
- var markerPath = path.join(rollbackRoot, swappedAt + ".marker.json");
109
+ var rollbackPath = nodePath.join(rollbackRoot, swappedAt);
110
+ var markerPath = nodePath.join(rollbackRoot, swappedAt + ".marker.json");
111
111
 
112
- if (fs.existsSync(rollbackPath) || fs.existsSync(markerPath)) {
112
+ if (nodeFs.existsSync(rollbackPath) || nodeFs.existsSync(markerPath)) {
113
113
  throw new RestoreRollbackError("restore-rollback/collision",
114
114
  "swap: a rollback at " + rollbackPath + " already exists — refusing to overwrite");
115
115
  }
116
116
 
117
- var hadDataDir = fs.existsSync(opts.dataDir);
117
+ var hadDataDir = nodeFs.existsSync(opts.dataDir);
118
118
 
119
- // Step 1: rename current dataDir → rollback path. Skipped on first
119
+ // Step 1: rename current dataDir → rollback nodePath. Skipped on first
120
120
  // restore (no existing dataDir).
121
121
  if (hadDataDir) {
122
- try { fs.renameSync(opts.dataDir, rollbackPath); }
122
+ try { nodeFs.renameSync(opts.dataDir, rollbackPath); }
123
123
  catch (e) {
124
124
  throw new RestoreRollbackError("restore-rollback/rename-existing-failed",
125
125
  "swap: could not move existing dataDir to rollback: " + ((e && e.message) || String(e)));
@@ -127,11 +127,11 @@ function swap(opts) {
127
127
  }
128
128
 
129
129
  // Step 2: rename staging → dataDir
130
- try { fs.renameSync(opts.stagingDir, opts.dataDir); }
130
+ try { nodeFs.renameSync(opts.stagingDir, opts.dataDir); }
131
131
  catch (e) {
132
132
  // Step 2 failed — try to undo step 1 so the operator's dataDir is back
133
133
  if (hadDataDir) {
134
- try { fs.renameSync(rollbackPath, opts.dataDir); }
134
+ try { nodeFs.renameSync(rollbackPath, opts.dataDir); }
135
135
  catch (_e) { /* dataDir is now in rollbackPath; operator must recover manually */ }
136
136
  }
137
137
  throw new RestoreRollbackError("restore-rollback/rename-staging-failed",
@@ -148,7 +148,7 @@ function swap(opts) {
148
148
  operator: opts.marker || null,
149
149
  };
150
150
  try {
151
- fs.writeFileSync(markerPath, JSON.stringify(marker, null, 2) + "\n", { mode: 0o600 });
151
+ nodeFs.writeFileSync(markerPath, JSON.stringify(marker, null, 2) + "\n", { mode: 0o600 });
152
152
  } catch (_e) { /* marker write is best-effort */ }
153
153
 
154
154
  return {
@@ -193,19 +193,19 @@ async function rollback(opts) {
193
193
  throw new RestoreRollbackError("restore-rollback/no-datadir",
194
194
  "rollback: opts.dataDir is required");
195
195
  }
196
- if (typeof opts.rollbackPath !== "string" || !fs.existsSync(opts.rollbackPath)) {
196
+ if (typeof opts.rollbackPath !== "string" || !nodeFs.existsSync(opts.rollbackPath)) {
197
197
  throw new RestoreRollbackError("restore-rollback/no-rollback",
198
198
  "rollback: opts.rollbackPath is required and must exist");
199
199
  }
200
200
 
201
201
  // Move the current dataDir aside (so the rollback's rename target is empty)
202
202
  var discardedAt = null;
203
- if (fs.existsSync(opts.dataDir)) {
203
+ if (nodeFs.existsSync(opts.dataDir)) {
204
204
  var rollbackRoot = _resolveRollbackRoot(opts);
205
205
  atomicFile.ensureDir(rollbackRoot);
206
206
  discardedAt = atomicFile.pathTimestamp();
207
- var discardedPath = path.join(rollbackRoot, "discarded-" + discardedAt);
208
- try { fs.renameSync(opts.dataDir, discardedPath); }
207
+ var discardedPath = nodePath.join(rollbackRoot, "discarded-" + discardedAt);
208
+ try { nodeFs.renameSync(opts.dataDir, discardedPath); }
209
209
  catch (e) {
210
210
  throw new RestoreRollbackError("restore-rollback/rename-existing-failed",
211
211
  "rollback: could not move current dataDir aside: " + ((e && e.message) || String(e)));
@@ -214,7 +214,7 @@ async function rollback(opts) {
214
214
  }
215
215
 
216
216
  // Rename the rollback dir back into dataDir's place
217
- try { fs.renameSync(opts.rollbackPath, opts.dataDir); }
217
+ try { nodeFs.renameSync(opts.rollbackPath, opts.dataDir); }
218
218
  catch (e) {
219
219
  throw new RestoreRollbackError("restore-rollback/rollback-rename-failed",
220
220
  "rollback: could not move rollback into dataDir: " + ((e && e.message) || String(e)) +
@@ -223,7 +223,7 @@ async function rollback(opts) {
223
223
 
224
224
  // Best-effort: clean up the marker file alongside the rollback path
225
225
  var markerPath = opts.rollbackPath + ".marker.json";
226
- try { if (fs.existsSync(markerPath)) fs.unlinkSync(markerPath); }
226
+ try { if (nodeFs.existsSync(markerPath)) nodeFs.unlinkSync(markerPath); }
227
227
  catch (_e) { /* marker cleanup is best-effort */ }
228
228
 
229
229
  return {
@@ -258,22 +258,22 @@ async function rollback(opts) {
258
258
  function list(opts) {
259
259
  opts = opts || {};
260
260
  var rollbackRoot = _resolveRollbackRoot(opts);
261
- if (!fs.existsSync(rollbackRoot)) return [];
262
- var entries = fs.readdirSync(rollbackRoot, { withFileTypes: true });
261
+ if (!nodeFs.existsSync(rollbackRoot)) return [];
262
+ var entries = nodeFs.readdirSync(rollbackRoot, { withFileTypes: true });
263
263
  var out = [];
264
264
  for (var i = 0; i < entries.length; i++) {
265
265
  if (!entries[i].isDirectory()) continue;
266
266
  var name = entries[i].name;
267
267
  if (name.indexOf("discarded-") === 0) continue; // discarded dirs aren't restore points
268
- var p = path.join(rollbackRoot, name);
268
+ var p = nodePath.join(rollbackRoot, name);
269
269
  var markerPath = p + ".marker.json";
270
270
  var marker = null;
271
- if (fs.existsSync(markerPath)) {
272
- try { marker = safeJson.parse(fs.readFileSync(markerPath, "utf8"), { maxBytes: C.BYTES.kib(64) }); }
271
+ if (nodeFs.existsSync(markerPath)) {
272
+ try { marker = safeJson.parse(nodeFs.readFileSync(markerPath, "utf8"), { maxBytes: C.BYTES.kib(64) }); }
273
273
  catch (_e) { marker = null; }
274
274
  }
275
275
  var stat;
276
- try { stat = fs.statSync(p); } catch (_e) { continue; }
276
+ try { stat = nodeFs.statSync(p); } catch (_e) { continue; }
277
277
  out.push({
278
278
  rollbackPath: p,
279
279
  swappedAt: (marker && marker.swappedAt) || stat.mtime.toISOString(),
@@ -311,18 +311,18 @@ function list(opts) {
311
311
  */
312
312
  function purge(opts) {
313
313
  opts = opts || {};
314
- nb.requireNonNegativeFiniteIntIfPresent(opts.keep,
314
+ numericBounds.requireNonNegativeFiniteIntIfPresent(opts.keep,
315
315
  "restore-rollback.purge: opts.keep", RestoreRollbackError, "restore-rollback/bad-keep");
316
316
  var keep = opts.keep !== undefined ? opts.keep : 0;
317
317
  var rollbackRoot = _resolveRollbackRoot(opts);
318
- if (!fs.existsSync(rollbackRoot)) return { kept: keep, deleted: [] };
318
+ if (!nodeFs.existsSync(rollbackRoot)) return { kept: keep, deleted: [] };
319
319
  // Always sweep "discarded-*" dirs — they're never restore points
320
- var entries = fs.readdirSync(rollbackRoot, { withFileTypes: true });
320
+ var entries = nodeFs.readdirSync(rollbackRoot, { withFileTypes: true });
321
321
  var deleted = [];
322
322
  for (var i = 0; i < entries.length; i++) {
323
323
  if (entries[i].isDirectory() && entries[i].name.indexOf("discarded-") === 0) {
324
- var p = path.join(rollbackRoot, entries[i].name);
325
- try { fs.rmSync(p, { recursive: true, force: true }); deleted.push(p); }
324
+ var p = nodePath.join(rollbackRoot, entries[i].name);
325
+ try { nodeFs.rmSync(p, { recursive: true, force: true }); deleted.push(p); }
326
326
  catch (_e) { /* best-effort */ }
327
327
  }
328
328
  }
@@ -333,9 +333,9 @@ function purge(opts) {
333
333
  for (var j = 0; j < toDelete.length; j++) {
334
334
  var rbPath = toDelete[j].rollbackPath;
335
335
  var mkPath = rbPath + ".marker.json";
336
- try { fs.rmSync(rbPath, { recursive: true, force: true }); deleted.push(rbPath); }
336
+ try { nodeFs.rmSync(rbPath, { recursive: true, force: true }); deleted.push(rbPath); }
337
337
  catch (_e) { /* best-effort */ }
338
- try { if (fs.existsSync(mkPath)) fs.unlinkSync(mkPath); }
338
+ try { if (nodeFs.existsSync(mkPath)) nodeFs.unlinkSync(mkPath); }
339
339
  catch (_e) { /* best-effort */ }
340
340
  }
341
341
  return { kept: keep, deleted: deleted };
package/lib/restore.js CHANGED
@@ -51,11 +51,11 @@
51
51
  * manual recovery)
52
52
  */
53
53
 
54
- var fs = require("fs");
54
+ var nodeFs = require("fs");
55
55
  var os = require("os");
56
- var path = require("path");
56
+ var nodePath = require("path");
57
57
  var C = require("./constants");
58
- var crypto = require("./crypto");
58
+ var bCrypto = require("./crypto");
59
59
  var numericChecks = require("./numeric-checks");
60
60
  var restoreBundle = require("./restore-bundle");
61
61
  var restoreRollback = require("./restore-rollback");
@@ -128,11 +128,11 @@ function create(opts) {
128
128
  while (stack.length > 0) {
129
129
  var current = stack.pop();
130
130
  var entries;
131
- try { entries = fs.readdirSync(current, { withFileTypes: true }); }
131
+ try { entries = nodeFs.readdirSync(current, { withFileTypes: true }); }
132
132
  catch (_e) { continue; }
133
133
  for (var i = 0; i < entries.length; i++) {
134
134
  var entry = entries[i];
135
- var full = path.join(current, entry.name);
135
+ var full = nodePath.join(current, entry.name);
136
136
  if (entry.isDirectory()) {
137
137
  stack.push(full);
138
138
  } else if (entry.isFile()) {
@@ -141,7 +141,7 @@ function create(opts) {
141
141
  return { tooManyFiles: true, fileCount: fileCount };
142
142
  }
143
143
  try {
144
- totalBytes += fs.statSync(full).size;
144
+ totalBytes += nodeFs.statSync(full).size;
145
145
  if (totalBytes > maxPulledBytes) {
146
146
  return { tooManyBytes: true, totalBytes: totalBytes };
147
147
  }
@@ -200,8 +200,8 @@ function create(opts) {
200
200
  "inspect: bundle '" + bundleId + "' not in storage");
201
201
  }
202
202
  await _preflightBundleSize(bundleId);
203
- var pullDir = path.join(os.tmpdir(),
204
- "blamejs-restore-inspect-" + crypto.generateToken(4));
203
+ var pullDir = nodePath.join(os.tmpdir(),
204
+ "blamejs-restore-inspect-" + bCrypto.generateToken(4));
205
205
  try {
206
206
  await storage.readBundle(bundleId, pullDir);
207
207
  var pulled = _walkPullDirFootprint(pullDir);
@@ -217,7 +217,7 @@ function create(opts) {
217
217
  }
218
218
  return restoreBundle.inspect({ bundleDir: pullDir });
219
219
  } finally {
220
- try { fs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
220
+ try { nodeFs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
221
221
  }
222
222
  }
223
223
 
@@ -234,13 +234,13 @@ function create(opts) {
234
234
  "run: bundle '" + bundleId + "' not in storage");
235
235
  }
236
236
 
237
- var pullId = crypto.generateToken(4);
238
- var pullDir = path.join(os.tmpdir(), "blamejs-restore-pull-" + pullId);
239
- var stagingDir = path.join(os.tmpdir(), "blamejs-restore-staging-" + pullId);
237
+ var pullId = bCrypto.generateToken(4);
238
+ var pullDir = nodePath.join(os.tmpdir(), "blamejs-restore-pull-" + pullId);
239
+ var stagingDir = nodePath.join(os.tmpdir(), "blamejs-restore-staging-" + pullId);
240
240
 
241
241
  function _cleanupTmp() {
242
- try { fs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
243
- try { fs.rmSync(stagingDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
242
+ try { nodeFs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
243
+ try { nodeFs.rmSync(stagingDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
244
244
  }
245
245
 
246
246
  // 1. Pull bundle out of storage
@@ -319,7 +319,7 @@ function create(opts) {
319
319
  } catch (e) {
320
320
  // Pull dir is safe to clean (the source bundle is in storage);
321
321
  // staging stays for manual recovery.
322
- try { fs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
322
+ try { nodeFs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
323
323
  _emitAudit("restore.failure",
324
324
  { bundleId: bundleId, reason: "swap: " + ((e && e.message) || String(e)) },
325
325
  "failure");
@@ -331,7 +331,7 @@ function create(opts) {
331
331
  }
332
332
 
333
333
  // 4. Clean up the pull dir (source bundle still in storage)
334
- try { fs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
334
+ try { nodeFs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
335
335
 
336
336
  var summary = {
337
337
  bundleId: bundleId,
package/lib/retry.js CHANGED
@@ -445,8 +445,58 @@ class CircuitBreaker {
445
445
  }
446
446
  }
447
447
 
448
+ /**
449
+ * @primitive b.retry.withBreaker
450
+ * @signature b.retry.withBreaker(fn, opts)
451
+ * @since 0.9.13
452
+ * @status stable
453
+ * @related b.retry.withRetry, b.circuitBreaker.create
454
+ *
455
+ * Compose `withRetry` + a CircuitBreaker so one retry-loop invocation
456
+ * counts as exactly one breaker call. The breaker observes the
457
+ * eventual outcome of the retry loop (success or final failure), not
458
+ * each intermediate retry attempt — otherwise every retried call
459
+ * inflates the breaker's failure counter and the breaker opens far
460
+ * sooner than intended.
461
+ *
462
+ * Every downstream consumer that wants this composition writes
463
+ * `breaker.wrap(() => b.retry.withRetry(fn, retryOpts))` by hand;
464
+ * this primitive captures the pattern so the convention is uniform.
465
+ *
466
+ * @opts
467
+ * retry: Object, // forwarded to withRetry — same options
468
+ * breaker: CircuitBreaker, // existing breaker instance (required)
469
+ *
470
+ * @example
471
+ * var cb = b.circuitBreaker.create({ name: "upstream-billing", failureThreshold: 5 });
472
+ * var result = await b.retry.withBreaker(async function () {
473
+ * return await fetch("https://billing.example.com/v1/charges");
474
+ * }, {
475
+ * retry: { maxAttempts: 3, baseDelayMs: 500 },
476
+ * breaker: cb,
477
+ * });
478
+ */
479
+ function withBreaker(fn, opts) {
480
+ opts = opts || {};
481
+ if (typeof fn !== "function") {
482
+ throw new TypeError("retry.withBreaker: fn must be a function, got " + typeof fn);
483
+ }
484
+ if (!opts.breaker || typeof opts.breaker.wrap !== "function") {
485
+ throw new TypeError("retry.withBreaker: opts.breaker must be a CircuitBreaker instance (with .wrap(fn))");
486
+ }
487
+ // breaker.wrap counts ONE breaker call regardless of how many retry
488
+ // attempts withRetry makes internally. The breaker only sees the
489
+ // final outcome — success closes it (after enough probes in
490
+ // half-open), or final failure opens it after `failureThreshold`
491
+ // exhausted-retry-loop calls.
492
+ return opts.breaker.wrap(function () {
493
+ return withRetry(fn, opts.retry || {});
494
+ });
495
+ }
496
+
448
497
  module.exports = {
449
498
  withRetry: withRetry,
499
+ withBreaker: withBreaker,
450
500
  isRetryable: isRetryable,
451
501
  backoffDelay: backoffDelay,
452
502
  CircuitBreaker: CircuitBreaker,
package/lib/router.js CHANGED
@@ -34,8 +34,8 @@
34
34
  */
35
35
  var http = require("http");
36
36
  var http2 = require("http2");
37
- var fs = require("fs");
38
- var path = require("path");
37
+ var nodeFs = require("fs");
38
+ var nodePath = require("path");
39
39
  var C = require("./constants");
40
40
  var requestHelpers = require("./request-helpers");
41
41
  var lazyRequire = require("./lazy-require");
@@ -296,8 +296,8 @@ class Router {
296
296
  this.routes = [];
297
297
  this.middleware = [];
298
298
  // WebSocket routes are kept separate from HTTP routes — they're
299
- // matched on the upgrade / Extended CONNECT path, not on a method
300
- // verb. Map<path, { handler, opts }>.
299
+ // matched on the upgrade / Extended CONNECT nodePath, not on a method
300
+ // verb. Map<nodePath, { handler, opts }>.
301
301
  this._wsRoutes = new Map();
302
302
  // Active WebSocket connections opened through router.ws(). Tracked
303
303
  // so router.closeWebSockets() can do a clean rolling-shutdown.
@@ -407,7 +407,7 @@ class Router {
407
407
  // process.exit(0);
408
408
  //
409
409
  // Tests use the same primitive in teardown — no parallel cleanup
410
- // path, no h1-upgrade detached-socket workaround.
410
+ // nodePath, no h1-upgrade detached-socket workaround.
411
411
  async closeWebSockets(opts) {
412
412
  opts = opts || {};
413
413
  var timeoutMs = typeof opts.timeoutMs === "number" ? opts.timeoutMs : C.TIME.seconds(5);
@@ -603,7 +603,7 @@ class Router {
603
603
 
604
604
  // ---- WebSocket route registration ----
605
605
  //
606
- // ws(path, handler, opts?)
606
+ // ws(nodePath, handler, opts?)
607
607
  // path — exact match. Path-param patterns aren't supported on
608
608
  // upgrade requests; operators that need dynamic paths
609
609
  // register one ws route per stable shape.
@@ -1089,7 +1089,7 @@ class Router {
1089
1089
  // server's emitter list clean for HTTP-only deployments.
1090
1090
  if (self._wsRoutes.size > 0) {
1091
1091
  // h1 upgrade event — fires for "Upgrade: websocket" from h1
1092
- // clients. Routes by path; refuses with 426 in h2-only mode.
1092
+ // clients. Routes by nodePath; refuses with 426 in h2-only mode.
1093
1093
  server.on("upgrade", function (req, socket, head) {
1094
1094
  var pathname = String(req.url || "/").split("?")[0];
1095
1095
  var route = self._wsRoutes.get(pathname);
@@ -1201,18 +1201,18 @@ class Router {
1201
1201
  */
1202
1202
  // Static file serving middleware
1203
1203
  function serveStatic(dir) {
1204
- var root = path.resolve(dir);
1204
+ var root = nodePath.resolve(dir);
1205
1205
  return (req, res, next) => {
1206
1206
  if (req.method !== "GET") return next();
1207
1207
  var rel = req.pathname;
1208
1208
  if (rel.includes("\0")) return next();
1209
- var filePath = path.resolve(path.join(root, rel));
1209
+ var filePath = nodePath.resolve(nodePath.join(root, rel));
1210
1210
  if (!filePath.startsWith(root)) return next();
1211
- if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) return next();
1211
+ if (!nodeFs.existsSync(filePath) || nodeFs.statSync(filePath).isDirectory()) return next();
1212
1212
 
1213
- var ext = path.extname(filePath).toLowerCase();
1213
+ var ext = nodePath.extname(filePath).toLowerCase();
1214
1214
  var mime = MIME_TYPES[ext] || "application/octet-stream";
1215
- var stat = fs.statSync(filePath);
1215
+ var stat = nodeFs.statSync(filePath);
1216
1216
  var hasVersion = req.url && req.url.includes("?v=");
1217
1217
  var cacheControl = hasVersion
1218
1218
  ? "public, max-age=31536000, immutable"
@@ -1222,7 +1222,7 @@ function serveStatic(dir) {
1222
1222
  "Content-Length": stat.size,
1223
1223
  "Cache-Control": cacheControl,
1224
1224
  });
1225
- fs.createReadStream(filePath).pipe(res);
1225
+ nodeFs.createReadStream(filePath).pipe(res);
1226
1226
  };
1227
1227
  }
1228
1228
 
package/lib/sandbox.js CHANGED
@@ -78,11 +78,11 @@
78
78
  * arbitrary source from the public internet.
79
79
  */
80
80
 
81
- var path = require("path");
81
+ var nodePath = require("path");
82
82
  var lazyRequire = require("./lazy-require");
83
83
  var validateOpts = require("./validate-opts");
84
84
  var numericBounds = require("./numeric-bounds");
85
- var constants = require("./constants");
85
+ var C = require("./constants");
86
86
  var { SandboxError } = require("./framework-error");
87
87
 
88
88
  var audit = lazyRequire(function () { return require("./audit"); });
@@ -110,14 +110,14 @@ var ALWAYS_AVAILABLE = Object.freeze([
110
110
  "Promise", "Error", "TypeError", "RangeError", "RegExp",
111
111
  ]);
112
112
 
113
- var WORKER_PATH = path.resolve(__dirname, "sandbox-worker.js");
113
+ var WORKER_PATH = nodePath.resolve(__dirname, "sandbox-worker.js");
114
114
 
115
115
  // Default caps. Sourced from C.* helpers so the unit lives at the call site.
116
116
  var DEFAULT_TIMEOUT_MS = 250;
117
- var MAX_TIMEOUT_MS = constants.TIME.seconds(10);
118
- var DEFAULT_MAX_BYTES = constants.BYTES.mib(64);
119
- var MAX_MAX_BYTES = constants.BYTES.gib(1);
120
- var MIN_MAX_BYTES = constants.BYTES.mib(4);
117
+ var MAX_TIMEOUT_MS = C.TIME.seconds(10);
118
+ var DEFAULT_MAX_BYTES = C.BYTES.mib(64);
119
+ var MAX_MAX_BYTES = C.BYTES.gib(1);
120
+ var MIN_MAX_BYTES = C.BYTES.mib(4);
121
121
 
122
122
  function _validateAllowed(allowed) {
123
123
  if (allowed === undefined || allowed === null) return [];
@@ -217,7 +217,7 @@ function run(opts) {
217
217
  // floor so the worker can boot. Round each cap down to a MiB integer.
218
218
  // Floors / caps are quanta of MiB chosen to fit a small embedded
219
219
  // worker; passed straight to v8's resourceLimits.
220
- var oneMib = constants.BYTES.mib(1);
220
+ var oneMib = C.BYTES.mib(1);
221
221
  // The MiB-unit caps below are integers passed directly to v8's
222
222
  // resourceLimits (already typed in MiB by the v8 API), not byte
223
223
  // counts - the constants helpers don't apply.