@blamejs/core 0.4.26 → 0.4.28

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 CHANGED
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.4.x
10
10
 
11
+ - **0.4.27** (2026-04-30) — primitive-drift sweep: regex + escape consolidation, IPv6 completion
12
+ - **0.4.26** (2026-04-30) — primitive-drift sweep: middleware audit context + safeUrl
11
13
  - **0.4.25** (2026-04-30) — b.objectStore.bucketOps: bucket-level operations (SigV4)
12
14
  - **0.4.24** (2026-04-30) — b.objectStore: multipart upload + server-side encryption
13
15
  - **0.4.23** (2026-04-30) — b.mail.dkim signing + calendar invites
package/lib/api-key.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * ownerId: "user-42",
14
14
  * scopes: ["read:users", "write:posts"],
15
15
  * metadata: { name: "Mobile app v3" },
16
- * expiresAt: Date.now() + 90 * 86400 * 1000,
16
+ * expiresAt: Date.now() + b.constants.TIME.days(90),
17
17
  * });
18
18
  * // issued.key — "bk_live_<idHex>_<secretHex>" (returned ONCE)
19
19
  * // issued.id — "<idHex>"
package/lib/cli.js CHANGED
@@ -34,6 +34,9 @@
34
34
  * before encryption or temporarily switch the file to plaintext.
35
35
  */
36
36
 
37
+ var fs = require("node:fs");
38
+ var nodeCrypto = require("node:crypto");
39
+ var os = require("node:os");
37
40
  var path = require("path");
38
41
  var apiSnapshot = require("./api-snapshot");
39
42
  var auditTools = require("./audit-tools");
@@ -898,11 +901,7 @@ async function _runBackup(args, ctx) {
898
901
  }
899
902
 
900
903
  if (sub === "verify") {
901
- var fs2 = require("node:fs");
902
- var os2 = require("node:os");
903
- var nodePath = require("node:path");
904
- var nodeCrypto = require("node:crypto");
905
- var stagingDir = nodePath.join(os2.tmpdir(),
904
+ var stagingDir = path.join(os.tmpdir(),
906
905
  "blamejs-backup-verify-" + nodeCrypto.randomBytes(8).toString("hex"));
907
906
  try {
908
907
  var r = await restoreBundle.extract({
@@ -917,7 +916,7 @@ async function _runBackup(args, ctx) {
917
916
  } catch (e) {
918
917
  return report.error((e && e.message) || String(e));
919
918
  } finally {
920
- try { fs2.rmSync(stagingDir, { recursive: true, force: true }); } catch (_e) { /* best-effort */ }
919
+ try { fs.rmSync(stagingDir, { recursive: true, force: true }); } catch (_e) { /* best-effort */ }
921
920
  }
922
921
  }
923
922
 
@@ -1118,7 +1117,7 @@ async function _runMtls(args, ctx) {
1118
1117
  validityDays: daysP,
1119
1118
  });
1120
1119
  if (outPath) {
1121
- require("node:fs").writeFileSync(outPath, p12.p12, { mode: 0o600 });
1120
+ fs.writeFileSync(outPath, p12.p12, { mode: 0o600 });
1122
1121
  report.write("p12 written: " + outPath);
1123
1122
  } else {
1124
1123
  // No --out: stream the bytes to stdout for piping. Operators
package/lib/db-query.js CHANGED
@@ -26,6 +26,7 @@
26
26
  * a derived hash throws (every encryption uses a fresh nonce — the lookup
27
27
  * would always return zero rows; failing loudly is safer).
28
28
  */
29
+ var { Readable } = require("node:stream");
29
30
  var cryptoField = require("./crypto-field");
30
31
  var { generateToken } = require("./crypto");
31
32
 
@@ -194,7 +195,6 @@ class Query {
194
195
  var stmt = this._db.prepare(sql);
195
196
  var table = this._table;
196
197
  var iter;
197
- var Readable = require("node:stream").Readable;
198
198
  try { iter = stmt.iterate.apply(stmt, this._whereParams); }
199
199
  catch (e) {
200
200
  var r = new Readable({ objectMode: true, read: function () {} });
package/lib/db.js CHANGED
@@ -42,6 +42,7 @@
42
42
  var fs = require("fs");
43
43
  var path = require("path");
44
44
  var { DatabaseSync } = require("node:sqlite");
45
+ var { Readable } = require("node:stream");
45
46
  var atomicFile = require("./atomic-file");
46
47
  var audit = require("./audit");
47
48
  var auditSign = require("./audit-sign");
@@ -796,7 +797,6 @@ function stream(sql) {
796
797
  var table = opts && typeof opts.table === "string" ? opts.table : null;
797
798
  var unseal = table ? cryptoField : null;
798
799
 
799
- var Readable = require("node:stream").Readable;
800
800
  var stmt;
801
801
  var iter;
802
802
  try {
package/lib/error-page.js CHANGED
@@ -39,8 +39,11 @@
39
39
  */
40
40
 
41
41
  var lazyRequire = require("./lazy-require");
42
+ var template = require("./template");
42
43
  var audit = lazyRequire(function () { return require("./audit"); });
43
44
 
45
+ var _esc = template.escapeHtml;
46
+
44
47
  // Status code → default short reason. Used when the error doesn't carry
45
48
  // its own message (e.g. a generic Error thrown from a route).
46
49
  var STATUS_REASONS = {
@@ -114,16 +117,6 @@ function _wantsJson(req, defaultFormat) {
114
117
  return req && req.method && req.method.toUpperCase() !== "GET";
115
118
  }
116
119
 
117
- function _esc(s) {
118
- if (s === undefined || s === null) return "";
119
- return String(s)
120
- .replace(/&/g, "&amp;")
121
- .replace(/</g, "&lt;")
122
- .replace(/>/g, "&gt;")
123
- .replace(/"/g, "&quot;")
124
- .replace(/'/g, "&#39;");
125
- }
126
-
127
120
  function _redactHeaders(headers) {
128
121
  if (!headers || typeof headers !== "object") return {};
129
122
  var out = {};
package/lib/forms.js CHANGED
@@ -36,6 +36,7 @@
36
36
  * forms.escapeHtml = template.escapeHtml (re-export for convenience)
37
37
  */
38
38
  var nodeCrypto = require("crypto");
39
+ var safeSchema = require("./safe-schema");
39
40
  var safeUrl = require("./safe-url");
40
41
  var template = require("./template");
41
42
 
@@ -319,10 +320,10 @@ function validate(spec, body) {
319
320
  }
320
321
  }
321
322
  if (f.type === "email" && typeof coerced === "string") {
322
- // Pragmatic email check RFC 5322 is impractical to regex
323
- // correctly; this catches obvious nonsense ("foo", "foo@",
324
- // "@bar") without over-engineering.
325
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(coerced)) {
323
+ // Same pragmatic check the rest of the framework uses
324
+ // (safeSchema.EMAIL_RE shared so we don't carry parallel
325
+ // copies of the same /^[^\s@]+@[^\s@]+\.[^\s@]+$/ regex).
326
+ if (!safeSchema.EMAIL_RE.test(coerced)) {
326
327
  errors[f.name] = (f.label || f.name) + " must be a valid email address";
327
328
  continue;
328
329
  }
@@ -64,6 +64,8 @@
64
64
  var http = require("http");
65
65
  var https = require("https");
66
66
  var http2 = require("http2");
67
+ var nodeCrypto = require("node:crypto");
68
+ var nodeStream = require("node:stream");
67
69
  var { URL } = require("url");
68
70
  var C = require("./constants");
69
71
  var pqcAgent = require("./pqc-agent");
@@ -321,7 +323,6 @@ function _attachJarCookie(headers, jar, url) {
321
323
  // parser accepts so round-trip from one blamejs app's outbound to
322
324
  // another's inbound is exact.
323
325
  function _buildMultipartBody(spec) {
324
- var nodeCrypto = require("node:crypto");
325
326
  var boundary = "----blamejs-mp-" + nodeCrypto.randomBytes(16).toString("hex");
326
327
  var CRLF = "\r\n";
327
328
  var parts = [];
@@ -701,7 +702,7 @@ function _requestH1(transport, u, opts) {
701
702
  // The framework's contract is to hand back the response stream
702
703
  // unmodified; fix-up via a passthrough keeps that contract while
703
704
  // observing the chunk sizes.
704
- var passthrough = new (require("node:stream").PassThrough)();
705
+ var passthrough = new nodeStream.PassThrough();
705
706
  res.on("data", function (chunk) { _emitDownload(chunk.length); passthrough.write(chunk); });
706
707
  res.on("end", function () { passthrough.end(); });
707
708
  res.on("error", function (e) { passthrough.destroy(e); });
package/lib/mail.js CHANGED
@@ -67,6 +67,7 @@ var audit = lazyRequire(function () { return require("./audit"); });
67
67
  var httpClient = lazyRequire(function () { return require("./http-client"); });
68
68
  var net = lazyRequire(function () { return require("net"); });
69
69
  var tls = lazyRequire(function () { return require("tls"); });
70
+ var safeSchema = require("./safe-schema");
70
71
  var validateOpts = require("./validate-opts");
71
72
  var { FrameworkError } = require("./framework-error");
72
73
 
@@ -80,10 +81,10 @@ class MailError extends FrameworkError {
80
81
  }
81
82
  }
82
83
 
83
- // Pragmatic email checksame shape as forms.validate. RFC 5322 in a
84
- // regex is a fool's errand; this catches obvious nonsense and lets
85
- // real-world addresses through.
86
- var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
84
+ // Pragmatic email regexshared with forms.validate / safe-schema.
85
+ // RFC 5322 in a regex is a fool's errand; this catches obvious nonsense
86
+ // and lets real-world addresses through.
87
+ var EMAIL_RE = safeSchema.EMAIL_RE;
87
88
 
88
89
  function _normalizeRecipientList(value, label) {
89
90
  if (value === undefined || value === null) return [];
@@ -26,23 +26,23 @@
26
26
  *
27
27
  * var bp = b.middleware.bodyParser({
28
28
  * json: {
29
- * limit: 1024 * 1024,
29
+ * limit: b.constants.BYTES.mib(1),
30
30
  * strict: true, // require the body to start with {/[
31
31
  * parseHook: function (parsed) { ... return validatedShape; },
32
32
  * },
33
33
  * urlencoded: {
34
- * limit: 1024 * 1024,
34
+ * limit: b.constants.BYTES.mib(1),
35
35
  * arrayLimit: 100, // for ?tag=a&tag=b → tag: ["a","b"]
36
36
  * },
37
- * text: { limit: 1024 * 1024, charset: "utf-8" },
38
- * raw: { limit: 10 * 1024 * 1024, contentTypes: ["application/octet-stream"] },
37
+ * text: { limit: b.constants.BYTES.mib(1), charset: "utf-8" },
38
+ * raw: { limit: b.constants.BYTES.mib(10), contentTypes: ["application/octet-stream"] },
39
39
  * multipart: {
40
40
  * tmpDir: os.tmpdir(),
41
- * fileSize: 10 * 1024 * 1024,
42
- * totalSize: 50 * 1024 * 1024,
41
+ * fileSize: b.constants.BYTES.mib(10),
42
+ * totalSize: b.constants.BYTES.mib(50),
43
43
  * fileCount: 20,
44
44
  * fieldCount: 100,
45
- * fieldSize: 1024 * 1024,
45
+ * fieldSize: b.constants.BYTES.mib(1),
46
46
  * mimeAllowlist: ["image/jpeg", "image/png", "application/pdf"], // null = any
47
47
  *
48
48
  * // Per-part predicate. Runs after sanitization + MIME checks but
@@ -64,8 +64,8 @@
64
64
  * // global mimeAllowlist for the named field; other fields still
65
65
  * // use the global list.
66
66
  * fields: {
67
- * avatar: { maxBytes: 2 * 1024 * 1024, mimeTypes: ["image/jpeg", "image/png"] },
68
- * document: { maxBytes: 25 * 1024 * 1024 },
67
+ * avatar: { maxBytes: b.constants.BYTES.mib(2), mimeTypes: ["image/jpeg", "image/png"] },
68
+ * document: { maxBytes: b.constants.BYTES.mib(25) },
69
69
  * },
70
70
  *
71
71
  * // When wired, fileFilter rejections emit body-parser.multipart.file_rejected
@@ -175,7 +175,7 @@ function create(config) {
175
175
  if (!tokenResp.access_token) {
176
176
  throw _err("AUTH_FAILED", "GCS token endpoint returned no access_token: " + res.body.toString("utf8"), true);
177
177
  }
178
- var expiresInMs = (tokenResp.expires_in || 3600) * 1000;
178
+ var expiresInMs = C.TIME.seconds(tokenResp.expires_in || 3600);
179
179
  cachedToken = {
180
180
  accessToken: tokenResp.access_token,
181
181
  expiresAt: Date.now() + expiresInMs,
@@ -54,6 +54,7 @@ var nodeCrypto = require("crypto");
54
54
  var sigv4 = require("./sigv4");
55
55
  var safeXml = require("../parsers/safe-xml");
56
56
  var safeUrl = require("../safe-url");
57
+ var template = require("../template");
57
58
  var httpClient = require("../http-client");
58
59
  var { ObjectStoreError } = require("../framework-error");
59
60
 
@@ -83,15 +84,18 @@ function _validateBucketName(name) {
83
84
  }
84
85
  }
85
86
 
86
- function _xmlEscape(s) {
87
- return String(s)
88
- .replace(/&/g, "&amp;")
89
- .replace(/</g, "&lt;")
90
- .replace(/>/g, "&gt;")
91
- .replace(/"/g, "&quot;")
92
- .replace(/'/g, "&apos;");
93
- }
87
+ // XML body strings flow through template.escapeHtml — `&#x27;` (which
88
+ // it emits for the apostrophe) is a numeric character reference and
89
+ // is valid in both XML and HTML, where `&apos;` is XML-only. AWS S3
90
+ // accepts both; using the shared HTML escape keeps the framework
91
+ // down to one canonical escape primitive.
92
+ var _xmlEscape = template.escapeHtml;
94
93
 
94
+ // AWS PutBucketLifecycle / PutBucketCors require a Content-MD5 header
95
+ // for body integrity (legacy AWS API requirement; SigV4 already covers
96
+ // integrity via x-amz-content-sha256 but the API still validates this).
97
+ // MD5 here is NOT a credential or security primitive — it's an AWS API
98
+ // shape. b.credentialHash is the wrong tool.
95
99
  function _md5Base64(buf) {
96
100
  return nodeCrypto.createHash("md5").update(buf).digest("base64");
97
101
  }
@@ -33,8 +33,9 @@ var cluster = require("./cluster");
33
33
  var clusterStorage = require("./cluster-storage");
34
34
  var { generateToken } = require("./crypto");
35
35
  var cryptoField = require("./crypto-field");
36
- var safeJson = require("./safe-json");
37
36
  var lazyRequire = require("./lazy-require");
37
+ var safeJson = require("./safe-json");
38
+ var scheduler = require("./scheduler");
38
39
  var { QueueError } = require("./framework-error");
39
40
 
40
41
  var _err = QueueError.factory;
@@ -241,7 +242,6 @@ function create(_config) {
241
242
  if (row && row.repeatCron) {
242
243
  try {
243
244
  var unsealedRow = cryptoField.unsealRow("_blamejs_jobs", row);
244
- var scheduler = require("./scheduler");
245
245
  var cron = scheduler.parseCron(unsealedRow.repeatCron);
246
246
  var nextMs = scheduler.nextCronFire(cron, new Date(nowMs), unsealedRow.repeatTimezone || null);
247
247
  await enqueue(unsealedRow.queueName,
package/lib/queue.js CHANGED
@@ -31,12 +31,13 @@
31
31
  * failed (status='failed')
32
32
  */
33
33
  var C = require("./constants");
34
- var localProto = require("./queue-local");
35
- var retryHelper = require("./retry");
36
- var safeAsync = require("./safe-async");
37
34
  var lazyRequire = require("./lazy-require");
35
+ var nodeCrypto = require("node:crypto");
38
36
  var observability = require("./observability");
39
37
  var protocolDispatcher = require("./protocol-dispatcher");
38
+ var localProto = require("./queue-local");
39
+ var retryHelper = require("./retry");
40
+ var safeAsync = require("./safe-async");
40
41
  var { QueueError } = require("./framework-error");
41
42
 
42
43
  var dispatcher = protocolDispatcher.create({
@@ -565,7 +566,6 @@ function enqueueFlow(spec) {
565
566
  return Promise.reject(e);
566
567
  }
567
568
 
568
- var nodeCrypto = require("node:crypto");
569
569
  var flowId = "flow-" + nodeCrypto.randomBytes(8).toString("hex");
570
570
 
571
571
  return observability.tap("queue.enqueueFlow",
package/lib/router.js CHANGED
@@ -25,6 +25,7 @@ var fs = require("fs");
25
25
  var path = require("path");
26
26
  var { URL } = require("url");
27
27
  var C = require("./constants");
28
+ var safeAsync = require("./safe-async");
28
29
  var websocket = require("./websocket");
29
30
  var { boot } = require("./log");
30
31
 
@@ -217,12 +218,13 @@ class Router {
217
218
  });
218
219
  });
219
220
 
220
- var allClosed = Promise.all(closes);
221
- var timeout = new Promise(function (resolve) {
222
- var t = setTimeout(resolve, timeoutMs);
223
- t.unref();
224
- });
225
- await Promise.race([allClosed, timeout]);
221
+ // Wait up to `timeoutMs` for graceful WS closes, then force-destroy
222
+ // any laggards. `safeAsync.sleep({ unref: true })` matches the
223
+ // framework's outbound-timeout convention.
224
+ await Promise.race([
225
+ Promise.all(closes),
226
+ safeAsync.sleep(timeoutMs, { unref: true }),
227
+ ]);
226
228
  // Force-destroy any laggards — at this point we've waited the full
227
229
  // timeout and they didn't ack. The operator chose timeoutMs; honor it.
228
230
  this._activeWsConns.forEach(function (conn) {
@@ -109,6 +109,7 @@
109
109
  * so the two stay distinct rather than one wrapping the other.
110
110
  */
111
111
 
112
+ var safeJson = require("./safe-json");
112
113
  var { defineClass } = require("./framework-error");
113
114
 
114
115
  var SafeSchemaError = defineClass("SafeSchemaError", { alwaysPermanent: true });
@@ -143,10 +144,17 @@ var ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
143
144
  // base64 (standard alphabet, with optional padding). Base64url variants
144
145
  // rejected — operators chain .regex(...) for that.
145
146
  var BASE64_RE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
146
- // IPv6 — covers full, compressed (::), and IPv4-mapped forms. Not
147
- // exhaustive on every legal corner, but rejects the common malformed
148
- // inputs operators actually see at HTTP boundaries.
149
- var IPV6_RE = /^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{0,4}|(?:[0-9a-fA-F]{1,4}:){1,6}(?::[0-9a-fA-F]{1,4}){1,1}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5})$/;
147
+ // IPv6 structural pattern full 8-hextet, every `::`-compressed shape,
148
+ // `::` and `::1` literals, IPv4-mapped (`::ffff:1.2.3.4`), and 6-prefix
149
+ // + IPv4 tail. Adapted from validator.js (Apache-2.0); zone IDs
150
+ // (`fe80::1%eth0`) are deliberately omitted — the framework rejects
151
+ // them as non-portable, matching `safe-json.formats.ipv6`. Bounded
152
+ // quantifiers, no nested-quantifier alternation, ReDoS-safe.
153
+ //
154
+ // `.ipv6()` schema method delegates to `safeJson.formats.ipv6` for
155
+ // stricter algorithmic validation; this regex is exported as a
156
+ // structural pattern for operators who want it directly.
157
+ var IPV6_RE = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
150
158
 
151
159
  // ---- helpers ----
152
160
 
@@ -424,22 +432,33 @@ function _stringMethods(schema, spec) {
424
432
  _fail(p, "string/datetime", "must be an ISO-8601 datetime with timezone");
425
433
  });
426
434
  };
435
+ // IP-address validators delegate to safe-json's algorithmic format
436
+ // checks rather than re-running regex matches. The algorithmic path
437
+ // handles edge cases that pure regex misses (compressed `::` shapes,
438
+ // IPv4-mapped `::ffff:1.2.3.4`, multi-`::` rejection, group-count
439
+ // bounds) and keeps the framework's IP-validation behavior in one
440
+ // tested place. IPV4_RE / IPV6_RE remain exported for operators who
441
+ // want the structural pattern, but `.ipv4()` / `.ipv6()` / `.ip()`
442
+ // are the canonical validation surface.
427
443
  schema.ipv4 = function () {
428
444
  return chain(function (v, p) {
429
- return IPV4_RE.test(v) ? { ok: true } :
430
- _fail(p, "string/ipv4", "must be a valid IPv4 address");
445
+ return (typeof v === "string" && safeJson.formats.ipv4(v))
446
+ ? { ok: true }
447
+ : _fail(p, "string/ipv4", "must be a valid IPv4 address");
431
448
  });
432
449
  };
433
450
  schema.ipv6 = function () {
434
451
  return chain(function (v, p) {
435
- return IPV6_RE.test(v) ? { ok: true } :
436
- _fail(p, "string/ipv6", "must be a valid IPv6 address");
452
+ return (typeof v === "string" && safeJson.formats.ipv6(v))
453
+ ? { ok: true }
454
+ : _fail(p, "string/ipv6", "must be a valid IPv6 address");
437
455
  });
438
456
  };
439
457
  schema.ip = function () {
440
458
  return chain(function (v, p) {
441
- return (IPV4_RE.test(v) || IPV6_RE.test(v)) ? { ok: true } :
442
- _fail(p, "string/ip", "must be a valid IP address (v4 or v6)");
459
+ return (typeof v === "string" && safeJson.formats.ip(v))
460
+ ? { ok: true }
461
+ : _fail(p, "string/ip", "must be a valid IP address (v4 or v6)");
443
462
  });
444
463
  };
445
464
  schema.cuid = function () {
@@ -1173,4 +1192,17 @@ module.exports = {
1173
1192
 
1174
1193
  // Errors
1175
1194
  SafeSchemaError: SafeSchemaError,
1195
+
1196
+ // Validation regexes — exported so other modules don't re-declare
1197
+ // their own copies. Pragmatic patterns; operators wanting RFC-strict
1198
+ // behavior chain `.refine()` on top of the schema instead.
1199
+ EMAIL_RE: EMAIL_RE,
1200
+ URL_RE: URL_RE,
1201
+ UUID_RE: UUID_RE,
1202
+ DATE_RE: DATE_RE,
1203
+ DATETIME_RE: DATETIME_RE,
1204
+ IPV4_RE: IPV4_RE,
1205
+ IPV6_RE: IPV6_RE,
1206
+ CUID_RE: CUID_RE,
1207
+ ULID_RE: ULID_RE,
1176
1208
  };
package/lib/session.js CHANGED
@@ -35,12 +35,13 @@
35
35
  * verify / count
36
36
  * — anywhere (any node can read shared session state)
37
37
  */
38
- var { generateToken, sha3Hash } = require("./crypto");
39
- var safeJson = require("./safe-json");
40
- var cryptoField = require("./crypto-field");
41
- var clusterStorage = require("./cluster-storage");
38
+ var audit = require("./audit");
42
39
  var cluster = require("./cluster");
40
+ var clusterStorage = require("./cluster-storage");
43
41
  var C = require("./constants");
42
+ var { generateToken, sha3Hash } = require("./crypto");
43
+ var cryptoField = require("./crypto-field");
44
+ var safeJson = require("./safe-json");
44
45
  var { SessionError } = require("./framework-error");
45
46
 
46
47
  var _err = SessionError.factory;
@@ -260,13 +261,12 @@ async function rotate(oldToken, opts) {
260
261
  // privilege transition so post-incident review can trace which
261
262
  // session id covered which privilege state.
262
263
  try {
263
- var audit = require("./audit");
264
264
  audit.emit({
265
265
  action: "auth.session.rotate",
266
266
  outcome: "success",
267
267
  metadata: { reason: opts.reason || "explicit" },
268
268
  });
269
- } catch (_e) { /* boot-order tolerance */ }
269
+ } catch (_e) { /* audit emit best-effort never block rotate() */ }
270
270
 
271
271
  return { token: newSid, expiresAt: expiresAt };
272
272
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.4.26",
3
+ "version": "0.4.28",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",