@blamejs/core 0.5.16 → 0.5.18

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/lib/testing.js CHANGED
@@ -60,6 +60,7 @@
60
60
  */
61
61
 
62
62
  var fs = require("node:fs");
63
+ var http = require("node:http");
63
64
  var os = require("node:os");
64
65
  var nodePath = require("node:path");
65
66
  var EventEmitter = require("node:events").EventEmitter;
@@ -567,7 +568,6 @@ function listenOnRandomPort(server, host) {
567
568
  // production traffic takes. Server is closed automatically when the
568
569
  // promise resolves or rejects.
569
570
  function request(target) {
570
- var http = require("node:http");
571
571
  // Resolve target → request listener
572
572
  var server;
573
573
  var ownsServer = false;
package/lib/totp.js CHANGED
@@ -60,6 +60,7 @@
60
60
  * 1Password, Bitwarden, Aegis, Microsoft Authenticator all do).
61
61
  */
62
62
  var nodeCrypto = require("crypto");
63
+ var crypto = require("./crypto");
63
64
  var { generateBytes, generateToken } = require("./crypto");
64
65
  var { AuthError } = require("./framework-error");
65
66
 
@@ -214,8 +215,7 @@ function verify(secret, code, opts) {
214
215
  try { expected = compute(secret, step, opts); }
215
216
  catch (_e) { return false; }
216
217
  var expectedBuf = Buffer.from(expected);
217
- if (expectedBuf.length === userBuf.length &&
218
- nodeCrypto.timingSafeEqual(expectedBuf, userBuf)) {
218
+ if (crypto.timingSafeEqual(expectedBuf, userBuf)) {
219
219
  return step;
220
220
  }
221
221
  }
package/lib/tracing.js CHANGED
@@ -81,7 +81,7 @@
81
81
  * async-context, which is fine for the surfaces we instrument.
82
82
  */
83
83
 
84
- var nodeCrypto = require("node:crypto");
84
+ var crypto = require("./crypto");
85
85
  var validateOpts = require("./validate-opts");
86
86
  var { defineClass } = require("./framework-error");
87
87
  var { resolveRoute, captureResponseStatus } = require("./request-helpers");
@@ -134,10 +134,10 @@ function _formatTraceparent(traceId, spanId, flags) {
134
134
  }
135
135
 
136
136
  function _newTraceId() {
137
- return nodeCrypto.randomBytes(16).toString("hex");
137
+ return crypto.generateToken(16); // 32 hex chars (W3C trace-context)
138
138
  }
139
139
  function _newSpanId() {
140
- return nodeCrypto.randomBytes(8).toString("hex");
140
+ return crypto.generateToken(8); // 16 hex chars (W3C trace-context)
141
141
  }
142
142
 
143
143
  // ---- Pass-through span (used when OTel isn't installed) ----
package/lib/webhook.js CHANGED
@@ -225,10 +225,10 @@ function _pqcVerify(publicKeyPem, data, expectedHex) {
225
225
  // a kid → signature pair. Whitespace tolerated around commas.
226
226
 
227
227
  function _parseSignatureHeader(headerValue) {
228
- var parts = headerValue.split(",");
228
+ var segs = requestHelpers.parseListHeader(headerValue);
229
229
  var t = null, id = null, sigs = {};
230
- for (var i = 0; i < parts.length; i++) {
231
- var seg = parts[i].trim();
230
+ for (var i = 0; i < segs.length; i++) {
231
+ var seg = segs[i];
232
232
  var eq = seg.indexOf("=");
233
233
  if (eq <= 0) continue; // skip malformed segments rather than failing whole header
234
234
  var name = seg.slice(0, eq);
@@ -539,7 +539,7 @@ function verifier(opts) {
539
539
 
540
540
  // Timestamp window: signed in seconds, compare to ms clock.
541
541
  var nowMs = nowFn();
542
- var ageMs = nowMs - (ts * 1000);
542
+ var ageMs = nowMs - C.TIME.seconds(ts);
543
543
  if (ageMs > toleranceMs) {
544
544
  throw _failure("EXPIRED", "webhook: timestamp older than toleranceMs (age=" + ageMs + "ms)", "expired", ctxReq);
545
545
  }
@@ -570,7 +570,7 @@ function verifier(opts) {
570
570
  }
571
571
 
572
572
  if (nonceStore) {
573
- var expireAt = (ts * 1000) + toleranceMs;
573
+ var expireAt = C.TIME.seconds(ts) + toleranceMs;
574
574
  var fresh = await nonceStore.checkAndInsert(parsed.id, expireAt);
575
575
  if (!fresh) {
576
576
  throw _failure("REPLAY", "webhook: id '" + parsed.id + "' has been seen before", "replay", ctxReq);
package/lib/websocket.js CHANGED
@@ -84,6 +84,8 @@
84
84
  var nodeCrypto = require("crypto");
85
85
  var { EventEmitter } = require("events");
86
86
  var C = require("./constants");
87
+ var requestHelpers = require("./request-helpers");
88
+ var safeAsync = require("./safe-async");
87
89
  var safeBuffer = require("./safe-buffer");
88
90
  var { FrameworkError } = require("./framework-error");
89
91
  var { boot } = require("./log");
@@ -192,7 +194,7 @@ function validateUpgradeRequest(req) {
192
194
  function negotiateSubprotocol(req, supported) {
193
195
  if (!supported || supported.length === 0) return null;
194
196
  var raw = (req.headers || {})["sec-websocket-protocol"] || "";
195
- var offered = raw.split(",").map(function (s) { return s.trim(); }).filter(Boolean);
197
+ var offered = requestHelpers.parseListHeader(raw);
196
198
  for (var i = 0; i < offered.length; i++) {
197
199
  if (supported.indexOf(offered[i]) !== -1) return offered[i];
198
200
  }
@@ -417,8 +419,8 @@ class WebSocketConnection extends EventEmitter {
417
419
  this._lastPongAt = Date.now();
418
420
 
419
421
  var self = this;
420
- this._pingTimer = setInterval(function () { self._heartbeat(pongMs); }, pingMs);
421
- this._pingTimer.unref();
422
+ this._pingTimer = safeAsync.repeating(function () { self._heartbeat(pongMs); },
423
+ pingMs, { name: "websocket-ping" });
422
424
 
423
425
  socket.on("data", function (chunk) { self._onData(chunk); });
424
426
  socket.on("error", function (err) {
@@ -446,7 +448,7 @@ class WebSocketConnection extends EventEmitter {
446
448
  if (this._state === STATE_CLOSED) return;
447
449
  this._state = STATE_CLOSED;
448
450
  if (error) this.lastError = error;
449
- if (this._pingTimer) { clearInterval(this._pingTimer); this._pingTimer = null; }
451
+ if (this._pingTimer) { this._pingTimer.stop(); this._pingTimer = null; }
450
452
  if (this._closeTimer) { clearTimeout(this._closeTimer); this._closeTimer = null; }
451
453
  // Surface diagnosable errors via 'error' first — but only if the
452
454
  // operator is listening AND this is a real diagnosable case.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.5.16",
3
+ "version": "0.5.18",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
@@ -1,224 +0,0 @@
1
- "use strict";
2
- /**
3
- * Security-focused CSV parser + writer.
4
- *
5
- * RFC 4180 compliant parsing + the operator-friendly defaults that the
6
- * RFC doesn't address:
7
- *
8
- * - Size + row-count + field-length limits (DoS prevention)
9
- * - BOM stripping
10
- * - Configurable delimiter (',' default; '\t' for TSV; ';' for European)
11
- * - Configurable quote char
12
- * - CRLF / LF / CR line endings all accepted
13
- *
14
- * SECURITY: writer prevents CSV/Excel formula injection. Excel and other
15
- * spreadsheet apps execute cells starting with '=', '+', '-', '@', or
16
- * tab/CR characters. By default the writer prefixes such cells with a
17
- * single quote so the formula doesn't execute when the file is opened.
18
- * Toggle with { preventFormulaInjection: false } for true RFC 4180 output.
19
- *
20
- * Public API:
21
- * csv.parse(input, opts?) → array of arrays | array of objects
22
- * csv.stringify(rows, opts?) → string (RFC 4180 + injection-safe)
23
- * csv.SafeCsvError → error class
24
- *
25
- * Defaults (parse):
26
- * maxBytes: 16 MiB
27
- * maxRows: 1,000,000
28
- * maxFieldBytes: 1 MiB
29
- * delimiter: ','
30
- * quote: '"'
31
- * header: true (first row is column names; rows returned as objects)
32
- * trim: false (don't trim cell whitespace by default)
33
- */
34
-
35
- var C = require("../constants");
36
- var safeBuffer = require("../safe-buffer");
37
- var { FrameworkError } = require("../framework-error");
38
-
39
- class SafeCsvError extends FrameworkError {
40
- constructor(message, code, position) {
41
- super(message);
42
- this.name = "SafeCsvError";
43
- this.code = code || "csv/invalid";
44
- this.position = position || null;
45
- this.isSafeCsvError = true;
46
- }
47
- }
48
-
49
- var DEFAULTS_PARSE = {
50
- maxBytes: C.BYTES.mib(16),
51
- maxRows: 1000000,
52
- maxFieldBytes: C.BYTES.mib(1),
53
- delimiter: ",",
54
- quote: '"',
55
- header: true,
56
- trim: false,
57
- };
58
-
59
- var DEFAULTS_STRINGIFY = {
60
- delimiter: ",",
61
- quote: '"',
62
- preventFormulaInjection: true,
63
- formulaPrefixChars: ["=", "+", "-", "@", "\t", "\r"],
64
- always_quote: false, // quote every field; default only quotes when needed
65
- newline: "\r\n", // RFC 4180
66
- header: null, // explicit array; null → derive from first object's keys
67
- };
68
-
69
- // ---- parse ----
70
-
71
- function parse(input, opts) {
72
- opts = Object.assign({}, DEFAULTS_PARSE, opts || {});
73
-
74
- input = safeBuffer.normalizeText(input, {
75
- maxBytes: opts.maxBytes,
76
- errorClass: SafeCsvError,
77
- typeCode: "csv/wrong-input-type",
78
- sizeCode: "csv/too-large",
79
- });
80
-
81
- var len = input.length;
82
- var pos = 0;
83
- var rows = [];
84
- var row = [];
85
- var field = "";
86
- var inQuote = false;
87
-
88
- function pushField() {
89
- if (Buffer.byteLength(field, "utf8") > opts.maxFieldBytes) {
90
- throw new SafeCsvError("field exceeds maxFieldBytes at row " + (rows.length + 1), "csv/field-too-large");
91
- }
92
- row.push(opts.trim ? field.trim() : field);
93
- field = "";
94
- }
95
- function pushRow() {
96
- pushField();
97
- rows.push(row);
98
- if (rows.length > opts.maxRows) {
99
- throw new SafeCsvError("row count exceeds maxRows", "csv/too-many-rows");
100
- }
101
- row = [];
102
- }
103
-
104
- while (pos < len) {
105
- var ch = input.charAt(pos);
106
- if (inQuote) {
107
- if (ch === opts.quote) {
108
- // Possible escaped quote (double-quote inside quoted field)
109
- if (pos + 1 < len && input.charAt(pos + 1) === opts.quote) {
110
- field += opts.quote;
111
- pos += 2;
112
- continue;
113
- }
114
- // End of quoted field
115
- inQuote = false;
116
- pos += 1;
117
- continue;
118
- }
119
- field += ch;
120
- pos += 1;
121
- } else {
122
- if (ch === opts.delimiter) {
123
- pushField();
124
- pos += 1;
125
- } else if (ch === "\r") {
126
- // CR or CRLF — both end the row
127
- pushRow();
128
- pos += 1;
129
- if (pos < len && input.charAt(pos) === "\n") pos += 1;
130
- } else if (ch === "\n") {
131
- pushRow();
132
- pos += 1;
133
- } else if (ch === opts.quote && field === "") {
134
- inQuote = true;
135
- pos += 1;
136
- } else {
137
- field += ch;
138
- pos += 1;
139
- }
140
- }
141
- }
142
- if (inQuote) throw new SafeCsvError("unterminated quoted field", "csv/unterminated-quote");
143
- // Final row (no trailing newline)
144
- if (field.length > 0 || row.length > 0) {
145
- pushRow();
146
- }
147
-
148
- if (opts.header) {
149
- if (rows.length === 0) return [];
150
- var header = rows[0];
151
- return rows.slice(1).map(function (r) {
152
- var obj = {};
153
- for (var i = 0; i < header.length; i++) obj[header[i]] = r[i] !== undefined ? r[i] : null;
154
- return obj;
155
- });
156
- }
157
- return rows;
158
- }
159
-
160
- // ---- stringify ----
161
-
162
- function stringify(rows, opts) {
163
- opts = Object.assign({}, DEFAULTS_STRINGIFY, opts || {});
164
- if (!Array.isArray(rows)) {
165
- throw new SafeCsvError("stringify expects an array of rows", "csv/wrong-input-type");
166
- }
167
- if (rows.length === 0) return "";
168
-
169
- // Determine header + row shape
170
- var header;
171
- var isObjectRows = false;
172
- if (Array.isArray(rows[0])) {
173
- isObjectRows = false;
174
- header = opts.header || null;
175
- } else if (typeof rows[0] === "object" && rows[0] !== null) {
176
- isObjectRows = true;
177
- header = opts.header || Object.keys(rows[0]);
178
- } else {
179
- throw new SafeCsvError("rows must be arrays or objects", "csv/wrong-input-type");
180
- }
181
-
182
- function escapeCell(value) {
183
- var s = value == null ? "" : String(value);
184
- if (opts.preventFormulaInjection && s.length > 0) {
185
- var first = s.charAt(0);
186
- if (opts.formulaPrefixChars.indexOf(first) !== -1) {
187
- s = "'" + s; // Excel-safe prefix
188
- }
189
- }
190
- var needsQuote = opts.always_quote ||
191
- s.indexOf(opts.delimiter) !== -1 ||
192
- s.indexOf(opts.quote) !== -1 ||
193
- s.indexOf("\n") !== -1 ||
194
- s.indexOf("\r") !== -1;
195
- if (needsQuote) {
196
- s = opts.quote + s.split(opts.quote).join(opts.quote + opts.quote) + opts.quote;
197
- }
198
- return s;
199
- }
200
-
201
- var out = [];
202
- if (header) {
203
- out.push(header.map(escapeCell).join(opts.delimiter));
204
- }
205
- for (var i = 0; i < rows.length; i++) {
206
- var r = rows[i];
207
- var cells;
208
- if (isObjectRows) {
209
- cells = header.map(function (k) { return escapeCell(r[k]); });
210
- } else {
211
- cells = r.map(escapeCell);
212
- }
213
- out.push(cells.join(opts.delimiter));
214
- }
215
- return out.join(opts.newline);
216
- }
217
-
218
- module.exports = {
219
- parse: parse,
220
- stringify: stringify,
221
- SafeCsvError: SafeCsvError,
222
- DEFAULTS_PARSE: DEFAULTS_PARSE,
223
- DEFAULTS_STRINGIFY: DEFAULTS_STRINGIFY,
224
- };