@blamejs/core 0.6.59 → 0.6.60
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 +2 -0
- package/lib/pagination.js +27 -3
- package/package.json +1 -1
- package/sbom.cyclonedx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.6.x
|
|
10
10
|
|
|
11
|
+
- **0.6.60** (2026-05-03) — `b.pagination.encodeCursor` correctness fixes for non-plain types in cursor state. The `_canonicalize` walker did `if (typeof === "object") Object.keys(...)`, which silently lost data on three classes of input: **Date** silently encoded as `{}` (Date instances have no own enumerable keys); **Buffer / Uint8Array** silently encoded as `{"0":97,"1":98,…}` (indexed-key serialisation); **circular references** stack-overflowed instead of throwing a clean error. After encode → decode the operator's data was either gone or unrecognisable. The fix: Date now serialises to its ISO string (matches stdlib `JSON.stringify` semantics — round-trips through encode/decode as the ISO string operators expect); Buffer / Uint8Array / Map / Set / RegExp throw `pagination/bad-state` with a message naming the offending constructor and pointing operators at the right conversion (`"convert to a plain primitive (string / number / iso-string) first"`); circular references throw `pagination/bad-state` cleanly via WeakSet cycle detection. **Tests** — 7 new layer-0 assertions: Date round-trip preserved, all 5 non-plain types reject cleanly, circular reference rejects without stack overflow. Smoke 7204→7211 / wiki e2e 178 / Linux 85.3s / eslint clean / shellcheck clean.
|
|
12
|
+
|
|
11
13
|
- **0.6.59** (2026-05-03) — HTTP/2 session teardown deduplication + http-client sweep. v0.6.58's inline `session.close() + session.destroy()` block in `lib/log-stream-otlp-grpc.js` was the right fix for the OTLP-gRPC sink hang, but the same bug class lives in `lib/http-client.js` — the h2 transport pool had 5 call sites running the bare `session.close()` (`_resetTransports`, the ALPN-fallback path, the h2 connect-error path, the h2c connect-error path, the idle-timeout handler, and `_resetForTest`) all of which leak the underlying TCP socket on idle / error / fallback paths in exactly the same way. New `lib/http2-teardown.js` exports `tearDownH2Session(session)` which performs the close()-then-destroy() routine; `lib/http-client.js` and `lib/log-stream-otlp-grpc.js` both import it. The OTLP-gRPC v0.6.58 inline block is removed in favour of the shared helper. **Tests** — verified in a node:24-alpine docker container: smoke 7204 checks in 85.3 seconds. Wiki e2e 178 / eslint clean / shellcheck clean.
|
|
12
14
|
|
|
13
15
|
- **0.6.58** (2026-05-03) — `b.logStream` OTLP-gRPC sink hang fix that has been silently breaking the npm-publish gate since v0.6.38. The sink's `close()` was calling `session.close()` (HTTP/2 *graceful* close — waits for in-flight streams before freeing the socket) but never `session.destroy()`. The graceful close completed but the underlying TCP socket stayed connected, blocking the test fixture's `server.close()` indefinitely on Linux CI runners. Same bug also added ~120 seconds of lingering latency to every local smoke run on Windows (smoke 196s → 76s after fix). The fix calls `session.close()` then `session.destroy()`; by the time we reach close(), all buffered records have been flushed via the awaited `inflightPromise` + final `_doExport`, so destroy() is structurally safe. **Operator impact** — the npm registry has been stuck at v0.6.37 since 2026-05-02; every tag from v0.6.38 → v0.6.57 timed out at the publish workflow's smoke step. With this fix the publish should reach `npm publish`. No layer-0 or integration test changes — the existing `log-stream-otlp-grpc.test.js` round-trip was passing locally because tests use small batches and the lingering socket happens to terminate before the test driver's overall timeout, but it was leaving the process unable to exit cleanly until the OS-level TCP timeout fired. Smoke 7204 / wiki e2e 178 / integration 16 files / eslint clean / shellcheck clean.
|
package/lib/pagination.js
CHANGED
|
@@ -108,13 +108,37 @@ var DEFAULT_MAX_LIMIT = 100;
|
|
|
108
108
|
|
|
109
109
|
// Canonical JSON — sorted keys at every depth. Mirrors safe-schema /
|
|
110
110
|
// audit-tools so verifier and producer hash exactly the same bytes.
|
|
111
|
-
|
|
111
|
+
//
|
|
112
|
+
// Cursor state must contain only plain data (string, number, boolean,
|
|
113
|
+
// null, plain object, array, Date). Buffer / typed-arrays / Map / Set /
|
|
114
|
+
// RegExp are rejected because the prior `Object.keys` walk silently
|
|
115
|
+
// serialised them as `{}` (Date) or `{"0":97,"1":98,…}` (Buffer) — the
|
|
116
|
+
// operator's data round-tripped through encode/decode as garbage. Date
|
|
117
|
+
// gets an explicit ISO-string conversion (matches stdlib JSON.stringify).
|
|
118
|
+
// Circular references throw cleanly instead of stack-overflowing.
|
|
119
|
+
function _canonicalize(value, seen) {
|
|
112
120
|
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
113
|
-
if (
|
|
121
|
+
if (value instanceof Date) return JSON.stringify(value.toISOString());
|
|
122
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array ||
|
|
123
|
+
value instanceof Map || value instanceof Set ||
|
|
124
|
+
value instanceof RegExp) {
|
|
125
|
+
throw new PaginationError("pagination/bad-state",
|
|
126
|
+
"cursor state cannot contain " + value.constructor.name +
|
|
127
|
+
"; convert to a plain primitive (string / number / iso-string) first");
|
|
128
|
+
}
|
|
129
|
+
seen = seen || new WeakSet();
|
|
130
|
+
if (seen.has(value)) {
|
|
131
|
+
throw new PaginationError("pagination/bad-state",
|
|
132
|
+
"cursor state contains a circular reference");
|
|
133
|
+
}
|
|
134
|
+
seen.add(value);
|
|
135
|
+
if (Array.isArray(value)) {
|
|
136
|
+
return "[" + value.map(function (v) { return _canonicalize(v, seen); }).join(",") + "]";
|
|
137
|
+
}
|
|
114
138
|
var keys = Object.keys(value).sort();
|
|
115
139
|
var parts = [];
|
|
116
140
|
for (var i = 0; i < keys.length; i++) {
|
|
117
|
-
parts.push(JSON.stringify(keys[i]) + ":" + _canonicalize(value[keys[i]]));
|
|
141
|
+
parts.push(JSON.stringify(keys[i]) + ":" + _canonicalize(value[keys[i]], seen));
|
|
118
142
|
}
|
|
119
143
|
return "{" + parts.join(",") + "}";
|
|
120
144
|
}
|
package/package.json
CHANGED
package/sbom.cyclonedx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:093bc38b-dc67-48ed-87ba-000e374831f6",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-05-03T13:
|
|
8
|
+
"timestamp": "2026-05-03T13:37:48.709Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/core@0.6.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.6.60",
|
|
23
23
|
"type": "library",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.6.
|
|
25
|
+
"version": "0.6.60",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "The Node framework that owns its stack.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/core@0.6.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.6.60",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/core@0.6.
|
|
57
|
+
"ref": "@blamejs/core@0.6.60",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|