@blamejs/core 0.8.43 → 0.8.49
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 +92 -0
- package/README.md +10 -10
- package/index.js +52 -0
- package/lib/a2a.js +159 -34
- package/lib/acme.js +762 -0
- package/lib/ai-pref.js +166 -43
- package/lib/api-key.js +108 -47
- package/lib/api-snapshot.js +157 -40
- package/lib/app-shutdown.js +113 -77
- package/lib/archive.js +337 -40
- package/lib/arg-parser.js +697 -0
- package/lib/asyncapi.js +99 -55
- package/lib/atomic-file.js +465 -104
- package/lib/audit-chain.js +123 -34
- package/lib/audit-daily-review.js +389 -0
- package/lib/audit-sign.js +302 -56
- package/lib/audit-tools.js +412 -63
- package/lib/audit.js +656 -35
- package/lib/auth/jwt-external.js +17 -0
- package/lib/auth/oauth.js +7 -0
- package/lib/auth-bot-challenge.js +505 -0
- package/lib/auth-header.js +92 -25
- package/lib/backup/bundle.js +26 -0
- package/lib/backup/index.js +512 -89
- package/lib/backup/manifest.js +168 -7
- package/lib/break-glass.js +415 -39
- package/lib/budr.js +103 -30
- package/lib/bundler.js +86 -66
- package/lib/cache.js +192 -72
- package/lib/chain-writer.js +65 -40
- package/lib/circuit-breaker.js +56 -33
- package/lib/cli-helpers.js +106 -75
- package/lib/cli.js +6 -30
- package/lib/cloud-events.js +99 -32
- package/lib/cluster-storage.js +162 -37
- package/lib/cluster.js +340 -49
- package/lib/codepoint-class.js +66 -0
- package/lib/compliance.js +424 -24
- package/lib/config-drift.js +111 -46
- package/lib/config.js +94 -40
- package/lib/consent.js +165 -18
- package/lib/constants.js +1 -0
- package/lib/content-credentials.js +153 -48
- package/lib/cookies.js +154 -62
- package/lib/credential-hash.js +133 -61
- package/lib/crypto-field.js +702 -18
- package/lib/crypto-hpke.js +256 -0
- package/lib/crypto.js +744 -22
- package/lib/csv.js +178 -35
- package/lib/daemon.js +456 -0
- package/lib/dark-patterns.js +186 -55
- package/lib/db-query.js +79 -2
- package/lib/db.js +1431 -60
- package/lib/ddl-change-control.js +523 -0
- package/lib/deprecate.js +195 -40
- package/lib/dev.js +82 -39
- package/lib/dora.js +67 -48
- package/lib/dr-runbook.js +368 -0
- package/lib/dsr.js +142 -11
- package/lib/dual-control.js +91 -56
- package/lib/events.js +120 -41
- package/lib/external-db-migrate.js +192 -2
- package/lib/external-db.js +795 -50
- package/lib/fapi2.js +122 -1
- package/lib/fda-21cfr11.js +395 -0
- package/lib/fdx.js +132 -2
- package/lib/file-type.js +87 -0
- package/lib/file-upload.js +93 -0
- package/lib/flag.js +82 -20
- package/lib/forms.js +132 -29
- package/lib/framework-error.js +169 -0
- package/lib/framework-schema.js +163 -35
- package/lib/gate-contract.js +849 -175
- package/lib/graphql-federation.js +68 -7
- package/lib/guard-all.js +172 -55
- package/lib/guard-archive.js +286 -124
- package/lib/guard-auth.js +194 -21
- package/lib/guard-cidr.js +190 -28
- package/lib/guard-csv.js +397 -51
- package/lib/guard-domain.js +213 -91
- package/lib/guard-email.js +236 -29
- package/lib/guard-filename.js +307 -75
- package/lib/guard-graphql.js +263 -30
- package/lib/guard-html.js +310 -116
- package/lib/guard-image.js +243 -30
- package/lib/guard-json.js +260 -54
- package/lib/guard-jsonpath.js +235 -23
- package/lib/guard-jwt.js +284 -30
- package/lib/guard-markdown.js +204 -22
- package/lib/guard-mime.js +190 -26
- package/lib/guard-oauth.js +277 -28
- package/lib/guard-pdf.js +251 -27
- package/lib/guard-regex.js +226 -18
- package/lib/guard-shell.js +229 -26
- package/lib/guard-svg.js +177 -10
- package/lib/guard-template.js +232 -21
- package/lib/guard-time.js +195 -29
- package/lib/guard-uuid.js +189 -30
- package/lib/guard-xml.js +259 -36
- package/lib/guard-yaml.js +241 -44
- package/lib/honeytoken.js +63 -27
- package/lib/html-balance.js +83 -0
- package/lib/http-client.js +486 -59
- package/lib/http-message-signature.js +582 -0
- package/lib/i18n.js +102 -49
- package/lib/iab-mspa.js +112 -32
- package/lib/iab-tcf.js +107 -2
- package/lib/inbox.js +90 -52
- package/lib/keychain.js +865 -0
- package/lib/legal-hold.js +374 -0
- package/lib/local-db-thin.js +320 -0
- package/lib/log-stream.js +281 -51
- package/lib/log.js +184 -86
- package/lib/mail-bounce.js +107 -62
- package/lib/mail.js +295 -58
- package/lib/mcp.js +108 -27
- package/lib/metrics.js +98 -89
- package/lib/middleware/age-gate.js +36 -0
- package/lib/middleware/ai-act-disclosure.js +37 -0
- package/lib/middleware/api-encrypt.js +45 -0
- package/lib/middleware/assetlinks.js +40 -0
- package/lib/middleware/asyncapi-serve.js +35 -0
- package/lib/middleware/attach-user.js +40 -0
- package/lib/middleware/bearer-auth.js +40 -0
- package/lib/middleware/body-parser.js +230 -0
- package/lib/middleware/bot-disclose.js +34 -0
- package/lib/middleware/bot-guard.js +39 -0
- package/lib/middleware/compression.js +37 -0
- package/lib/middleware/cookies.js +32 -0
- package/lib/middleware/cors.js +40 -0
- package/lib/middleware/csp-nonce.js +40 -0
- package/lib/middleware/csp-report.js +34 -0
- package/lib/middleware/csrf-protect.js +43 -0
- package/lib/middleware/daily-byte-quota.js +53 -85
- package/lib/middleware/db-role-for.js +40 -0
- package/lib/middleware/dpop.js +40 -0
- package/lib/middleware/error-handler.js +37 -14
- package/lib/middleware/fetch-metadata.js +39 -0
- package/lib/middleware/flag-context.js +34 -0
- package/lib/middleware/gpc.js +33 -0
- package/lib/middleware/headers.js +35 -0
- package/lib/middleware/health.js +46 -0
- package/lib/middleware/host-allowlist.js +30 -0
- package/lib/middleware/network-allowlist.js +38 -0
- package/lib/middleware/openapi-serve.js +34 -0
- package/lib/middleware/rate-limit.js +160 -18
- package/lib/middleware/request-id.js +36 -18
- package/lib/middleware/request-log.js +37 -0
- package/lib/middleware/require-aal.js +29 -0
- package/lib/middleware/require-auth.js +32 -0
- package/lib/middleware/require-bound-key.js +41 -0
- package/lib/middleware/require-content-type.js +32 -0
- package/lib/middleware/require-methods.js +27 -0
- package/lib/middleware/require-mtls.js +33 -0
- package/lib/middleware/require-step-up.js +37 -0
- package/lib/middleware/security-headers.js +44 -0
- package/lib/middleware/security-txt.js +38 -0
- package/lib/middleware/span-http-server.js +37 -0
- package/lib/middleware/sse.js +36 -0
- package/lib/middleware/trace-log-correlation.js +33 -0
- package/lib/middleware/trace-propagate.js +32 -0
- package/lib/middleware/tus-upload.js +90 -0
- package/lib/middleware/web-app-manifest.js +53 -0
- package/lib/mtls-ca.js +100 -70
- package/lib/network-byte-quota.js +308 -0
- package/lib/network-heartbeat.js +135 -0
- package/lib/network-tls.js +534 -4
- package/lib/network.js +103 -0
- package/lib/notify.js +114 -43
- package/lib/ntp-check.js +192 -51
- package/lib/observability.js +145 -47
- package/lib/openapi.js +90 -44
- package/lib/outbox.js +99 -1
- package/lib/pagination.js +168 -86
- package/lib/parsers/index.js +16 -5
- package/lib/permissions.js +93 -40
- package/lib/pqc-agent.js +84 -8
- package/lib/pqc-software.js +94 -60
- package/lib/process-spawn.js +95 -21
- package/lib/pubsub.js +96 -66
- package/lib/queue.js +375 -54
- package/lib/redact.js +793 -21
- package/lib/render.js +139 -47
- package/lib/request-helpers.js +485 -121
- package/lib/restore-bundle.js +142 -39
- package/lib/restore-rollback.js +136 -45
- package/lib/retention.js +178 -50
- package/lib/retry.js +116 -33
- package/lib/router.js +475 -23
- package/lib/safe-async.js +543 -94
- package/lib/safe-buffer.js +337 -41
- package/lib/safe-json.js +467 -62
- package/lib/safe-jsonpath.js +285 -0
- package/lib/safe-schema.js +631 -87
- package/lib/safe-sql.js +221 -59
- package/lib/safe-url.js +278 -46
- package/lib/sandbox-worker.js +135 -0
- package/lib/sandbox.js +358 -0
- package/lib/scheduler.js +135 -70
- package/lib/self-update.js +647 -0
- package/lib/session-device-binding.js +431 -0
- package/lib/session.js +259 -49
- package/lib/slug.js +138 -26
- package/lib/ssrf-guard.js +316 -56
- package/lib/storage.js +433 -70
- package/lib/subject.js +405 -23
- package/lib/template.js +148 -8
- package/lib/tenant-quota.js +545 -0
- package/lib/testing.js +440 -53
- package/lib/time.js +291 -23
- package/lib/tls-exporter.js +239 -0
- package/lib/tracing.js +90 -74
- package/lib/uuid.js +97 -22
- package/lib/vault/index.js +284 -22
- package/lib/vault/seal-pem-file.js +66 -0
- package/lib/watcher.js +368 -0
- package/lib/webhook.js +196 -63
- package/lib/websocket.js +393 -68
- package/lib/wiki-concepts.js +338 -0
- package/lib/worker-pool.js +464 -0
- package/package.json +3 -3
- package/sbom.cyclonedx.json +7 -7
package/lib/app-shutdown.js
CHANGED
|
@@ -1,83 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* @module b.appShutdown
|
|
4
|
+
* @nav Production
|
|
5
|
+
* @title App Shutdown
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* subsystem's; the result is dropped requests, half-completed jobs,
|
|
11
|
-
* stuck cluster leases that block the next pod from acquiring.
|
|
7
|
+
* @intro
|
|
8
|
+
* Graceful shutdown orchestrator — drain in-flight requests, flush
|
|
9
|
+
* audit, close DB, release the cluster lease, then exit. Configurable
|
|
10
|
+
* timeouts and signal handlers wire SIGTERM / SIGINT (and any
|
|
11
|
+
* operator-supplied signals) into a single phase-ordered shutdown.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* SIGTERM is the contract between Kubernetes / systemd / `docker
|
|
14
|
+
* stop` and the framework. Production rolling restarts depend on the
|
|
15
|
+
* server draining cleanly. Without orchestration each subsystem's
|
|
16
|
+
* shutdown races every other subsystem's — the result is dropped
|
|
17
|
+
* requests, half-completed jobs, and stuck cluster leases that block
|
|
18
|
+
* the next pod from acquiring. The orchestrator runs phases in array
|
|
19
|
+
* order with per-phase budgets so a slow phase cannot starve later
|
|
20
|
+
* ones; a phase failure is logged but does not skip the remaining
|
|
21
|
+
* phases (the DB still closes even if jobs drain timed out).
|
|
17
22
|
*
|
|
18
|
-
*
|
|
23
|
+
* `b.appShutdown.standardPhases(components)` builds the canonical
|
|
24
|
+
* ordering — mark-draining → scheduler → jobs → websockets →
|
|
25
|
+
* http-server → cluster → db → external-db — given a components map.
|
|
26
|
+
* Operators with custom topology call it directly and prepend or
|
|
27
|
+
* append their own phases. `b.appShutdown.pidLock(path)` is a single-
|
|
28
|
+
* instance file lock for daemons that must run exactly once on a
|
|
29
|
+
* host; it composes with the orchestrator via `addPhase` so the lock
|
|
30
|
+
* is released as part of graceful shutdown.
|
|
19
31
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* (tracked via the same middleware) up to
|
|
28
|
-
* phaseTimeoutMs
|
|
29
|
-
* 5. afterDrain operator hook (run after in-flight drained)
|
|
30
|
-
* 6. scheduler scheduler.stop() if registered
|
|
31
|
-
* 7. jobs jobs.shutdown() / queue.shutdown() — let
|
|
32
|
-
* current handlers finish, refuse new lease
|
|
33
|
-
* 8. websockets router.closeWebSockets() if HTTP server
|
|
34
|
-
* 9. http-server server.close() — waits for keepalive
|
|
35
|
-
* connections to drain
|
|
36
|
-
* 10. cluster cluster.shutdown() — release lease cleanly
|
|
37
|
-
* so the next pod can acquire it without
|
|
38
|
-
* waiting out the lease TTL
|
|
39
|
-
* 11. db db.close() — flushes encrypted-at-rest
|
|
40
|
-
* snapshot, closes SQLite handle
|
|
41
|
-
* 12. external-db externalDb.shutdown() — drain pool
|
|
32
|
+
* Idempotency: `shutdown()` is idempotent. Calling it twice returns
|
|
33
|
+
* the same Promise. Signal handlers route through the same call so
|
|
34
|
+
* SIGTERM, SIGINT, an `uncaughtException` reaching the operator hook,
|
|
35
|
+
* and a manual `orchestrator.shutdown()` all converge on one
|
|
36
|
+
* orchestration. When `b.tracing` has an active registry every phase
|
|
37
|
+
* runs inside a span named `shutdown.<phase>` so per-phase durations
|
|
38
|
+
* surface in the operator's tracing exporter.
|
|
42
39
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* in array order. The standard phase set above is added by
|
|
46
|
-
* createApp via the components map; standalone callers build their
|
|
47
|
-
* own.
|
|
48
|
-
*
|
|
49
|
-
* var orchestrator = b.appShutdown.create({
|
|
50
|
-
* graceMs: 30000,
|
|
51
|
-
* phases: [
|
|
52
|
-
* { name: "beforeStop", run: async function () { ... } },
|
|
53
|
-
* { name: "drain-in-flight", run: orchestrator.waitInFlight, timeoutMs: 10000 },
|
|
54
|
-
* { name: "db", run: function () { db.close(); } },
|
|
55
|
-
* ],
|
|
56
|
-
* installSignalHandlers: true, // SIGTERM + SIGINT auto-call shutdown
|
|
57
|
-
* });
|
|
58
|
-
*
|
|
59
|
-
* var result = await orchestrator.shutdown();
|
|
60
|
-
* // → { ok, phases: [{ name, ms, ok, error? }, ...], totalMs, draining }
|
|
61
|
-
*
|
|
62
|
-
* // Mounted as middleware to refuse new requests during drain +
|
|
63
|
-
* // track in-flight count.
|
|
64
|
-
* router.use(orchestrator.middleware());
|
|
65
|
-
*
|
|
66
|
-
* orchestrator.draining(); // true once shutdown() has been called
|
|
67
|
-
* orchestrator.inFlight(); // current in-flight count
|
|
68
|
-
*
|
|
69
|
-
* Idempotency: shutdown() is idempotent. Calling it twice returns
|
|
70
|
-
* the same Promise. Signal handlers fire it via `installSignalHandlers`
|
|
71
|
-
* so SIGTERM + SIGINT both route through the same orchestration.
|
|
72
|
-
*
|
|
73
|
-
* Per-phase timeouts: each phase has a budget. If a phase doesn't
|
|
74
|
-
* complete within timeoutMs it's marked failed and the orchestrator
|
|
75
|
-
* moves to the next phase. Default = remaining grace divided by
|
|
76
|
-
* remaining phases (so a slow phase doesn't starve later ones).
|
|
77
|
-
*
|
|
78
|
-
* Tracing integration: when b.tracing has an active registry, each
|
|
79
|
-
* phase runs inside a span named "shutdown.<phase>" so operators see
|
|
80
|
-
* which phase took how long in their tracing exporter.
|
|
40
|
+
* @card
|
|
41
|
+
* Graceful shutdown orchestrator — drain in-flight requests, flush audit, close DB, release the cluster lease, then exit.
|
|
81
42
|
*/
|
|
82
43
|
|
|
83
44
|
var safeAsync = require("./safe-async");
|
|
@@ -93,6 +54,41 @@ var log = boot("app-shutdown");
|
|
|
93
54
|
|
|
94
55
|
var DEFAULT_GRACE_MS = C.TIME.seconds(30);
|
|
95
56
|
|
|
57
|
+
/**
|
|
58
|
+
* @primitive b.appShutdown.create
|
|
59
|
+
* @signature b.appShutdown.create(opts)
|
|
60
|
+
* @since 0.6.0
|
|
61
|
+
* @related b.appShutdown.standardPhases, b.appShutdown.pidLock
|
|
62
|
+
*
|
|
63
|
+
* Build a graceful-shutdown orchestrator. Returns an instance with
|
|
64
|
+
* `shutdown()` (idempotent — second call returns the same Promise),
|
|
65
|
+
* `middleware()` (refuses new requests with 503 + tracks in-flight
|
|
66
|
+
* count), `waitInFlight()`, `addPhase()`, `installSignals()`,
|
|
67
|
+
* `uninstallSignals()`, `draining()`, and `inFlight()`. Each phase has
|
|
68
|
+
* a per-phase budget; the default is remaining grace divided by
|
|
69
|
+
* remaining phases so a slow phase doesn't starve later ones. A phase
|
|
70
|
+
* failure is logged but does not skip the remaining phases.
|
|
71
|
+
*
|
|
72
|
+
* @opts
|
|
73
|
+
* graceMs: number, // total budget across all phases (default 30000)
|
|
74
|
+
* phases: array, // [{ name, run: async fn, timeoutMs? }]
|
|
75
|
+
* installSignalHandlers: boolean, // wire SIGTERM/SIGINT (default false)
|
|
76
|
+
* signals: array, // signal names (default ["SIGTERM","SIGINT"])
|
|
77
|
+
* onUncaught: function, // hook for uncaughtException / unhandledRejection
|
|
78
|
+
* installUncaught: boolean, // wire uncaughtException handler unconditionally
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* var orchestrator = b.appShutdown.create({
|
|
82
|
+
* graceMs: 30000,
|
|
83
|
+
* phases: [
|
|
84
|
+
* { name: "before-stop", run: async function () { return "ok"; } },
|
|
85
|
+
* { name: "db", run: function () { return; }, timeoutMs: 5000 },
|
|
86
|
+
* ],
|
|
87
|
+
* });
|
|
88
|
+
* var result = await orchestrator.shutdown();
|
|
89
|
+
* result.ok; // → true
|
|
90
|
+
* result.phases.length; // → 2
|
|
91
|
+
*/
|
|
96
92
|
function create(opts) {
|
|
97
93
|
opts = opts || {};
|
|
98
94
|
nb.requirePositiveFiniteIntIfPresent(opts.graceMs,
|
|
@@ -331,10 +327,26 @@ function create(opts) {
|
|
|
331
327
|
};
|
|
332
328
|
}
|
|
333
329
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
330
|
+
/**
|
|
331
|
+
* @primitive b.appShutdown.standardPhases
|
|
332
|
+
* @signature b.appShutdown.standardPhases(components)
|
|
333
|
+
* @since 0.6.0
|
|
334
|
+
* @related b.appShutdown.create
|
|
335
|
+
*
|
|
336
|
+
* Build the canonical phases array for a components map. The order is
|
|
337
|
+
* mark-draining → scheduler → jobs (or queue) → websockets →
|
|
338
|
+
* http-server → cluster → db → external-db. Each entry carries a
|
|
339
|
+
* conservative `timeoutMs`. Operators wire the result into
|
|
340
|
+
* `b.appShutdown.create({ phases })`; with a non-standard topology they
|
|
341
|
+
* prepend or append their own entries to the returned array.
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* var phases = b.appShutdown.standardPhases({
|
|
345
|
+
* db: { close: function () { return; } },
|
|
346
|
+
* });
|
|
347
|
+
* phases.length; // → 1
|
|
348
|
+
* phases[0].name; // → "db"
|
|
349
|
+
*/
|
|
338
350
|
function standardPhases(components) {
|
|
339
351
|
components = components || {};
|
|
340
352
|
var phases = [];
|
|
@@ -440,6 +452,30 @@ function standardPhases(components) {
|
|
|
440
452
|
var nodeFs = require("fs");
|
|
441
453
|
var nodePath = require("path");
|
|
442
454
|
|
|
455
|
+
/**
|
|
456
|
+
* @primitive b.appShutdown.pidLock
|
|
457
|
+
* @signature b.appShutdown.pidLock(lockPath)
|
|
458
|
+
* @since 0.6.0
|
|
459
|
+
* @related b.appShutdown.create
|
|
460
|
+
*
|
|
461
|
+
* Single-instance file lock for daemons that must run exactly once on
|
|
462
|
+
* a host. Returns `{ acquire, release, held, path }`. `acquire()`
|
|
463
|
+
* writes the current PID atomically (open with O_EXCL + write + fsync)
|
|
464
|
+
* and refuses to acquire if another live process already holds the
|
|
465
|
+
* lock; stale lock files (PID gone) are reaped automatically. On
|
|
466
|
+
* Windows the underlying advisory flock is unavailable, so the lock
|
|
467
|
+
* file's exclusive presence is the lock. Compose with the orchestrator
|
|
468
|
+
* by passing `release` as a phase via `addPhase`.
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* var lock = b.appShutdown.pidLock("/tmp/blamejs-doc-example.pid");
|
|
472
|
+
* try {
|
|
473
|
+
* lock.acquire();
|
|
474
|
+
* lock.held(); // → true
|
|
475
|
+
* } finally {
|
|
476
|
+
* lock.release();
|
|
477
|
+
* }
|
|
478
|
+
*/
|
|
443
479
|
function pidLock(lockPath) {
|
|
444
480
|
if (typeof lockPath !== "string" || lockPath.length === 0) {
|
|
445
481
|
throw new AppShutdownError("app-shutdown/bad-pidlock-path",
|