@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.
- package/CHANGELOG.md +3 -0
- package/lib/a2a.js +11 -11
- package/lib/acme.js +5 -5
- package/lib/ai-input.js +2 -2
- package/lib/api-key.js +4 -4
- package/lib/api-snapshot.js +10 -7
- package/lib/app-shutdown.js +2 -2
- package/lib/app.js +5 -5
- package/lib/archive.js +8 -8
- package/lib/argon2-builtin.js +2 -2
- package/lib/atomic-file.js +53 -53
- package/lib/audit-sign.js +8 -8
- package/lib/audit-tools.js +22 -22
- package/lib/audit.js +29 -17
- package/lib/auth/dpop.js +3 -3
- package/lib/auth/sd-jwt-vc.js +2 -2
- package/lib/backup/bundle.js +17 -17
- package/lib/backup/index.js +36 -36
- package/lib/budr.js +3 -3
- package/lib/bundler.js +20 -20
- package/lib/circuit-breaker.js +24 -9
- package/lib/cli.js +25 -26
- package/lib/cluster.js +2 -2
- package/lib/compliance-sanctions.js +2 -2
- package/lib/config-drift.js +15 -15
- package/lib/content-credentials.js +4 -4
- package/lib/credential-hash.js +3 -3
- package/lib/crypto.js +145 -0
- package/lib/daemon.js +19 -19
- package/lib/db-file-lifecycle.js +24 -24
- package/lib/db-schema.js +2 -2
- package/lib/db.js +35 -35
- package/lib/dev.js +10 -10
- package/lib/dr-runbook.js +5 -5
- package/lib/dsr.js +22 -15
- package/lib/dual-control.js +2 -2
- package/lib/external-db-migrate.js +2 -2
- package/lib/external-db.js +2 -2
- package/lib/fdx.js +2 -2
- package/lib/file-upload.js +30 -30
- package/lib/flag-providers.js +4 -4
- package/lib/gate-contract.js +5 -5
- package/lib/graphql-federation.js +4 -7
- package/lib/honeytoken.js +6 -6
- package/lib/http-client-cookie-jar.js +6 -6
- package/lib/http-client.js +18 -18
- package/lib/i18n.js +5 -5
- package/lib/inbox.js +21 -15
- package/lib/keychain.js +9 -9
- package/lib/legal-hold.js +2 -2
- package/lib/local-db-thin.js +9 -9
- package/lib/log-stream-local.js +17 -17
- package/lib/log-stream-syslog.js +2 -2
- package/lib/log-stream.js +3 -3
- package/lib/mail-bounce.js +2 -2
- package/lib/mail-mdn.js +2 -2
- package/lib/mail-srs.js +2 -2
- package/lib/mail.js +4 -4
- package/lib/mcp.js +2 -2
- package/lib/metrics.js +249 -2
- package/lib/middleware/api-encrypt.js +16 -16
- package/lib/middleware/body-parser.js +16 -16
- package/lib/middleware/compression.js +3 -3
- package/lib/middleware/csp-nonce.js +4 -4
- package/lib/middleware/health.js +7 -7
- package/lib/middleware/idempotency-key.js +250 -0
- package/lib/migrations.js +3 -3
- package/lib/mtls-ca.js +26 -26
- package/lib/mtls-engine-default.js +5 -5
- package/lib/network-dns.js +2 -2
- package/lib/network-nts.js +2 -2
- package/lib/network-proxy.js +3 -3
- package/lib/network-smtp-policy.js +2 -2
- package/lib/network-tls.js +17 -17
- package/lib/network.js +13 -13
- package/lib/notify.js +3 -3
- package/lib/object-store/gcs-bucket-ops.js +2 -2
- package/lib/object-store/gcs.js +5 -5
- package/lib/object-store/index.js +6 -6
- package/lib/object-store/local.js +19 -19
- package/lib/object-store/sigv4.js +3 -3
- package/lib/observability-tracer.js +4 -4
- package/lib/otel-export.js +3 -3
- package/lib/pagination.js +5 -5
- package/lib/parsers/safe-xml.js +3 -3
- package/lib/pqc-agent.js +116 -26
- package/lib/pqc-gate.js +5 -5
- package/lib/pubsub-redis.js +2 -2
- package/lib/queue-local.js +3 -3
- package/lib/queue.js +2 -2
- package/lib/redis-client.js +4 -4
- package/lib/restore-bundle.js +18 -18
- package/lib/restore-rollback.js +34 -34
- package/lib/restore.js +16 -16
- package/lib/retry.js +50 -0
- package/lib/router.js +13 -13
- package/lib/sandbox.js +8 -8
- package/lib/sec-cyber.js +3 -3
- package/lib/security-assert.js +2 -2
- package/lib/seeders.js +4 -4
- package/lib/self-update-standalone-verifier.js +280 -0
- package/lib/self-update.js +32 -26
- package/lib/session-device-binding.js +2 -2
- package/lib/static.js +22 -22
- package/lib/template.js +19 -19
- package/lib/testing.js +7 -7
- package/lib/tls-exporter.js +5 -5
- package/lib/tracing.js +3 -3
- package/lib/vault/index.js +11 -11
- package/lib/vault/passphrase-ops.js +37 -37
- package/lib/vault/passphrase-source.js +2 -2
- package/lib/vault/rotate.js +70 -66
- package/lib/vault/seal-pem-file.js +26 -26
- package/lib/watcher.js +23 -23
- package/lib/webhook.js +10 -10
- package/lib/worker-pool.js +6 -6
- package/lib/ws-client.js +4 -4
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/backup/bundle.js
CHANGED
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
* compressor (gzip, zstd) downstream of the framework primitive.
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
|
-
var
|
|
50
|
-
var
|
|
49
|
+
var nodeFs = require("fs");
|
|
50
|
+
var nodePath = require("path");
|
|
51
51
|
var atomicFile = require("../atomic-file");
|
|
52
|
-
var
|
|
52
|
+
var bCrypto = require("./crypto");
|
|
53
53
|
var backupManifest = require("./manifest");
|
|
54
54
|
var validateOpts = require("../validate-opts");
|
|
55
55
|
var { defineClass } = require("../framework-error");
|
|
@@ -68,19 +68,19 @@ function _emit(cb, ev) {
|
|
|
68
68
|
function _encryptedPathFor(relativePath) {
|
|
69
69
|
// POSIX-normalize separators in the bundle so manifests written on
|
|
70
70
|
// Windows and Linux look the same on disk.
|
|
71
|
-
var posix = relativePath.split(
|
|
71
|
+
var posix = relativePath.split(nodePath.sep).join("/");
|
|
72
72
|
return "files/" + posix + ".enc";
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
async function create(opts) {
|
|
76
76
|
var t0 = Date.now();
|
|
77
77
|
opts = opts || {};
|
|
78
|
-
if (typeof opts.dataDir !== "string" || !
|
|
78
|
+
if (typeof opts.dataDir !== "string" || !nodeFs.existsSync(opts.dataDir)) {
|
|
79
79
|
throw new BackupBundleError("backup-bundle/no-datadir",
|
|
80
80
|
"create: opts.dataDir is required and must exist");
|
|
81
81
|
}
|
|
82
82
|
validateOpts.requireNonEmptyString(opts.outDir, "create: opts.outDir", BackupBundleError, "backup-bundle/no-outdir");
|
|
83
|
-
if (
|
|
83
|
+
if (nodeFs.existsSync(opts.outDir)) {
|
|
84
84
|
throw new BackupBundleError("backup-bundle/outdir-exists",
|
|
85
85
|
"create: outDir already exists: " + opts.outDir +
|
|
86
86
|
" (refusing to overwrite — pick a fresh path)");
|
|
@@ -104,11 +104,11 @@ async function create(opts) {
|
|
|
104
104
|
var progress = opts.progressCallback;
|
|
105
105
|
|
|
106
106
|
atomicFile.ensureDir(outDir);
|
|
107
|
-
atomicFile.ensureDir(
|
|
107
|
+
atomicFile.ensureDir(nodePath.join(outDir, "files"));
|
|
108
108
|
|
|
109
109
|
// 1. Encrypt the vault key JSON
|
|
110
110
|
_emit(progress, { phase: "wrap_vault_key" });
|
|
111
|
-
var wrappedVk = await
|
|
111
|
+
var wrappedVk = await bCrypto.encryptWithFreshSalt(opts.vaultKeyJson, passphrase);
|
|
112
112
|
|
|
113
113
|
// 2. Walk each include entry, encrypt the bytes, emit a blob
|
|
114
114
|
var fileEntries = [];
|
|
@@ -124,8 +124,8 @@ async function create(opts) {
|
|
|
124
124
|
throw new BackupBundleError("backup-bundle/bad-include",
|
|
125
125
|
"create: files[" + i + "].relativePath must be a relative path (got '" + entry.relativePath + "')");
|
|
126
126
|
}
|
|
127
|
-
var srcPath =
|
|
128
|
-
if (!
|
|
127
|
+
var srcPath = nodePath.join(dataDir, entry.relativePath);
|
|
128
|
+
if (!nodeFs.existsSync(srcPath)) {
|
|
129
129
|
if (entry.required) {
|
|
130
130
|
throw new BackupBundleError("backup-bundle/missing-required",
|
|
131
131
|
"create: required file missing: " + entry.relativePath);
|
|
@@ -133,7 +133,7 @@ async function create(opts) {
|
|
|
133
133
|
_emit(progress, { phase: "skip_missing", relativePath: entry.relativePath });
|
|
134
134
|
continue;
|
|
135
135
|
}
|
|
136
|
-
var stat =
|
|
136
|
+
var stat = nodeFs.statSync(srcPath);
|
|
137
137
|
if (!stat.isFile()) {
|
|
138
138
|
// Directories aren't supported in this slice — the bundler
|
|
139
139
|
// operates on a flat list of files. Operator wanting a recursive
|
|
@@ -143,12 +143,12 @@ async function create(opts) {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
_emit(progress, { phase: "read", relativePath: entry.relativePath, size: stat.size });
|
|
146
|
-
var plain =
|
|
147
|
-
var checksum =
|
|
148
|
-
var encResult = await
|
|
146
|
+
var plain = nodeFs.readFileSync(srcPath);
|
|
147
|
+
var checksum = bCrypto.checksum(plain);
|
|
148
|
+
var encResult = await bCrypto.encryptWithFreshSalt(plain, passphrase);
|
|
149
149
|
var encPath = _encryptedPathFor(entry.relativePath);
|
|
150
|
-
var destFull =
|
|
151
|
-
atomicFile.ensureDir(
|
|
150
|
+
var destFull = nodePath.join(outDir, encPath);
|
|
151
|
+
atomicFile.ensureDir(nodePath.dirname(destFull));
|
|
152
152
|
atomicFile.writeSync(destFull, encResult.encrypted, { fileMode: 0o600 });
|
|
153
153
|
|
|
154
154
|
var kind = entry.kind || "raw";
|
|
@@ -217,7 +217,7 @@ async function create(opts) {
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
|
-
var manifestPath =
|
|
220
|
+
var manifestPath = nodePath.join(outDir, "manifest.json");
|
|
221
221
|
atomicFile.writeSync(manifestPath, backupManifest.serialize(manifest), { fileMode: 0o600 });
|
|
222
222
|
|
|
223
223
|
var durationMs = Date.now() - t0;
|
package/lib/backup/index.js
CHANGED
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
* PQC-encrypted backup bundles — sealed columns + audit chain + keyring.
|
|
49
49
|
*/
|
|
50
50
|
|
|
51
|
-
var
|
|
51
|
+
var nodeFs = require("fs");
|
|
52
52
|
var os = require("os");
|
|
53
|
-
var
|
|
54
|
-
var
|
|
53
|
+
var nodePath = require("path");
|
|
54
|
+
var bCrypto = require("../crypto");
|
|
55
55
|
var atomicFile = require("../atomic-file");
|
|
56
56
|
var backupBundle = require("./bundle");
|
|
57
57
|
var backupManifest = require("./manifest");
|
|
@@ -93,16 +93,16 @@ function _isValidBundleId(s) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function _generateBundleId() {
|
|
96
|
-
return atomicFile.pathTimestamp() + "-" +
|
|
96
|
+
return atomicFile.pathTimestamp() + "-" + bCrypto.generateToken(4);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
function _dirSize(p) {
|
|
100
100
|
var total = 0;
|
|
101
|
-
var entries =
|
|
101
|
+
var entries = nodeFs.readdirSync(p, { withFileTypes: true });
|
|
102
102
|
for (var i = 0; i < entries.length; i++) {
|
|
103
|
-
var f =
|
|
103
|
+
var f = nodePath.join(p, entries[i].name);
|
|
104
104
|
if (entries[i].isDirectory()) total += _dirSize(f);
|
|
105
|
-
else if (entries[i].isFile()) total +=
|
|
105
|
+
else if (entries[i].isFile()) total += nodeFs.statSync(f).size;
|
|
106
106
|
}
|
|
107
107
|
return total;
|
|
108
108
|
}
|
|
@@ -150,7 +150,7 @@ function localStorage(opts) {
|
|
|
150
150
|
throw new BackupError("backup/bad-bundle-id",
|
|
151
151
|
"bundleId must match the framework's timestamp+suffix format");
|
|
152
152
|
}
|
|
153
|
-
return
|
|
153
|
+
return nodePath.join(root, bundleId);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
return {
|
|
@@ -158,7 +158,7 @@ function localStorage(opts) {
|
|
|
158
158
|
async writeBundle(bundleId, sourceDir) {
|
|
159
159
|
atomicFile.ensureDir(root);
|
|
160
160
|
var dest = _bundlePath(bundleId);
|
|
161
|
-
if (
|
|
161
|
+
if (nodeFs.existsSync(dest)) {
|
|
162
162
|
throw new BackupError("backup/bundle-exists",
|
|
163
163
|
"writeBundle: bundle '" + bundleId + "' already exists in storage");
|
|
164
164
|
}
|
|
@@ -166,26 +166,26 @@ function localStorage(opts) {
|
|
|
166
166
|
},
|
|
167
167
|
async readBundle(bundleId, destDir) {
|
|
168
168
|
var src = _bundlePath(bundleId);
|
|
169
|
-
if (!
|
|
169
|
+
if (!nodeFs.existsSync(src)) {
|
|
170
170
|
throw new BackupError("backup/bundle-not-found",
|
|
171
171
|
"readBundle: '" + bundleId + "' not in storage at " + root);
|
|
172
172
|
}
|
|
173
|
-
if (
|
|
173
|
+
if (nodeFs.existsSync(destDir)) {
|
|
174
174
|
throw new BackupError("backup/dest-exists",
|
|
175
175
|
"readBundle: destDir already exists: " + destDir);
|
|
176
176
|
}
|
|
177
177
|
atomicFile.copyDirRecursive(src, destDir);
|
|
178
178
|
},
|
|
179
179
|
async listBundles() {
|
|
180
|
-
if (!
|
|
181
|
-
var entries =
|
|
180
|
+
if (!nodeFs.existsSync(root)) return [];
|
|
181
|
+
var entries = nodeFs.readdirSync(root, { withFileTypes: true });
|
|
182
182
|
var out = [];
|
|
183
183
|
for (var i = 0; i < entries.length; i++) {
|
|
184
184
|
if (!entries[i].isDirectory()) continue;
|
|
185
185
|
if (!_isValidBundleId(entries[i].name)) continue;
|
|
186
|
-
var p =
|
|
186
|
+
var p = nodePath.join(root, entries[i].name);
|
|
187
187
|
var stat;
|
|
188
|
-
try { stat =
|
|
188
|
+
try { stat = nodeFs.statSync(p); } catch (_e) { continue; }
|
|
189
189
|
var size;
|
|
190
190
|
try { size = _dirSize(p); } catch (_e) { size = 0; }
|
|
191
191
|
out.push({
|
|
@@ -200,11 +200,11 @@ function localStorage(opts) {
|
|
|
200
200
|
},
|
|
201
201
|
async deleteBundle(bundleId) {
|
|
202
202
|
var p = _bundlePath(bundleId);
|
|
203
|
-
if (!
|
|
204
|
-
|
|
203
|
+
if (!nodeFs.existsSync(p)) return;
|
|
204
|
+
nodeFs.rmSync(p, { recursive: true, force: true });
|
|
205
205
|
},
|
|
206
206
|
async hasBundle(bundleId) {
|
|
207
|
-
try { return
|
|
207
|
+
try { return nodeFs.existsSync(_bundlePath(bundleId)); }
|
|
208
208
|
catch (_e) { return false; }
|
|
209
209
|
},
|
|
210
210
|
};
|
|
@@ -285,10 +285,10 @@ async function _resolveVaultKeyJson(vaultKeyJsonOpt) {
|
|
|
285
285
|
* var path = require("node:path");
|
|
286
286
|
* var os = require("node:os");
|
|
287
287
|
*
|
|
288
|
-
* var dataDir =
|
|
289
|
-
* var root =
|
|
290
|
-
*
|
|
291
|
-
*
|
|
288
|
+
* var dataDir = nodeFs.mkdtempSync(nodePath.join(os.tmpdir(), "backup-data-"));
|
|
289
|
+
* var root = nodeFs.mkdtempSync(nodePath.join(os.tmpdir(), "backup-root-"));
|
|
290
|
+
* nodeFs.writeFileSync(nodePath.join(dataDir, "db.enc"), Buffer.from([1, 2, 3]));
|
|
291
|
+
* nodeFs.writeFileSync(nodePath.join(dataDir, "db.key.enc"), Buffer.from([4, 5, 6]));
|
|
292
292
|
*
|
|
293
293
|
* var engine = b.backup.create({
|
|
294
294
|
* dataDir: dataDir,
|
|
@@ -308,7 +308,7 @@ async function _resolveVaultKeyJson(vaultKeyJsonOpt) {
|
|
|
308
308
|
*/
|
|
309
309
|
function create(opts) {
|
|
310
310
|
opts = opts || {};
|
|
311
|
-
if (typeof opts.dataDir !== "string" || !
|
|
311
|
+
if (typeof opts.dataDir !== "string" || !nodeFs.existsSync(opts.dataDir)) {
|
|
312
312
|
throw new BackupError("backup/no-datadir",
|
|
313
313
|
"create: opts.dataDir is required and must exist");
|
|
314
314
|
}
|
|
@@ -456,7 +456,7 @@ function create(opts) {
|
|
|
456
456
|
runOpts = runOpts || {};
|
|
457
457
|
var t0 = Date.now();
|
|
458
458
|
var bundleId = _generateBundleId();
|
|
459
|
-
var stagingDir =
|
|
459
|
+
var stagingDir = nodePath.join(os.tmpdir(),
|
|
460
460
|
"blamejs-backup-staging-" + bundleId.replace(/[:.]/g, "-"));
|
|
461
461
|
|
|
462
462
|
// Flush the live DB to disk so the snapshot is current. Default
|
|
@@ -500,7 +500,7 @@ function create(opts) {
|
|
|
500
500
|
progressCallback: runOpts.progressCallback,
|
|
501
501
|
});
|
|
502
502
|
} catch (e) {
|
|
503
|
-
try {
|
|
503
|
+
try { nodeFs.rmSync(stagingDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
|
|
504
504
|
_emitAudit("backup.failure",
|
|
505
505
|
{ bundleId: bundleId, reason: (e && e.message) || String(e) }, "failure");
|
|
506
506
|
throw e;
|
|
@@ -509,7 +509,7 @@ function create(opts) {
|
|
|
509
509
|
try {
|
|
510
510
|
await storage.writeBundle(bundleId, stagingDir);
|
|
511
511
|
} catch (e) {
|
|
512
|
-
try {
|
|
512
|
+
try { nodeFs.rmSync(stagingDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
|
|
513
513
|
_emitAudit("backup.failure",
|
|
514
514
|
{ bundleId: bundleId, reason: "storage.writeBundle: " + ((e && e.message) || String(e)) },
|
|
515
515
|
"failure");
|
|
@@ -517,7 +517,7 @@ function create(opts) {
|
|
|
517
517
|
"writing bundle to storage failed: " + ((e && e.message) || String(e)));
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
-
try {
|
|
520
|
+
try { nodeFs.rmSync(stagingDir, { recursive: true, force: true }); } catch (_e) { /* best-effort */ }
|
|
521
521
|
|
|
522
522
|
var summary = {
|
|
523
523
|
bundleId: bundleId,
|
|
@@ -675,11 +675,11 @@ function create(opts) {
|
|
|
675
675
|
}
|
|
676
676
|
// Newest bundle (storage.listBundles returns newest first).
|
|
677
677
|
var bundleId = bundles[0].bundleId;
|
|
678
|
-
var stagingDir =
|
|
678
|
+
var stagingDir = nodePath.join(testOpts.restoreTo,
|
|
679
679
|
"test-" + bundleId.replace(/[:.]/g, "-"));
|
|
680
680
|
// Refuse to overwrite an existing dir — operators get a fresh
|
|
681
681
|
// restore every drill.
|
|
682
|
-
if (
|
|
682
|
+
if (nodeFs.existsSync(stagingDir)) {
|
|
683
683
|
_emitAudit("backup.test.failed",
|
|
684
684
|
{ bundleId: bundleId, reason: "stagingDir already exists: " + stagingDir },
|
|
685
685
|
"failure");
|
|
@@ -688,12 +688,12 @@ function create(opts) {
|
|
|
688
688
|
var manifestPath, manifest, sigVerification;
|
|
689
689
|
try {
|
|
690
690
|
await storage.readBundle(bundleId, stagingDir);
|
|
691
|
-
manifestPath =
|
|
692
|
-
if (!
|
|
691
|
+
manifestPath = nodePath.join(stagingDir, "manifest.json");
|
|
692
|
+
if (!nodeFs.existsSync(manifestPath)) {
|
|
693
693
|
throw new BackupError("backup/test-no-manifest",
|
|
694
694
|
"manifest.json missing under restored bundle " + bundleId);
|
|
695
695
|
}
|
|
696
|
-
manifest = backupManifest.parse(
|
|
696
|
+
manifest = backupManifest.parse(nodeFs.readFileSync(manifestPath, "utf8"));
|
|
697
697
|
// Verify the manifest signature so a tampered backup test
|
|
698
698
|
// surfaces here, not as a regulator finding later.
|
|
699
699
|
sigVerification = backupManifest.verifySignature(manifest, {
|
|
@@ -740,7 +740,7 @@ function create(opts) {
|
|
|
740
740
|
// Best-effort cleanup so the staging dir doesn't accumulate
|
|
741
741
|
// across drills.
|
|
742
742
|
if (testOpts.cleanup !== false) {
|
|
743
|
-
try {
|
|
743
|
+
try { nodeFs.rmSync(stagingDir, { recursive: true, force: true }); }
|
|
744
744
|
catch (_e) { /* tmpdir cleanup best-effort */ }
|
|
745
745
|
}
|
|
746
746
|
}
|
|
@@ -804,12 +804,12 @@ function verifyManifestSignature(target, opts) {
|
|
|
804
804
|
opts = opts || {};
|
|
805
805
|
var manifest;
|
|
806
806
|
if (typeof target === "string") {
|
|
807
|
-
var manifestPath =
|
|
808
|
-
if (!
|
|
807
|
+
var manifestPath = nodePath.join(target, "manifest.json");
|
|
808
|
+
if (!nodeFs.existsSync(manifestPath)) {
|
|
809
809
|
throw new BackupError("backup/no-manifest",
|
|
810
810
|
"verifyManifestSignature: manifest.json missing at " + manifestPath);
|
|
811
811
|
}
|
|
812
|
-
try { manifest = backupManifest.parse(
|
|
812
|
+
try { manifest = backupManifest.parse(nodeFs.readFileSync(manifestPath, "utf8")); }
|
|
813
813
|
catch (e) {
|
|
814
814
|
throw new BackupError("backup/bad-manifest",
|
|
815
815
|
"verifyManifestSignature: parse failed: " + ((e && e.message) || String(e)));
|
package/lib/budr.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* Backup / Disaster-Recovery RTO/RPO declaration primitive for regulated workloads (HIPAA / DORA / ISO 22301:2019 / NIST SP 800-34).
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
var
|
|
27
|
+
var numericBounds = require("./numeric-bounds");
|
|
28
28
|
var validateOpts = require("./validate-opts");
|
|
29
29
|
var audit = require("./audit");
|
|
30
30
|
var { defineClass } = require("./framework-error");
|
|
@@ -85,8 +85,8 @@ function declare(opts) {
|
|
|
85
85
|
throw BudrError.factory("BAD_SERVICE",
|
|
86
86
|
"budr.declare: service must match " + SERVICE_RE);
|
|
87
87
|
}
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.rtoMs, "budr.declare: rtoMs", BudrError, "BAD_RTO");
|
|
89
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.rpoMs, "budr.declare: rpoMs", BudrError, "BAD_RPO");
|
|
90
90
|
if (typeof opts.rtoMs !== "number" || typeof opts.rpoMs !== "number") {
|
|
91
91
|
throw BudrError.factory("BAD_TARGETS",
|
|
92
92
|
"budr.declare: rtoMs and rpoMs are required positive integer milliseconds");
|
package/lib/bundler.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* override via `opts.hashLen` between 4 and 64). Source maps written
|
|
27
27
|
* by an engine land as `<hashed>.<ext>.map` siblings.
|
|
28
28
|
*
|
|
29
|
-
* Watch mode: `bundler.watch(callback)` arms `
|
|
29
|
+
* Watch mode: `bundler.watch(callback)` arms `nodeFs.watch` on each
|
|
30
30
|
* entry's directory, debounces bursts via `opts.graceMs` (default
|
|
31
31
|
* 100 ms), and rebuilds the entire entry set on change.
|
|
32
32
|
*
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
* Client-side asset bundler — produces content-hashed `dist/<name>.<hash>.<ext>` files plus a `manifest.json` mapping logical name to hashed filename.
|
|
44
44
|
*/
|
|
45
45
|
|
|
46
|
-
var
|
|
47
|
-
var
|
|
48
|
-
var
|
|
46
|
+
var nodePath = require("path");
|
|
47
|
+
var nodeFs = require("fs");
|
|
48
|
+
var bCrypto = require("./crypto");
|
|
49
49
|
var atomicFile = require("./atomic-file");
|
|
50
50
|
var logModule = require("./log");
|
|
51
|
-
var
|
|
51
|
+
var numericBounds = require("./numeric-bounds");
|
|
52
52
|
var safeJson = require("./safe-json");
|
|
53
53
|
var validateOpts = require("./validate-opts");
|
|
54
54
|
var { defineClass } = require("./framework-error");
|
|
@@ -67,7 +67,7 @@ var DEFAULT_GRACE_MS = 100;
|
|
|
67
67
|
function _hashContent(buf, hexLen) {
|
|
68
68
|
// SHA3-512 → take the first hexLen hex chars. Same family as the
|
|
69
69
|
// framework's other content fingerprints (no SHA-256 for new code).
|
|
70
|
-
return
|
|
70
|
+
return bCrypto.sha3Hash(buf).slice(0, hexLen);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function _hashedName(baseName, hash, ext) {
|
|
@@ -200,7 +200,7 @@ function _validateEngine(eng) {
|
|
|
200
200
|
* Build a content-hashed asset pipeline for a fixed set of named
|
|
201
201
|
* entries. The returned object exposes `build()` (one-shot rebuild,
|
|
202
202
|
* resolves to `{ outputs, manifestPath, manifest, durationMs }`),
|
|
203
|
-
* `watch(callback)` (arm `
|
|
203
|
+
* `watch(callback)` (arm `nodeFs.watch` and debounce-rebuild on change),
|
|
204
204
|
* and `close()` (drop watchers and pending timers).
|
|
205
205
|
*
|
|
206
206
|
* Throws `BundlerError` at config time on missing / malformed entries,
|
|
@@ -257,7 +257,7 @@ function create(opts) {
|
|
|
257
257
|
|
|
258
258
|
var entries = Object.assign({}, opts.entries);
|
|
259
259
|
var cwd = opts.cwd || process.cwd();
|
|
260
|
-
var outdir =
|
|
260
|
+
var outdir = nodePath.isAbsolute(opts.outdir) ? opts.outdir : nodePath.resolve(cwd, opts.outdir);
|
|
261
261
|
var manifestName = (opts.manifest === false || opts.manifest === null)
|
|
262
262
|
? null
|
|
263
263
|
: (typeof opts.manifest === "string" && opts.manifest.length > 0
|
|
@@ -266,24 +266,24 @@ function create(opts) {
|
|
|
266
266
|
var hashOn = opts.hash !== false;
|
|
267
267
|
var hashLen = DEFAULT_HASH_LEN;
|
|
268
268
|
if (opts.hashLen !== undefined) {
|
|
269
|
-
if (!
|
|
269
|
+
if (!numericBounds.isPositiveFiniteInt(opts.hashLen) ||
|
|
270
270
|
opts.hashLen < MIN_HASH_LEN || opts.hashLen > MAX_HASH_LEN) {
|
|
271
271
|
throw new BundlerError("bundler/bad-hash-len",
|
|
272
272
|
"bundler.create: opts.hashLen must be a positive finite integer " +
|
|
273
273
|
"between " + MIN_HASH_LEN + " and " + MAX_HASH_LEN +
|
|
274
|
-
"; got " +
|
|
274
|
+
"; got " + numericBounds.shape(opts.hashLen));
|
|
275
275
|
}
|
|
276
276
|
hashLen = opts.hashLen;
|
|
277
277
|
}
|
|
278
278
|
var log = opts.log || null;
|
|
279
279
|
|
|
280
|
-
// Test seam: tests pass a fake watcher so we don't actually
|
|
280
|
+
// Test seam: tests pass a fake watcher so we don't actually nodeFs.watch
|
|
281
281
|
var watchFn = opts._watch || function (dirOrFile, wopts, listener) {
|
|
282
|
-
return
|
|
282
|
+
return nodeFs.watch(dirOrFile, wopts, listener);
|
|
283
283
|
};
|
|
284
284
|
var setTimeoutFn = opts._setTimeout || setTimeout;
|
|
285
285
|
var clearTimeoutFn = opts._clearTimeout || clearTimeout;
|
|
286
|
-
|
|
286
|
+
numericBounds.requireNonNegativeFiniteIntIfPresent(opts.graceMs,
|
|
287
287
|
"bundler.create: opts.graceMs", BundlerError, "bundler/bad-grace-ms");
|
|
288
288
|
var graceMs = opts.graceMs !== undefined ? opts.graceMs : DEFAULT_GRACE_MS;
|
|
289
289
|
|
|
@@ -292,7 +292,7 @@ function create(opts) {
|
|
|
292
292
|
var watching = false;
|
|
293
293
|
|
|
294
294
|
function _resolveEntry(p) {
|
|
295
|
-
return
|
|
295
|
+
return nodePath.isAbsolute(p) ? p : nodePath.resolve(cwd, p);
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
var _logVia = logModule.makeViaOrFallback(log, bootLog);
|
|
@@ -308,9 +308,9 @@ function create(opts) {
|
|
|
308
308
|
for (var i = 0; i < names.length; i++) {
|
|
309
309
|
var name = names[i];
|
|
310
310
|
var entryPath = _resolveEntry(entries[name]);
|
|
311
|
-
var ext =
|
|
311
|
+
var ext = nodePath.extname(entryPath);
|
|
312
312
|
var raw;
|
|
313
|
-
try { raw =
|
|
313
|
+
try { raw = nodeFs.readFileSync(entryPath); }
|
|
314
314
|
catch (e) {
|
|
315
315
|
throw new BundlerError("bundler/read-failed",
|
|
316
316
|
"could not read entry '" + name + "' at " + entryPath +
|
|
@@ -333,7 +333,7 @@ function create(opts) {
|
|
|
333
333
|
var sourceMap = transformed && transformed.sourceMap;
|
|
334
334
|
var hash = hashOn ? _hashContent(content, hashLen) : null;
|
|
335
335
|
var outName = hashOn ? _hashedName(name, hash, ext) : (name + ext);
|
|
336
|
-
var outPath =
|
|
336
|
+
var outPath = nodePath.join(outdir, outName);
|
|
337
337
|
// atomic-file write so a concurrent reader (the http server
|
|
338
338
|
// serving outdir) never sees a partial file
|
|
339
339
|
atomicFile.writeSync(outPath, content, { mode: 0o644 });
|
|
@@ -360,7 +360,7 @@ function create(opts) {
|
|
|
360
360
|
|
|
361
361
|
var manifestPath = null;
|
|
362
362
|
if (manifestName) {
|
|
363
|
-
manifestPath =
|
|
363
|
+
manifestPath = nodePath.join(outdir, manifestName);
|
|
364
364
|
atomicFile.writeSync(
|
|
365
365
|
manifestPath,
|
|
366
366
|
safeJson.stringify(manifest, null, 2) + "\n",
|
|
@@ -407,8 +407,8 @@ function create(opts) {
|
|
|
407
407
|
// Watch the entry's directory (single-file watches are flaky
|
|
408
408
|
// across editors that write-then-rename). Filter events to the
|
|
409
409
|
// entry's basename only.
|
|
410
|
-
var dir =
|
|
411
|
-
var base =
|
|
410
|
+
var dir = nodePath.dirname(entryPath);
|
|
411
|
+
var base = nodePath.basename(entryPath);
|
|
412
412
|
var w;
|
|
413
413
|
try {
|
|
414
414
|
w = watchFn(dir, { persistent: false }, function (eventType, filename) {
|
package/lib/circuit-breaker.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* Top-level circuit-breaker primitive.
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
var
|
|
27
|
+
var retryHelper = require("./retry");
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* @primitive b.circuitBreaker.create
|
|
@@ -34,11 +34,18 @@ var retry = require("./retry");
|
|
|
34
34
|
* @related b.retry, b.httpClient
|
|
35
35
|
*
|
|
36
36
|
* Build a circuit-breaker. Returns a CircuitBreaker instance with
|
|
37
|
-
* `wrap(fn)` (executes `fn` if the breaker is closed; throws
|
|
38
|
-
* with `code: "
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
37
|
+
* `wrap(fn)` (executes `fn` if the breaker is closed; throws an
|
|
38
|
+
* `Error` with `code: "CIRCUIT_OPEN"` + `isObjectStoreError: true` +
|
|
39
|
+
* `permanent: false` when open), `state()`, `reset()`, and
|
|
40
|
+
* `onStateChange(handler)` listener registration. Pass-through
|
|
41
|
+
* factory: identical instance shape to `b.retry.CircuitBreaker`,
|
|
42
|
+
* with the framework's `create(opts)` vocabulary.
|
|
43
|
+
*
|
|
44
|
+
* The `CIRCUIT_OPEN` error code is a pre-v1 artifact — every other
|
|
45
|
+
* framework error class uses namespaced codes (`retry/...`). The
|
|
46
|
+
* rename is deferred to v0.10 with a deprecation cycle so existing
|
|
47
|
+
* operators who match `err.code === "CIRCUIT_OPEN"` aren't broken
|
|
48
|
+
* in a patch.
|
|
42
49
|
*
|
|
43
50
|
* @opts
|
|
44
51
|
* name: string, // identifier used in audit + state-change events
|
|
@@ -65,14 +72,22 @@ var retry = require("./retry");
|
|
|
65
72
|
* result.value; // → 42
|
|
66
73
|
*/
|
|
67
74
|
function create(opts) {
|
|
68
|
-
|
|
75
|
+
// The CircuitBreaker class constructor is `(name, opts)` — passing a
|
|
76
|
+
// single opts object lands it in the positional `name` slot, and the
|
|
77
|
+
// validator throws "name must be a non-empty string, got object."
|
|
78
|
+
// The factory's documented shape is `create({ name, ...opts })`;
|
|
79
|
+
// split the name out of opts before invoking the constructor.
|
|
80
|
+
// Caught by hermitstash-sync operator review against v0.9.12.
|
|
81
|
+
opts = opts || {};
|
|
82
|
+
var name = (opts && typeof opts.name === "string") ? opts.name : "";
|
|
83
|
+
return new retryHelper.CircuitBreaker(name, opts);
|
|
69
84
|
}
|
|
70
85
|
|
|
71
86
|
module.exports = {
|
|
72
87
|
create: create,
|
|
73
|
-
CircuitBreaker:
|
|
88
|
+
CircuitBreaker: retryHelper.CircuitBreaker,
|
|
74
89
|
// Forward the error class so operators catching breaker rejections
|
|
75
90
|
// can `instanceof` against the framework's RetryError without
|
|
76
91
|
// requiring a separate b.retry import.
|
|
77
|
-
RetryError:
|
|
92
|
+
RetryError: retryHelper.RetryError,
|
|
78
93
|
};
|