hbsig 0.2.8 → 0.3.0
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/cjs/commit.js +263 -43
- package/cjs/encode-utils.js +10 -4
- package/cjs/encode.js +66 -19
- package/cjs/erl_json.js +12 -3
- package/cjs/erl_str.js +5 -0
- package/cjs/flat.js +70 -13
- package/cjs/httpsig.js +159 -173
- package/cjs/parser.js +30 -6
- package/cjs/send.js +8 -2
- package/cjs/signer-utils.js +5 -1
- package/cjs/signer.js +140 -281
- package/cjs/structured.js +140 -146
- package/cjs/test.js +0 -15
- package/esm/commit.js +174 -19
- package/esm/encode-utils.js +10 -4
- package/esm/encode.js +52 -15
- package/esm/erl_json.js +4 -1
- package/esm/erl_str.js +5 -0
- package/esm/flat.js +61 -7
- package/esm/httpsig.js +118 -113
- package/esm/parser.js +26 -8
- package/esm/send.js +8 -3
- package/esm/signer-utils.js +5 -1
- package/esm/signer.js +66 -174
- package/esm/structured.js +97 -98
- package/esm/test.js +2 -6
- package/package.json +2 -3
package/cjs/commit.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
3
|
Object.defineProperty(exports, "__esModule", {
|
|
5
4
|
value: true
|
|
6
5
|
});
|
|
@@ -8,6 +7,8 @@ exports.commit = void 0;
|
|
|
8
7
|
var _id = require("./id.js");
|
|
9
8
|
var _utils = require("./utils.js");
|
|
10
9
|
var _signerUtils = require("./signer-utils.js");
|
|
10
|
+
var _crypto = _interopRequireDefault(require("crypto"));
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
11
12
|
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
12
13
|
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
13
14
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
@@ -16,16 +17,58 @@ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object
|
|
|
16
17
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
17
18
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
18
19
|
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
|
|
19
|
-
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
20
|
-
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
21
20
|
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
22
21
|
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
22
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
23
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
24
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
25
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
26
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
27
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
28
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
29
|
+
// Helper to compute SHA-256 content-digest in RFC 9530 format
|
|
30
|
+
var computeContentDigest = function computeContentDigest(body) {
|
|
31
|
+
var bodyBuffer;
|
|
32
|
+
if (Buffer.isBuffer(body)) {
|
|
33
|
+
bodyBuffer = body;
|
|
34
|
+
} else if (body instanceof Blob) {
|
|
35
|
+
return null; // Can't compute synchronously for Blob
|
|
36
|
+
} else if (typeof body === "string") {
|
|
37
|
+
bodyBuffer = Buffer.from(body, "binary");
|
|
38
|
+
} else {
|
|
39
|
+
bodyBuffer = Buffer.from(String(body), "binary");
|
|
40
|
+
}
|
|
41
|
+
var hash = _crypto["default"].createHash("sha256").update(bodyBuffer).digest("base64");
|
|
42
|
+
return "sha-256=:".concat(hash, ":");
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Helper to build ao-types string from an object
|
|
46
|
+
var buildAoTypes = function buildAoTypes(obj) {
|
|
47
|
+
var types = [];
|
|
48
|
+
for (var _i = 0, _Object$entries = Object.entries(obj); _i < _Object$entries.length; _i++) {
|
|
49
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
50
|
+
key = _Object$entries$_i[0],
|
|
51
|
+
value = _Object$entries$_i[1];
|
|
52
|
+
if (typeof value === "number") {
|
|
53
|
+
types.push("".concat(key, "=\"").concat(Number.isInteger(value) ? "integer" : "float", "\""));
|
|
54
|
+
} else if (typeof value === "boolean") {
|
|
55
|
+
types.push("".concat(key, "=\"atom\""));
|
|
56
|
+
} else if (value === null) {
|
|
57
|
+
types.push("".concat(key, "=\"atom\""));
|
|
58
|
+
} else if (_typeof(value) === "symbol") {
|
|
59
|
+
// Symbols are Erlang atoms
|
|
60
|
+
types.push("".concat(key, "=\"atom\""));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return types.length > 0 ? types.join(", ") : null;
|
|
64
|
+
};
|
|
65
|
+
|
|
23
66
|
// todo: handle @
|
|
24
67
|
var commit = exports.commit = /*#__PURE__*/function () {
|
|
25
68
|
var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(obj, opts) {
|
|
26
|
-
var msg, _yield$verify, components, body, inlineBodyKey, _iterator, _step,
|
|
69
|
+
var msg, _yield$verify, components, body, inlineBodyKey, NATIVE_BODY_KEYS, isNativeBodyKey, headerLookup, _i2, _Object$entries2, _Object$entries2$_i, k, v, objLookup, _i3, _Object$entries3, _Object$entries3$_i, _k, _v, _iterator, _step, _v2, _key, originalValue, headerValue, bodyContent, arrayBuffer, committedSetLower, bodyKeysLower, _i4, _Object$entries4, _Object$entries4$_i, key, value, aoTypes, rsaId, pub, pubKeyBase64, committer, extractKeyidFromSigInput, keyid, committedFields, bodyFieldName, extractSignature, rawSignature, meta, sigs, committed, _t;
|
|
27
70
|
return _regenerator().w(function (_context) {
|
|
28
|
-
while (1) switch (_context.n) {
|
|
71
|
+
while (1) switch (_context.p = _context.n) {
|
|
29
72
|
case 0:
|
|
30
73
|
_context.n = 1;
|
|
31
74
|
return opts.signer(obj, opts);
|
|
@@ -36,73 +79,250 @@ var commit = exports.commit = /*#__PURE__*/function () {
|
|
|
36
79
|
case 2:
|
|
37
80
|
_yield$verify = _context.v;
|
|
38
81
|
components = _yield$verify.decodedSignatureInput.components;
|
|
39
|
-
body = {}; // Check for inline-body-key
|
|
40
|
-
inlineBodyKey = msg.headers["inline-body-key"]; //
|
|
82
|
+
body = {}; // Check for inline-body-key (indicates a field was moved to HTTP body during encoding)
|
|
83
|
+
inlineBodyKey = msg.headers["inline-body-key"] || msg.headers["ao-body-key"]; // Body field names that HyperBEAM's inline_key() recognizes natively.
|
|
84
|
+
// For these, normalize_for_encoding() re-derives ao-body-key automatically.
|
|
85
|
+
// For custom names (e.g., "json"), we must keep ao-body-key in committed list
|
|
86
|
+
// so HyperBEAM knows which field to inline during verification.
|
|
87
|
+
NATIVE_BODY_KEYS = new Set(["body", "data"]);
|
|
88
|
+
isNativeBodyKey = !inlineBodyKey || NATIVE_BODY_KEYS.has(inlineBodyKey); // Build body from committed components.
|
|
89
|
+
// IMPORTANT: Use original typed values from obj (preserves integers, booleans, etc.)
|
|
90
|
+
// instead of string values from headers. This is critical because:
|
|
91
|
+
// 1. HTTP headers are always strings (e.g., quantity: 100 → "100")
|
|
92
|
+
// 2. We include ao-types to tell HyperBEAM the original types
|
|
93
|
+
// 3. HyperBEAM applies ao-types BEFORE signature verification
|
|
94
|
+
// 4. If we send strings, HyperBEAM converts to integers, breaking signature
|
|
95
|
+
// 5. If we send original integers, HyperBEAM re-encodes them as strings for verification
|
|
96
|
+
//
|
|
97
|
+
// Always skip content-digest and inline-body-key (transport artifacts re-derived by HyperBEAM).
|
|
98
|
+
// Skip ao-body-key only for native body keys (HyperBEAM re-derives it).
|
|
99
|
+
// Keep ao-body-key for custom body keys (HyperBEAM needs it to find the body field).
|
|
100
|
+
// Create case-insensitive lookup maps
|
|
101
|
+
headerLookup = new Map();
|
|
102
|
+
for (_i2 = 0, _Object$entries2 = Object.entries(msg.headers); _i2 < _Object$entries2.length; _i2++) {
|
|
103
|
+
_Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2), k = _Object$entries2$_i[0], v = _Object$entries2$_i[1];
|
|
104
|
+
headerLookup.set(k.toLowerCase(), v);
|
|
105
|
+
}
|
|
106
|
+
// Case-insensitive lookup for original object values (preserves types)
|
|
107
|
+
objLookup = new Map();
|
|
108
|
+
for (_i3 = 0, _Object$entries3 = Object.entries(obj); _i3 < _Object$entries3.length; _i3++) {
|
|
109
|
+
_Object$entries3$_i = _slicedToArray(_Object$entries3[_i3], 2), _k = _Object$entries3$_i[0], _v = _Object$entries3$_i[1];
|
|
110
|
+
objLookup.set(_k.toLowerCase(), _v);
|
|
111
|
+
}
|
|
41
112
|
_iterator = _createForOfIteratorHelper(components);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
113
|
+
_context.p = 3;
|
|
114
|
+
_iterator.s();
|
|
115
|
+
case 4:
|
|
116
|
+
if ((_step = _iterator.n()).done) {
|
|
117
|
+
_context.n = 10;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
_v2 = _step.value;
|
|
121
|
+
_key = _v2 === "@path" ? "path" : _v2;
|
|
122
|
+
if (!(_key === "content-length")) {
|
|
123
|
+
_context.n = 5;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
return _context.a(3, 9);
|
|
127
|
+
case 5:
|
|
128
|
+
if (!(_key === "content-digest")) {
|
|
129
|
+
_context.n = 6;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
return _context.a(3, 9);
|
|
133
|
+
case 6:
|
|
134
|
+
if (!(_key === "inline-body-key")) {
|
|
135
|
+
_context.n = 7;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
return _context.a(3, 9);
|
|
139
|
+
case 7:
|
|
140
|
+
if (!(isNativeBodyKey && _key === "ao-body-key")) {
|
|
141
|
+
_context.n = 8;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
return _context.a(3, 9);
|
|
145
|
+
case 8:
|
|
146
|
+
// Prefer original value from input object (preserves integer/boolean/etc. types)
|
|
147
|
+
// Fall back to header string value if not found in original object
|
|
148
|
+
originalValue = objLookup.get(_key.toLowerCase());
|
|
149
|
+
if (originalValue !== undefined) {
|
|
150
|
+
body[_key] = originalValue;
|
|
151
|
+
} else {
|
|
152
|
+
headerValue = headerLookup.get(_key);
|
|
153
|
+
if (headerValue !== undefined) {
|
|
154
|
+
body[_key] = headerValue;
|
|
47
155
|
}
|
|
48
|
-
|
|
49
|
-
// Handle body resolution
|
|
50
|
-
} catch (err) {
|
|
51
|
-
_iterator.e(err);
|
|
52
|
-
} finally {
|
|
53
|
-
_iterator.f();
|
|
54
156
|
}
|
|
157
|
+
case 9:
|
|
158
|
+
_context.n = 4;
|
|
159
|
+
break;
|
|
160
|
+
case 10:
|
|
161
|
+
_context.n = 12;
|
|
162
|
+
break;
|
|
163
|
+
case 11:
|
|
164
|
+
_context.p = 11;
|
|
165
|
+
_t = _context.v;
|
|
166
|
+
_iterator.e(_t);
|
|
167
|
+
case 12:
|
|
168
|
+
_context.p = 12;
|
|
169
|
+
_iterator.f();
|
|
170
|
+
return _context.f(12);
|
|
171
|
+
case 13:
|
|
172
|
+
// Handle body resolution - restore the inlined field to its AO-Core name
|
|
173
|
+
bodyContent = null;
|
|
55
174
|
if (!msg.body) {
|
|
56
|
-
_context.n =
|
|
175
|
+
_context.n = 17;
|
|
57
176
|
break;
|
|
58
177
|
}
|
|
59
178
|
if (!(msg.body instanceof Blob)) {
|
|
60
|
-
_context.n =
|
|
179
|
+
_context.n = 15;
|
|
61
180
|
break;
|
|
62
181
|
}
|
|
63
|
-
_context.n =
|
|
182
|
+
_context.n = 14;
|
|
64
183
|
return msg.body.arrayBuffer();
|
|
65
|
-
case
|
|
184
|
+
case 14:
|
|
66
185
|
arrayBuffer = _context.v;
|
|
67
186
|
bodyContent = Buffer.from(arrayBuffer);
|
|
68
|
-
_context.n =
|
|
187
|
+
_context.n = 16;
|
|
69
188
|
break;
|
|
70
|
-
case
|
|
189
|
+
case 15:
|
|
71
190
|
bodyContent = msg.body;
|
|
72
|
-
case
|
|
73
|
-
//
|
|
74
|
-
if (inlineBodyKey
|
|
75
|
-
body
|
|
191
|
+
case 16:
|
|
192
|
+
// Put body content under the original field name (e.g., "data", "json")
|
|
193
|
+
if (inlineBodyKey) {
|
|
194
|
+
body[inlineBodyKey] = bodyContent;
|
|
76
195
|
} else {
|
|
77
196
|
body.body = bodyContent;
|
|
78
197
|
}
|
|
79
|
-
case
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
198
|
+
case 17:
|
|
199
|
+
// Include non-committed fields from the original object as JSON values.
|
|
200
|
+
// This covers: (1) body-key fields (arrays/objects encoded as multipart,
|
|
201
|
+
// which can't be included as raw multipart in JSON), and (2) any other
|
|
202
|
+
// fields excluded from signing. These fields are unsigned but present
|
|
203
|
+
// so HyperBEAM can parse them directly as JSON types.
|
|
204
|
+
// Use case-insensitive matching: signing normalizes keys to lowercase,
|
|
205
|
+
// but the original object may use mixed case (e.g., "To" vs "to").
|
|
206
|
+
committedSetLower = new Set(components.map(function (v) {
|
|
207
|
+
return (v === "@path" ? "path" : v).toLowerCase();
|
|
208
|
+
})); // Also track lowercase keys already in body to prevent duplicates
|
|
209
|
+
bodyKeysLower = new Set(Object.keys(body).map(function (k) {
|
|
210
|
+
return k.toLowerCase();
|
|
211
|
+
}));
|
|
212
|
+
_i4 = 0, _Object$entries4 = Object.entries(obj);
|
|
213
|
+
case 18:
|
|
214
|
+
if (!(_i4 < _Object$entries4.length)) {
|
|
215
|
+
_context.n = 24;
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
_Object$entries4$_i = _slicedToArray(_Object$entries4[_i4], 2), key = _Object$entries4$_i[0], value = _Object$entries4$_i[1];
|
|
219
|
+
if (!(key === "method")) {
|
|
220
|
+
_context.n = 19;
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
return _context.a(3, 23);
|
|
224
|
+
case 19:
|
|
225
|
+
if (!committedSetLower.has(key.toLowerCase())) {
|
|
226
|
+
_context.n = 20;
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
return _context.a(3, 23);
|
|
230
|
+
case 20:
|
|
231
|
+
if (!bodyKeysLower.has(key.toLowerCase())) {
|
|
232
|
+
_context.n = 21;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
return _context.a(3, 23);
|
|
236
|
+
case 21:
|
|
237
|
+
if (!(value === undefined)) {
|
|
238
|
+
_context.n = 22;
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
return _context.a(3, 23);
|
|
242
|
+
case 22:
|
|
243
|
+
body[key] = value;
|
|
244
|
+
bodyKeysLower.add(key.toLowerCase());
|
|
245
|
+
case 23:
|
|
246
|
+
_i4++;
|
|
247
|
+
_context.n = 18;
|
|
248
|
+
break;
|
|
249
|
+
case 24:
|
|
250
|
+
// Include ao-types so HyperBEAM knows how to convert string values to proper types.
|
|
251
|
+
// First check if it was set by the signer, otherwise compute it from the original object
|
|
252
|
+
aoTypes = msg.headers["ao-types"];
|
|
253
|
+
if (!aoTypes) {
|
|
254
|
+
// Compute ao-types from the original typed values in obj
|
|
255
|
+
aoTypes = buildAoTypes(obj);
|
|
256
|
+
}
|
|
257
|
+
if (aoTypes) {
|
|
258
|
+
body["ao-types"] = aoTypes;
|
|
259
|
+
}
|
|
83
260
|
rsaId = (0, _id.rsaid)(msg.headers);
|
|
84
261
|
pub = (0, _signerUtils.extractPubKey)(msg.headers);
|
|
85
|
-
|
|
262
|
+
pubKeyBase64 = pub.toString("base64");
|
|
263
|
+
committer = (0, _utils.toAddr)(pubKeyBase64); // Extract keyid from signature-input to ensure it matches what was signed
|
|
264
|
+
// Format: sig-xxx=("field1"...);alg="...";keyid="..."
|
|
265
|
+
// The keyid may already have a scheme prefix (e.g., "publickey:base64data")
|
|
266
|
+
extractKeyidFromSigInput = function extractKeyidFromSigInput(sigInput) {
|
|
267
|
+
if (!sigInput) return null;
|
|
268
|
+
var match = sigInput.match(/keyid="([^"]+)"/);
|
|
269
|
+
if (!match) return null;
|
|
270
|
+
// Return as-is - the keyid already includes the prefix from signing
|
|
271
|
+
return match[1];
|
|
272
|
+
};
|
|
273
|
+
keyid = extractKeyidFromSigInput(msg.headers["signature-input"]) || "publickey:".concat(pubKeyBase64); // Build the list of committed fields.
|
|
274
|
+
// Always transform HTTPSig transport keys to AO-Core keys:
|
|
275
|
+
// - Replace content-digest with the body field name (HyperBEAM re-derives content-digest)
|
|
276
|
+
// - For native body keys ("body", "data"): also remove ao-body-key (HyperBEAM re-derives it)
|
|
277
|
+
// - For custom body keys (e.g., "json"): keep ao-body-key (HyperBEAM needs it to find the field)
|
|
278
|
+
committedFields = components.map(function (v) {
|
|
279
|
+
return v === "@path" ? "path" : v;
|
|
280
|
+
});
|
|
281
|
+
if (committedFields.includes("content-digest") && (inlineBodyKey || msg.body)) {
|
|
282
|
+
bodyFieldName = inlineBodyKey || "body";
|
|
283
|
+
committedFields = committedFields.filter(function (k) {
|
|
284
|
+
return k !== "content-digest";
|
|
285
|
+
});
|
|
286
|
+
if (!committedFields.includes(bodyFieldName)) {
|
|
287
|
+
committedFields.push(bodyFieldName);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (isNativeBodyKey) {
|
|
291
|
+
// For native body keys, ao-body-key is a transport artifact - remove it
|
|
292
|
+
committedFields = committedFields.filter(function (k) {
|
|
293
|
+
return k !== "ao-body-key";
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Extract just the base64 signature data from the header format "sig-xxx=:base64data:"
|
|
298
|
+
// HyperBEAM expects raw base64 without colons (uses b64fast:encode/decode)
|
|
299
|
+
extractSignature = function extractSignature(sigHeader) {
|
|
300
|
+
if (!sigHeader) return sigHeader;
|
|
301
|
+
// Match the base64 data between colons after the label
|
|
302
|
+
var match = sigHeader.match(/=:([^:]+):/);
|
|
303
|
+
return match ? match[1] : sigHeader;
|
|
304
|
+
};
|
|
305
|
+
rawSignature = extractSignature(msg.headers.signature);
|
|
86
306
|
meta = {
|
|
307
|
+
type: "rsa-pss-sha512",
|
|
87
308
|
alg: "rsa-pss-sha512",
|
|
88
|
-
"commitment-device": "httpsig@1.0"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
alg: "hmac-sha256",
|
|
92
|
-
"commitment-device": "httpsig@1.0"
|
|
309
|
+
"commitment-device": "httpsig@1.0",
|
|
310
|
+
keyid: keyid,
|
|
311
|
+
committed: committedFields
|
|
93
312
|
};
|
|
94
313
|
sigs = {
|
|
95
|
-
signature:
|
|
314
|
+
signature: rawSignature,
|
|
96
315
|
"signature-input": msg.headers["signature-input"]
|
|
97
|
-
};
|
|
316
|
+
}; // Only include the RSA commitment - HMAC commitments are created by HyperBEAM
|
|
317
|
+
// when needed and require the server's HMAC key which we don't have
|
|
98
318
|
committed = _objectSpread({
|
|
99
|
-
commitments: _defineProperty(
|
|
319
|
+
commitments: _defineProperty({}, rsaId, _objectSpread(_objectSpread({}, meta), {}, {
|
|
100
320
|
committer: committer
|
|
101
|
-
}, sigs))
|
|
321
|
+
}, sigs))
|
|
102
322
|
}, body);
|
|
103
323
|
return _context.a(2, committed);
|
|
104
324
|
}
|
|
105
|
-
}, _callee);
|
|
325
|
+
}, _callee, null, [[3, 11, 12, 13]]);
|
|
106
326
|
}));
|
|
107
327
|
return function commit(_x, _x2) {
|
|
108
328
|
return _ref.apply(this, arguments);
|
package/cjs/encode-utils.js
CHANGED
|
@@ -154,21 +154,27 @@ function getValueByPath(obj, path) {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
// Get the ao-type for a value
|
|
157
|
+
// Note: dev_codec_structured.erl only supports: integer, float, atom, list, map
|
|
158
|
+
// Do NOT use empty-binary, empty-list, empty-message as they cause binary_to_existing_atom errors
|
|
157
159
|
function getAoType(value) {
|
|
158
160
|
if (typeof value === "boolean" || value === null || value === undefined || _typeof(value) === "symbol") {
|
|
159
161
|
return "atom";
|
|
160
162
|
} else if (typeof value === "number") {
|
|
161
163
|
return Number.isInteger(value) ? "integer" : "float";
|
|
162
164
|
} else if (typeof value === "string" && value.length === 0) {
|
|
163
|
-
|
|
165
|
+
// Empty strings become empty binaries naturally - no type annotation needed
|
|
166
|
+
return null;
|
|
164
167
|
} else if (isBytes(value) && (value.length === 0 || value.byteLength === 0)) {
|
|
165
|
-
|
|
168
|
+
// Empty buffers become empty binaries naturally - no type annotation needed
|
|
169
|
+
return null;
|
|
166
170
|
} else if (Array.isArray(value) && value.length === 0) {
|
|
167
|
-
|
|
171
|
+
// Use "list" for empty arrays (structured codec compatible)
|
|
172
|
+
return "list";
|
|
168
173
|
} else if (Array.isArray(value)) {
|
|
169
174
|
return "list";
|
|
170
175
|
} else if (isPojo(value) && Object.keys(value).length === 0) {
|
|
171
|
-
|
|
176
|
+
// Use "map" for empty objects (structured codec compatible)
|
|
177
|
+
return "map";
|
|
172
178
|
}
|
|
173
179
|
return null;
|
|
174
180
|
}
|
package/cjs/encode.js
CHANGED
|
@@ -48,7 +48,19 @@ function handleSingleEmptyBinaryField(obj) {
|
|
|
48
48
|
var fieldValue = obj[fieldName];
|
|
49
49
|
if ((0, _encodeUtils.isBytes)(fieldValue) && (fieldValue.length === 0 || fieldValue.byteLength === 0)) {
|
|
50
50
|
var headers = {};
|
|
51
|
-
|
|
51
|
+
// For 'body' field, we can't send it as a header (reserved name, gets stripped).
|
|
52
|
+
// Use inline-body-key to signal that body was present but empty.
|
|
53
|
+
// The modOut function will reconstruct body: <<>> when it sees inline-body-key: body
|
|
54
|
+
// with no actual body content.
|
|
55
|
+
if (fieldName.toLowerCase() === "body") {
|
|
56
|
+
headers["inline-body-key"] = "body";
|
|
57
|
+
return {
|
|
58
|
+
headers: headers,
|
|
59
|
+
body: undefined
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// Include the key with empty value - empty string becomes empty binary in Erlang
|
|
63
|
+
headers[fieldName.toLowerCase()] = "";
|
|
52
64
|
return {
|
|
53
65
|
headers: headers,
|
|
54
66
|
body: undefined
|
|
@@ -261,14 +273,17 @@ function processHeaderFields(obj, bodyKeys, headers, headerTypes) {
|
|
|
261
273
|
headerTypes.push("".concat(key.toLowerCase(), "=\"").concat(Number.isInteger(value) ? "integer" : "float", "\""));
|
|
262
274
|
} else if (typeof value === "string") {
|
|
263
275
|
if (value.length === 0) {
|
|
264
|
-
|
|
276
|
+
// Empty string becomes empty binary in Erlang - no ao-types needed
|
|
277
|
+
headers[key] = "";
|
|
265
278
|
} else if ((0, _encodeUtils.hasNonAscii)(value)) {
|
|
266
279
|
return 1; // continue
|
|
267
280
|
} else {
|
|
268
281
|
headers[key] = value;
|
|
269
282
|
}
|
|
270
283
|
} else if (Array.isArray(value) && value.length === 0) {
|
|
271
|
-
|
|
284
|
+
// Empty array - use list type annotation
|
|
285
|
+
headers[key] = "";
|
|
286
|
+
headerTypes.push("".concat(key.toLowerCase(), "=\"list\""));
|
|
272
287
|
} else if (Array.isArray(value) && !value.some(function (item) {
|
|
273
288
|
return (0, _encodeUtils.isPojo)(item);
|
|
274
289
|
})) {
|
|
@@ -283,9 +298,18 @@ function processHeaderFields(obj, bodyKeys, headers, headerTypes) {
|
|
|
283
298
|
headerTypes.push("".concat(key.toLowerCase(), "=\"list\""));
|
|
284
299
|
}
|
|
285
300
|
} else if ((0, _encodeUtils.isBytes)(value) && (value.length === 0 || value.byteLength === 0)) {
|
|
286
|
-
|
|
301
|
+
// Empty buffer becomes empty binary in Erlang - no ao-types needed
|
|
302
|
+
// For 'body' field, we can't send it as a header (reserved name, gets stripped).
|
|
303
|
+
// Use inline-body-key to signal that body was present but empty.
|
|
304
|
+
if (key.toLowerCase() === "body") {
|
|
305
|
+
headers["inline-body-key"] = "body";
|
|
306
|
+
} else {
|
|
307
|
+
headers[key] = "";
|
|
308
|
+
}
|
|
287
309
|
} else if ((0, _encodeUtils.isPojo)(value) && Object.keys(value).length === 0) {
|
|
288
|
-
|
|
310
|
+
// Empty object - use map type annotation
|
|
311
|
+
headers[key] = "";
|
|
312
|
+
headerTypes.push("".concat(key.toLowerCase(), "=\"map\""));
|
|
289
313
|
}
|
|
290
314
|
} else {
|
|
291
315
|
// Fields that need body still get type annotations
|
|
@@ -354,18 +378,31 @@ function handleSingleBodyKeyOptimization(_x5, _x6, _x7, _x8) {
|
|
|
354
378
|
} // Step 11: Sort body keys
|
|
355
379
|
function _handleSingleBodyKeyOptimization() {
|
|
356
380
|
_handleSingleBodyKeyOptimization = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(obj, bodyKeys, headers, headerTypes) {
|
|
357
|
-
var singleKey, value, contentToHash, bodyContent, bodyBuffer, encoder, encoded, contentDigest, base64;
|
|
381
|
+
var otherFields, singleKey, value, contentToHash, bodyContent, bodyBuffer, encoder, encoded, contentDigest, base64;
|
|
358
382
|
return _regenerator().w(function (_context5) {
|
|
359
383
|
while (1) switch (_context5.n) {
|
|
360
384
|
case 0:
|
|
385
|
+
// Skip this optimization if there are other header fields - need full multipart encoding
|
|
386
|
+
// to be compatible with HyperBEAM scheduler endpoint
|
|
387
|
+
otherFields = Object.keys(obj).filter(function (k) {
|
|
388
|
+
return !bodyKeys.includes(k) && !bodyKeys.some(function (bk) {
|
|
389
|
+
return bk.startsWith("".concat(k, "/"));
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
if (!(otherFields.length > 0 && bodyKeys.length === 1)) {
|
|
393
|
+
_context5.n = 1;
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
return _context5.a(2, null);
|
|
397
|
+
case 1:
|
|
361
398
|
if (!(bodyKeys.length === 1)) {
|
|
362
|
-
_context5.n =
|
|
399
|
+
_context5.n = 3;
|
|
363
400
|
break;
|
|
364
401
|
}
|
|
365
402
|
singleKey = bodyKeys[0];
|
|
366
403
|
value = (0, _encodeUtils.getValueByPath)(obj, singleKey); // Apply optimization for binary data OR strings with newlines
|
|
367
404
|
if (!((0, _encodeUtils.isBytes)(value) && value.length > 0 || typeof value === "string" && value.includes("\n"))) {
|
|
368
|
-
_context5.n =
|
|
405
|
+
_context5.n = 3;
|
|
369
406
|
break;
|
|
370
407
|
}
|
|
371
408
|
bodyContent = value;
|
|
@@ -379,9 +416,9 @@ function _handleSingleBodyKeyOptimization() {
|
|
|
379
416
|
contentToHash = encoded.buffer;
|
|
380
417
|
bodyContent = value;
|
|
381
418
|
}
|
|
382
|
-
_context5.n =
|
|
419
|
+
_context5.n = 2;
|
|
383
420
|
return (0, _encodeUtils.sha256)(contentToHash);
|
|
384
|
-
case
|
|
421
|
+
case 2:
|
|
385
422
|
contentDigest = _context5.v;
|
|
386
423
|
base64 = _base64url["default"].toBase64(_base64url["default"].encode(contentDigest));
|
|
387
424
|
headers["content-digest"] = "sha-256=:".concat(base64, ":");
|
|
@@ -395,7 +432,7 @@ function _handleSingleBodyKeyOptimization() {
|
|
|
395
432
|
headers: headers,
|
|
396
433
|
body: bodyContent
|
|
397
434
|
});
|
|
398
|
-
case
|
|
435
|
+
case 3:
|
|
399
436
|
return _context5.a(2, null);
|
|
400
437
|
}
|
|
401
438
|
}, _callee5);
|
|
@@ -520,14 +557,16 @@ function processArrayItems(value, indicesWithOwnParts, hasNestedObjectParts, pat
|
|
|
520
557
|
return;
|
|
521
558
|
}
|
|
522
559
|
if (typeof item === "string" && item === "") {
|
|
523
|
-
|
|
560
|
+
// Empty strings become empty binaries naturally - no type annotation needed
|
|
524
561
|
} else if ((0, _encodeUtils.isPojo)(item) && Object.keys(item).length === 0) {
|
|
525
|
-
|
|
562
|
+
// Use "map" instead of "empty-message" for structured codec compatibility
|
|
563
|
+
partTypes.push("".concat(index, "=\"map\""));
|
|
526
564
|
} else if ((0, _encodeUtils.isPojo)(item)) {
|
|
527
565
|
// Non-empty objects are handled elsewhere
|
|
528
566
|
} else if (Array.isArray(item)) {
|
|
529
567
|
if (item.length === 0) {
|
|
530
|
-
|
|
568
|
+
// Use "list" instead of "empty-list" for structured codec compatibility
|
|
569
|
+
partTypes.push("".concat(index, "=\"list\""));
|
|
531
570
|
} else {
|
|
532
571
|
partTypes.push("".concat(index, "=\"list\""));
|
|
533
572
|
var encodedItems = item.map(function (subItem) {
|
|
@@ -591,7 +630,7 @@ function processArrayItems(value, indicesWithOwnParts, hasNestedObjectParts, pat
|
|
|
591
630
|
} else if ((0, _encodeUtils.isBytes)(item)) {
|
|
592
631
|
var buffer = (0, _encodeUtils.toBuffer)(item);
|
|
593
632
|
if (buffer.length === 0) {
|
|
594
|
-
|
|
633
|
+
// Empty buffers become empty binaries naturally - no type annotation needed
|
|
595
634
|
} else {
|
|
596
635
|
partTypes.push("".concat(index, "=\"binary\""));
|
|
597
636
|
}
|
|
@@ -681,12 +720,13 @@ function processObjectFields(value, bodyKey, sortedBodyKeys) {
|
|
|
681
720
|
var arrayTypes = [];
|
|
682
721
|
|
|
683
722
|
// First collect array types
|
|
723
|
+
// Note: Use "list" for both empty and non-empty arrays for structured codec compatibility
|
|
684
724
|
for (var _i3 = 0, _Object$entries3 = Object.entries(value); _i3 < _Object$entries3.length; _i3++) {
|
|
685
725
|
var _Object$entries3$_i = _slicedToArray(_Object$entries3[_i3], 2),
|
|
686
726
|
k = _Object$entries3$_i[0],
|
|
687
727
|
v = _Object$entries3$_i[1];
|
|
688
728
|
if (Array.isArray(v)) {
|
|
689
|
-
arrayTypes.push("".concat(k.toLowerCase(), "=\"
|
|
729
|
+
arrayTypes.push("".concat(k.toLowerCase(), "=\"list\""));
|
|
690
730
|
}
|
|
691
731
|
}
|
|
692
732
|
|
|
@@ -716,11 +756,12 @@ function processObjectFields(value, bodyKey, sortedBodyKeys) {
|
|
|
716
756
|
} else if (typeof _v === "number") {
|
|
717
757
|
objectTypes.push("".concat(_k.toLowerCase(), "=\"").concat(Number.isInteger(_v) ? "integer" : "float", "\""));
|
|
718
758
|
} else if (typeof _v === "string" && _v.length === 0) {
|
|
719
|
-
|
|
759
|
+
// Empty strings become empty binaries naturally - no type annotation needed
|
|
720
760
|
} else if ((0, _encodeUtils.isBytes)(_v) && (_v.length === 0 || _v.byteLength === 0)) {
|
|
721
|
-
|
|
761
|
+
// Empty buffers become empty binaries naturally - no type annotation needed
|
|
722
762
|
} else if ((0, _encodeUtils.isPojo)(_v) && Object.keys(_v).length === 0) {
|
|
723
|
-
|
|
763
|
+
// Use "map" instead of "empty-message" for structured codec compatibility
|
|
764
|
+
objectTypes.push("".concat(_k.toLowerCase(), "=\"map\""));
|
|
724
765
|
}
|
|
725
766
|
if (typeof _v === "string") {
|
|
726
767
|
if (_v.length === 0) {
|
|
@@ -745,6 +786,12 @@ function processObjectFields(value, bodyKey, sortedBodyKeys) {
|
|
|
745
786
|
key: _k,
|
|
746
787
|
buffer: buffer
|
|
747
788
|
});
|
|
789
|
+
} else if (Array.isArray(_v) && _v.length === 0) {
|
|
790
|
+
// Empty array - include the key so Erlang knows it exists
|
|
791
|
+
fieldLines.push("".concat(_k, ": "));
|
|
792
|
+
} else if ((0, _encodeUtils.isPojo)(_v) && Object.keys(_v).length === 0) {
|
|
793
|
+
// Empty object - include the key so Erlang knows it exists
|
|
794
|
+
fieldLines.push("".concat(_k, ": "));
|
|
748
795
|
} else if (Array.isArray(_v) && _v.length > 0) {
|
|
749
796
|
var _childPath = "".concat(bodyKey, "/").concat(_k);
|
|
750
797
|
if (!sortedBodyKeys.includes(_childPath)) {
|
package/cjs/erl_json.js
CHANGED
|
@@ -8,10 +8,14 @@ exports.erl_json_to = erl_json_to;
|
|
|
8
8
|
exports.normalize = normalize;
|
|
9
9
|
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
10
10
|
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
11
|
-
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
12
|
-
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
13
11
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
14
12
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
13
|
+
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
|
|
14
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
15
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
16
|
+
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
|
|
17
|
+
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
|
|
18
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
15
19
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
16
20
|
/**
|
|
17
21
|
* Codec for converting between JS objects with Erlang types and annotated JSON
|
|
@@ -76,7 +80,12 @@ function normalize(obj) {
|
|
|
76
80
|
}
|
|
77
81
|
if (binaryMode) {
|
|
78
82
|
// In binary mode, convert strings to buffers
|
|
79
|
-
|
|
83
|
+
// Use binary/latin1 encoding only if all chars are <= 255 (preserves raw bytes)
|
|
84
|
+
// Use UTF-8 for strings with chars > 255 (proper multi-byte encoding)
|
|
85
|
+
var needsUtf8 = _toConsumableArray(obj).some(function (ch) {
|
|
86
|
+
return ch.codePointAt(0) > 255;
|
|
87
|
+
});
|
|
88
|
+
return Buffer.from(obj, needsUtf8 ? "utf8" : "binary");
|
|
80
89
|
} else {
|
|
81
90
|
// In string mode, strings stay as strings
|
|
82
91
|
return obj;
|
package/cjs/erl_str.js
CHANGED
|
@@ -38,6 +38,11 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
|
|
|
38
38
|
*/
|
|
39
39
|
function erl_str_from(str) {
|
|
40
40
|
var binaryMode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
41
|
+
// Handle null/undefined input
|
|
42
|
+
if (str === null || str === undefined) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
41
46
|
// Handle the new response format
|
|
42
47
|
if (str.startsWith("#erl_response{")) {
|
|
43
48
|
var rawMatch = str.match(/#erl_response\{raw=([^]*?),formatted=([^]*?)\}$/);
|