@blamejs/core 0.12.58 → 0.12.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 CHANGED
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.12.x
10
10
 
11
+ - v0.12.60 (2026-05-25) — **`b.jsonMergePatch` — JSON Merge Patch (RFC 7396).** The simpler companion to JSON Patch: b.jsonMergePatch.merge applies an RFC 7396 merge patch (application/merge-patch+json), the partial-document PATCH body. A member present in the patch replaces or — for nested objects — merges into the target, a member whose value is null removes that key, and a patch that is itself an array, scalar, or null replaces the target wholesale. The inputs are never mutated (the merge runs on a deep copy) and member keys are written as literal own properties, so a "__proto__" member cannot reach any prototype. Verified against every RFC 7396 Appendix A test case. **Added:** *`b.jsonMergePatch.merge(target, patch)`* — Applies a JSON Merge Patch to a target document and returns the result without mutating either input. When the patch is an object it overlays the target (a null member deletes the key, a nested object merges recursively, any other value replaces); when the patch is an array, scalar, or null it replaces the whole target. Member keys are set via `Object.defineProperty`, so a `"__proto__"` member becomes a literal own key rather than altering a prototype. Pairs with `b.jsonPatch` — merge patch reads like the resource you want, JSON Patch expresses precise operations (array reordering, literal-null values).
12
+
11
13
  - v0.12.58 (2026-05-25) — **`b.jsonPointer` (RFC 6901) + `b.jsonPatch` (RFC 6902) — JSON Pointer + Patch.** Two related JSON primitives. b.jsonPointer.get references a value within a JSON document by RFC 6901 path (/foo/0/bar), handling the ~1 / ~0 escapes and array indices, and refusing pointers that do not resolve. b.jsonPatch.apply applies an RFC 6902 patch — add / remove / replace / move / copy / test — the standard HTTP PATCH payload (application/json-patch+json). It is atomic: operations run against a deep copy, so a failure at any step (an out-of-range index, a missing source, or a failed test) throws and leaves the input document untouched, and test compares values structurally. Verified against the official json-patch/json-patch-tests conformance suite (every enabled result and error case) plus the RFC 6901 §5 pointer examples. **Added:** *`b.jsonPointer.get(doc, pointer)` / `b.jsonPointer.parse(pointer)`* — Resolve an RFC 6901 JSON Pointer against a document — walking object keys and array indices, decoding `~1` → `/` and `~0` → `~`, and returning the whole document for the empty pointer. Throws `json-pointer/not-found` for a missing key, an out-of-range or non-numeric (leading-zero) array index, or descent into a primitive, and `json-pointer/bad-pointer` for a non-`/`-prefixed pointer. `parse` exposes the decoded reference tokens. · *`b.jsonPatch.apply(doc, operations)`* — Apply an RFC 6902 patch and return the result. Supports `add` (insert / append with `-` for arrays, set for objects, whole-document for `""`), `remove`, `replace` (overwrite an existing location), `move`, `copy`, and `test` (structural equality). Atomic — the patch runs on a deep copy, so the input `doc` is never mutated and any failure (unknown op, missing `path` / `value` / `from`, bad index, failed `test`, or moving a location into its own child) throws a typed error. Paths are RFC 6901 pointers resolved through `b.jsonPointer`; suitable for HTTP PATCH endpoints.
12
14
 
13
15
  - v0.12.57 (2026-05-25) — **`b.linkHeader` — RFC 8288 Web Linking (HTTP Link header) codec.** Parse and build the HTTP Link header (RFC 8288) — the standard way to convey resource relations, most visibly REST pagination (Link: <…?page=2>; rel="next"). b.linkHeader.parse returns one { uri, rel, params } per link, splitting multiple links on top-level commas and unwrapping quoted parameter values via the framework's RFC 8941 structured-field helpers, so a comma inside a quoted title never fake-splits the list; rel is exposed as its array of space-separated relation types. b.linkHeader.serialize is the inverse, angle-bracketing the URI, emitting rel first, and double-quoting parameter values. Verified against the RFC 8288 §3.5 examples and GitHub-style pagination links. **Added:** *`b.linkHeader.parse(headerValue)` / `b.linkHeader.serialize(links)`* — `parse` reads an HTTP Link header into `[{ uri, rel, params }]` — `uri` is the angle-bracketed target, `rel` the array of space-separated relation types, `params` the remaining parameters with quoted values unwrapped (a repeated parameter keeps the first occurrence per RFC 8288 §3.4). It refuses a link without a bracketed URI, an unterminated URI or quoted parameter, control bytes, and oversized headers. `serialize` builds the header value from `{ uri, rel, params? }` objects (or a single one), angle-bracketing the URI and double-quoting parameter values. Composes the framework's structured-field splitter so quoting is handled consistently; pair with `b.pagination` to emit standard `rel="next"` / `rel="prev"` navigation.
package/README.md CHANGED
@@ -99,7 +99,7 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
99
99
  - **Signed webhooks + API encryption** — SLH-DSA-SHAKE-256f default; ML-DSA-65 opt-in; ECIES API encryption (`b.webhook`, `b.crypto`)
100
100
  - **HPKE / HTTP signatures** — RFC 9180 HPKE with ML-KEM-1024 + HKDF-SHA3-512 + ChaCha20-Poly1305 (`b.crypto.hpke`); RFC 9421 HTTP Message Signatures with derived components and ed25519 / ML-DSA-65 (`b.crypto.httpSig`); RFC 9530 Content-Digest / Repr-Digest body-integrity fields (SHA-256 / SHA-512, legacy algorithms refused — `b.contentDigest`) to sign the digest rather than the whole body
101
101
  - **Link header** — RFC 8288 Web Linking codec (`b.linkHeader.parse` / `serialize`): parse and build `Link: <uri>; rel="next"` relations, the standard REST pagination mechanism; quote-aware (a comma inside a quoted parameter never splits the list)
102
- - **JSON Pointer / Patch** — RFC 6901 `b.jsonPointer.get` (reference a value by `/foo/0/bar`) + RFC 6902 `b.jsonPatch.apply` (atomic add / remove / replace / move / copy / test for HTTP PATCH; the input document is never mutated, structural `test` comparison)
102
+ - **JSON Pointer / Patch** — RFC 6901 `b.jsonPointer.get` (reference a value by `/foo/0/bar`) + RFC 6902 `b.jsonPatch.apply` (atomic add / remove / replace / move / copy / test for HTTP PATCH; the input document is never mutated, structural `test` comparison) + RFC 7396 `b.jsonMergePatch.merge` (the `merge-patch+json` partial-document format; both PATCH formats are prototype-pollution-safe)
103
103
  - **Canonical JSON** — RFC 8785 JSON Canonicalization Scheme (`b.canonicalJson.stringifyJcs`): the deterministic, sorted-key byte form to hash or sign (custom credentials, receipts, deterministic request signing); UTF-16 key ordering + ECMAScript number formatting, with a lenient `stringify` variant for Buffers / Dates / BigInts
104
104
  - **Structured Fields** — full RFC 9651 codec (`b.structuredFields.parse` / `serialize`): Items / Lists / Dictionaries, Inner Lists, Parameters, and every bare-item type (Integer / Decimal / String / Token / Byte Sequence / Boolean / Date / Display String) with strict grammar + range enforcement — the parser behind Content-Digest, Client Hints, and HTTP Message Signatures
105
105
  - **CMS codec** — RFC 5652 Cryptographic Message Syntax encoder + decoder with PQC signers (ML-DSA-65 / ML-DSA-87 / SLH-DSA-SHAKE-256f; RFC 9909 + 9881) and KEMRecipientInfo recipients (ML-KEM-1024; RFC 9629 + 9936); ChaCha20-Poly1305 content encryption (RFC 8103) so Efail-class malleability cannot apply (`b.cms`)
package/index.js CHANGED
@@ -400,6 +400,7 @@ var canonicalJson = require("./lib/canonical-json");
400
400
  var linkHeader = require("./lib/link-header");
401
401
  var jsonPointer = require("./lib/json-pointer");
402
402
  var jsonPatch = require("./lib/json-patch");
403
+ var jsonMergePatch = require("./lib/json-merge-patch");
403
404
  var standardWebhooks = require("./lib/standard-webhooks");
404
405
  var lro = require("./lib/lro");
405
406
  var jsonApi = require("./lib/jsonapi");
@@ -421,6 +422,7 @@ module.exports = {
421
422
  linkHeader: linkHeader,
422
423
  jsonPointer: jsonPointer,
423
424
  jsonPatch: jsonPatch,
425
+ jsonMergePatch: jsonMergePatch,
424
426
  standardWebhooks: standardWebhooks,
425
427
  lro: lro,
426
428
  jsonApi: jsonApi,
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ /**
3
+ * @module b.jsonMergePatch
4
+ * @nav Data
5
+ * @title JSON Merge Patch
6
+ *
7
+ * @intro
8
+ * Apply a JSON Merge Patch (RFC 7396) — the
9
+ * <code>application/merge-patch+json</code> body of an HTTP
10
+ * <code>PATCH</code>. A merge patch is a partial document overlaid on
11
+ * the target: a member present in the patch replaces (or, for nested
12
+ * objects, is merged into) the target, a member whose value is
13
+ * <code>null</code> removes that key, and a patch that is anything
14
+ * other than an object (an array, scalar, or <code>null</code>)
15
+ * replaces the target wholesale.
16
+ *
17
+ * It is the simpler companion to the operation-based JSON Patch
18
+ * (<code>b.jsonPatch</code>): merge patch can't reorder arrays or
19
+ * express a value that is genuinely <code>null</code>, but it reads
20
+ * like the resource you want. <code>merge</code> returns a new
21
+ * document and never mutates its inputs, and writes member keys as
22
+ * literal own properties (a <code>"__proto__"</code> key cannot reach
23
+ * the prototype).
24
+ *
25
+ * @card
26
+ * JSON Merge Patch (RFC 7396) — overlay a partial document for HTTP
27
+ * PATCH: present members replace / merge, <code>null</code> deletes, a
28
+ * non-object patch replaces wholesale. The simple companion to JSON
29
+ * Patch; immutable and prototype-pollution-safe.
30
+ */
31
+
32
+ var { defineClass } = require("./framework-error");
33
+
34
+ var JsonMergePatchError = defineClass("JsonMergePatchError", { alwaysPermanent: true });
35
+
36
+ function _ownGet(obj, key) {
37
+ var d = Object.getOwnPropertyDescriptor(obj, key); // own value, bypassing the __proto__ getter
38
+ return d ? d.value : undefined;
39
+ }
40
+ function _ownSet(obj, key, value) {
41
+ Object.defineProperty(obj, key, { value: value, writable: true, enumerable: true, configurable: true });
42
+ }
43
+ function _isPlainObject(v) { return v !== null && typeof v === "object" && !Array.isArray(v); }
44
+
45
+ function _merge(target, patch) {
46
+ // A non-object patch (array, scalar, or null) replaces the target.
47
+ if (!_isPlainObject(patch)) return patch;
48
+ // Object patch merges into an object target; if the target is not an
49
+ // object, RFC 7396 §2 starts from an empty object.
50
+ var out = _isPlainObject(target) ? target : {};
51
+ var keys = Object.keys(patch);
52
+ for (var i = 0; i < keys.length; i += 1) {
53
+ var name = keys[i];
54
+ var val = _ownGet(patch, name);
55
+ if (val === null) {
56
+ if (Object.prototype.hasOwnProperty.call(out, name)) delete out[name];
57
+ } else {
58
+ _ownSet(out, name, _merge(Object.prototype.hasOwnProperty.call(out, name) ? _ownGet(out, name) : undefined, val));
59
+ }
60
+ }
61
+ return out;
62
+ }
63
+
64
+ /**
65
+ * @primitive b.jsonMergePatch.merge
66
+ * @signature b.jsonMergePatch.merge(target, patch)
67
+ * @since 0.12.59
68
+ * @status stable
69
+ * @compliance soc2
70
+ * @related b.jsonPatch.apply
71
+ *
72
+ * Apply a JSON Merge Patch (RFC 7396) to a target document and return the
73
+ * result. When the patch is an object, each member overlays the target —
74
+ * a <code>null</code> value deletes the key, a nested object merges
75
+ * recursively, and any other value replaces; when the patch is itself an
76
+ * array, scalar, or <code>null</code>, it replaces the whole target. The
77
+ * inputs are never mutated (the work happens on a deep copy) and member
78
+ * keys are written as literal own properties, so a <code>"__proto__"</code>
79
+ * member cannot alter any prototype.
80
+ *
81
+ * @example
82
+ * b.jsonMergePatch.merge({ a: "b", c: { d: "e" } }, { a: "z", c: { d: null, f: 1 } });
83
+ * // → { a: "z", c: { f: 1 } }
84
+ */
85
+ function merge(target, patch) {
86
+ if (typeof patch === "undefined") throw new JsonMergePatchError("json-merge-patch/bad-patch", "jsonMergePatch.merge: patch is required (use null to replace the target with null)");
87
+ return _merge(structuredClone(target), structuredClone(patch));
88
+ }
89
+
90
+ module.exports = {
91
+ merge: merge,
92
+ JsonMergePatchError: JsonMergePatchError,
93
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.12.58",
3
+ "version": "0.12.60",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
package/sbom.cdx.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:f2b36704-2d8b-4410-9d50-785fba41343d",
5
+ "serialNumber": "urn:uuid:1329fa76-6890-4008-be5d-6db27e71506c",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-05-26T00:20:31.737Z",
8
+ "timestamp": "2026-05-26T01:30:30.570Z",
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.12.58",
22
+ "bom-ref": "@blamejs/core@0.12.60",
23
23
  "type": "application",
24
24
  "name": "blamejs",
25
- "version": "0.12.58",
25
+ "version": "0.12.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.12.58",
29
+ "purl": "pkg:npm/%40blamejs/core@0.12.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.12.58",
57
+ "ref": "@blamejs/core@0.12.60",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]