@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.
Files changed (222) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/README.md +10 -10
  3. package/index.js +52 -0
  4. package/lib/a2a.js +159 -34
  5. package/lib/acme.js +762 -0
  6. package/lib/ai-pref.js +166 -43
  7. package/lib/api-key.js +108 -47
  8. package/lib/api-snapshot.js +157 -40
  9. package/lib/app-shutdown.js +113 -77
  10. package/lib/archive.js +337 -40
  11. package/lib/arg-parser.js +697 -0
  12. package/lib/asyncapi.js +99 -55
  13. package/lib/atomic-file.js +465 -104
  14. package/lib/audit-chain.js +123 -34
  15. package/lib/audit-daily-review.js +389 -0
  16. package/lib/audit-sign.js +302 -56
  17. package/lib/audit-tools.js +412 -63
  18. package/lib/audit.js +656 -35
  19. package/lib/auth/jwt-external.js +17 -0
  20. package/lib/auth/oauth.js +7 -0
  21. package/lib/auth-bot-challenge.js +505 -0
  22. package/lib/auth-header.js +92 -25
  23. package/lib/backup/bundle.js +26 -0
  24. package/lib/backup/index.js +512 -89
  25. package/lib/backup/manifest.js +168 -7
  26. package/lib/break-glass.js +415 -39
  27. package/lib/budr.js +103 -30
  28. package/lib/bundler.js +86 -66
  29. package/lib/cache.js +192 -72
  30. package/lib/chain-writer.js +65 -40
  31. package/lib/circuit-breaker.js +56 -33
  32. package/lib/cli-helpers.js +106 -75
  33. package/lib/cli.js +6 -30
  34. package/lib/cloud-events.js +99 -32
  35. package/lib/cluster-storage.js +162 -37
  36. package/lib/cluster.js +340 -49
  37. package/lib/codepoint-class.js +66 -0
  38. package/lib/compliance.js +424 -24
  39. package/lib/config-drift.js +111 -46
  40. package/lib/config.js +94 -40
  41. package/lib/consent.js +165 -18
  42. package/lib/constants.js +1 -0
  43. package/lib/content-credentials.js +153 -48
  44. package/lib/cookies.js +154 -62
  45. package/lib/credential-hash.js +133 -61
  46. package/lib/crypto-field.js +702 -18
  47. package/lib/crypto-hpke.js +256 -0
  48. package/lib/crypto.js +744 -22
  49. package/lib/csv.js +178 -35
  50. package/lib/daemon.js +456 -0
  51. package/lib/dark-patterns.js +186 -55
  52. package/lib/db-query.js +79 -2
  53. package/lib/db.js +1431 -60
  54. package/lib/ddl-change-control.js +523 -0
  55. package/lib/deprecate.js +195 -40
  56. package/lib/dev.js +82 -39
  57. package/lib/dora.js +67 -48
  58. package/lib/dr-runbook.js +368 -0
  59. package/lib/dsr.js +142 -11
  60. package/lib/dual-control.js +91 -56
  61. package/lib/events.js +120 -41
  62. package/lib/external-db-migrate.js +192 -2
  63. package/lib/external-db.js +795 -50
  64. package/lib/fapi2.js +122 -1
  65. package/lib/fda-21cfr11.js +395 -0
  66. package/lib/fdx.js +132 -2
  67. package/lib/file-type.js +87 -0
  68. package/lib/file-upload.js +93 -0
  69. package/lib/flag.js +82 -20
  70. package/lib/forms.js +132 -29
  71. package/lib/framework-error.js +169 -0
  72. package/lib/framework-schema.js +163 -35
  73. package/lib/gate-contract.js +849 -175
  74. package/lib/graphql-federation.js +68 -7
  75. package/lib/guard-all.js +172 -55
  76. package/lib/guard-archive.js +286 -124
  77. package/lib/guard-auth.js +194 -21
  78. package/lib/guard-cidr.js +190 -28
  79. package/lib/guard-csv.js +397 -51
  80. package/lib/guard-domain.js +213 -91
  81. package/lib/guard-email.js +236 -29
  82. package/lib/guard-filename.js +307 -75
  83. package/lib/guard-graphql.js +263 -30
  84. package/lib/guard-html.js +310 -116
  85. package/lib/guard-image.js +243 -30
  86. package/lib/guard-json.js +260 -54
  87. package/lib/guard-jsonpath.js +235 -23
  88. package/lib/guard-jwt.js +284 -30
  89. package/lib/guard-markdown.js +204 -22
  90. package/lib/guard-mime.js +190 -26
  91. package/lib/guard-oauth.js +277 -28
  92. package/lib/guard-pdf.js +251 -27
  93. package/lib/guard-regex.js +226 -18
  94. package/lib/guard-shell.js +229 -26
  95. package/lib/guard-svg.js +177 -10
  96. package/lib/guard-template.js +232 -21
  97. package/lib/guard-time.js +195 -29
  98. package/lib/guard-uuid.js +189 -30
  99. package/lib/guard-xml.js +259 -36
  100. package/lib/guard-yaml.js +241 -44
  101. package/lib/honeytoken.js +63 -27
  102. package/lib/html-balance.js +83 -0
  103. package/lib/http-client.js +486 -59
  104. package/lib/http-message-signature.js +582 -0
  105. package/lib/i18n.js +102 -49
  106. package/lib/iab-mspa.js +112 -32
  107. package/lib/iab-tcf.js +107 -2
  108. package/lib/inbox.js +90 -52
  109. package/lib/keychain.js +865 -0
  110. package/lib/legal-hold.js +374 -0
  111. package/lib/local-db-thin.js +320 -0
  112. package/lib/log-stream.js +281 -51
  113. package/lib/log.js +184 -86
  114. package/lib/mail-bounce.js +107 -62
  115. package/lib/mail.js +295 -58
  116. package/lib/mcp.js +108 -27
  117. package/lib/metrics.js +98 -89
  118. package/lib/middleware/age-gate.js +36 -0
  119. package/lib/middleware/ai-act-disclosure.js +37 -0
  120. package/lib/middleware/api-encrypt.js +45 -0
  121. package/lib/middleware/assetlinks.js +40 -0
  122. package/lib/middleware/asyncapi-serve.js +35 -0
  123. package/lib/middleware/attach-user.js +40 -0
  124. package/lib/middleware/bearer-auth.js +40 -0
  125. package/lib/middleware/body-parser.js +230 -0
  126. package/lib/middleware/bot-disclose.js +34 -0
  127. package/lib/middleware/bot-guard.js +39 -0
  128. package/lib/middleware/compression.js +37 -0
  129. package/lib/middleware/cookies.js +32 -0
  130. package/lib/middleware/cors.js +40 -0
  131. package/lib/middleware/csp-nonce.js +40 -0
  132. package/lib/middleware/csp-report.js +34 -0
  133. package/lib/middleware/csrf-protect.js +43 -0
  134. package/lib/middleware/daily-byte-quota.js +53 -85
  135. package/lib/middleware/db-role-for.js +40 -0
  136. package/lib/middleware/dpop.js +40 -0
  137. package/lib/middleware/error-handler.js +37 -14
  138. package/lib/middleware/fetch-metadata.js +39 -0
  139. package/lib/middleware/flag-context.js +34 -0
  140. package/lib/middleware/gpc.js +33 -0
  141. package/lib/middleware/headers.js +35 -0
  142. package/lib/middleware/health.js +46 -0
  143. package/lib/middleware/host-allowlist.js +30 -0
  144. package/lib/middleware/network-allowlist.js +38 -0
  145. package/lib/middleware/openapi-serve.js +34 -0
  146. package/lib/middleware/rate-limit.js +160 -18
  147. package/lib/middleware/request-id.js +36 -18
  148. package/lib/middleware/request-log.js +37 -0
  149. package/lib/middleware/require-aal.js +29 -0
  150. package/lib/middleware/require-auth.js +32 -0
  151. package/lib/middleware/require-bound-key.js +41 -0
  152. package/lib/middleware/require-content-type.js +32 -0
  153. package/lib/middleware/require-methods.js +27 -0
  154. package/lib/middleware/require-mtls.js +33 -0
  155. package/lib/middleware/require-step-up.js +37 -0
  156. package/lib/middleware/security-headers.js +44 -0
  157. package/lib/middleware/security-txt.js +38 -0
  158. package/lib/middleware/span-http-server.js +37 -0
  159. package/lib/middleware/sse.js +36 -0
  160. package/lib/middleware/trace-log-correlation.js +33 -0
  161. package/lib/middleware/trace-propagate.js +32 -0
  162. package/lib/middleware/tus-upload.js +90 -0
  163. package/lib/middleware/web-app-manifest.js +53 -0
  164. package/lib/mtls-ca.js +100 -70
  165. package/lib/network-byte-quota.js +308 -0
  166. package/lib/network-heartbeat.js +135 -0
  167. package/lib/network-tls.js +534 -4
  168. package/lib/network.js +103 -0
  169. package/lib/notify.js +114 -43
  170. package/lib/ntp-check.js +192 -51
  171. package/lib/observability.js +145 -47
  172. package/lib/openapi.js +90 -44
  173. package/lib/outbox.js +99 -1
  174. package/lib/pagination.js +168 -86
  175. package/lib/parsers/index.js +16 -5
  176. package/lib/permissions.js +93 -40
  177. package/lib/pqc-agent.js +84 -8
  178. package/lib/pqc-software.js +94 -60
  179. package/lib/process-spawn.js +95 -21
  180. package/lib/pubsub.js +96 -66
  181. package/lib/queue.js +375 -54
  182. package/lib/redact.js +793 -21
  183. package/lib/render.js +139 -47
  184. package/lib/request-helpers.js +485 -121
  185. package/lib/restore-bundle.js +142 -39
  186. package/lib/restore-rollback.js +136 -45
  187. package/lib/retention.js +178 -50
  188. package/lib/retry.js +116 -33
  189. package/lib/router.js +475 -23
  190. package/lib/safe-async.js +543 -94
  191. package/lib/safe-buffer.js +337 -41
  192. package/lib/safe-json.js +467 -62
  193. package/lib/safe-jsonpath.js +285 -0
  194. package/lib/safe-schema.js +631 -87
  195. package/lib/safe-sql.js +221 -59
  196. package/lib/safe-url.js +278 -46
  197. package/lib/sandbox-worker.js +135 -0
  198. package/lib/sandbox.js +358 -0
  199. package/lib/scheduler.js +135 -70
  200. package/lib/self-update.js +647 -0
  201. package/lib/session-device-binding.js +431 -0
  202. package/lib/session.js +259 -49
  203. package/lib/slug.js +138 -26
  204. package/lib/ssrf-guard.js +316 -56
  205. package/lib/storage.js +433 -70
  206. package/lib/subject.js +405 -23
  207. package/lib/template.js +148 -8
  208. package/lib/tenant-quota.js +545 -0
  209. package/lib/testing.js +440 -53
  210. package/lib/time.js +291 -23
  211. package/lib/tls-exporter.js +239 -0
  212. package/lib/tracing.js +90 -74
  213. package/lib/uuid.js +97 -22
  214. package/lib/vault/index.js +284 -22
  215. package/lib/vault/seal-pem-file.js +66 -0
  216. package/lib/watcher.js +368 -0
  217. package/lib/webhook.js +196 -63
  218. package/lib/websocket.js +393 -68
  219. package/lib/wiki-concepts.js +338 -0
  220. package/lib/worker-pool.js +464 -0
  221. package/package.json +3 -3
  222. package/sbom.cyclonedx.json +7 -7
package/lib/deprecate.js CHANGED
@@ -1,53 +1,49 @@
1
1
  "use strict";
2
2
  /**
3
- * deprecate — runtime deprecation API for the framework's LTS contract.
3
+ * @module b.deprecate
4
+ * @nav Production
5
+ * @title Deprecate
4
6
  *
5
- * Operators see a one-time stderr warning the first time deprecated
6
- * surface is used, with the version it was deprecated in and the
7
- * version it'll be removed in. Production runs suppress warnings by
8
- * default (operators don't want stderr noise from deprecated paths in
9
- * code they don't own), but BLAMEJS_DEPRECATIONS env var inverts that
10
- * for visibility-on-demand.
7
+ * @intro
8
+ * Runtime deprecation-warning system for the framework's LTS
9
+ * contract. Operators see a one-time stderr warning the first time
10
+ * deprecated surface is used, naming the version it was deprecated
11
+ * in and the version it will be removed in so behavior changes
12
+ * ship visible at least one minor before the breakage lands in a
13
+ * major.
11
14
  *
12
- * var dep = b.deprecate;
15
+ * Three usage shapes:
13
16
  *
14
- * // Direct warning at the call site
15
- * dep.warn("auth.legacyVerify", {
16
- * since: "0.2.0",
17
- * removeIn: "0.4.0",
18
- * message: "use auth.password.verify(stored, plain) instead",
19
- * hint: "see MIGRATING.md#0-2-to-0-4",
20
- * });
17
+ * - `warn(name, opts)` — emit a warning at the call site of an
18
+ * inline deprecated path.
19
+ * - `wrap(fn, name, opts)` — return a wrapper around the
20
+ * replacement function so calls to the old name auto-warn and
21
+ * delegate.
22
+ * - `alias(target, oldKey, newKey, opts)` — define `oldKey` on
23
+ * `target` as a getter/setter that warns on access and reads
24
+ * through to `newKey`.
21
25
  *
22
- * // Wrap an old function so calls trigger the warning automatically
23
- * var legacyVerify = dep.wrap(newVerify, "auth.legacyVerify", {
24
- * since: "0.2.0", removeIn: "0.4.0",
25
- * message: "renamed to auth.password.verify",
26
- * });
26
+ * Warnings dedupe by `(name, since)` calling `warn` ten thousand
27
+ * times with the same args emits a single stderr line; the per-name
28
+ * call counter is still incremented so `list()` reflects real usage
29
+ * volume for ops dashboards.
27
30
  *
28
- * // Mark a property as deprecated; access triggers the warning
29
- * dep.alias(targetObj, "oldKey", "newKey", {
30
- * since: "0.2.0", removeIn: "0.4.0",
31
- * });
32
- *
33
- * dep.list(); // → [{ name, since, removeIn, callCount, firstSeen }]
34
- * dep.reset(); // clears the seen-set; tests
31
+ * `BLAMEJS_DEPRECATIONS` env var controls runtime behavior:
35
32
  *
36
- * BLAMEJS_DEPRECATIONS env var controls runtime behavior:
37
- * "warn" — stderr warning on first use of each (name, since) pair
38
- * (default outside production)
39
- * "silent" — skip entirely (default in production)
40
- * "error" — throw on first use; development tool to surface every
41
- * deprecated call site as a hard failure during a sweep
33
+ * - `"warn"` — stderr warning on first use (default outside
34
+ * production).
35
+ * - `"silent"` — skip entirely (default in production; operators
36
+ * do not want stderr noise from deprecated paths
37
+ * in code they do not own).
38
+ * - `"error"` — throw on first use; development tool to surface
39
+ * every deprecated call site as a hard failure
40
+ * during a sweep.
42
41
  *
43
- * Mode resolution order:
44
- * 1. process.env.BLAMEJS_DEPRECATIONS if set
45
- * 2. "silent" when process.env.NODE_ENV === "production"
46
- * 3. "warn" otherwise
42
+ * Mode resolution: explicit env var first, then `"silent"` when
43
+ * `NODE_ENV=production`, otherwise `"warn"`.
47
44
  *
48
- * Warnings dedupe by (name, since) — calling deprecate.warn(...) ten
49
- * thousand times with the same args produces one stderr line. The
50
- * call counter is still incremented so dep.list() shows usage volume.
45
+ * @card
46
+ * Runtime deprecation-warning system for the framework's LTS contract.
51
47
  */
52
48
 
53
49
  var safeEnv = require("./parsers/safe-env");
@@ -95,6 +91,38 @@ function _validateOpts(opts, fnName) {
95
91
  validateOpts.requireNonEmptyString(opts.removeIn, fnName + ": opts.removeIn (version string)", DeprecateError, "deprecate/bad-opts");
96
92
  }
97
93
 
94
+ /**
95
+ * @primitive b.deprecate.warn
96
+ * @signature b.deprecate.warn(name, opts)
97
+ * @since 0.1.90
98
+ * @status stable
99
+ * @related b.deprecate.wrap, b.deprecate.alias, b.deprecate.list
100
+ *
101
+ * Emit a deprecation warning for an inline call site. First call for a
102
+ * given `(name, since)` pair writes a single line to stderr in `"warn"`
103
+ * mode, throws `DeprecateError` in `"error"` mode, and is suppressed
104
+ * in `"silent"` mode. Subsequent calls dedupe but still increment the
105
+ * per-name call counter so `list()` reports real usage volume. Throws
106
+ * `DeprecateError` (`deprecate/bad-name`) on missing `name` and
107
+ * (`deprecate/bad-opts`) when `since` or `removeIn` are missing or
108
+ * empty.
109
+ *
110
+ * @opts
111
+ * since: string, // required; semver this surface was deprecated in
112
+ * removeIn: string, // required; semver of planned removal
113
+ * message: string, // optional human-readable replacement guidance
114
+ * hint: string, // optional cross-reference (MIGRATING.md anchor)
115
+ *
116
+ * @example
117
+ * var b = require("@blamejs/core");
118
+ * b.deprecate.warn("auth.legacyVerify", {
119
+ * since: "0.2.0",
120
+ * removeIn: "0.4.0",
121
+ * message: "use auth.password.verify(stored, plain) instead",
122
+ * hint: "see MIGRATING.md#0-2-to-0-4",
123
+ * });
124
+ * // → undefined (stderr: [blamejs:deprecated] auth.legacyVerify (since 0.2.0); removed in 0.4.0 — ...)
125
+ */
98
126
  function warn(name, opts) {
99
127
  if (typeof name !== "string" || name.length === 0) {
100
128
  throw new DeprecateError("deprecate/bad-name",
@@ -137,6 +165,38 @@ function warn(name, opts) {
137
165
  // Wrap a function so calling it issues a deprecation warning + delegates.
138
166
  // The wrapper preserves the original function's `.length` (arity) so
139
167
  // callers introspecting it as a callable see the same shape.
168
+ /**
169
+ * @primitive b.deprecate.wrap
170
+ * @signature b.deprecate.wrap(fn, name, opts)
171
+ * @since 0.1.90
172
+ * @status stable
173
+ * @related b.deprecate.warn, b.deprecate.alias
174
+ *
175
+ * Return a function that warns on first invocation then delegates to
176
+ * `fn` with the same `this` and arguments. Use to keep the old export
177
+ * name working through one minor version after rename. The wrapper's
178
+ * `.name` is set to `<name>:deprecated` for stack-trace clarity. Same
179
+ * dedupe + mode rules as `warn`. Throws `DeprecateError`
180
+ * (`deprecate/bad-target`) when `fn` is not a function and
181
+ * (`deprecate/bad-name`) on missing `name`.
182
+ *
183
+ * @opts
184
+ * since: string, // required; semver this surface was deprecated in
185
+ * removeIn: string, // required; semver of planned removal
186
+ * message: string, // optional human-readable replacement guidance
187
+ * hint: string, // optional cross-reference (MIGRATING.md anchor)
188
+ *
189
+ * @example
190
+ * var b = require("@blamejs/core");
191
+ * function passwordVerify(stored, plain) { return stored === plain; }
192
+ * var legacyVerify = b.deprecate.wrap(passwordVerify, "auth.legacyVerify", {
193
+ * since: "0.2.0",
194
+ * removeIn: "0.4.0",
195
+ * message: "renamed to auth.password.verify",
196
+ * });
197
+ * var ok = legacyVerify("stored", "plain");
198
+ * // → false (stderr warns once on first call)
199
+ */
140
200
  function wrap(fn, name, opts) {
141
201
  if (typeof fn !== "function") {
142
202
  throw new DeprecateError("deprecate/bad-target",
@@ -159,6 +219,39 @@ function wrap(fn, name, opts) {
159
219
  // Define `oldKey` on `target` as a getter that warns then returns
160
220
  // `target[newKey]`. The setter writes through so existing assignments
161
221
  // still work, but the getter access trips the warning.
222
+ /**
223
+ * @primitive b.deprecate.alias
224
+ * @signature b.deprecate.alias(target, oldKey, newKey, opts)
225
+ * @since 0.1.90
226
+ * @status stable
227
+ * @related b.deprecate.wrap, b.deprecate.warn
228
+ *
229
+ * Define a getter/setter on `target.oldKey` that warns on access and
230
+ * reads/writes through to `target.newKey`. Use to keep a renamed
231
+ * property accessible for one minor without losing visibility into
232
+ * who still reads the old name. The aliased property is non-
233
+ * enumerable (so it does not leak into `Object.keys` / JSON
234
+ * serialization) but configurable so tests can redefine it. Throws
235
+ * `DeprecateError` (`deprecate/bad-target`) when `target` is not an
236
+ * object and (`deprecate/bad-name`) on missing `oldKey` / `newKey`.
237
+ *
238
+ * @opts
239
+ * since: string, // required; semver this surface was deprecated in
240
+ * removeIn: string, // required; semver of planned removal
241
+ * message: string, // optional override; defaults to "use 'newKey' instead"
242
+ * hint: string, // optional cross-reference (MIGRATING.md anchor)
243
+ * aliasName: string, // optional override for the warned identifier
244
+ *
245
+ * @example
246
+ * var b = require("@blamejs/core");
247
+ * var settings = { timeout: 5000 };
248
+ * b.deprecate.alias(settings, "timeoutMs", "timeout", {
249
+ * since: "0.3.0",
250
+ * removeIn: "0.5.0",
251
+ * });
252
+ * var v = settings.timeoutMs;
253
+ * // → 5000 (stderr warns once on first read)
254
+ */
162
255
  function alias(target, oldKey, newKey, opts) {
163
256
  if (!target || typeof target !== "object") {
164
257
  throw new DeprecateError("deprecate/bad-target",
@@ -187,6 +280,28 @@ function alias(target, oldKey, newKey, opts) {
187
280
  });
188
281
  }
189
282
 
283
+ /**
284
+ * @primitive b.deprecate.list
285
+ * @signature b.deprecate.list()
286
+ * @since 0.1.90
287
+ * @status stable
288
+ * @related b.deprecate.warn, b.deprecate.reset, b.deprecate.getMode
289
+ *
290
+ * Return an array of every deprecated identifier hit during this
291
+ * process's lifetime — `{ name, since, removeIn, callCount,
292
+ * firstSeen }`. Sorted most-frequent first, ties broken by earliest
293
+ * `firstSeen`. Use from an ops dashboard or boot diagnostic to surface
294
+ * which deprecated paths the running deployment still exercises so
295
+ * the team can prioritize migrations before the `removeIn` major
296
+ * lands.
297
+ *
298
+ * @example
299
+ * var b = require("@blamejs/core");
300
+ * b.deprecate.warn("auth.legacyVerify", { since: "0.2.0", removeIn: "0.4.0" });
301
+ * var rows = b.deprecate.list();
302
+ * // → [{ name: "auth.legacyVerify", since: "0.2.0", removeIn: "0.4.0",
303
+ * // callCount: 1, firstSeen: "2026-05-09T12:00:00.000Z" }]
304
+ */
190
305
  function list() {
191
306
  var out = [];
192
307
  _seen.forEach(function (v) {
@@ -206,9 +321,49 @@ function list() {
206
321
  return out;
207
322
  }
208
323
 
324
+ /**
325
+ * @primitive b.deprecate.reset
326
+ * @signature b.deprecate.reset()
327
+ * @since 0.1.90
328
+ * @status stable
329
+ * @related b.deprecate.list, b.deprecate.warn
330
+ *
331
+ * Clear the seen-set so subsequent `warn` / `wrap` / `alias` calls
332
+ * re-emit their first-use warnings. Used by tests that exercise the
333
+ * deprecation path repeatedly; not meant for production code, where
334
+ * the dedupe is intentional.
335
+ *
336
+ * @example
337
+ * var b = require("@blamejs/core");
338
+ * b.deprecate.warn("auth.legacyVerify", { since: "0.2.0", removeIn: "0.4.0" });
339
+ * b.deprecate.reset();
340
+ * var seen = b.deprecate.list();
341
+ * // → []
342
+ */
209
343
  function reset() { _seen.clear(); }
210
344
 
211
345
  // Export the resolved mode so tests + ops dashboards can introspect
346
+ /**
347
+ * @primitive b.deprecate.getMode
348
+ * @signature b.deprecate.getMode()
349
+ * @since 0.1.90
350
+ * @status stable
351
+ * @related b.deprecate.warn, b.deprecate.list
352
+ *
353
+ * Return the resolved deprecation mode for the current process —
354
+ * `"warn"`, `"silent"`, or `"error"`. Resolution order: explicit
355
+ * `BLAMEJS_DEPRECATIONS` env var, then `"silent"` when
356
+ * `NODE_ENV=production`, otherwise `"warn"`. Use from boot diagnostics
357
+ * or test setup to confirm the active posture before exercising
358
+ * deprecated paths.
359
+ *
360
+ * @example
361
+ * var b = require("@blamejs/core");
362
+ * var mode = b.deprecate.getMode();
363
+ * // → "warn" (development default)
364
+ * // → "silent" (NODE_ENV=production)
365
+ * // → "error" (BLAMEJS_DEPRECATIONS=error)
366
+ */
212
367
  function getMode() { return _modeFromEnv(); }
213
368
 
214
369
  module.exports = {
package/lib/dev.js CHANGED
@@ -1,50 +1,51 @@
1
1
  "use strict";
2
2
  /**
3
- * dev — file-watch + child-process restart for iteration loops.
3
+ * @module b.dev
4
+ * @nav Tools
5
+ * @title Dev
4
6
  *
5
- * The framework's hot-reload primitive. Spawn the app as a child
6
- * process, watch the source directories, and restart the child when a
7
- * file changes. On-disk state (vault keys, encrypted DB, sealed
8
- * cookies) survives the restart because the child re-opens the files
9
- * only in-process state is lost, which is the correct semantic for
10
- * "I just edited a route handler and want to see it."
7
+ * @intro
8
+ * Dev-mode helpers hot-reload signal (file watch + child-process
9
+ * restart), route-list dump exposed via `dev.stats()`, and a request
10
+ * inspector courtesy of `stdio: 'inherit'` so the operator sees the
11
+ * spawned app's logs unchanged.
11
12
  *
12
- * var dev = b.dev.create({
13
- * command: "node",
14
- * args: ["./server.js"],
15
- * watch: ["./routes", "./views", "./public", "./lib"],
16
- * ignore: [/node_modules/, /\.db$/, /\.tmp$/, /^\./],
17
- * graceMs: 250, // debounce: collapse bursts into one restart
18
- * killSignal: "SIGTERM",
19
- * killTimeoutMs: 4000, // SIGKILL after this if SIGTERM is ignored
20
- * log: logInstance, // optional structured logger
21
- * env: { ...process.env, BLAMEJS_DEV: "1" },
22
- * cwd: process.cwd(),
23
- * });
13
+ * The hot-reload loop spawns the app as a child process, watches the
14
+ * source directories with `fs.watch({ recursive: true })`, and
15
+ * restarts the child when an unignored file changes. On-disk state
16
+ * (vault keys, encrypted DB, sealed cookies) survives the restart
17
+ * because the child re-opens the files; only in-process state is
18
+ * lost, which is the correct semantic for "I just edited a route
19
+ * handler and want to see it."
24
20
  *
25
- * await dev.start(); // launches child + arms watchers
26
- * await dev.stop(); // signals child + closes watchers
21
+ * Hygiene baked in:
22
+ * - Bursts of file events (save-everything keystrokes, multi-file
23
+ * format-on-save) collapse into one restart via the `graceMs`
24
+ * debounce (default 250 ms).
25
+ * - A restart-in-flight queues at most one follow-up — many edits
26
+ * during a slow restart yield two restarts, not N.
27
+ * - Ignored kinds by default: `node_modules/`, `.git/`, dotfiles,
28
+ * SQLite journal/WAL/SHM siblings, `.log`, editor scratch files
29
+ * (`.swp`, `~$`).
30
+ * - Crash without a pending stop/restart leaves the child corpse
31
+ * in place and waits for a file change rather than spawn-thrashing.
32
+ * - Graceful kill via `SIGTERM`; `SIGKILL` escalation after
33
+ * `killTimeoutMs` (default 4000 ms) if the child ignores it.
27
34
  *
28
- * dev.stats(); // { pid, running, restarts, lastRestartAt }
35
+ * Production refusal: `dev.create()` throws `dev/refused-in-production`
36
+ * when `NODE_ENV=production`, unless the operator explicitly sets
37
+ * `opts.allowProduction: true` with an audited reason. This is what
38
+ * `blamejs dev` (CLI) calls; production deployments that accidentally
39
+ * wire it crash loudly at boot rather than spawning shells on every
40
+ * save.
29
41
  *
30
- * Operator-side, this is what `blamejs dev` will call (next CLI slice).
42
+ * Test seams: `opts._spawn(cmd, args, sopts)` and
43
+ * `opts._watch(dir, wopts, listener)` default to `child_process.spawn`
44
+ * and `fs.watch`; unit tests pass fakes to drive the engine without
45
+ * real subprocesses.
31
46
  *
32
- * Engine hygiene:
33
- * - Bursts of file events (a saved-everything keystroke, a multi-
34
- * file format-on-save) collapse into one restart via debounce.
35
- * - The child is spawned with stdio: 'inherit' so the operator sees
36
- * their app's output unchanged.
37
- * - Parent SIGINT/SIGTERM are forwarded: stop() before exit so an
38
- * orphan child can't outlive the dev session.
39
- * - Restart in-flight when a new event arrives: queue one followup,
40
- * no more — many edits during a slow restart still result in only
41
- * two restarts, not N.
42
- *
43
- * Test seams:
44
- * opts._spawn(cmd, args, sopts) → child-process-shaped object
45
- * opts._watch(dir, wopts, listener) → fs.watcher-shaped object
46
- * These default to child_process.spawn and fs.watch; tests pass
47
- * fakes to drive the engine without real subprocesses.
47
+ * @card
48
+ * Dev-mode helpers hot-reload signal (file watch + child-process restart), route-list dump exposed via `dev.stats()`, and a request inspector courtesy of `stdio: 'inherit'` so the operator sees the spawned app's logs unchanged.
48
49
  */
49
50
 
50
51
  var path = require("path");
@@ -114,6 +115,48 @@ function _logVia(log, level, message, fields) {
114
115
  emit(line);
115
116
  }
116
117
 
118
+ /**
119
+ * @primitive b.dev.create
120
+ * @signature b.dev.create(opts)
121
+ * @since 0.4.0
122
+ * @status stable
123
+ *
124
+ * Build a hot-reload supervisor — spawn `opts.command` with `opts.args`,
125
+ * watch `opts.watch` directories, and restart the child on every
126
+ * unignored file change. Returns `{ start, stop, restart, stats }`;
127
+ * `stats()` reports `{ pid, running, restarts, lastRestartAt, watchers }`.
128
+ *
129
+ * Throws `DevError` at config time on a missing command, a non-finite
130
+ * `graceMs` / `killTimeoutMs`, or an attempt to load with
131
+ * `NODE_ENV=production` (without `opts.allowProduction`).
132
+ *
133
+ * @opts
134
+ * command: string, // required — program to spawn (e.g. "node")
135
+ * args: [string], // argv after command; default []
136
+ * watch: [string], // directories to watch (recursive); default ["."]
137
+ * ignore: [RegExp | string], // appended to the framework default-ignore list
138
+ * graceMs: number, // debounce window (ms); default 250
139
+ * killSignal: string, // initial kill signal; default "SIGTERM"
140
+ * killTimeoutMs: number, // SIGKILL escalation budget (ms); default 4000
141
+ * log: object, // structured logger ({ info, warn, error })
142
+ * env: object, // child env; default process.env
143
+ * cwd: string, // child cwd; default process.cwd()
144
+ * stdio: string | array, // child stdio; default "inherit"
145
+ * allowProduction: boolean, // override the production refusal (audited reason required)
146
+ *
147
+ * @example
148
+ * var dev = b.dev.create({
149
+ * command: "node",
150
+ * args: ["./server.js"],
151
+ * watch: ["./routes", "./views", "./lib"],
152
+ * ignore: [/\.tmp$/],
153
+ * graceMs: 250,
154
+ * });
155
+ *
156
+ * // await dev.start();
157
+ * // dev.stats(); // → { pid: <number>, running: true, restarts: 0, lastRestartAt: null, watchers: 3 }
158
+ * // await dev.stop();
159
+ */
117
160
  function create(opts) {
118
161
  opts = opts || {};
119
162
  validateOpts.requireNonEmptyString(opts.command, "dev.create: opts.command (the program to spawn)", DevError, "dev/no-command");
package/lib/dora.js CHANGED
@@ -1,56 +1,29 @@
1
1
  "use strict";
2
2
  /**
3
- * b.dora — DORA Article 17 ICT-related incident-reporting workflow.
3
+ * @module b.dora
4
+ * @nav Compliance
5
+ * @title DORA
4
6
  *
5
- * Digital Operational Resilience Act (Regulation (EU) 2022/2554)
6
- * Article 17 requires every "financial entity" subject to DORA to
7
- * classify, document, and report ICT-related incidents according to
8
- * the harmonized RTS template (Commission Delegated Regulation
9
- * 2024/1772). This primitive is the framework hook — operators wire
10
- * it into their incident-management workflow; the framework owns the
11
- * classification rubric, the three-stage report shape (initial /
12
- * intermediate / final), and the audit-chain integration.
7
+ * @intro
8
+ * DORA Article 17 ICT-related incident-reporting workflow. The
9
+ * Digital Operational Resilience Act (Regulation (EU) 2022/2554)
10
+ * Article 17 requires every "financial entity" subject to DORA to
11
+ * classify, document, and report ICT-related incidents according to
12
+ * the harmonized RTS template (Commission Delegated Regulation
13
+ * 2024/1772). The framework owns the classification rubric, the
14
+ * three-stage report shape (initial / intermediate / final), and
15
+ * the audit-chain integration; operators wire the produced
16
+ * RTS-template-shaped records into their submission code (channel
17
+ * + ESA / national-supervisor credentials are operator-specific —
18
+ * the framework does NOT submit on the operator's behalf).
13
19
  *
14
- * var dora = b.dora.create({ audit: b.audit });
20
+ * Adjacent regimes (NIS2 Art. 23, CRA Art. 14, HIPAA breach
21
+ * notification) share the deadline-tracking shape; reference
22
+ * constants live on the module so operators don't pin literal hour
23
+ * counts in their reporters.
15
24
  *
16
- * var classification = dora.classify({
17
- * dataAffected: "phi" | "financial" | "personal" | "operational" | "none",
18
- * systemsAffected: ["payments-gateway", "core-ledger"],
19
- * durationMs: C.TIME.hours(4),
20
- * severityIndicator: "critical" | "high" | "medium" | "low",
21
- * economicImpact: { eur: 50000 },
22
- * affectedClients: 1200,
23
- * geographicScope: ["DE", "FR"],
24
- * reputationalImpact: "media" | "internal" | "none",
25
- * });
26
- * // → { classification: "major" | "significant" | "minor",
27
- * // mustReport: true|false, mustReportInitialBy: ms-since-detection,
28
- * // reasons: [...] }
29
- *
30
- * var initial = dora.report({
31
- * incidentId: "INC-2026-0042",
32
- * classification: "major",
33
- * stage: "initial",
34
- * detectedAt: Date.now() - C.TIME.minutes(60),
35
- * description: "Payment-gateway outage — 2-hour customer-facing impact",
36
- * causeKnown: false,
37
- * mitigationStarted: true,
38
- * });
39
- *
40
- * // 72h after detection: intermediate update
41
- * dora.report(Object.assign({}, initial, { stage: "intermediate", ... }));
42
- * // 1 month later (or upon closure): final report
43
- * dora.report(Object.assign({}, initial, { stage: "final", rootCause: "...", ... }));
44
- *
45
- * Audit posture (audit namespace "dora"):
46
- * - dora.incident.classified — every classify() call
47
- * - dora.incident.reported — every report() submission
48
- * - dora.incident.draftFinal — every draftFinalReport() generation
49
- *
50
- * The primitive does NOT submit to ESAs / national supervisors — that
51
- * step is operator-side (channel + credentials are operator-specific).
52
- * The primitive produces the RTS-template-shaped record that the
53
- * operator's submission code drops into the regulator's API.
25
+ * @card
26
+ * DORA Article 17 ICT-related incident-reporting workflow.
54
27
  */
55
28
 
56
29
  var lazyRequire = require("./lazy-require");
@@ -253,6 +226,52 @@ function _validateReportInput(input) {
253
226
 
254
227
  // ---- Public surface ----
255
228
 
229
+ /**
230
+ * @primitive b.dora.create
231
+ * @signature b.dora.create(opts)
232
+ * @since 0.7.25
233
+ * @status stable
234
+ * @compliance dora, nis2, cra, hipaa
235
+ * @related b.audit.safeEmit
236
+ *
237
+ * Build a DORA reporter handle exposing `classify`, `report`, and
238
+ * `draftFinalReport`. `classify` runs the RTS 2024/1772 Articles
239
+ * 1-12 thresholds (severity / affected clients / economic impact /
240
+ * geographic scope / duration / reputational / sensitive-data
241
+ * classes) and returns the regulatory tier (`"major"` / `"significant"`
242
+ * / `"minor"`) plus a deadline hint. `report` validates and shapes
243
+ * the operator's payload into an RTS-template record carrying the
244
+ * `nextStageDueAt` deadline (Art. 19 — 24h initial / 72h intermediate
245
+ * / 30-day final). `draftFinalReport` clones a prior record into a
246
+ * Stage-final skeleton with the operator-fillable fields zeroed.
247
+ * Each call emits an audit row in the `dora.*` namespace.
248
+ *
249
+ * @opts
250
+ * audit: boolean (default true; set false to skip audit emits),
251
+ * observability: boolean (reserved — observability counter is always
252
+ * best-effort and ignored on failure),
253
+ *
254
+ * @example
255
+ * var dora = b.dora.create({ audit: true });
256
+ * var rv = dora.classify({
257
+ * dataAffected: "financial",
258
+ * severityIndicator: "critical",
259
+ * affectedClients: 1200,
260
+ * economicImpact: { eur: 50000 },
261
+ * durationMs: 4 * 60 * 60 * 1000,
262
+ * });
263
+ * rv.classification; // → "major"
264
+ * rv.mustReport; // → true
265
+ *
266
+ * var initial = dora.report({
267
+ * incidentId: "INC-2026-0042",
268
+ * classification: rv.classification,
269
+ * stage: "initial",
270
+ * detectedAt: Date.now(),
271
+ * description: "Payment-gateway outage — 2h customer-facing impact",
272
+ * });
273
+ * initial.stage; // → "initial"
274
+ */
256
275
  function create(opts) {
257
276
  opts = opts || {};
258
277
  validateOpts(opts, ["audit", "observability"], "dora.create");