@blamejs/core 0.8.43 → 0.8.50

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 +93 -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
@@ -82,6 +82,50 @@ var DEFAULT_CSP =
82
82
  "require-trusted-types-for 'script'; " +
83
83
  "trusted-types 'allow-duplicates' default;";
84
84
 
85
+ /**
86
+ * @primitive b.middleware.securityHeaders
87
+ * @signature b.middleware.securityHeaders(req, res, next)
88
+ * @since 0.1.0
89
+ * @related b.middleware.cspNonce, b.middleware.cspReport, b.middleware.cors
90
+ *
91
+ * Sets the OWASP-aligned response headers every modern app should
92
+ * send. Constructed via `b.middleware.securityHeaders(opts)`; the
93
+ * resulting middleware has the `(req, res, next)` shape shown above.
94
+ * Headers include: HSTS (2-year max-age + includeSubDomains + preload), X-CTO
95
+ * nosniff, X-Frame-Options DENY, Referrer-Policy no-referrer, an
96
+ * extensive Permissions-Policy denylist (camera / geolocation /
97
+ * payment / Privacy-Sandbox attribution-reporting / bluetooth /
98
+ * etc.), COOP same-origin, CORP same-origin, Origin-Agent-Cluster
99
+ * `?1`, and a strict default CSP with `require-trusted-types-for
100
+ * 'script'`. Each header can be softened by passing the option
101
+ * value or disabled by passing `false`. Mount FIRST (after
102
+ * `requestId`) so headers are set before any response could be
103
+ * partially sent.
104
+ *
105
+ * @opts
106
+ * {
107
+ * hsts: string|false,
108
+ * contentTypeOptions: "nosniff"|false,
109
+ * frameOptions: "DENY"|"SAMEORIGIN"|false,
110
+ * referrerPolicy: string|false,
111
+ * permissionsPolicy: string|false,
112
+ * coop: string|false,
113
+ * coep: string|false,
114
+ * corp: string|false,
115
+ * originAgentCluster: "?1"|"?0"|false,
116
+ * dnsPrefetchControl: "off"|"on"|false,
117
+ * csp: string|false,
118
+ * reportingEndpoints: object,
119
+ * trustProxy: boolean|number,
120
+ * }
121
+ *
122
+ * @example
123
+ * var b = require("@blamejs/core");
124
+ * var app = b.router.create();
125
+ * app.use(b.middleware.securityHeaders({
126
+ * hsts: "max-age=63072000; includeSubDomains; preload",
127
+ * }));
128
+ */
85
129
  function create(opts) {
86
130
  opts = opts || {};
87
131
  validateOpts(opts, [
@@ -56,6 +56,44 @@ function _isoFuture(s) {
56
56
  return d.getTime() > Date.now();
57
57
  }
58
58
 
59
+ /**
60
+ * @primitive b.middleware.securityTxt
61
+ * @signature b.middleware.securityTxt(opts)
62
+ * @since 0.1.0
63
+ * @related b.middleware.assetlinks, b.middleware.webAppManifest
64
+ *
65
+ * Serves an RFC 9116 `/.well-known/security.txt` body so security
66
+ * researchers find the disclosure policy. With `alsoAtRoot: true`
67
+ * also serves at `/security.txt`. Required fields per §2.5 are
68
+ * `Contact` and `Expires` — the middleware throws at create-time
69
+ * when either is missing OR `Expires` is in the past, and
70
+ * sanitizes every value against CR / LF / NUL (RFC 9116 forbids
71
+ * those in field values). Operators with PGP keys, hall-of-fame
72
+ * URLs, hiring pages, etc. populate the optional fields.
73
+ *
74
+ * @opts
75
+ * {
76
+ * contact: string[], // required, ≥1 entry
77
+ * expires: string, // required, future ISO timestamp
78
+ * encryption: string[],
79
+ * policy: string,
80
+ * ack: string,
81
+ * preferredLanguages: string[],
82
+ * hiring: string,
83
+ * canonical: string|string[],
84
+ * alsoAtRoot: boolean,
85
+ * audit: boolean,
86
+ * }
87
+ *
88
+ * @example
89
+ * var b = require("@blamejs/core");
90
+ * var app = b.router.create();
91
+ * app.use(b.middleware.securityTxt({
92
+ * contact: ["mailto:security@example.com"],
93
+ * expires: "2099-01-01T00:00:00Z",
94
+ * policy: "https://example.com/security/policy",
95
+ * }));
96
+ */
59
97
  function create(opts) {
60
98
  validateOpts.requireObject(opts, "middleware.securityTxt", SecurityTxtError);
61
99
  validateOpts(opts, [
@@ -113,6 +113,43 @@ function _captureResponseHeaderAttrs(res, captureList, prefix) {
113
113
  return out;
114
114
  }
115
115
 
116
+ /**
117
+ * @primitive b.middleware.spanHttpServer
118
+ * @signature b.middleware.spanHttpServer(opts)
119
+ * @since 0.1.0
120
+ * @related b.middleware.tracePropagate, b.middleware.traceLogCorrelation
121
+ *
122
+ * Auto-creates a root OTel span per HTTP request, populates the
123
+ * `http.request.method`, `http.route`, `url.scheme`, `url.path`,
124
+ * `server.address`, `client.address`, `user_agent.original`, and
125
+ * `http.response.status_code` semconv attributes, attaches the
126
+ * span to `req.span`, and ends it on response close. Span kind is
127
+ * `server`. `ignorePaths` (strings or RegExp) keeps `/healthz` and
128
+ * static-asset routes out of the span volume.
129
+ * `captureRequestHeaders` / `captureResponseHeaders` add operator-
130
+ * chosen header attributes (e.g. `x-tenant-id`).
131
+ *
132
+ * @opts
133
+ * {
134
+ * tracer: object, // required
135
+ * onEnd: function(span): void,
136
+ * ignorePaths: Array<string|RegExp>,
137
+ * captureRequestHeaders: string[],
138
+ * captureResponseHeaders: string[],
139
+ * spanNameFn: function(req): string,
140
+ * audit: boolean,
141
+ * }
142
+ *
143
+ * @example
144
+ * var b = require("@blamejs/core");
145
+ * var app = b.router.create();
146
+ * var tracer = b.observability.tracer.create({ service: "checkout" });
147
+ * app.use(b.middleware.tracePropagate());
148
+ * app.use(b.middleware.spanHttpServer({
149
+ * tracer: tracer,
150
+ * ignorePaths: ["/healthz"],
151
+ * }));
152
+ */
116
153
  function create(opts) {
117
154
  validateOpts.requireObject(opts, "middleware.spanHttpServer", SpanHttpError);
118
155
  validateOpts(opts, [
@@ -64,6 +64,42 @@ function _formatEvent(msg) {
64
64
  });
65
65
  }
66
66
 
67
+ /**
68
+ * @primitive b.middleware.sse
69
+ * @signature b.middleware.sse(handler, opts)
70
+ * @since 0.1.0
71
+ * @related b.sse.serializeEvent, b.middleware.compression
72
+ *
73
+ * Server-Sent Events handler — one-way streaming from server to
74
+ * browser over a single HTTP response with `Content-Type:
75
+ * text/event-stream`. Browsers reconnect automatically with
76
+ * `Last-Event-ID` so the operator's handler can resume from the
77
+ * last delivered event. Refuses CRLF / NUL injection in `event` /
78
+ * `id` (CVE-2026-33128 / 29085 / 44217 class) — the framework
79
+ * does NOT silently strip; it returns a structured error.
80
+ * Heartbeat (default 15s) keeps corporate proxies / Heroku-style
81
+ * idle-timeouts from killing the stream; pass `heartbeatMs: false`
82
+ * to disable. SSE streams should NOT be compressed —
83
+ * `b.middleware.compression` already skips `text/event-stream`.
84
+ *
85
+ * The handler receives `(channel, req)`. The channel exposes
86
+ * `send({ id, event, data, retry })`, `ping(comment)`, `close()`,
87
+ * `onAbort(fn)`.
88
+ *
89
+ * @opts
90
+ * {
91
+ * heartbeatMs: number|false, // default 15000
92
+ * headers: object, // extra response headers
93
+ * }
94
+ *
95
+ * @example
96
+ * var b = require("@blamejs/core");
97
+ * var app = b.router.create();
98
+ * app.get("/events", b.middleware.sse(async function (channel, req) {
99
+ * channel.send({ id: 1, event: "tick", data: { count: 1 } });
100
+ * channel.close();
101
+ * }, { heartbeatMs: 15000 }));
102
+ */
67
103
  function create(handler, opts) {
68
104
  if (typeof handler !== "function") {
69
105
  throw new Error("middleware.sse: handler must be a function (channel, req) => ...");
@@ -102,6 +102,39 @@ function _wrapLogger(baseLogger, req, opts) {
102
102
  return wrapped;
103
103
  }
104
104
 
105
+ /**
106
+ * @primitive b.middleware.traceLogCorrelation
107
+ * @signature b.middleware.traceLogCorrelation(opts)
108
+ * @since 0.1.0
109
+ * @related b.middleware.tracePropagate, b.middleware.spanHttpServer
110
+ *
111
+ * Wraps the operator's `b.log` instance for the request lifetime
112
+ * so every `log() / info() / warn() / error() / debug()` call
113
+ * inside the handler auto-includes the canonical `trace_id` +
114
+ * `span_id` (and tenant attributes from W3C Baggage when present).
115
+ * Thin adapter — does not change levels, sinks, or the API
116
+ * surface; logs pass through with the trace fields injected via
117
+ * the meta-object second argument. When `req.trace` isn't set
118
+ * (operator forgot to mount `tracePropagate` first), the wrapper
119
+ * is a no-op pass-through.
120
+ *
121
+ * @opts
122
+ * {
123
+ * logger: object, // required b.log instance
124
+ * reqField: string, // default "log" → req.log
125
+ * includeBaggage: boolean, // default true
126
+ * audit: boolean,
127
+ * }
128
+ *
129
+ * @example
130
+ * var b = require("@blamejs/core");
131
+ * var app = b.router.create();
132
+ * app.use(b.middleware.tracePropagate());
133
+ * app.use(b.middleware.traceLogCorrelation({
134
+ * logger: b.log.boot("api"),
135
+ * reqField: "log",
136
+ * }));
137
+ */
105
138
  function create(opts) {
106
139
  validateOpts.requireObject(opts, "middleware.traceLogCorrelation", TraceLogError);
107
140
  validateOpts(opts, [
@@ -45,6 +45,38 @@ var TracePropagateError = defineClass("TracePropagateError", { alwaysPermanent:
45
45
  var observability = lazyRequire(function () { return require("../observability"); });
46
46
  var audit = lazyRequire(function () { return require("../audit"); });
47
47
 
48
+ /**
49
+ * @primitive b.middleware.tracePropagate
50
+ * @signature b.middleware.tracePropagate(opts)
51
+ * @since 0.1.0
52
+ * @related b.middleware.traceLogCorrelation, b.middleware.spanHttpServer
53
+ *
54
+ * Consumes the inbound `traceparent` header per W3C Trace Context
55
+ * and stamps `req.trace = { traceId, parentId, sampled,
56
+ * hadUpstream, tracestate }` for downstream handlers and outbound
57
+ * HTTP propagation. With `generateIfMissing: true` (default) the
58
+ * middleware synthesises a fresh trace when the inbound header is
59
+ * absent or malformed, and stamps `hadUpstream: false` so downstream
60
+ * code can tell locally-originated traces apart. `setResponseHeader:
61
+ * true` echoes the resolved `traceparent` on the response so the
62
+ * client sees what the server actually used.
63
+ *
64
+ * @opts
65
+ * {
66
+ * generateIfMissing: boolean, // default true
67
+ * auditOnMissing: boolean, // default false
68
+ * setResponseHeader: boolean, // default false
69
+ * audit: boolean,
70
+ * }
71
+ *
72
+ * @example
73
+ * var b = require("@blamejs/core");
74
+ * var app = b.router.create();
75
+ * app.use(b.middleware.tracePropagate({
76
+ * generateIfMissing: true,
77
+ * setResponseHeader: true,
78
+ * }));
79
+ */
48
80
  function create(opts) {
49
81
  opts = opts || {};
50
82
  validateOpts(opts, [
@@ -157,6 +157,35 @@ function _parseChecksumHeader(headerValue, allowedSet) {
157
157
  return { algo: algo, nodeAlgo: nodeAlgo, digestB64: digestB64 };
158
158
  }
159
159
 
160
+ /**
161
+ * @primitive b.middleware.tusUpload.memoryStore
162
+ * @signature b.middleware.tusUpload.memoryStore(opts)
163
+ * @since 0.1.0
164
+ * @related b.middleware.tusUpload
165
+ *
166
+ * In-memory upload store for the TUS middleware. Suitable for
167
+ * dev / single-node demos / test fixtures — every upload is kept
168
+ * in process memory until completion or expiry. Production
169
+ * operators wire a disk- or object-store-backed implementation
170
+ * matching the same `{ create, head, append, terminate, sweep }`
171
+ * shape. `maxSize` caps the per-upload byte budget; uploads above
172
+ * that fail-fast at PATCH time. `defaultExpirationMs` sets the
173
+ * retention window after creation.
174
+ *
175
+ * @opts
176
+ * {
177
+ * maxSize: number,
178
+ * defaultExpirationMs: number, // default 24h
179
+ * }
180
+ *
181
+ * @example
182
+ * var b = require("@blamejs/core");
183
+ * var store = b.middleware.tusUpload.memoryStore({
184
+ * maxSize: b.constants.BYTES.gib(2),
185
+ * defaultExpirationMs: b.constants.TIME.hours(24),
186
+ * });
187
+ * // store is the { create, head, append, ... } object passed to tusUpload({ store })
188
+ */
160
189
  function memoryStore(opts) {
161
190
  opts = opts || {};
162
191
  var maxSize = opts.maxSize;
@@ -293,6 +322,49 @@ function _readChunk(req, maxChunkSize) {
293
322
  });
294
323
  }
295
324
 
325
+ /**
326
+ * @primitive b.middleware.tusUpload
327
+ * @signature b.middleware.tusUpload(opts)
328
+ * @since 0.1.0
329
+ * @related b.middleware.tusUpload.memoryStore, b.middleware.tusUpload.close
330
+ *
331
+ * tus.io v1.0.0 resumable-upload protocol implementation. Wires
332
+ * POST (creation), HEAD (offset query), PATCH (append),
333
+ * DELETE (termination), and OPTIONS (discovery) per the spec.
334
+ * Implements `creation`, `creation-with-upload`, `expiration`,
335
+ * `checksum`, and `termination` extensions. Concatenation (§4.6)
336
+ * is intentionally out of scope — operators that need parallel-
337
+ * chunk assembly compose it in their own store layer. Refuses
338
+ * checksum-mismatch with HTTP 460 per §3.5; expired uploads are
339
+ * swept on a periodic timer driven by the store. Hot-path PATCH
340
+ * lifecycle metrics route through `observability.safeEvent` rather
341
+ * than the audit chain.
342
+ *
343
+ * @opts
344
+ * {
345
+ * mountPath: string, // required
346
+ * store: object, // required
347
+ * maxSize: number,
348
+ * maxChunkSize: number,
349
+ * expirationSec: number,
350
+ * extensions: string[],
351
+ * checksumAlgorithms: string[],
352
+ * onCreate: async function(uploadId, meta): void,
353
+ * onComplete: async function(uploadId, meta): void,
354
+ * onTerminate: async function(uploadId): void,
355
+ * audit: boolean,
356
+ * }
357
+ *
358
+ * @example
359
+ * var b = require("@blamejs/core");
360
+ * var app = b.router.create();
361
+ * var store = b.middleware.tusUpload.memoryStore({ maxSize: b.constants.BYTES.gib(2) });
362
+ * app.use(b.middleware.tusUpload({
363
+ * mountPath: "/uploads",
364
+ * store: store,
365
+ * maxSize: b.constants.BYTES.gib(2),
366
+ * }));
367
+ */
296
368
  function create(opts) {
297
369
  validateOpts.requireObject(opts, "middleware.tusUpload", TusError);
298
370
  validateOpts(opts, [
@@ -637,6 +709,24 @@ function create(opts) {
637
709
  };
638
710
  }
639
711
 
712
+ /**
713
+ * @primitive b.middleware.tusUpload.close
714
+ * @signature b.middleware.tusUpload.close(middleware)
715
+ * @since 0.1.0
716
+ * @related b.middleware.tusUpload
717
+ *
718
+ * Releases resources held by a TUS upload middleware instance —
719
+ * timers, periodic-sweep handles, and any store-close hook the
720
+ * operator wired. Operators call this on graceful shutdown so the
721
+ * sweep timer doesn't keep the process alive. Tolerant of
722
+ * middleware values that don't expose a `close` method (no-op).
723
+ *
724
+ * @example
725
+ * var b = require("@blamejs/core");
726
+ * var store = b.middleware.tusUpload.memoryStore({});
727
+ * var tus = b.middleware.tusUpload({ mountPath: "/uploads", store: store });
728
+ * b.middleware.tusUpload.close(tus);
729
+ */
640
730
  function close(middleware) {
641
731
  // Reserved for future store-close hook; the sweep timer is the only
642
732
  // resource currently bound, and it lives inside the middleware closure.
@@ -37,6 +37,59 @@ var observability = lazyRequire(function () { return require("../observability")
37
37
 
38
38
  function _isPlainArray(x) { return Array.isArray(x); }
39
39
 
40
+ /**
41
+ * @primitive b.middleware.webAppManifest
42
+ * @signature b.middleware.webAppManifest(opts)
43
+ * @since 0.1.0
44
+ * @related b.middleware.assetlinks, b.middleware.securityTxt
45
+ *
46
+ * Serves the W3C Web App Manifest at `/manifest.webmanifest`
47
+ * (and `/manifest.json` when `alsoAtJsonPath: true`). Manifest is
48
+ * JSON-serialized once at create-time and emitted with
49
+ * `Content-Type: application/manifest+json`. Throws at create-time
50
+ * when `name`, `start_url`, or an icons array is missing — those
51
+ * are the W3C-required fields for an installable PWA. Operator
52
+ * fields outside the W3C allowlist throw at boot so typos surface
53
+ * early.
54
+ *
55
+ * @opts
56
+ * {
57
+ * name: string, // required
58
+ * start_url: string, // required
59
+ * icons: Array<{ src, sizes, type }>, // required, ≥1
60
+ * short_name: string,
61
+ * description: string,
62
+ * scope: string,
63
+ * display: string,
64
+ * display_override: string[],
65
+ * orientation: string,
66
+ * theme_color: string,
67
+ * background_color: string,
68
+ * screenshots: array,
69
+ * shortcuts: array,
70
+ * categories: string[],
71
+ * lang: string,
72
+ * dir: string,
73
+ * id: string,
74
+ * prefer_related_applications: boolean,
75
+ * related_applications: array,
76
+ * alsoAtJsonPath: boolean,
77
+ * audit: boolean,
78
+ * }
79
+ *
80
+ * @example
81
+ * var b = require("@blamejs/core");
82
+ * var app = b.router.create();
83
+ * app.use(b.middleware.webAppManifest({
84
+ * name: "Example App",
85
+ * start_url: "/",
86
+ * display: "standalone",
87
+ * icons: [
88
+ * { src: "/icons/192.png", sizes: "192x192", type: "image/png" },
89
+ * { src: "/icons/512.png", sizes: "512x512", type: "image/png" },
90
+ * ],
91
+ * }));
92
+ */
40
93
  function create(opts) {
41
94
  validateOpts.requireObject(opts, "middleware.webAppManifest", WebAppManifestError);
42
95
  // Allowlist subset of W3C-spec attributes operators commonly set.
package/lib/mtls-ca.js CHANGED
@@ -1,80 +1,55 @@
1
1
  "use strict";
2
2
  /**
3
- * mtls-ca — mTLS Certificate Authority management.
3
+ * @module b.mtlsCa
4
+ * @nav Crypto
5
+ * @title mTLS CA
4
6
  *
5
- * Storage, sealed-loading dispatch, generation tagging, atomic commit.
6
- * Cert issuance (CA generation, client cert signing, PKCS#12 packaging)
7
- * delegates to a pluggable engine. The framework ships a default
8
- * pure-JS engine (lib/mtls-engine-default.js, backed by the vendored
9
- * @peculiar/x509 + pkijs bundle); operators with custom requirements
10
- * pass their own via opts.engine.
7
+ * @intro
8
+ * Mutual TLS Certificate Authority internal CA cert issuance,
9
+ * mTLS gate setup, fingerprint pinning.
11
10
  *
12
- * var ca = b.mtlsCa.create({
13
- * dataDir: "./data",
14
- * paths: {
15
- * caKey: "ca.key",
16
- * caKeySealed: "ca.key.sealed",
17
- * caCert: "ca.crt",
18
- * },
19
- * vault: b.vault, // optional; required when sealed
20
- * caKeySealedMode: "required", // "required" (default) | "disabled"
21
- * generation: 1, // current CA generation for OU=CAv{N}
22
- * engine: myCertEngine, // optional — defaults to b.mtlsEngine
23
- * });
24
- *
25
- * Files (relative to dataDir):
26
- * ca.crt CA certificate (PEM, plaintext on disk)
27
- * ca.key CA private key (PEM, plaintext on disk)
28
- * ca.key.sealed CA private key (vault.seal of PEM bytes)
29
- *
30
- * caKeySealedMode (defaults to "required"):
31
- * "required" sealed file required; refuse plaintext (default — vault
32
- * must be wired)
33
- * "disabled" plaintext required; refuse sealed (dev-only opt-out;
34
- * operator must justify with audited reason)
35
- *
36
- * The legacy "auto" mode (load whichever exists, fall back to plaintext
37
- * when no sealed file is present) was removed; it defaulted to writing
38
- * plaintext on a fresh install, which is the inverse of the framework's
39
- * security-defaults-on posture for at-rest key material.
40
- *
41
- * Generation tagging: every CA cert issued by the framework embeds a
42
- * "OU=CAv{N}" RDN in its subject DN. Status reads that back so an
43
- * upgrade flow can detect legacy CAs (a pre-rotation CA whose key
44
- * parameters are below the current bar) and prompt regeneration
45
- * without breaking active mTLS clients.
11
+ * The framework owns storage, sealed-loading dispatch, generation
12
+ * tagging, and atomic commit. Cert issuance (CA generation, client
13
+ * cert signing, PKCS#12 packaging) delegates to a pluggable engine
14
+ * so the operator chooses the X.509 toolchain. The default pure-JS
15
+ * engine lives in `lib/mtls-engine-default.js` (backed by the
16
+ * vendored @peculiar/x509 + pkijs bundle); operators with custom
17
+ * requirements pass their own via `opts.engine`.
46
18
  *
47
- * Issuance surface (delegates to opts.engine):
19
+ * Files relative to `dataDir`: `ca.crt` (PEM cert, plaintext),
20
+ * `ca.key` (PEM key, plaintext — refused under `caKeySealedMode:
21
+ * "required"`), `ca.key.sealed` (vault.seal of the PEM bytes — the
22
+ * default at-rest shape), `revocations.json` (revocation registry),
23
+ * `ca.crl` (signed CRL derived from the registry).
48
24
  *
49
- * ca.initCA()
50
- * returns existing { caCertPem, caKeyPem } or generates a fresh
51
- * pair via engine.generateCa() and atomically commits it.
25
+ * `caKeySealedMode` defaults to "required" — sealed file required,
26
+ * plaintext refused. The legacy "auto" fallback was removed; it
27
+ * defaulted to writing plaintext on a fresh install, which is the
28
+ * inverse of the framework's security-defaults-on posture for
29
+ * at-rest key material. The "disabled" mode is a dev-only opt-out
30
+ * (operator must justify with audited reason).
52
31
  *
53
- * ca.generateClientCert({ cn, validityDays })
54
- * calls engine.signClientCert with the CA loaded.
32
+ * Generation tagging: every CA cert issued by the framework embeds
33
+ * an `OU=CAv{N}` RDN in its subject DN. `parseGeneration` reads that
34
+ * back so an upgrade flow can detect legacy CAs and prompt
35
+ * regeneration without breaking active mTLS clients.
55
36
  *
56
- * ca.generateClientP12({ cn, password, validityDays })
57
- * calls engine.packageP12 with the CA loaded.
58
- *
59
- * Engine contract (default lib/mtls-engine-default.js, override via
60
- * opts.engine):
61
- *
62
- * {
63
- * async generateCa({ generation })
64
- * returns { caCertPem, caKeyPem },
37
+ * Engine contract:
38
+ * async generateCa({ generation }) -> { caCertPem, caKeyPem }
65
39
  * async signClientCert({ cn, validityDays, caCertPem, caKeyPem })
66
- * returns { cert, key, ca, issuedAt, expiresAt },
40
+ * -> { cert, key, ca, issuedAt, expiresAt }
67
41
  * async packageP12({ cn, password, validityDays, caCertPem, caKeyPem })
68
- * returns { p12, certPem, issuedAt, expiresAt },
69
- * }
42
+ * -> { p12, certPem, issuedAt, expiresAt }
43
+ *
44
+ * The engine returns the cert PEM but does NOT compute a
45
+ * fingerprint — the framework hashes the cert via
46
+ * `b.crypto.sha3Hash(certPem)` so the SHA3-512 posture stays
47
+ * consistent across the stack. Operators who need the X.509-
48
+ * conventional SHA-256 fingerprint (browser cert-details panels,
49
+ * openssl interop) compute it separately from the cert PEM.
70
50
  *
71
- * Note: the engine returns the cert PEM (`certPem`) but does NOT
72
- * compute a fingerprintthe framework hashes the cert via
73
- * `b.crypto.sha3Hash(certPem)` for any audit / display purpose,
74
- * keeping the SHA3-512 posture consistent across the rest of the
75
- * stack. Operators who want the X.509-conventional SHA-256
76
- * fingerprint (for browser cert-details panels, openssl interop)
77
- * compute it separately from the cert PEM.
51
+ * @card
52
+ * Mutual TLS Certificate Authorityinternal CA cert issuance, mTLS gate setup, fingerprint pinning.
78
53
  */
79
54
 
80
55
  var fs = require("fs");
@@ -133,10 +108,27 @@ function _resolvePaths(dataDir, paths) {
133
108
  };
134
109
  }
135
110
 
136
- // Parse "OU=CAv{N}" from a PEM cert's subject DN. Returns the integer
137
- // N (defaulting to 1 for untagged legacy CAs) or 0 when the cert is
138
- // unreadable. Untagged returning 1 means the first regen lifts a legacy
139
- // CA to generation 2 without misidentifying it as fresh.
111
+ /**
112
+ * @primitive b.mtlsCa.parseGeneration
113
+ * @signature b.mtlsCa.parseGeneration(certPem)
114
+ * @since 0.7.68
115
+ * @related b.mtlsCa.create
116
+ *
117
+ * Read the `OU=CAv{N}` generation tag from a PEM CA certificate's
118
+ * subject DN. Returns the integer `N`, defaulting to `1` for untagged
119
+ * legacy CAs (so the first regen lifts a legacy CA to generation 2
120
+ * without misidentifying it as fresh) or `0` when the cert is
121
+ * unreadable. Operators wire this into upgrade flows that detect
122
+ * pre-rotation CAs whose key parameters are below the current bar.
123
+ *
124
+ * @example
125
+ * var pem = "-----BEGIN CERTIFICATE-----\n(invalid)\n-----END CERTIFICATE-----\n";
126
+ * b.mtlsCa.parseGeneration(pem);
127
+ * // → 0
128
+ *
129
+ * b.mtlsCa.parseGeneration(null);
130
+ * // → 0
131
+ */
140
132
  function parseGeneration(certPem) {
141
133
  if (typeof certPem !== "string" && !Buffer.isBuffer(certPem)) return 0;
142
134
  try {
@@ -149,6 +141,44 @@ function parseGeneration(certPem) {
149
141
  }
150
142
  }
151
143
 
144
+ /**
145
+ * @primitive b.mtlsCa.create
146
+ * @signature b.mtlsCa.create(opts)
147
+ * @since 0.7.68
148
+ * @related b.mtlsCa.parseGeneration, b.crypto.sha3Hash
149
+ *
150
+ * Build an mTLS CA handle bound to `opts.dataDir`. The handle owns
151
+ * sealed-loading of the CA private key, generation tagging on issued
152
+ * certs, atomic commit of newly generated material, and a pluggable
153
+ * engine for the X.509 work itself. Returns an object with
154
+ * `initCA()`, `generateClientCert({ cn, validityDays })`,
155
+ * `generateClientP12({ cn, password, validityDays })`, plus
156
+ * revocation helpers.
157
+ *
158
+ * Throws `MtlsCaError` at config-time on bad opts (missing dataDir,
159
+ * sealed-mode mismatch, missing vault when seal required).
160
+ *
161
+ * @opts
162
+ * dataDir: string, // required — base for cert / key / revocation files
163
+ * paths: { caKey, caKeySealed, caCert, revocations, crl }, // override defaults
164
+ * vault: object, // b.vault — required when caKeySealedMode = "required"
165
+ * caKeySealedMode: string, // "required" (default) | "disabled"
166
+ * generation: number, // current CA generation for OU=CAv{N}
167
+ * engine: object, // pluggable X.509 engine; default lib/mtls-engine-default
168
+ *
169
+ * @example
170
+ * var fs = require("fs");
171
+ * var os = require("os");
172
+ * var path = require("path");
173
+ * var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-mtls-"));
174
+ * var ca = b.mtlsCa.create({
175
+ * dataDir: dir,
176
+ * caKeySealedMode: "disabled",
177
+ * generation: 1,
178
+ * });
179
+ * typeof ca.initCA;
180
+ * // → "function"
181
+ */
152
182
  function create(opts) {
153
183
  opts = opts || {};
154
184
  validateOpts(opts, [