hbsig 0.2.8 → 0.3.1
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 +11 -7
- package/cjs/signer-utils.js +14 -12
- 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 -2
package/cjs/flat.js
CHANGED
|
@@ -90,6 +90,60 @@ function pathToParts(path) {
|
|
|
90
90
|
throw new Error("Path must be a string or array");
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Convert a value to string format to match Erlang codec behavior
|
|
95
|
+
* Erlang dev_codec_flat only handles binaries, so all leaf values become strings
|
|
96
|
+
* @param {*} value - Value to convert
|
|
97
|
+
* @returns {string|Object} - String for leaf values, or recursively processed object
|
|
98
|
+
*/
|
|
99
|
+
function valueToString(value) {
|
|
100
|
+
if (_typeof(value) === "object" && value !== null && !Array.isArray(value) && !Buffer.isBuffer(value)) {
|
|
101
|
+
// Recursively process nested objects
|
|
102
|
+
var result = {};
|
|
103
|
+
for (var _i2 = 0, _Object$entries2 = Object.entries(value); _i2 < _Object$entries2.length; _i2++) {
|
|
104
|
+
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
|
|
105
|
+
k = _Object$entries2$_i[0],
|
|
106
|
+
v = _Object$entries2$_i[1];
|
|
107
|
+
result[k] = valueToString(v);
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
if (typeof value === "string") {
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
if (typeof value === "number") {
|
|
115
|
+
return String(value);
|
|
116
|
+
}
|
|
117
|
+
if (typeof value === "boolean") {
|
|
118
|
+
return String(value);
|
|
119
|
+
}
|
|
120
|
+
if (value === null) {
|
|
121
|
+
return "null";
|
|
122
|
+
}
|
|
123
|
+
if (Array.isArray(value)) {
|
|
124
|
+
// Convert array elements to strings first, then format as Erlang list
|
|
125
|
+
// Erlang's io_lib:format("~p", [List]) produces binary syntax like [<<"a">>,<<"b">>]
|
|
126
|
+
var elements = value.map(function (v) {
|
|
127
|
+
if (typeof v === "string") {
|
|
128
|
+
return "<<\"".concat(v, "\">>");
|
|
129
|
+
} else if (typeof v === "number") {
|
|
130
|
+
return String(v);
|
|
131
|
+
} else if (typeof v === "boolean") {
|
|
132
|
+
return v ? "true" : "false";
|
|
133
|
+
} else if (v === null) {
|
|
134
|
+
return "null";
|
|
135
|
+
} else {
|
|
136
|
+
return String(v);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return "[".concat(elements.join(","), "]");
|
|
140
|
+
}
|
|
141
|
+
if (Buffer.isBuffer(value)) {
|
|
142
|
+
return value.toString();
|
|
143
|
+
}
|
|
144
|
+
return String(value);
|
|
145
|
+
}
|
|
146
|
+
|
|
93
147
|
/**
|
|
94
148
|
* Helper function to inject a value at a specific path in a nested object
|
|
95
149
|
* @param {Array} pathParts - Array of path parts
|
|
@@ -100,20 +154,23 @@ function injectAtPath(pathParts, value, obj) {
|
|
|
100
154
|
if (pathParts.length === 0) {
|
|
101
155
|
throw new Error("Path cannot be empty");
|
|
102
156
|
}
|
|
157
|
+
|
|
158
|
+
// Convert value to match Erlang codec behavior
|
|
159
|
+
var convertedValue = valueToString(value);
|
|
103
160
|
if (pathParts.length === 1) {
|
|
104
161
|
var _key = pathParts[0];
|
|
105
162
|
if (_key in obj) {
|
|
106
163
|
var existing = obj[_key];
|
|
107
164
|
|
|
108
165
|
// If both are objects, merge them
|
|
109
|
-
if (_typeof(existing) === "object" && existing !== null && _typeof(
|
|
110
|
-
obj[_key] = _objectSpread(_objectSpread({}, existing),
|
|
166
|
+
if (_typeof(existing) === "object" && existing !== null && _typeof(convertedValue) === "object" && convertedValue !== null && !Array.isArray(existing) && !Array.isArray(convertedValue)) {
|
|
167
|
+
obj[_key] = _objectSpread(_objectSpread({}, existing), convertedValue);
|
|
111
168
|
} else {
|
|
112
169
|
// Path collision
|
|
113
|
-
throw new Error("Path collision at key: ".concat(_key, ", existing: ").concat(JSON.stringify(existing), ", value: ").concat(JSON.stringify(
|
|
170
|
+
throw new Error("Path collision at key: ".concat(_key, ", existing: ").concat(JSON.stringify(existing), ", value: ").concat(JSON.stringify(convertedValue)));
|
|
114
171
|
}
|
|
115
172
|
} else {
|
|
116
|
-
obj[_key] =
|
|
173
|
+
obj[_key] = convertedValue;
|
|
117
174
|
}
|
|
118
175
|
return;
|
|
119
176
|
}
|
|
@@ -125,7 +182,7 @@ function injectAtPath(pathParts, value, obj) {
|
|
|
125
182
|
} else if (_typeof(obj[key]) !== "object" || obj[key] === null) {
|
|
126
183
|
throw new Error("Cannot create nested path at non-object key: ".concat(key));
|
|
127
184
|
}
|
|
128
|
-
injectAtPath(rest,
|
|
185
|
+
injectAtPath(rest, convertedValue, obj[key]);
|
|
129
186
|
}
|
|
130
187
|
|
|
131
188
|
/**
|
|
@@ -137,10 +194,10 @@ function injectAtPath(pathParts, value, obj) {
|
|
|
137
194
|
function flattenRecursive(value, currentPath, result) {
|
|
138
195
|
if (_typeof(value) === "object" && value !== null && !Array.isArray(value)) {
|
|
139
196
|
// It's an object, recurse into it
|
|
140
|
-
for (var
|
|
141
|
-
var _Object$
|
|
142
|
-
key = _Object$
|
|
143
|
-
subValue = _Object$
|
|
197
|
+
for (var _i3 = 0, _Object$entries3 = Object.entries(value); _i3 < _Object$entries3.length; _i3++) {
|
|
198
|
+
var _Object$entries3$_i = _slicedToArray(_Object$entries3[_i3], 2),
|
|
199
|
+
key = _Object$entries3$_i[0],
|
|
200
|
+
subValue = _Object$entries3$_i[1];
|
|
144
201
|
var newPath = [].concat(_toConsumableArray(currentPath), [key]);
|
|
145
202
|
flattenRecursive(subValue, newPath, result);
|
|
146
203
|
}
|
|
@@ -166,10 +223,10 @@ function serialize(map) {
|
|
|
166
223
|
try {
|
|
167
224
|
var flattened = flat_to(map);
|
|
168
225
|
var lines = [];
|
|
169
|
-
for (var
|
|
170
|
-
var _Object$
|
|
171
|
-
key = _Object$
|
|
172
|
-
value = _Object$
|
|
226
|
+
for (var _i4 = 0, _Object$entries4 = Object.entries(flattened); _i4 < _Object$entries4.length; _i4++) {
|
|
227
|
+
var _Object$entries4$_i = _slicedToArray(_Object$entries4[_i4], 2),
|
|
228
|
+
key = _Object$entries4$_i[0],
|
|
229
|
+
value = _Object$entries4$_i[1];
|
|
173
230
|
lines.push("".concat(key, ": ").concat(value));
|
|
174
231
|
}
|
|
175
232
|
return {
|
package/cjs/httpsig.js
CHANGED
|
@@ -9,10 +9,7 @@ exports.structured_from = structured_from;
|
|
|
9
9
|
exports.structured_to = structured_to;
|
|
10
10
|
var _fastSha = require("fast-sha256");
|
|
11
11
|
var _flat = require("./flat.js");
|
|
12
|
-
|
|
13
|
-
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."); }
|
|
14
|
-
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
|
|
15
|
-
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
|
|
12
|
+
var _structured = require("./structured.js");
|
|
16
13
|
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); }
|
|
17
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; }
|
|
18
15
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -143,18 +140,19 @@ function boundaryFromParts(parts) {
|
|
|
143
140
|
return bytesToBase64url(hashBytes);
|
|
144
141
|
}
|
|
145
142
|
|
|
146
|
-
// Helper to determine inline key
|
|
143
|
+
// Helper to determine inline key - matches Erlang's inline_key/2
|
|
147
144
|
function inlineKey(msg) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
145
|
+
// Check for ao-body-key (Erlang uses ao-body-key, not inline-body-key)
|
|
146
|
+
var aoBodyKey = msg["ao-body-key"];
|
|
147
|
+
if (aoBodyKey) {
|
|
148
|
+
return [{}, aoBodyKey];
|
|
151
149
|
}
|
|
152
150
|
if ("body" in msg) {
|
|
153
151
|
return [{}, "body"];
|
|
154
152
|
}
|
|
155
153
|
if ("data" in msg) {
|
|
156
154
|
return [{
|
|
157
|
-
"
|
|
155
|
+
"ao-body-key": "data"
|
|
158
156
|
}, "data"];
|
|
159
157
|
}
|
|
160
158
|
return [{}, "body"];
|
|
@@ -213,6 +211,13 @@ function ungroupIds(msg) {
|
|
|
213
211
|
return result;
|
|
214
212
|
}
|
|
215
213
|
|
|
214
|
+
// Get the size of a map (matches Erlang's maps:size behavior)
|
|
215
|
+
// This counts ALL keys including ao-types - empty means literally {}
|
|
216
|
+
function mapSize(obj) {
|
|
217
|
+
if (_typeof(obj) !== "object" || obj === null) return 0;
|
|
218
|
+
return Object.keys(obj).length;
|
|
219
|
+
}
|
|
220
|
+
|
|
216
221
|
// Group maps for body encoding - following Erlang logic exactly
|
|
217
222
|
function groupMaps(map) {
|
|
218
223
|
var parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
|
|
@@ -242,8 +247,19 @@ function groupMaps(map) {
|
|
|
242
247
|
var normKey = normalizeKey(key);
|
|
243
248
|
var flatK = parent ? "".concat(parent, "/").concat(normKey) : normKey;
|
|
244
249
|
if (_typeof(value) === "object" && value !== null && !Array.isArray(value) && !Buffer.isBuffer(value)) {
|
|
245
|
-
//
|
|
246
|
-
|
|
250
|
+
// Check size of the nested object (including metadata keys like ao-types)
|
|
251
|
+
// Empty means literally {} - a map with only ao-types is NOT empty
|
|
252
|
+
var size = mapSize(value);
|
|
253
|
+
if (size === 0) {
|
|
254
|
+
// Empty map (no data keys) - add empty-message marker
|
|
255
|
+
// This matches Erlang's group_maps behavior for empty maps
|
|
256
|
+
newTop[flatK] = {
|
|
257
|
+
"ao-types": "empty-message"
|
|
258
|
+
};
|
|
259
|
+
} else {
|
|
260
|
+
// Recursively process nested objects
|
|
261
|
+
newTop = groupMaps(value, flatK, newTop);
|
|
262
|
+
}
|
|
247
263
|
} else if (typeof value === "string" && value.length > MAX_HEADER_LENGTH) {
|
|
248
264
|
// Value too large for header, lift to top level
|
|
249
265
|
newTop[flatK] = value;
|
|
@@ -271,124 +287,74 @@ function groupMaps(map) {
|
|
|
271
287
|
}
|
|
272
288
|
}
|
|
273
289
|
|
|
290
|
+
// Helper to compute content-digest for a body value
|
|
291
|
+
function computePartDigest(bodyValue) {
|
|
292
|
+
var bodyBytes;
|
|
293
|
+
if (Buffer.isBuffer(bodyValue)) {
|
|
294
|
+
bodyBytes = new Uint8Array(bodyValue);
|
|
295
|
+
} else if (typeof bodyValue === "string") {
|
|
296
|
+
bodyBytes = stringToBytes(bodyValue, "binary");
|
|
297
|
+
} else {
|
|
298
|
+
bodyBytes = stringToBytes(String(bodyValue), "binary");
|
|
299
|
+
}
|
|
300
|
+
var hashBytes = (0, _fastSha.hash)(bodyBytes);
|
|
301
|
+
return "sha-256=:".concat(bytesToBase64(hashBytes), ":");
|
|
302
|
+
}
|
|
303
|
+
|
|
274
304
|
// Encode multipart body part
|
|
305
|
+
// NOTE: This matches Erlang's encode_body_part/4 which does NOT apply inline_key
|
|
306
|
+
// logic to nested parts. For nested maps, ALL fields become headers (except 'body'
|
|
307
|
+
// which becomes the part body). The inline_key logic is only for top-level messages.
|
|
275
308
|
function encodeBodyPart(partName, bodyPart, inlineKey) {
|
|
276
309
|
var disposition = partName === inlineKey ? "inline" : "form-data;name=\"".concat(partName, "\"");
|
|
277
|
-
var isInline = partName === inlineKey;
|
|
278
310
|
if (_typeof(bodyPart) === "object" && bodyPart !== null && !Array.isArray(bodyPart) && !Buffer.isBuffer(bodyPart)) {
|
|
279
|
-
//
|
|
280
|
-
var
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
// Keep ao-types as-is (Buffer or string)
|
|
293
|
-
var valueStr = value;
|
|
294
|
-
if (Buffer.isBuffer(value)) {
|
|
295
|
-
valueStr = value.toString("binary");
|
|
296
|
-
}
|
|
297
|
-
allEntries.push({
|
|
298
|
-
key: "ao-types",
|
|
299
|
-
line: "ao-types: ".concat(valueStr)
|
|
300
|
-
});
|
|
301
|
-
} else {
|
|
302
|
-
// Handle Buffer values properly
|
|
303
|
-
var _valueStr = value;
|
|
304
|
-
if (Buffer.isBuffer(value)) {
|
|
305
|
-
// Use binary/latin1 encoding to preserve all byte values 0-255
|
|
306
|
-
_valueStr = value.toString("binary");
|
|
307
|
-
}
|
|
308
|
-
allEntries.push({
|
|
309
|
-
key: key,
|
|
310
|
-
line: "".concat(key, ": ").concat(_valueStr)
|
|
311
|
-
});
|
|
312
|
-
}
|
|
311
|
+
// Collect all headers (everything except 'body' and 'priv')
|
|
312
|
+
var allEntries = [];
|
|
313
|
+
for (var _i6 = 0, _Object$entries6 = Object.entries(bodyPart); _i6 < _Object$entries6.length; _i6++) {
|
|
314
|
+
var _Object$entries6$_i = _slicedToArray(_Object$entries6[_i6], 2),
|
|
315
|
+
key = _Object$entries6$_i[0],
|
|
316
|
+
value = _Object$entries6$_i[1];
|
|
317
|
+
if (key === "body" || key === "priv") continue;
|
|
318
|
+
|
|
319
|
+
// Handle Buffer values properly
|
|
320
|
+
var valueStr = value;
|
|
321
|
+
if (Buffer.isBuffer(value)) {
|
|
322
|
+
// Use binary/latin1 encoding to preserve all byte values 0-255
|
|
323
|
+
valueStr = value.toString("binary");
|
|
313
324
|
}
|
|
314
|
-
|
|
315
|
-
// Add content-disposition
|
|
316
325
|
allEntries.push({
|
|
317
|
-
key:
|
|
318
|
-
line: "
|
|
326
|
+
key: key,
|
|
327
|
+
line: "".concat(key, ": ").concat(valueStr)
|
|
319
328
|
});
|
|
329
|
+
}
|
|
320
330
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
331
|
+
// Add content-disposition
|
|
332
|
+
allEntries.push({
|
|
333
|
+
key: "content-disposition",
|
|
334
|
+
line: "content-disposition: ".concat(disposition)
|
|
335
|
+
});
|
|
325
336
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
337
|
+
// Sort all entries by key alphabetically - matches Erlang behavior
|
|
338
|
+
allEntries.sort(function (a, b) {
|
|
339
|
+
return a.key.localeCompare(b.key);
|
|
340
|
+
});
|
|
330
341
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
lines.push(body);
|
|
336
|
-
}
|
|
337
|
-
return lines.join(CRLF);
|
|
338
|
-
} else {
|
|
339
|
-
// For parts WITHOUT ao-types
|
|
340
|
-
var _allEntries = [];
|
|
341
|
-
for (var _i7 = 0, _Object$entries7 = Object.entries(bodyPart); _i7 < _Object$entries7.length; _i7++) {
|
|
342
|
-
var _Object$entries7$_i = _slicedToArray(_Object$entries7[_i7], 2),
|
|
343
|
-
_key = _Object$entries7$_i[0],
|
|
344
|
-
_value = _Object$entries7$_i[1];
|
|
345
|
-
if (_key === "body") continue;
|
|
346
|
-
// Handle Buffer values properly
|
|
347
|
-
var _valueStr2 = _value;
|
|
348
|
-
if (Buffer.isBuffer(_value)) {
|
|
349
|
-
// Use binary/latin1 encoding to preserve all byte values 0-255
|
|
350
|
-
_valueStr2 = _value.toString("binary");
|
|
351
|
-
}
|
|
352
|
-
_allEntries.push({
|
|
353
|
-
key: _key,
|
|
354
|
-
line: "".concat(_key, ": ").concat(_valueStr2)
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
var _lines = [];
|
|
358
|
-
if (isInline) {
|
|
359
|
-
// Inline parts without ao-types: sort ALL fields alphabetically including content-disposition
|
|
360
|
-
_allEntries.push({
|
|
361
|
-
key: "content-disposition",
|
|
362
|
-
line: "content-disposition: ".concat(disposition)
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
// Sort by key
|
|
366
|
-
_allEntries.sort(function (a, b) {
|
|
367
|
-
return a.key.localeCompare(b.key);
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
// Extract the lines
|
|
371
|
-
_lines.push.apply(_lines, _toConsumableArray(_allEntries.map(function (entry) {
|
|
372
|
-
return entry.line;
|
|
373
|
-
})));
|
|
374
|
-
} else {
|
|
375
|
-
// Regular parts: content-disposition first, then fields
|
|
376
|
-
_lines.push("content-disposition: ".concat(disposition));
|
|
377
|
-
_lines.push.apply(_lines, _toConsumableArray(_allEntries.map(function (entry) {
|
|
378
|
-
return entry.line;
|
|
379
|
-
})));
|
|
380
|
-
}
|
|
342
|
+
// Build the lines
|
|
343
|
+
var lines = allEntries.map(function (entry) {
|
|
344
|
+
return entry.line;
|
|
345
|
+
});
|
|
381
346
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
return _lines.join(CRLF);
|
|
347
|
+
// Only the 'body' field (if present) becomes the part body
|
|
348
|
+
var body = bodyPart.body;
|
|
349
|
+
if (body !== "" && body !== undefined && body !== null) {
|
|
350
|
+
lines.push(""); // Always add empty line before body
|
|
351
|
+
lines.push(Buffer.isBuffer(body) ? body.toString("binary") : String(body));
|
|
389
352
|
}
|
|
353
|
+
return lines.join(CRLF);
|
|
390
354
|
} else if (typeof bodyPart === "string" || Buffer.isBuffer(bodyPart)) {
|
|
391
|
-
|
|
355
|
+
// Use binary/latin1 encoding to preserve byte values 0-255
|
|
356
|
+
var bodyStr = Buffer.isBuffer(bodyPart) ? bodyPart.toString("binary") : bodyPart;
|
|
357
|
+
return "content-disposition: ".concat(disposition).concat(DOUBLE_CRLF).concat(bodyStr);
|
|
392
358
|
}
|
|
393
359
|
return "";
|
|
394
360
|
}
|
|
@@ -406,8 +372,8 @@ function isBinaryData(buf) {
|
|
|
406
372
|
// Count non-text bytes
|
|
407
373
|
var controlCount = 0;
|
|
408
374
|
var highByteCount = 0;
|
|
409
|
-
for (var
|
|
410
|
-
var _byte = buf[
|
|
375
|
+
for (var _i7 = 0; _i7 < checkLength; _i7++) {
|
|
376
|
+
var _byte = buf[_i7];
|
|
411
377
|
// Non-printable chars (except CR/LF/TAB)
|
|
412
378
|
if (_byte < 32 && _byte !== 9 && _byte !== 10 && _byte !== 13) {
|
|
413
379
|
controlCount++;
|
|
@@ -561,11 +527,11 @@ function parseMultipart(contentType, body) {
|
|
|
561
527
|
delete restHeaders["content-disposition"];
|
|
562
528
|
|
|
563
529
|
// Add each header from the inline part to the top level of result
|
|
564
|
-
for (var
|
|
565
|
-
var _Object$
|
|
566
|
-
key = _Object$
|
|
567
|
-
|
|
568
|
-
result[key] =
|
|
530
|
+
for (var _i8 = 0, _Object$entries7 = Object.entries(restHeaders); _i8 < _Object$entries7.length; _i8++) {
|
|
531
|
+
var _Object$entries7$_i = _slicedToArray(_Object$entries7[_i8], 2),
|
|
532
|
+
key = _Object$entries7$_i[0],
|
|
533
|
+
_value = _Object$entries7$_i[1];
|
|
534
|
+
result[key] = _value;
|
|
569
535
|
}
|
|
570
536
|
|
|
571
537
|
// If there's body content in the inline part, add it as 'body'
|
|
@@ -670,10 +636,10 @@ function httpsig_from(http) {
|
|
|
670
636
|
// Convert flat structure to nested using flat.js
|
|
671
637
|
var flat = {};
|
|
672
638
|
var nonFlat = {};
|
|
673
|
-
for (var
|
|
674
|
-
var _Object$
|
|
675
|
-
key = _Object$
|
|
676
|
-
value = _Object$
|
|
639
|
+
for (var _i9 = 0, _Object$entries8 = Object.entries(withBodyKeys); _i9 < _Object$entries8.length; _i9++) {
|
|
640
|
+
var _Object$entries8$_i = _slicedToArray(_Object$entries8[_i9], 2),
|
|
641
|
+
key = _Object$entries8$_i[0],
|
|
642
|
+
value = _Object$entries8$_i[1];
|
|
677
643
|
if (key.includes("/")) {
|
|
678
644
|
flat[key] = value;
|
|
679
645
|
} else {
|
|
@@ -706,10 +672,10 @@ function httpsig_from(http) {
|
|
|
706
672
|
delete result["content-digest"];
|
|
707
673
|
|
|
708
674
|
// Extract hashpaths if any
|
|
709
|
-
for (var
|
|
710
|
-
var
|
|
711
|
-
if (
|
|
712
|
-
delete result[
|
|
675
|
+
for (var _i0 = 0, _Object$keys = Object.keys(result); _i0 < _Object$keys.length; _i0++) {
|
|
676
|
+
var _key = _Object$keys[_i0];
|
|
677
|
+
if (_key.startsWith("hashpath")) {
|
|
678
|
+
delete result[_key];
|
|
713
679
|
}
|
|
714
680
|
}
|
|
715
681
|
return result;
|
|
@@ -717,12 +683,20 @@ function httpsig_from(http) {
|
|
|
717
683
|
|
|
718
684
|
/**
|
|
719
685
|
* Convert TABM to HTTP message
|
|
686
|
+
* Implements bundle mode like Erlang's dev_codec_httpsig_conv:to/3 with bundle=true
|
|
720
687
|
*/
|
|
721
688
|
function httpsig_to(tabm) {
|
|
722
689
|
if (typeof tabm === "string") return tabm;
|
|
723
690
|
|
|
691
|
+
// Bundle logic: TABM → structured → TABM
|
|
692
|
+
// This matches Erlang's behavior when bundle=true:
|
|
693
|
+
// 1. Convert TABM to structured@1.0 (interprets ao-types, decodes to native types)
|
|
694
|
+
// 2. Convert back to TABM (re-encodes with ao-types)
|
|
695
|
+
var structured = (0, _structured.structured_to)(tabm);
|
|
696
|
+
var bundledTabm = (0, _structured.structured_from)(structured);
|
|
697
|
+
|
|
724
698
|
// Group IDs
|
|
725
|
-
var withGroupedIds = groupIds(
|
|
699
|
+
var withGroupedIds = groupIds(bundledTabm);
|
|
726
700
|
|
|
727
701
|
// Remove private and signature-related keys
|
|
728
702
|
var stripped = _objectSpread({}, withGroupedIds);
|
|
@@ -736,24 +710,25 @@ function httpsig_to(tabm) {
|
|
|
736
710
|
inlineKeyVal = _inlineKey4[1];
|
|
737
711
|
|
|
738
712
|
// Check if this is a flat structure that should stay as headers
|
|
739
|
-
// A flat structure has no nested objects (maps)
|
|
713
|
+
// A flat structure has no nested objects (maps), excluding:
|
|
714
|
+
// - Arrays (JS arrays, not numbered maps)
|
|
715
|
+
// - Buffers
|
|
716
|
+
// Note: List-encoded maps (numbered maps with .="list") ARE nested maps
|
|
717
|
+
// and should trigger multipart encoding, matching Erlang's behavior
|
|
740
718
|
var hasNestedMaps = Object.values(stripped).some(function (value) {
|
|
741
|
-
|
|
719
|
+
// Not an object
|
|
720
|
+
if (_typeof(value) !== "object" || value === null) return false;
|
|
721
|
+
// Arrays and Buffers are not nested maps
|
|
722
|
+
if (Array.isArray(value) || Buffer.isBuffer(value)) return false;
|
|
723
|
+
// Any other object (including list-encoded maps) is a nested map
|
|
724
|
+
return true;
|
|
742
725
|
});
|
|
743
726
|
|
|
744
727
|
// If it's just a flat map with strings/primitives, keep as headers
|
|
745
728
|
// This matches Erlang's behavior where flat maps don't become multipart
|
|
746
729
|
if (!hasNestedMaps) {
|
|
747
730
|
// For flat structures, just return with normalized keys
|
|
748
|
-
|
|
749
|
-
var result = _objectSpread({}, inlineFieldHdrs);
|
|
750
|
-
for (var _i10 = 0, _Object$entries0 = Object.entries(stripped); _i10 < _Object$entries0.length; _i10++) {
|
|
751
|
-
var _Object$entries0$_i = _slicedToArray(_Object$entries0[_i10], 2),
|
|
752
|
-
key = _Object$entries0$_i[0],
|
|
753
|
-
value = _Object$entries0$_i[1];
|
|
754
|
-
// Keep Buffers as Buffers - don't convert to strings
|
|
755
|
-
result[key] = value;
|
|
756
|
-
}
|
|
731
|
+
var result = _objectSpread(_objectSpread({}, inlineFieldHdrs), stripped);
|
|
757
732
|
|
|
758
733
|
// Handle inline body key - move data from inline key to body
|
|
759
734
|
if (inlineKeyVal && inlineKeyVal !== "body" && result[inlineKeyVal]) {
|
|
@@ -761,9 +736,22 @@ function httpsig_to(tabm) {
|
|
|
761
736
|
delete result[inlineKeyVal];
|
|
762
737
|
}
|
|
763
738
|
|
|
764
|
-
// If
|
|
739
|
+
// If the only field is ao-types (no actual data), return empty object
|
|
740
|
+
// This matches Erlang's behavior where ao-types-only messages become empty
|
|
741
|
+
var dataKeys = Object.keys(result).filter(function (k) {
|
|
742
|
+
return k !== "ao-types" && k !== "ao-ids" && k !== "inline-body-key" && k !== "ao-body-key";
|
|
743
|
+
});
|
|
744
|
+
if (dataKeys.length === 0) {
|
|
745
|
+
return {};
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// If there's a non-empty body, add content-digest
|
|
749
|
+
// Erlang doesn't add content-digest for empty bodies (<<>>)
|
|
765
750
|
if (result.body) {
|
|
766
|
-
|
|
751
|
+
var bodyIsEmpty = Buffer.isBuffer(result.body) ? result.body.length === 0 : typeof result.body === "string" && result.body.length === 0;
|
|
752
|
+
if (!bodyIsEmpty) {
|
|
753
|
+
return addContentDigest(result);
|
|
754
|
+
}
|
|
767
755
|
}
|
|
768
756
|
return result;
|
|
769
757
|
}
|
|
@@ -773,26 +761,26 @@ function httpsig_to(tabm) {
|
|
|
773
761
|
var headers = _objectSpread({}, inlineFieldHdrs);
|
|
774
762
|
|
|
775
763
|
// Process each field - ao-types at top level should go to headers
|
|
776
|
-
for (var
|
|
777
|
-
var _Object$
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if (
|
|
764
|
+
for (var _i1 = 0, _Object$entries9 = Object.entries(stripped); _i1 < _Object$entries9.length; _i1++) {
|
|
765
|
+
var _Object$entries9$_i = _slicedToArray(_Object$entries9[_i1], 2),
|
|
766
|
+
key = _Object$entries9$_i[0],
|
|
767
|
+
value = _Object$entries9$_i[1];
|
|
768
|
+
if (key === "ao-types") {
|
|
781
769
|
// Top-level ao-types goes to headers only
|
|
782
770
|
// Keep as Buffer if it's a Buffer, otherwise use as-is
|
|
783
|
-
headers[
|
|
784
|
-
} else if (
|
|
785
|
-
bodyMap[
|
|
786
|
-
} else if (_typeof(
|
|
787
|
-
bodyMap[
|
|
788
|
-
} else if (typeof
|
|
789
|
-
headers[normalizeKey(
|
|
790
|
-
} else if (Buffer.isBuffer(
|
|
771
|
+
headers[key] = value;
|
|
772
|
+
} else if (key === "body" || key === inlineKeyVal) {
|
|
773
|
+
bodyMap[key === inlineKeyVal ? inlineKeyVal : "body"] = value;
|
|
774
|
+
} else if (_typeof(value) === "object" && value !== null && !Array.isArray(value) && !Buffer.isBuffer(value)) {
|
|
775
|
+
bodyMap[key] = value;
|
|
776
|
+
} else if (typeof value === "string" && value.length <= MAX_HEADER_LENGTH && key !== "ao-types") {
|
|
777
|
+
headers[normalizeKey(key)] = value;
|
|
778
|
+
} else if (Buffer.isBuffer(value) && value.length <= MAX_HEADER_LENGTH && key !== "ao-types") {
|
|
791
779
|
// Keep buffers as buffers for headers
|
|
792
|
-
headers[normalizeKey(
|
|
793
|
-
} else if (
|
|
780
|
+
headers[normalizeKey(key)] = value;
|
|
781
|
+
} else if (key !== "ao-types") {
|
|
794
782
|
// Only add to bodyMap if it's not ao-types
|
|
795
|
-
bodyMap[
|
|
783
|
+
bodyMap[key] = value;
|
|
796
784
|
}
|
|
797
785
|
}
|
|
798
786
|
|
|
@@ -821,22 +809,22 @@ function httpsig_to(tabm) {
|
|
|
821
809
|
try {
|
|
822
810
|
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
823
811
|
var _step4$value = _slicedToArray(_step4.value, 2),
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
if (_typeof(
|
|
827
|
-
var encoded = encodeBodyPart("".concat(
|
|
812
|
+
_key2 = _step4$value[0],
|
|
813
|
+
_value2 = _step4$value[1];
|
|
814
|
+
if (_typeof(_value2) === "object" && _value2 !== null && Object.keys(_value2).length === 1 && "body" in _value2) {
|
|
815
|
+
var encoded = encodeBodyPart("".concat(_key2, "/body"), _value2, "body");
|
|
828
816
|
parts.push({
|
|
829
|
-
name: "".concat(
|
|
817
|
+
name: "".concat(_key2, "/body"),
|
|
830
818
|
body: encoded
|
|
831
819
|
});
|
|
832
|
-
bodyKeysList.push(
|
|
820
|
+
bodyKeysList.push(_key2);
|
|
833
821
|
} else {
|
|
834
|
-
var _encoded = encodeBodyPart(
|
|
822
|
+
var _encoded = encodeBodyPart(_key2, _value2, inlineKeyVal);
|
|
835
823
|
parts.push({
|
|
836
|
-
name:
|
|
824
|
+
name: _key2,
|
|
837
825
|
body: _encoded
|
|
838
826
|
});
|
|
839
|
-
bodyKeysList.push(
|
|
827
|
+
bodyKeysList.push(_key2);
|
|
840
828
|
}
|
|
841
829
|
}
|
|
842
830
|
} catch (err) {
|
|
@@ -850,9 +838,7 @@ function httpsig_to(tabm) {
|
|
|
850
838
|
});
|
|
851
839
|
var finalBody = bodyParts.join(CRLF) + "".concat(CRLF, "--").concat(boundary, "--");
|
|
852
840
|
var _result2 = _objectSpread(_objectSpread({}, headers), {}, {
|
|
853
|
-
|
|
854
|
-
return "\"".concat(k, "\"");
|
|
855
|
-
}).join(", "),
|
|
841
|
+
// Note: body-keys is NOT included in httpsig output - it's only used for parsing
|
|
856
842
|
"content-type": "multipart/form-data; boundary=\"".concat(boundary, "\""),
|
|
857
843
|
body: finalBody
|
|
858
844
|
});
|